// m_tc.position() (== position()). The character below position() is not included
// if the last movement command was exclusive (MoveExclusive).
// The value of m_tc.anchor() is not used.
+//
#include "fakevimhandler.h"
#include "fakevimsyntax.h"
#include <QtCore/QProcess>
#include <QtCore/QRegExp>
#include <QtCore/QTextStream>
+#include <QtCore/QTimer>
#include <QtCore/QtAlgorithms>
#include <QtCore/QStack>
#define EDITOR(s) (m_textedit ? m_textedit->s : m_plaintextedit->s)
const int ParagraphSeparator = 0x00002029;
+typedef QLatin1String _;
using namespace Qt;
+/*! A \e Mode represents one of the basic modes of operation of FakeVim.
+*/
enum Mode
{
InsertMode,
+ ReplaceMode,
CommandMode,
ExMode,
- SearchForwardMode,
- SearchBackwardMode,
};
+/*! A \e SubMode is used for things that require one more data item
+ and are 'nested' behind a \l Mode.
+*/
enum SubMode
{
NoSubMode,
- ChangeSubMode, // used for c
- DeleteSubMode, // used for d
- FilterSubMode, // used for !
- IndentSubMode, // used for =
- RegisterSubMode, // used for "
- ReplaceSubMode, // used for R and r
- ShiftLeftSubMode, // used for <
- ShiftRightSubMode, // used for >
- TransformSubMode, // used for ~/gu/gU
- WindowSubMode, // used for Ctrl-w
- YankSubMode, // used for y
- ZSubMode, // used for z
- CapitalZSubMode // used for Z
+ ChangeSubMode, // Used for c
+ DeleteSubMode, // Used for d
+ FilterSubMode, // Used for !
+ IndentSubMode, // Used for =
+ RegisterSubMode, // Used for "
+ ShiftLeftSubMode, // Used for <
+ ShiftRightSubMode, // Used for >
+ TransformSubMode, // Used for ~/gu/gU
+ WindowSubMode, // Used for Ctrl-w
+ YankSubMode, // Used for y
+ ZSubMode, // Used for z
+ CapitalZSubMode, // Used for Z
+ ReplaceSubMode, // Used for r
};
+/*! A \e SubSubMode is used for things that require one more data item
+ and are 'nested' behind a \l SubMode.
+*/
enum SubSubMode
{
- // typically used for things that require one more data item
- // and are 'nested' behind a mode
NoSubSubMode,
- FtSubSubMode, // used for f, F, t, T
- MarkSubSubMode, // used for m
- BackTickSubSubMode, // used for `
- TickSubSubMode, // used for '
- InvertCaseSubSubMode, // used for ~
- DownCaseSubSubMode, // used for gu
- UpCaseSubSubMode, // used for gU
- ReplaceSubSubMode, // used for r after visual mode
- TextObjectSubSubMode, // used for thing like iw, aW, as etc.
+ FtSubSubMode, // Used for f, F, t, T.
+ MarkSubSubMode, // Used for m.
+ BackTickSubSubMode, // Used for `.
+ TickSubSubMode, // Used for '.
+ InvertCaseSubSubMode, // Used for ~.
+ DownCaseSubSubMode, // Used for gu.
+ UpCaseSubSubMode, // Used for gU.
+ TextObjectSubSubMode, // Used for thing like iw, aW, as etc.
+ SearchSubSubMode,
};
enum VisualMode
MoveLineWise,
};
-enum RangeMode
-{
- RangeCharMode, // v
- RangeLineMode, // V
- RangeLineModeExclusive, // like above, but keep one newline when deleting
- RangeBlockMode, // Ctrl-v
- RangeBlockAndTailMode, // Ctrl-v for D and X
-};
+/*!
+ \enum RangeMode
+
+ The \e RangeMode serves as a means to define how the "Range" between
+ the \l cursor and the \l anchor position is to be interpreted.
+
+ \value RangeCharMode Entered by pressing \key v. The range includes
+ all characters between cursor and anchor.
+ \value RangeLineMode Entered by pressing \key V. The range includes
+ all lines between the line of the cursor and
+ the line of the anchor.
+ \value RangeLineModeExclusice Like \l RangeLineMode, but keeps one
+ newline when deleting.
+ \value RangeBlockMode Entered by pressing \key Ctrl-v. The range includes
+ all characters with line and column coordinates
+ between line and columns coordinates of cursor and
+ anchor.
+ \value RangeBlockAndTailMode Like \l RangeBlockMode, but also includes
+ all characters in the affected lines up to the end
+ of these lines.
+*/
enum EventResult
{
struct Column
{
Column(int p, int l) : physical(p), logical(l) {}
- int physical; // number of characters in the data
- int logical; // column on screen
+ int physical; // Number of characters in the data.
+ int logical; // Column on screen.
};
struct CursorPosition
struct Register
{
Register() : rangemode(RangeCharMode) {}
+ Register(const QString &c) : contents(c), rangemode(RangeCharMode) {}
Register(const QString &c, RangeMode m) : contents(c), rangemode(m) {}
QString contents;
RangeMode rangemode;
};
-struct Range
+QDebug operator<<(QDebug ts, const Register ®)
{
- Range()
- : beginPos(-1), endPos(-1), rangemode(RangeCharMode)
- {}
+ return ts << reg.contents;
+}
- Range(int b, int e, RangeMode m = RangeCharMode)
- : beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m)
- {}
+struct SearchData
+{
+ SearchData() { init(); }
- QString toString() const
- {
- return QString("%1-%2 (mode: %3)").arg(beginPos).arg(endPos)
- .arg(rangemode);
- }
+ void init() { forward = true; mustMove = true; highlightMatches = true;
+ highlightCursor = true; }
- int beginPos;
- int endPos;
- RangeMode rangemode;
+ QString needle;
+ bool forward;
+ bool mustMove;
+ bool highlightMatches;
+ bool highlightCursor;
};
-QDebug &operator<<(QDebug &ts, const QList<QTextEdit::ExtraSelection> &sels)
+
+Range::Range()
+ : beginPos(-1), endPos(-1), rangemode(RangeCharMode)
+{}
+
+Range::Range(int b, int e, RangeMode m)
+ : beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m)
+{}
+
+QString Range::toString() const
+{
+ return QString("%1-%2 (mode: %3)").arg(beginPos).arg(endPos)
+ .arg(rangemode);
+}
+
+QDebug operator<<(QDebug ts, const Range &range)
+{
+ return ts << '[' << range.beginPos << ',' << range.endPos << ']';
+}
+
+
+
+ExCommand::ExCommand(const QString &c, const QString &a, const Range &r)
+ : cmd(c), hasBang(false), args(a), range(r)
+{}
+
+QDebug operator<<(QDebug ts, const ExCommand &cmd)
+{
+ return ts << cmd.cmd << ' ' << cmd.args << ' ' << cmd.range;
+}
+
+QDebug operator<<(QDebug ts, const QList<QTextEdit::ExtraSelection> &sels)
{
foreach (const QTextEdit::ExtraSelection &sel, sels)
ts << "SEL: " << sel.cursor.anchor() << sel.cursor.position();
{
QString res;
for (int i = 0, n = ba.size(); i != n; ++i) {
- QChar c = ba.at(i);
+ const QChar c = ba.at(i);
+ const int cc = c.unicode();
if (c.isPrint())
res += c;
+ else if (cc == '\n')
+ res += _("<CR>");
else
- res += QString("\\x%1").arg(c.unicode(), 2, 16);
+ res += QString("\\x%1").arg(c.unicode(), 2, 16, QLatin1Char('0'));
}
return res;
}
return true;
}
-inline QString msgE20MarkNotSet(const QString &text)
+inline QString msgMarkNotSet(const QString &text)
{
- return FakeVimHandler::tr("E20: Mark '%1' not set").arg(text);
+ return FakeVimHandler::tr("Mark '%1' not set").arg(text);
}
class Input
explicit Input(QChar x)
: m_key(x.unicode()), m_xkey(x.unicode()), m_modifiers(0), m_text(x) {}
- Input(int k, int m, QString t)
+ Input(int k, int m, const QString &t)
: m_key(k), m_modifiers(m), m_text(t)
{
// m_xkey is only a cache.
{
return m_xkey >= '0' && m_xkey <= '9';
}
+
bool isKey(int c) const
{
return !m_modifiers && m_key == c;
}
+ bool isBackspace() const
+ {
+ return m_key == Key_Backspace || isControl('h');
+ }
+
+ bool isReturn() const
+ {
+ return m_key == Key_Return;
+ }
+
+ bool isEscape() const
+ {
+ return isKey(Key_Escape) || isKey(27) || isControl('c')
+ || isControl(Key_BracketLeft);
+ }
+
bool is(int c) const
{
return m_xkey == c && (m_modifiers == 0 || m_modifiers == Qt::ShiftModifier);
bool operator==(const Input &a) const
{
- return a.m_key == m_key && m_text == a.m_text;
+ return a.m_key == m_key && a.m_modifiers == m_modifiers
+ && m_text == a.m_text;
}
+ bool operator!=(const Input &a) const { return !operator==(a); }
+
QString text() const { return m_text; }
+ QChar asChar() const
+ {
+ return (m_text.size() == 1 ? m_text.at(0) : QChar());
+ }
+
int key() const { return m_key; }
+ QDebug dump(QDebug ts) const
+ {
+ return ts << m_key << '-' << m_modifiers << '-'
+ << quoteUnprintable(m_text);
+ }
private:
int m_key;
int m_xkey;
QString m_text;
};
-typedef QVector<Input> Inputs;
+QDebug operator<<(QDebug ts, const Input &input) { return input.dump(ts); }
+
+class Inputs : public QVector<Input>
+{
+public:
+ Inputs() {}
+ explicit Inputs(const QString &str) { parseFrom(str); }
+ void parseFrom(const QString &str);
+};
+
+
+void Inputs::parseFrom(const QString &str)
+{
+ const int n = str.size();
+ for (int i = 0; i < n; ++i) {
+ uint c0 = str.at(i).unicode(), c1 = 0, c2 = 0, c3 = 0, c4 = 0, c5 = 0;
+ if (i + 1 < n)
+ c1 = str.at(i + 1).unicode();
+ if (i + 2 < n)
+ c2 = str.at(i + 2).unicode();
+ if (i + 3 < n)
+ c3 = str.at(i + 3).unicode();
+ if (i + 4 < n)
+ c4 = str.at(i + 4).unicode();
+ if (i + 5 < n)
+ c5 = str.at(i + 5).unicode();
+ if (c0 == '<') {
+ if ((c1 == 'C' || c1 == 'c') && c2 == '-' && c4 == '>') {
+ uint c = (c3 < 90 ? c3 : c3 - 32);
+ append(Input(c, Qt::ControlModifier, QString(QChar(c - 64))));
+ i += 4;
+ } else {
+ append(Input(QLatin1Char(c0)));
+ }
+ } else {
+ append(Input(QLatin1Char(c0)));
+ }
+ }
+}
+
+class History
+{
+public:
+ History() : m_index(0) {}
+ void append(const QString &item)
+ { //qDebug() << "APP: " << item << m_items;
+ m_items.removeAll(item);
+ m_items.append(item); m_index = m_items.size() - 1; }
+ void down() { m_index = qMin(m_index + 1, m_items.size()); }
+ void up() { m_index = qMax(m_index - 1, 0); }
+ //void clear() { m_items.clear(); m_index = 0; }
+ void restart() { m_index = m_items.size(); }
+ QString current() const { return m_items.value(m_index, QString()); }
+ QStringList items() const { return m_items; }
+private:
+ QStringList m_items;
+ int m_index;
+};
+
// Mappings for a specific mode.
-class ModeMapping : private QList<QPair<Inputs, Inputs> >
+class ModeMapping : public QList<QPair<Inputs, Inputs> >
{
public:
ModeMapping() { test(); }
}
}
- // Returns 'false' if more input input is needed to decide whether a
- // mapping needs to be applied. If a decision can be made, return 'true',
+ // Returns 'false' if more input is needed to decide whether a mapping
+ // needs to be applied. If a decision can be made, return 'true',
// and replace *input with the mapped data.
- bool mappingDone(Inputs *input) const
+ bool mappingDone(Inputs *inputs) const
{
- Q_UNUSED(input);
// FIXME: inefficient.
for (int i = 0; i != size(); ++i) {
+ const Inputs &haystack = at(i).first;
// A mapping
- if (startsWith(at(i).first, *input)) {
- if (at(i).first.size() != input->size())
+ if (startsWith(haystack, *inputs)) {
+ if (haystack.size() != inputs->size())
return false; // This can be extended.
// Actual mapping.
- *input = at(i).second;
+ *inputs = at(i).second;
return true;
}
}
- // No extensible mapping found. Use input as-is.
+ // No extensible mapping found. Use inputs as-is.
return true;
}
if (needle.size() > haystack.size())
return false;
for (int i = 0; i != needle.size(); ++i) {
- if (needle.at(i).text() != haystack.at(i).text())
+ if (needle.at(i) != haystack.at(i))
return false;
}
return true;
}
};
+
class FakeVimHandler::Private : public QObject
{
Q_OBJECT
void handleCommand(const QString &cmd); // Sets m_tc + handleExCommand
void handleExCommand(const QString &cmd);
- // updates marks positions by the difference in positionChange
+ // Updates marks positions by the difference in positionChange.
void fixMarks(int positionAction, int positionChange);
void installEventFilter();
EventResult handleKey(const Input &);
Q_SLOT EventResult handleKey2();
EventResult handleInsertMode(const Input &);
+ EventResult handleReplaceMode(const Input &);
EventResult handleCommandMode(const Input &);
EventResult handleRegisterMode(const Input &);
- EventResult handleMiniBufferModes(const Input &);
+ EventResult handleExMode(const Input &);
+ EventResult handleSearchSubSubMode(const Input &);
EventResult handleCommandSubSubMode(const Input &);
void finishMovement(const QString &dotCommand = QString());
void finishMovement(const QString &dotCommand, int count);
void resetCommandMode();
- void search(const QString &needle, bool forward, bool incSearch = false);
+ void search(const SearchData &sd);
void highlightMatches(const QString &needle);
void stopIncrementalFind();
bool atEndOfLine() const
{ return m_tc.atBlockEnd() && m_tc.block().length() > 1; }
- int lastPositionInDocument() const; // last valid pos in doc
+ int lastPositionInDocument() const; // Returns last valid position in doc.
int firstPositionInLine(int line) const; // 1 based line, 0 based pos
int lastPositionInLine(int line) const; // 1 based line, 0 based pos
int lineForPosition(int pos) const; // 1 based line, 0 based pos
QString lineContents(int line) const; // 1 based line
- void setLineContents(int line, const QString &contents) const; // 1 based line
+ void setLineContents(int line, const QString &contents); // 1 based line
int linesOnScreen() const;
int columnsOnScreen() const;
int linesInDocument() const;
- // all zero-based counting
+ // The following use all zero-based counting.
int cursorLineOnScreen() const;
int cursorLineInDocument() const;
int physicalCursorColumnInDocument() const; // as stored in the data
void setCursorPosition(const CursorPosition &p)
{ setPosition(p.position); scrollToLineInDocument(p.scrollLine); }
- // helper functions for indenting
+ // Helper functions for indenting/
bool isElectricCharacter(QChar c) const;
void indentSelectedText(QChar lastTyped = QChar());
int indentText(const Range &range, QChar lastTyped = QChar());
void moveToMatchingParanthesis();
void moveToWordBoundary(bool simple, bool forward, bool changeWord = false);
- // to reduce line noise
+ // Convenience wrappers to reduce line noise.
void moveToEndOfDocument() { m_tc.movePosition(EndOfDocument, MoveAnchor); }
void moveToStartOfLine();
void moveToEndOfLine();
bool handleFfTt(QString key);
- // helper function for handleExCommand. return 1 based line index.
+ // Helper function for handleExCommand returning 1 based line index.
int readLineCode(QString &cmd);
- void selectRange(int beginLine, int endLine);
void enterInsertMode();
+ void enterReplaceMode();
void enterCommandMode();
void enterExMode();
void showRedMessage(const QString &msg);
void notImplementedYet();
void updateMiniBuffer();
void updateSelection();
+ void updateCursor();
QWidget *editor() const;
QChar characterAtCursor() const
{ return m_tc.document()->characterAt(m_tc.position()); }
void beginEditBlock() { UNDO_DEBUG("BEGIN EDIT BLOCK"); m_tc.beginEditBlock(); }
void beginEditBlock(int pos) { setUndoPosition(pos); beginEditBlock(); }
void endEditBlock() { UNDO_DEBUG("END EDIT BLOCK"); m_tc.endEditBlock(); }
- void joinPreviousEditBlock() { UNDO_DEBUG("JOIN EDIT BLOCK"); m_tc.joinPreviousEditBlock(); }
-
- // this asks the layer above (e.g. the fake vim plugin or the
- // stand-alone test application to handle the command)
- void passUnknownExCommand(const QString &cmd);
- // this asks the layer above (e.g. the fake vim plugin or the
- // stand-alone test application to handle the set command)
- void passUnknownSetCommand(const QString &cmd);
+ void joinPreviousEditBlock() { UNDO_DEBUG("JOIN"); m_tc.joinPreviousEditBlock(); }
+ void breakEditBlock()
+ { m_tc.beginEditBlock(); m_tc.insertText("x");
+ m_tc.deletePreviousChar(); m_tc.endEditBlock(); }
bool isVisualMode() const { return m_visualMode != NoVisualMode; }
bool isNoVisualMode() const { return m_visualMode == NoVisualMode; }
void selectBlockTextObject(bool inner, char left, char right);
void selectQuotedStringTextObject(bool inner, int type);
+ Q_SLOT void importSelection();
+
public:
QTextEdit *m_textedit;
QPlainTextEdit *m_plaintextedit;
QTextCursor m_tc;
int m_oldPosition; // copy from last event to check for external changes
int m_anchor;
- static QHash<int, Register> m_registers;
int m_register;
QString m_mvcount;
QString m_opcount;
bool m_anchorPastEnd;
bool m_positionPastEnd; // '$' & 'l' in visual mode can move past eol
- bool isSearchMode() const
- { return m_mode == SearchForwardMode || m_mode == SearchBackwardMode; }
int m_gflag; // whether current command started with 'g'
+ QString m_commandPrefix;
QString m_commandBuffer;
QString m_currentFileName;
QString m_currentMessage;
bool m_lastSearchForward;
bool m_findPending;
QString m_lastInsertion;
+ QString m_lastDeletion;
int anchor() const { return m_anchor; }
int position() const { return m_tc.position(); }
- typedef void (FakeVimHandler::Private::*Transformation)(int, QTextCursor *);
- void transformText(const Range &range, Transformation transformation);
-
- void removeSelectedText();
+ struct TransformationData
+ {
+ TransformationData(const QString &s, const QVariant &d)
+ : from(s), extraData(d) {}
+ QString from;
+ QString to;
+ QVariant extraData;
+ };
+ typedef void (Private::*Transformation)(TransformationData *td);
+ void transformText(const Range &range, Transformation transformation,
+ const QVariant &extraData = QVariant());
+
+ void insertText(const Register ®);
void removeText(const Range &range);
- void removeTransform(int, QTextCursor *);
+ void removeTransform(TransformationData *td);
- void invertCaseSelectedText();
- void invertCaseTransform(int, QTextCursor *);
+ void invertCase(const Range &range);
+ void invertCaseTransform(TransformationData *td);
- void upCaseSelectedText();
- void upCaseTransform(int, QTextCursor *);
+ void upCase(const Range &range);
+ void upCaseTransform(TransformationData *td);
- void downCaseSelectedText();
- void downCaseTransform(int, QTextCursor *);
+ void downCase(const Range &range);
+ void downCaseTransform(TransformationData *td);
- QChar m_replacingCharacter;
- void replaceSelectedText(); // replace each character with m_replacingCharacter
- void replaceTransform(int, QTextCursor *);
+ void replaceText(const Range &range, const QString &str);
+ void replaceByStringTransform(TransformationData *td);
+ void replaceByCharTransform(TransformationData *td);
- QString selectedText() const { return text(Range(position(), anchor())); }
- QString text(const Range &range) const;
+ QString selectText(const Range &range) const;
+ void setCurrentRange(const Range &range);
+ Range currentRange() const { return Range(position(), anchor(), m_rangemode); }
- void yankSelectedText();
void yankText(const Range &range, int toregister = '"');
void pasteText(bool afterCursor);
void redo();
void setUndoPosition(int pos);
QMap<int, int> m_undoCursorPosition; // revision -> position
- bool m_beginEditBlock;
// extra data for '.'
void replay(const QString &text, int count);
- void setDotCommand(const QString &cmd) { m_dotCommand = cmd; }
- void setDotCommand(const QString &cmd, int n) { m_dotCommand = cmd.arg(n); }
- QString m_dotCommand;
- bool m_inReplay; // true if we are executing a '.'
+ void setDotCommand(const QString &cmd) { g.dotCommand = cmd; }
+ void setDotCommand(const QString &cmd, int n) { g.dotCommand = cmd.arg(n); }
// extra data for ';'
QString m_semicolonCount;
Input m_semicolonType; // 'f', 'F', 't', 'T'
QString m_semicolonKey;
- // history for '/'
- QString lastSearchString() const;
- static QStringList m_searchHistory;
- int m_searchHistoryIndex;
-
- // history for ':'
- static QStringList m_commandHistory;
- int m_commandHistoryIndex;
-
// visual line mode
void enterVisualMode(VisualMode visualMode);
void leaveVisualMode();
VisualMode m_visualMode;
// marks as lines
+ int mark(int code) const;
+ void setMark(int code, int position);
QHash<int, int> m_marks;
- QString m_oldNeedle;
// vi style configuration
QVariant config(int code) const { return theFakeVimSetting(code)->value(); }
int m_targetColumn; // -1 if past end of line
int m_visualTargetColumn; // 'l' can move past eol in visual mode only
-
int m_cursorWidth;
// auto-indent
void handleStartOfLine();
void recordJump();
- void recordNewUndo();
QVector<CursorPosition> m_jumpListUndo;
QVector<CursorPosition> m_jumpListRedo;
QList<QTextEdit::ExtraSelection> m_searchSelections;
+ QTextCursor m_searchCursor;
+ QString m_oldNeedle;
- bool handleExCommandHelper(const QString &cmd); // Returns success.
- QString extractCommand(const QString &line, int *beginLine, int *endLine);
- bool handleExBangCommand(const QString &line);
- bool handleExDeleteCommand(const QString &line);
- bool handleExGotoCommand(const QString &line);
- bool handleExHistoryCommand(const QString &line);
- bool handleExMapCommand(const QString &line);
- bool handleExNormalCommand(const QString &line);
- bool handleExReadCommand(const QString &line);
- bool handleExRedoCommand(const QString &line);
- bool handleExSetCommand(const QString &line);
- bool handleExShiftRightCommand(const QString &line);
- bool handleExSourceCommand(const QString &line);
- bool handleExSubstituteCommand(const QString &line);
- bool handleExWriteCommand(const QString &line);
-
- // All mappings.
- typedef QHash<char, ModeMapping> Mappings;
- static Mappings m_mappings;
-
- QVector<Input> m_pendingInput;
+ bool handleExCommandHelper(const ExCommand &cmd); // Returns success.
+ bool handleExPluginCommand(const ExCommand &cmd); // Handled by plugin?
+ bool handleExBangCommand(const ExCommand &cmd);
+ bool handleExDeleteCommand(const ExCommand &cmd);
+ bool handleExGotoCommand(const ExCommand &cmd);
+ bool handleExHistoryCommand(const ExCommand &cmd);
+ bool handleExRegisterCommand(const ExCommand &cmd);
+ bool handleExMapCommand(const ExCommand &cmd);
+ bool handleExNormalCommand(const ExCommand &cmd);
+ bool handleExReadCommand(const ExCommand &cmd);
+ bool handleExRedoCommand(const ExCommand &cmd);
+ bool handleExSetCommand(const ExCommand &cmd);
+ bool handleExShiftCommand(const ExCommand &cmd);
+ bool handleExSourceCommand(const ExCommand &cmd);
+ bool handleExSubstituteCommand(const ExCommand &cmd);
+ bool handleExWriteCommand(const ExCommand &cmd);
void timerEvent(QTimerEvent *ev);
- int m_inputTimer;
void setupCharClass();
int charClass(QChar c, bool simple) const;
signed char m_charClass[256];
+
+ static struct GlobalData
+ {
+ GlobalData()
+ {
+ inReplay = false;
+ inputTimer = -1;
+ }
+
+ // Input.
+ Inputs pendingInput;
+ int inputTimer;
+
+ // Repetition.
+ QString dotCommand;
+ bool inReplay; // true if we are executing a '.'
+
+ // History for searches.
+ History searchHistory;
+
+ // History for :ex commands.
+ History commandHistory;
+
+ QHash<int, Register> registers;
+
+ // All mappings.
+ typedef QHash<char, ModeMapping> Mappings;
+ Mappings mappings;
+ } g;
};
-QStringList FakeVimHandler::Private::m_searchHistory;
-QStringList FakeVimHandler::Private::m_commandHistory;
-QHash<int, Register> FakeVimHandler::Private::m_registers;
-FakeVimHandler::Private::Mappings FakeVimHandler::Private::m_mappings;
+FakeVimHandler::Private::GlobalData FakeVimHandler::Private::g;
FakeVimHandler::Private::Private(FakeVimHandler *parent, QWidget *widget)
{
m_movetype = MoveInclusive;
m_anchor = 0;
m_cursorWidth = EDITOR(cursorWidth());
- m_inReplay = false;
m_justAutoIndented = 0;
m_rangemode = RangeCharMode;
- m_beginEditBlock = true;
- m_inputTimer = -1;
setupCharClass();
}
KEY_DEBUG("SHORTCUT OVERRIDE" << key << " PASSING: " << m_passing);
if (key == Key_Escape) {
+ if (m_subsubmode == SearchSubSubMode)
+ return true;
// Not sure this feels good. People often hit Esc several times
if (isNoVisualMode() && m_mode == CommandMode)
return false;
// Try to compensate for code completion
if (dist > 0 && dist <= physicalCursorColumnInDocument()) {
Range range(m_oldPosition, m_tc.position());
- m_lastInsertion.append(text(range));
+ m_lastInsertion.append(selectText(range));
}
} else if (!isVisualMode()) {
if (atEndOfLine())
// key = shift(key);
//}
- QTC_ASSERT(
- !(m_mode != InsertMode && m_tc.atBlockEnd() && m_tc.block().length() > 1),
+ QTC_ASSERT(m_mode == InsertMode || m_mode == ReplaceMode
+ || !m_tc.atBlockEnd() || m_tc.block().length() <= 1,
qDebug() << "Cursor at EOL before key handler");
EventResult result = handleKey(Input(key, mods, ev->text()));
// We fake vi-style end-of-line behaviour
m_fakeEnd = atEndOfLine() && m_mode == CommandMode && !isVisualBlockMode();
- QTC_ASSERT(
- !(m_mode != InsertMode && m_tc.atBlockEnd() && m_tc.block().length() > 1),
+ QTC_ASSERT(m_mode == InsertMode || m_mode == ReplaceMode
+ || !m_tc.atBlockEnd() || m_tc.block().length() <= 1,
qDebug() << "Cursor at EOL after key handler");
if (m_fakeEnd)
EDITOR(setTextCursor(m_tc));
m_oldPosition = m_tc.position();
}
+
+ if (hasConfig(ConfigShowMarks))
+ updateSelection();
+
return result;
}
void FakeVimHandler::Private::installEventFilter()
{
+ EDITOR(viewport()->installEventFilter(q));
EDITOR(installEventFilter(q));
}
void FakeVimHandler::Private::setupWidget()
{
enterCommandMode();
- //EDITOR(setCursorWidth(QFontMetrics(ed->font()).width(QChar('x')));
if (m_textedit) {
m_textedit->setLineWrapMode(QTextEdit::NoWrap);
} else if (m_plaintextedit) {
m_plaintextedit->setLineWrapMode(QPlainTextEdit::NoWrap);
}
m_wasReadOnly = EDITOR(isReadOnly());
- //EDITOR(setReadOnly(true));
updateEditor();
+ importSelection();
+ updateMiniBuffer();
+ updateCursor();
+}
+void FakeVimHandler::Private::importSelection()
+{
QTextCursor tc = EDITOR(textCursor());
+ int pos = tc.position();
+ int anc = tc.anchor();
if (tc.hasSelection()) {
- int pos = tc.position();
- int anc = tc.anchor();
- m_marks['<'] = anc;
- m_marks['>'] = pos;
- m_anchor = anc;
- m_visualMode = VisualCharMode;
- tc.clearSelection();
- EDITOR(setTextCursor(tc));
- m_tc = tc; // needed in updateSelection
- updateSelection();
+ // FIXME: Why?
+ if (pos < anc)
+ --anc;
+ else
+ tc.movePosition(Left, KeepAnchor);
}
-
- updateMiniBuffer();
+ setMark('<', anc);
+ setMark('>', pos);
+ m_anchor = anc;
+ Qt::KeyboardModifiers mods = QApplication::keyboardModifiers();
+ if (!tc.hasSelection())
+ m_visualMode = NoVisualMode;
+ else if (mods & Qt::ControlModifier)
+ m_visualMode = VisualBlockMode;
+ else if (mods & Qt::AltModifier)
+ m_visualMode = VisualBlockMode;
+ else if (mods & Qt::ShiftModifier)
+ m_visualMode = VisualLineMode;
+ else
+ m_visualMode = VisualCharMode;
+ m_tc = tc; // needed in updateSelection
+ tc.clearSelection();
+ EDITOR(setTextCursor(tc));
+ updateSelection();
}
void FakeVimHandler::Private::updateEditor()
//showBlackMessage(QString());
//updateMiniBuffer();
//EDITOR(removeEventFilter(q));
- EDITOR(setReadOnly(m_wasReadOnly));
- EDITOR(setCursorWidth(m_cursorWidth));
- EDITOR(setOverwriteMode(false));
+ //EDITOR(setReadOnly(m_wasReadOnly));
const int charWidth = QFontMetrics(EDITOR(font())).width(QChar(' '));
EDITOR(setTabStopWidth(charWidth * tabSize));
if (isVisualLineMode()) {
m_tc = EDITOR(textCursor());
- int beginLine = lineForPosition(m_marks['<']);
- int endLine = lineForPosition(m_marks['>']);
+ int beginLine = lineForPosition(mark('<'));
+ int endLine = lineForPosition(mark('>'));
m_tc.setPosition(firstPositionInLine(beginLine), MoveAnchor);
m_tc.setPosition(lastPositionInLine(endLine), KeepAnchor);
EDITOR(setTextCursor(m_tc));
- } else if (isVisualCharMode()) {
+ } else if (isVisualCharMode() || isVisualBlockMode()) {
m_tc = EDITOR(textCursor());
- m_tc.setPosition(m_marks['<'], MoveAnchor);
- m_tc.setPosition(m_marks['>'], KeepAnchor);
+ m_tc.setPosition(mark('<'), MoveAnchor);
+ m_tc.setPosition(mark('>'), KeepAnchor);
EDITOR(setTextCursor(m_tc));
}
m_visualMode = NoVisualMode;
+ // Force "ordinary" cursor.
+ m_mode = InsertMode;
+ m_submode = NoSubMode;
+ m_subsubmode = NoSubSubMode;
+ updateCursor();
updateSelection();
}
EventResult FakeVimHandler::Private::handleKey(const Input &input)
{
- if (m_mode == InsertMode || m_mode == CommandMode) {
- m_pendingInput.append(input);
+ KEY_DEBUG("HANDLE INPUT: " << input);
+ if (m_mode == ExMode)
+ return handleExMode(input);
+ if (m_subsubmode == SearchSubSubMode)
+ return handleSearchSubSubMode(input);
+ if (m_mode == InsertMode || m_mode == ReplaceMode || m_mode == CommandMode) {
+ g.pendingInput.append(input);
const char code = m_mode == InsertMode ? 'i' : 'n';
- if (m_mappings[code].mappingDone(&m_pendingInput))
+ if (g.mappings[code].mappingDone(&g.pendingInput))
return handleKey2();
- if (m_inputTimer != -1)
- killTimer(m_inputTimer);
- m_inputTimer = startTimer(1000);
+ if (g.inputTimer != -1)
+ killTimer(g.inputTimer);
+ g.inputTimer = startTimer(1000);
return EventHandled;
}
- if (m_mode == ExMode || m_mode == SearchForwardMode
- || m_mode == SearchBackwardMode)
- return handleMiniBufferModes(input);
return EventUnhandled;
}
setUndoPosition(m_tc.position());
if (m_mode == InsertMode) {
EventResult result = EventUnhandled;
- foreach (const Input &in, m_pendingInput) {
+ foreach (const Input &in, g.pendingInput) {
EventResult r = handleInsertMode(in);
if (r == EventHandled)
result = EventHandled;
}
- m_pendingInput.clear();
+ g.pendingInput.clear();
+ return result;
+ }
+ if (m_mode == ReplaceMode) {
+ EventResult result = EventUnhandled;
+ foreach (const Input &in, g.pendingInput) {
+ EventResult r = handleReplaceMode(in);
+ if (r == EventHandled)
+ result = EventHandled;
+ }
+ g.pendingInput.clear();
return result;
}
if (m_mode == CommandMode) {
EventResult result = EventUnhandled;
- foreach (const Input &in, m_pendingInput) {
+ foreach (const Input &in, g.pendingInput) {
EventResult r = handleCommandMode(in);
if (r == EventHandled)
result = EventHandled;
}
- m_pendingInput.clear();
+ g.pendingInput.clear();
return result;
}
return EventUnhandled;
if (!isVisualMode()) {
m_anchor = m_tc.position();
} else {
- // m_marks['<'] = m_tc.position();
+ // setMark('<', m_tc.position());
}
}
enterExMode();
m_currentMessage.clear();
m_commandBuffer = QString(".,+%1!").arg(qAbs(endLine - beginLine));
- m_commandHistory.append(QString());
- m_commandHistoryIndex = m_commandHistory.size() - 1;
+ //g.commandHistory.append(QString());
updateMiniBuffer();
+ updateCursor();
return;
}
if (isVisualMode())
- m_marks['>'] = m_tc.position();
+ setMark('>', m_tc.position());
if (m_submode == ChangeSubMode
|| m_submode == DeleteSubMode
if (m_movetype == MoveInclusive) {
if (anchor() <= position()) {
- if ( !m_tc.atBlockEnd())
+ if (!m_tc.atBlockEnd())
moveRight(); // correction
} else {
m_anchor++;
}
if (m_submode != TransformSubMode) {
- yankSelectedText();
+ yankText(currentRange(), m_register);
if (m_movetype == MoveLineWise)
- m_registers[m_register].rangemode = RangeLineMode;
+ g.registers[m_register].rangemode = RangeLineMode;
}
m_positionPastEnd = m_anchorPastEnd = false;
if (m_submode == ChangeSubMode) {
if (m_rangemode == RangeLineMode)
m_rangemode = RangeLineModeExclusive;
- removeSelectedText();
+ removeText(currentRange());
if (!dotCommand.isEmpty())
setDotCommand(QLatin1Char('c') + dotCommand);
if (m_movetype == MoveLineWise)
insertAutomaticIndentation(true);
endEditBlock();
+ setUndoPosition(position());
enterInsertMode();
- m_beginEditBlock = false;
m_submode = NoSubMode;
} else if (m_submode == DeleteSubMode) {
- removeSelectedText();
+ removeText(currentRange());
if (!dotCommand.isEmpty())
setDotCommand(QLatin1Char('d') + dotCommand);
if (m_movetype == MoveLineWise)
const int la = lineForPosition(anchor());
const int lp = lineForPosition(position());
if (m_register != '"') {
- setPosition(m_marks[m_register]);
+ setPosition(mark(m_register));
moveToStartOfLine();
} else {
if (anchor() <= position())
showBlackMessage(QString("%1 lines yanked").arg(qAbs(la - lp) + 1));
} else if (m_submode == TransformSubMode) {
if (m_subsubmode == InvertCaseSubSubMode) {
- invertCaseSelectedText();
+ invertCase(currentRange());
if (!dotCommand.isEmpty())
setDotCommand(QLatin1Char('~') + dotCommand);
} else if (m_subsubmode == UpCaseSubSubMode) {
- upCaseSelectedText();
+ upCase(currentRange());
if (!dotCommand.isEmpty())
setDotCommand("gU" + dotCommand);
} else if (m_subsubmode == DownCaseSubSubMode) {
- downCaseSelectedText();
+ downCase(currentRange());
if (!dotCommand.isEmpty())
setDotCommand("gu" + dotCommand);
- } else if (m_subsubmode == ReplaceSubSubMode) {
- replaceSelectedText();
- if (!dotCommand.isEmpty())
- setDotCommand("r" + dotCommand);
}
m_submode = NoSubMode;
m_subsubmode = NoSubSubMode;
if (m_movetype == MoveLineWise)
handleStartOfLine();
endEditBlock();
- } else if (m_submode == ReplaceSubMode) {
- m_submode = NoSubMode;
} else if (m_submode == IndentSubMode) {
recordJump();
beginEditBlock();
indentSelectedText();
endEditBlock();
m_submode = NoSubMode;
- updateMiniBuffer();
} else if (m_submode == ShiftRightSubMode) {
recordJump();
shiftRegionRight(1);
m_submode = NoSubMode;
- updateMiniBuffer();
} else if (m_submode == ShiftLeftSubMode) {
recordJump();
shiftRegionLeft(1);
m_submode = NoSubMode;
- updateMiniBuffer();
}
resetCommandMode();
+ updateSelection();
+ updateMiniBuffer();
+ updateCursor();
}
void FakeVimHandler::Private::resetCommandMode()
m_register = '"';
m_tc.clearSelection();
m_rangemode = RangeCharMode;
-
- updateSelection();
- updateMiniBuffer();
}
void FakeVimHandler::Private::updateSelection()
{
QList<QTextEdit::ExtraSelection> selections = m_searchSelections;
+ if (!m_searchCursor.isNull()) {
+ QTextEdit::ExtraSelection sel;
+ sel.cursor = m_searchCursor;
+ sel.format = m_searchCursor.blockCharFormat();
+ sel.format.setForeground(Qt::white);
+ sel.format.setBackground(Qt::black);
+ selections.append(sel);
+ }
if (isVisualMode()) {
QTextEdit::ExtraSelection sel;
sel.cursor = m_tc;
sel.format.setBackground(Qt::black);
#endif
const int cursorPos = m_tc.position();
- const int anchorPos = m_marks['<'];
+ const int anchorPos = mark('<');
//qDebug() << "POS: " << cursorPos << " ANCHOR: " << anchorPos;
if (isVisualCharMode()) {
sel.cursor.setPosition(qMin(cursorPos, anchorPos), MoveAnchor);
}
}
//qDebug() << "SELECTION: " << selections;
+ if (hasConfig(ConfigShowMarks)) {
+ for (QHashIterator<int, int> it(m_marks); it.hasNext(); ) {
+ it.next();
+ QTextEdit::ExtraSelection sel;
+ sel.cursor = m_tc;
+ sel.cursor.setPosition(it.value(), MoveAnchor);
+ sel.cursor.setPosition(it.value() + 1, KeepAnchor);
+ sel.format = m_tc.blockCharFormat();
+ sel.format.setForeground(Qt::blue);
+ sel.format.setBackground(Qt::green);
+ selections.append(sel);
+ }
+ }
emit q->selectionChanged(selections);
}
msg = "-- VISUAL BLOCK --";
}
} else if (m_mode == InsertMode) {
- if (m_submode == ReplaceSubMode)
- msg = "-- REPLACE --";
- else
- msg = "-- INSERT --";
- } else {
- if (m_mode == SearchForwardMode)
- msg += '/';
- else if (m_mode == SearchBackwardMode)
- msg += '?';
- else if (m_mode == ExMode)
- msg += ':';
+ msg = "-- INSERT --";
+ } else if (m_mode == ReplaceMode) {
+ msg = "-- REPLACE --";
+ } else if (!m_commandPrefix.isEmpty()) {
+ //QTC_ASSERT(m_mode == ExMode || m_subsubmode == SearchSubSubMode,
+ // qDebug() << "MODE: " << m_mode << m_subsubmode);
+ msg = m_commandPrefix;
foreach (QChar c, m_commandBuffer) {
if (c.unicode() < 32) {
msg += '^';
}
if (!msg.isEmpty() && m_mode != CommandMode)
msg += QChar(10073); // '|'; // FIXME: Use a real "cursor"
+ } else {
+ QTC_ASSERT(m_mode == CommandMode && m_subsubmode != SearchSubSubMode, /**/);
+ msg = "-- COMMAND --";
}
emit q->commandBufferChanged(msg);
int linesInDoc = linesInDocument();
int l = cursorLineInDocument();
QString status;
- const QString pos = QString::fromLatin1("%1,%2").arg(l + 1).arg(physicalCursorColumnInDocument() + 1);
+ const QString pos = QString::fromLatin1("%1,%2")
+ .arg(l + 1).arg(physicalCursorColumnInDocument() + 1);
// FIXME: physical "-" logical
if (linesInDoc != 0) {
status = FakeVimHandler::tr("%1%2%").arg(pos, -10).arg(l * 100 / linesInDoc, 4);
selectQuotedStringTextObject(m_subsubdata.is('i'), input.key());
m_subsubmode = NoSubSubMode;
finishMovement();
- } else if (m_submode == TransformSubMode && m_subsubmode == ReplaceSubSubMode) {
- if (isVisualLineMode())
- m_rangemode = RangeLineMode;
- else if (isVisualBlockMode())
- m_rangemode = RangeBlockMode;
- if (!input.text().isEmpty() && input.text().at(0).isPrint()) {
- leaveVisualMode();
- m_replacingCharacter = input.text().at(0);
- finishMovement();
- }
} else if (m_subsubmode == MarkSubSubMode) {
- m_marks[input.key()] = m_tc.position();
+ setMark(input.asChar().unicode(), m_tc.position());
m_subsubmode = NoSubSubMode;
} else if (m_subsubmode == BackTickSubSubMode
|| m_subsubmode == TickSubSubMode) {
- if (m_marks.contains(input.key())) {
- setPosition(m_marks[input.key()]);
+ int m = mark(input.asChar().unicode());
+ if (m != -1) {
+ setPosition(m);
if (m_subsubmode == TickSubSubMode)
moveToFirstNonBlankOnLine();
finishMovement();
} else {
- showRedMessage(msgE20MarkNotSet(input.text()));
+ showRedMessage(msgMarkNotSet(input.text()));
}
m_subsubmode = NoSubSubMode;
} else {
{
EventResult handled = EventHandled;
- if (input.isKey(Key_Escape) || input.isControl(Key_BracketLeft)) {
+ if (input.isEscape()) {
if (isVisualMode()) {
leaveVisualMode();
} else if (m_submode != NoSubMode) {
finishMovement();
} else {
resetCommandMode();
+ updateSelection();
+ updateMiniBuffer();
}
} else if (m_subsubmode != NoSubSubMode) {
handleCommandSubSubMode(input);
emit q->windowCommandRequested(input.key());
m_submode = NoSubMode;
} else if (m_submode == RegisterSubMode) {
- m_register = input.key();
+ m_register = input.asChar().unicode();
m_submode = NoSubMode;
m_rangemode = RangeLineMode;
+ } else if (m_submode == ReplaceSubMode) {
+ if (isVisualMode()) {
+ if (isVisualLineMode())
+ m_rangemode = RangeLineMode;
+ else if (isVisualBlockMode())
+ m_rangemode = RangeBlockMode;
+ else
+ m_rangemode = RangeCharMode;
+ leaveVisualMode();
+ Range range = currentRange();
+ Transformation tr =
+ &FakeVimHandler::Private::replaceByCharTransform;
+ transformText(range, tr, input.asChar());
+ setPosition(range.beginPos);
+ } else if (count() <= rightDist()) {
+ setAnchor();
+ moveRight(count());
+ replaceText(currentRange(), QString(count(), input.asChar()));
+ moveLeft();
+ setTargetColumn();
+ setDotCommand("%1r" + input.text(), count());
+ }
+ m_submode = NoSubMode;
+ finishMovement();
} else if (m_submode == ChangeSubMode && input.is('c')) { // tested
moveToStartOfLine();
setAnchor();
finishMovement();
} else if (m_submode == ZSubMode) {
//qDebug() << "Z_MODE " << cursorLineInDocument() << linesOnScreen();
- if (input.isKey(Key_Return) || input.is('t')) {
+ if (input.isReturn() || input.is('t')) {
// Cursor line to top of window.
if (!m_mvcount.isEmpty())
setPosition(firstPositionInLine(count()));
scrollUp(- cursorLineOnScreen());
- if (input.isKey(Key_Return))
+ if (input.isReturn())
moveToFirstNonBlankOnLine();
finishMovement();
} else if (input.is('.') || input.is('z')) {
handleExCommand(QString(QLatin1Char('x')));
else if (input.is('Q'))
handleExCommand("q!");
- } else if (m_submode == ReplaceSubMode) {
- const QString text = input.text();
- if (count() <= (rightDist() + atEndOfLine()) && text.size() == 1
- && (text.at(0).isPrint() || text.at(0).isSpace())) {
- if (atEndOfLine())
- moveLeft();
- setAnchor();
- moveRight(count());
- removeSelectedText();
- m_tc.insertText(QString(count(), text.at(0)));
- m_movetype = MoveExclusive;
- setDotCommand("%1r" + text, count());
- moveLeft();
- }
- setTargetColumn();
- m_submode = NoSubMode;
- finishMovement();
} else if (input.isDigit()) {
if (input.is('0') && m_mvcount.isEmpty()) {
m_movetype = MoveExclusive;
finishMovement();
} else if (input.is(':')) {
enterExMode();
+ g.commandHistory.restart();
m_currentMessage.clear();
m_commandBuffer.clear();
if (isVisualMode())
m_commandBuffer = "'<,'>";
- m_commandHistory.append(QString());
- m_commandHistoryIndex = m_commandHistory.size() - 1;
updateMiniBuffer();
} else if (input.is('/') || input.is('?')) {
+ m_lastSearchForward = input.is('/');
+ g.searchHistory.restart();
if (hasConfig(ConfigUseCoreSearch)) {
// re-use the core dialog.
m_findPending = true;
- m_lastSearchForward = (input.is('/'));
EDITOR(setTextCursor(m_tc));
emit q->findRequested(!m_lastSearchForward);
m_tc = EDITOR(textCursor());
} else {
// FIXME: make core find dialog sufficiently flexible to
// produce the "default vi" behaviour too. For now, roll our own.
- enterExMode(); // to get the cursor disabled
m_currentMessage.clear();
- m_mode = (input.is('/')) ? SearchForwardMode : SearchBackwardMode;
- m_commandBuffer.clear();
- m_searchHistory.append(QString());
- m_searchHistoryIndex = m_searchHistory.size() - 1;
+ m_movetype = MoveExclusive;
+ m_subsubmode = SearchSubSubMode;
+ m_commandPrefix = QLatin1Char(m_lastSearchForward ? '/' : '?');
+ m_commandBuffer = QString();
+ updateCursor();
updateMiniBuffer();
}
} else if (input.is('`')) {
m_subsubmode = BackTickSubSubMode;
} else if (input.is('#') || input.is('*')) {
// FIXME: That's not proper vim behaviour
- m_tc.select(QTextCursor::WordUnderCursor);
- QString needle = "\\<" + m_tc.selection().toPlainText() + "\\>";
- m_searchHistory.append(needle);
- m_lastSearchForward = (input.is('*'));
- updateMiniBuffer();
- search(needle, m_lastSearchForward);
- recordJump();
+ QTextCursor tc = m_tc;
+ tc.select(QTextCursor::WordUnderCursor);
+ QString needle = "\\<" + tc.selection().toPlainText() + "\\>";
+ g.searchHistory.append(needle);
+ m_lastSearchForward = input.is('*');
+ m_currentMessage.clear();
+ m_commandPrefix = QLatin1Char(m_lastSearchForward ? '/' : '?');
+ m_commandBuffer = needle;
+ SearchData sd;
+ sd.needle = needle;
+ sd.forward = m_lastSearchForward;
+ sd.highlightCursor = false;
+ sd.highlightMatches = true;
+ search(sd);
+ //m_searchCursor = QTextCursor();
+ //updateSelection();
+ //updateMiniBuffer();
} else if (input.is('\'')) {
m_subsubmode = TickSubSubMode;
} else if (input.is('|')) {
enterExMode();
m_currentMessage.clear();
m_commandBuffer = "'<,'>!";
- m_commandHistory.append(QString());
- m_commandHistoryIndex = m_commandHistory.size() - 1;
+ //g.commandHistory.append(QString());
updateMiniBuffer();
} else if (input.is('"')) {
m_submode = RegisterSubMode;
- } else if (input.isKey(Key_Return)) {
+ } else if (input.isReturn()) {
moveToStartOfLine();
moveDown();
moveToFirstNonBlankOnLine();
} else if (input.is(',')) {
passShortcuts(true);
} else if (input.is('.')) {
- //qDebug() << "REPEATING" << quoteUnprintable(m_dotCommand) << count();
- QString savedCommand = m_dotCommand;
- m_dotCommand.clear();
+ //qDebug() << "REPEATING" << quoteUnprintable(g.dotCommand) << count()
+ // << input;
+ QString savedCommand = g.dotCommand;
+ g.dotCommand.clear();
replay(savedCommand, count());
enterCommandMode();
- m_dotCommand = savedCommand;
+ g.dotCommand = savedCommand;
} else if (input.is('<') && isNoVisualMode()) {
m_submode = ShiftLeftSubMode;
} else if (input.is('<') && isVisualMode()) {
finishMovement();
} else if ((!isVisualMode() && input.is('a')) || (isVisualMode() && input.is('A'))) {
leaveVisualMode();
+ setUndoPosition(position());
+ breakEditBlock();
enterInsertMode();
m_lastInsertion.clear();
if (!atEndOfLine())
moveRight();
updateMiniBuffer();
} else if (input.is('A')) {
+ setUndoPosition(position());
+ breakEditBlock();
enterInsertMode();
moveBehindEndOfLine();
setDotCommand(QString(QLatin1Char('A')));
} else if ((input.is('d') || input.is('x')) && isVisualLineMode()) {
leaveVisualMode();
m_rangemode = RangeLineMode;
- yankSelectedText();
- removeSelectedText();
+ yankText(currentRange(), m_register);
+ removeText(currentRange());
handleStartOfLine();
} else if ((input.is('d') || input.is('x')) && isVisualBlockMode()) {
leaveVisualMode();
m_rangemode = RangeBlockMode;
- yankSelectedText();
- removeSelectedText();
+ yankText(currentRange(), m_register);
+ removeText(currentRange());
setPosition(qMin(position(), anchor()));
} else if (input.is('D') && isNoVisualMode()) {
if (atEndOfLine())
leaveVisualMode();
m_rangemode = RangeLineMode;
m_submode = NoSubMode;
- yankSelectedText();
- removeSelectedText();
+ yankText(currentRange(), m_register);
+ removeText(currentRange());
moveToFirstNonBlankOnLine();
} else if ((input.is('D') || input.is('X')) && isVisualBlockMode()) {
leaveVisualMode();
m_rangemode = RangeBlockAndTailMode;
- yankSelectedText();
- removeSelectedText();
+ yankText(currentRange(), m_register);
+ removeText(currentRange());
setPosition(qMin(position(), anchor()));
} else if (input.isControl('d')) {
int sline = cursorLineOnScreen();
m_tc.setPosition(firstPositionInLine(n), KeepAnchor);
}
finishMovement(dotCommand);
- } else if (input.is('h') || input.isKey(Key_Left)
- || input.isKey(Key_Backspace) || input.isControl('h')) {
+ } else if (input.is('h') || input.isKey(Key_Left) || input.isBackspace()) {
m_movetype = MoveExclusive;
int n = qMin(count(), leftDist());
if (m_fakeEnd && m_tc.block().length() > 1)
finishMovement();
} else if (!isVisualMode() && (input.is('i') || input.isKey(Key_Insert))) {
setDotCommand(QString(QLatin1Char('i'))); // setDotCommand("%1i", count());
+ setUndoPosition(position());
+ breakEditBlock();
enterInsertMode();
updateMiniBuffer();
if (atEndOfLine())
m_gflag = false;
m_tc.clearSelection();
}
+ setUndoPosition(position());
+ breakEditBlock();
enterInsertMode();
} else if (input.isControl('i')) {
if (!m_jumpListRedo.isEmpty()) {
setAnchor();
moveRight();
if (m_gflag) {
- removeSelectedText();
+ removeText(currentRange());
} else {
while (characterAtCursor() == ' '
|| characterAtCursor() == '\t')
moveRight();
- removeSelectedText();
+ removeText(currentRange());
m_tc.insertText(QString(QLatin1Char(' ')));
}
}
handleStartOfLine();
finishMovement();
} else if (input.is('n') || input.is('N')) {
- search(lastSearchString(), m_lastSearchForward);
- recordJump();
+ SearchData sd;
+ sd.needle = g.searchHistory.current();
+ sd.forward = input.is('n') ? m_lastSearchForward : !m_lastSearchForward;
+ sd.highlightCursor = false;
+ sd.highlightMatches = true;
+ search(sd);
} else if (isVisualMode() && (input.is('o') || input.is('O'))) {
int pos = position();
setPosition(anchor());
if (m_positionPastEnd)
m_visualTargetColumn = -1;
updateSelection();
- } else if (input.is('o') || input.is('O')) {
- beginEditBlock();
+ } else if (input.is('o')) {
setDotCommand("%1o", count());
+ setUndoPosition(position());
+ breakEditBlock();
enterInsertMode();
- m_beginEditBlock = false;
+ beginEditBlock(position());
moveToFirstNonBlankOnLine();
- if (input.is('O'))
- moveToStartOfLine();
- else
- moveBehindEndOfLine();
- m_tc.insertText("\n");
- if (input.is('O'))
- moveUp();
- insertAutomaticIndentation(input.is('o'));
+ moveBehindEndOfLine();
+ insertText(Register("\n"));
+ insertAutomaticIndentation(true);
+ endEditBlock();
+ } else if (input.is('O')) {
+ setDotCommand("%1O", count());
+ setUndoPosition(position());
+ breakEditBlock();
+ enterInsertMode();
+ beginEditBlock(position());
+ moveToFirstNonBlankOnLine();
+ moveToStartOfLine();
+ insertText(Register("\n"));
+ moveUp();
+ insertAutomaticIndentation(false);
endEditBlock();
} else if (input.isControl('o')) {
if (!m_jumpListUndo.isEmpty()) {
setTargetColumn();
setDotCommand("%1p", count());
finishMovement();
- } else if (isVisualMode() && input.is('r')) {
- m_submode = TransformSubMode;
- m_subsubmode = ReplaceSubSubMode;
} else if (input.is('r')) {
m_submode = ReplaceSubMode;
- setDotCommand(QString(QLatin1Char('r')));
} else if (!isVisualMode() && input.is('R')) {
- // FIXME: right now we repeat the insertion count() times,
- // but not the deletion
- m_lastInsertion.clear();
- enterInsertMode();
- m_submode = ReplaceSubMode;
- setDotCommand(QString(QLatin1Char('R')));
+ setUndoPosition(position());
+ breakEditBlock();
+ enterReplaceMode();
updateMiniBuffer();
} else if (input.isControl('r')) {
redo();
moveLeft();
setAnchor();
moveRight(qMin(count(), rightDist()));
- yankSelectedText();
- removeSelectedText();
+ yankText(currentRange(), m_register);
+ removeText(currentRange());
setDotCommand("%1s", count());
m_opcount.clear();
m_mvcount.clear();
+ setUndoPosition(position());
+ breakEditBlock();
enterInsertMode();
} else if (input.is('S')) {
if (!isVisualMode()) {
setPosition(lastPositionInLine(line + count() - 1));
}
setDotCommand("%1S", count());
+ setUndoPosition(position());
+ breakEditBlock();
enterInsertMode();
- m_beginEditBlock = false;
m_submode = ChangeSubMode;
m_movetype = MoveLineWise;
finishMovement();
if (leftDist() > 0) {
setAnchor();
moveLeft(qMin(count(), leftDist()));
- yankSelectedText();
- removeSelectedText();
+ yankText(currentRange(), m_register);
+ removeText(currentRange());
}
finishMovement();
} else if ((m_submode == YankSubMode && input.is('y'))
|| (input.is('Y') && isVisualLineMode())
|| (input.is('Y') && isVisualCharMode())) {
m_rangemode = RangeLineMode;
- yankSelectedText();
+ yankText(currentRange(), m_register);
setPosition(qMin(position(), anchor()));
moveToStartOfLine();
leaveVisualMode();
finishMovement();
} else if ((input.is('y') || input.is('Y')) && isVisualBlockMode()) {
m_rangemode = RangeBlockMode;
- yankSelectedText();
+ yankText(currentRange(), m_register);
setPosition(qMin(position(), anchor()));
leaveVisualMode();
finishMovement();
} else if (input.is('Z')) {
m_submode = CapitalZSubMode;
} else if (!m_gflag && input.is('~') && !isVisualMode()) {
+ m_movetype = MoveExclusive;
if (!atEndOfLine()) {
beginEditBlock();
setAnchor();
moveRight(qMin(count(), rightDist()));
if (input.is('~')) {
- invertCaseSelectedText();
+ invertCase(currentRange());
setDotCommand("%1~", count());
} else if (input.is('u')) {
- downCaseSelectedText();
+ downCase(currentRange());
setDotCommand("%1gu", count());
} else if (input.is('U')) {
- upCaseSelectedText();
+ upCase(currentRange());
setDotCommand("%1gU", count());
}
endEditBlock();
|| (m_gflag && input.is('u') && !isVisualMode())
|| (m_gflag && input.is('U') && !isVisualMode())) {
m_gflag = false;
+ m_movetype = MoveExclusive;
if (atEndOfLine())
moveLeft();
setAnchor();
|| (m_gflag && input.is('u') && isVisualMode())
|| (m_gflag && input.is('U') && isVisualMode())) {
m_gflag = false;
+ m_movetype = MoveExclusive;
if (isVisualLineMode())
m_rangemode = RangeLineMode;
else if (isVisualBlockMode())
} else if (input.is(Key_Delete)) {
setAnchor();
moveRight(qMin(1, rightDist()));
- removeSelectedText();
+ removeText(currentRange());
} else if (input.is(Key_BracketLeft) || input.is(Key_BracketRight)) {
} else if (input.isControl(Key_BracketRight)) {
return handled;
}
+EventResult FakeVimHandler::Private::handleReplaceMode(const Input &input)
+{
+ if (input.isEscape()) {
+ moveLeft(qMin(1, leftDist()));
+ setTargetColumn();
+ m_submode = NoSubMode;
+ m_mode = CommandMode;
+ finishMovement();
+ } else {
+ joinPreviousEditBlock();
+ if (!atEndOfLine()) {
+ setAnchor();
+ moveRight();
+ m_lastDeletion += selectText(Range(position(), anchor()));
+ removeText(currentRange());
+ }
+ const QString text = input.text();
+ m_lastInsertion += text;
+ insertText(text);
+ endEditBlock();
+ }
+ return EventHandled;
+}
+
EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
{
//const int key = input.key;
//const QString &text = input.text;
- if (input.isKey(Key_Escape) || input.isKey(27) || input.isControl('c') ||
- input.isControl(Key_BracketLeft)) {
+ if (input.isEscape()) {
if (isVisualBlockMode() && !m_lastInsertion.contains('\n')) {
leaveVisualMode();
joinPreviousEditBlock();
setTargetColumn();
for (int i = 0; i < m_visualInsertCount; ++i) {
moveDown();
- m_tc.insertText(m_lastInsertion);
+ insertText(m_lastInsertion);
}
moveLeft(1);
Range range(pos, position(), RangeBlockMode);
setDotCommand("p");
endEditBlock();
} else {
- // normal insertion. start with '1', as one instance was
- // already physically inserted while typing
- QString data = m_lastInsertion;
- for (int i = 1; i < count(); ++i) {
- m_tc.insertText(m_lastInsertion);
+ // Normal insertion. Start with '1', as one instance was
+ // already physically inserted while typing.
+ QString data;
+ for (int i = 1; i < count(); ++i)
data += m_lastInsertion;
- }
+ insertText(data);
moveLeft(qMin(1, leftDist()));
setTargetColumn();
leaveVisualMode();
- recordNewUndo();
}
- m_dotCommand += m_lastInsertion;
- m_dotCommand += QChar(27);
+ g.dotCommand += m_lastInsertion;
+ g.dotCommand += QChar(27);
enterCommandMode();
m_submode = NoSubMode;
} else if (input.isKey(Key_Insert)) {
- if (m_submode == ReplaceSubMode) {
- EDITOR(setCursorWidth(m_cursorWidth));
- EDITOR(setOverwriteMode(false));
- m_submode = NoSubMode;
- } else {
- EDITOR(setCursorWidth(m_cursorWidth));
- EDITOR(setOverwriteMode(true));
- m_submode = ReplaceSubMode;
- }
+ if (m_mode == ReplaceMode)
+ m_mode = InsertMode;
+ else
+ m_mode = ReplaceMode;
+ updateCursor();
} else if (input.isKey(Key_Left)) {
moveLeft(count());
setTargetColumn();
moveBehindEndOfLine();
setTargetColumn();
m_lastInsertion.clear();
- } else if (input.isKey(Key_Return)) {
+ } else if (input.isReturn()) {
m_submode = NoSubMode;
- m_tc.insertBlock();
+ insertText(Register("\n"));
m_lastInsertion += "\n";
insertAutomaticIndentation(true);
setTargetColumn();
- } else if (input.isKey(Key_Backspace) || input.isControl('h')) {
+ } else if (input.isBackspace()) {
joinPreviousEditBlock();
m_justAutoIndented = 0;
if (!m_lastInsertion.isEmpty() || hasConfig(ConfigBackspace, "start")) {
m_lastInsertion.clear(); // FIXME
} else {
m_tc.deletePreviousChar();
+ fixMarks(position(), -1);
m_lastInsertion.chop(1);
}
setTargetColumn();
const int col = physicalCursorColumnInDocument();
QString str = QString(ts - col % ts, ' ');
m_lastInsertion.append(str);
- m_tc.insertText(str);
+ insertText(str);
setTargetColumn();
} else if (input.isControl('d')) {
// remove one level of indentation from the current line
//} else if (key >= control('a') && key <= control('z')) {
// // ignore these
} else if (!input.text().isEmpty()) {
- const QString text = input.text();
- if (m_beginEditBlock) {
- beginEditBlock();
- m_beginEditBlock = false;
- } else {
- joinPreviousEditBlock();
- }
+ joinPreviousEditBlock();
m_justAutoIndented = 0;
+ const QString text = input.text();
m_lastInsertion.append(text);
- if (m_submode == ReplaceSubMode) {
- if (atEndOfLine())
- m_submode = NoSubMode;
- else
- m_tc.deleteChar();
- }
- m_tc.insertText(text);
+ insertText(text);
if (hasConfig(ConfigSmartIndent) && isElectricCharacter(text.at(0))) {
const QString leftText = m_tc.block().text()
.left(m_tc.position() - 1 - m_tc.block().position());
if (leftText.simplified().isEmpty()) {
- Range range(position(), position());
- range.rangemode = m_rangemode;
+ Range range(position(), position(), m_rangemode);
indentText(range, text.at(0));
}
}
- if (!m_inReplay)
+ if (!g.inReplay)
emit q->completionRequested();
setTargetColumn();
endEditBlock();
return EventHandled;
}
-EventResult FakeVimHandler::Private::handleMiniBufferModes(const Input &input)
+EventResult FakeVimHandler::Private::handleExMode(const Input &input)
{
- if (input.isKey(Key_Escape) || input.isControl('c')
- || input.isControl(Key_BracketLeft)) {
+ if (input.isEscape()) {
m_commandBuffer.clear();
enterCommandMode();
updateMiniBuffer();
- } else if (input.isKey(Key_Backspace)) {
+ } else if (input.isBackspace()) {
if (m_commandBuffer.isEmpty()) {
+ m_commandPrefix.clear();
enterCommandMode();
} else {
m_commandBuffer.chop(1);
if (!m_commandBuffer.isEmpty())
m_commandBuffer.chop(1);
updateMiniBuffer();
- } else if (input.isKey(Key_Return) && m_mode == ExMode) {
+ } else if (input.isReturn()) {
if (!m_commandBuffer.isEmpty()) {
- m_commandHistory.takeLast();
- m_commandHistory.append(m_commandBuffer);
+ //g.commandHistory.takeLast();
+ g.commandHistory.append(m_commandBuffer);
handleExCommand(m_commandBuffer);
- if (m_textedit || m_plaintextedit) {
+ if (m_textedit || m_plaintextedit)
leaveVisualMode();
- }
}
- } else if (input.isKey(Key_Return) && isSearchMode()
- && !hasConfig(ConfigIncSearch)) {
- if (!m_commandBuffer.isEmpty()) {
- m_searchHistory.takeLast();
- m_searchHistory.append(m_commandBuffer);
- m_lastSearchForward = (m_mode == SearchForwardMode);
- search(lastSearchString(), m_lastSearchForward);
- recordJump();
- }
- enterCommandMode();
updateMiniBuffer();
- } else if ((input.isKey(Key_Up) || input.isKey(Key_PageUp)) && isSearchMode()) {
- // FIXME: This and the three cases below are wrong as vim
- // takes only matching entries in the history into account.
- if (m_searchHistoryIndex > 0) {
- --m_searchHistoryIndex;
- showBlackMessage(m_searchHistory.at(m_searchHistoryIndex));
- }
- } else if ((input.isKey(Key_Up) || input.isKey(Key_PageUp)) && m_mode == ExMode) {
- if (m_commandHistoryIndex > 0) {
- --m_commandHistoryIndex;
- showBlackMessage(m_commandHistory.at(m_commandHistoryIndex));
- }
- } else if ((input.isKey(Key_Down) || input.isKey(Key_PageDown)) && isSearchMode()) {
- if (m_searchHistoryIndex < m_searchHistory.size() - 1) {
- ++m_searchHistoryIndex;
- showBlackMessage(m_searchHistory.at(m_searchHistoryIndex));
- }
- } else if ((input.isKey(Key_Down) || input.isKey(Key_PageDown)) && m_mode == ExMode) {
- if (m_commandHistoryIndex < m_commandHistory.size() - 1) {
- ++m_commandHistoryIndex;
- showBlackMessage(m_commandHistory.at(m_commandHistoryIndex));
- }
+ } else if (input.isKey(Key_Up) || input.isKey(Key_PageUp)) {
+ g.commandHistory.up();
+ m_commandBuffer = g.commandHistory.current();
+ updateMiniBuffer();
+ } else if (input.isKey(Key_Down) || input.isKey(Key_PageDown)) {
+ g.commandHistory.down();
+ m_commandBuffer = g.commandHistory.current();
+ updateMiniBuffer();
} else if (input.isKey(Key_Tab)) {
m_commandBuffer += QChar(9);
updateMiniBuffer();
- } else if (input.isKey(Key_Return) && isSearchMode()
- && hasConfig(ConfigIncSearch)) {
+ } else if (!input.text().isEmpty()) {
+ m_commandBuffer += input.text();
+ updateMiniBuffer();
+ } else {
+ qDebug() << "IGNORED IN EX-MODE: " << input.key() << input.text();
+ return EventUnhandled;
+ }
+ return EventHandled;
+}
+
+EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
+{
+ if (input.isEscape()) {
+ m_commandBuffer.clear();
+ m_searchCursor = QTextCursor();
+ updateSelection();
+ enterCommandMode();
+ updateMiniBuffer();
+ } else if (input.isBackspace()) {
+ if (m_commandBuffer.isEmpty()) {
+ m_commandPrefix.clear();
+ m_searchCursor = QTextCursor();
+ enterCommandMode();
+ } else {
+ m_commandBuffer.chop(1);
+ }
+ updateMiniBuffer();
+ } else if (input.isKey(Key_Left)) {
+ if (!m_commandBuffer.isEmpty())
+ m_commandBuffer.chop(1);
+ updateMiniBuffer();
+ } else if (input.isReturn()) {
+ m_searchCursor = QTextCursor();
+ QString needle = m_commandBuffer;
+ if (!needle.isEmpty()) {
+ g.searchHistory.append(needle);
+ if (!hasConfig(ConfigIncSearch)) {
+ SearchData sd;
+ sd.needle = needle;
+ sd.forward = m_lastSearchForward;
+ sd.highlightCursor = false;
+ sd.highlightMatches = true;
+ search(sd);
+ }
+ finishMovement(m_commandPrefix + needle + "\n");
+ }
enterCommandMode();
- QString needle = m_commandBuffer.mid(1); // FIXME: why
highlightMatches(needle);
updateMiniBuffer();
- } else if (isSearchMode() && hasConfig(ConfigIncSearch)) {
- m_commandBuffer = m_commandBuffer.mid(1); // FIXME: why
- QString needle = m_commandBuffer + input.text();
- search(needle, m_lastSearchForward, true);
+ } else if (input.isKey(Key_Up) || input.isKey(Key_PageUp)) {
+ // FIXME: This and the three cases below are wrong as vim
+ // takes only matching entries in the history into account.
+ g.searchHistory.up();
+ showBlackMessage(g.searchHistory.current());
+ } else if (input.isKey(Key_Down) || input.isKey(Key_PageDown)) {
+ g.searchHistory.down();
+ showBlackMessage(g.searchHistory.current());
+ } else if (input.isKey(Key_Tab)) {
+ m_commandBuffer += QChar(9);
updateMiniBuffer();
- recordJump();
} else if (!input.text().isEmpty()) {
m_commandBuffer += input.text();
updateMiniBuffer();
- } else {
- qDebug() << "IGNORED IN MINIBUFFER MODE: " << input.key() << input.text();
- return EventUnhandled;
}
+
+ if (hasConfig(ConfigIncSearch) && !input.isReturn() && !input.isEscape()) {
+ SearchData sd;
+ sd.needle = m_commandBuffer;
+ sd.forward = m_lastSearchForward;
+ sd.mustMove = false;
+ sd.highlightCursor = true;
+ sd.highlightMatches = false;
+ search(sd);
+ }
+
+ //else {
+ // qDebug() << "IGNORED IN SEARCH MODE: " << input.key() << input.text();
+ // return EventUnhandled;
+ //}
return EventHandled;
}
-// 1 based.
+// This uses 1 based line counting.
int FakeVimHandler::Private::readLineCode(QString &cmd)
{
//qDebug() << "CMD: " << cmd;
return -1;
QChar c = cmd.at(0);
cmd = cmd.mid(1);
- if (c == '.')
+ if (c == '.') {
+ if (cmd.isEmpty())
+ return cursorLineInDocument() + 1;
+ QChar c1 = cmd.at(0);
+ if (c1 == '+' || c1 == '-') {
+ // Repeat for things like .+4
+ cmd = cmd.mid(1);
+ return cursorLineInDocument() + readLineCode(cmd);
+ }
return cursorLineInDocument() + 1;
+ }
if (c == '$')
return linesInDocument();
if (c == '\'' && !cmd.isEmpty()) {
- int mark = m_marks.value(cmd.at(0).unicode());
- if (!mark) {
- showRedMessage(msgE20MarkNotSet(cmd.at(0)));
+ if (cmd.isEmpty()) {
+ showRedMessage(msgMarkNotSet(QString()));
+ return -1;
+ }
+ int m = mark(cmd.at(0).unicode());
+ if (m == -1) {
+ showRedMessage(msgMarkNotSet(cmd.at(0)));
cmd = cmd.mid(1);
return -1;
}
cmd = cmd.mid(1);
- return lineForPosition(mark);
+ return lineForPosition(m);
}
if (c == '-') {
int n = readLineCode(cmd);
return cursorLineInDocument() + 1 + (n == -1 ? 1 : n);
}
if (c == '\'' && !cmd.isEmpty()) {
- int pos = m_marks.value(cmd.at(0).unicode(), -1);
- //qDebug() << " MARK: " << cmd.at(0) << pos << lineForPosition(pos);
+ int pos = mark(cmd.at(0).unicode());
if (pos == -1) {
- showRedMessage(msgE20MarkNotSet(cmd.at(0)));
+ showRedMessage(msgMarkNotSet(cmd.at(0)));
cmd = cmd.mid(1);
return -1;
}
//qDebug() << "N: " << n;
return n;
}
- // not parsed
+ // Parsing failed.
cmd = c + cmd;
return -1;
}
-void FakeVimHandler::Private::selectRange(int beginLine, int endLine)
+void FakeVimHandler::Private::setCurrentRange(const Range &range)
{
- if (beginLine == -1)
- beginLine = cursorLineInDocument();
- if (endLine == -1)
- endLine = cursorLineInDocument();
- if (beginLine > endLine)
- qSwap(beginLine, endLine);
- setAnchor(firstPositionInLine(beginLine));
- if (endLine == linesInDocument())
- setPosition(lastPositionInLine(endLine));
- else
- setPosition(firstPositionInLine(endLine + 1));
+ setAnchor(range.beginPos);
+ setPosition(range.endPos);
+ m_rangemode = range.rangemode;
}
// use handleExCommand for invoking commands that might move the cursor
EDITOR(setTextCursor(m_tc));
}
-QString FakeVimHandler::Private::extractCommand(const QString &line,
- int *beginLine, int *endLine)
-{
- QString cmd = line;
- *beginLine = -1;
- *endLine = -1;
-
- // FIXME: that seems to be different for %w and %s
- if (cmd.startsWith(QLatin1Char('%')))
- cmd = "1,$" + cmd.mid(1);
-
- int lineNumber = readLineCode(cmd);
- if (lineNumber != -1)
- *beginLine = lineNumber;
-
- if (cmd.startsWith(',')) {
- cmd = cmd.mid(1);
- lineNumber = readLineCode(cmd);
- if (lineNumber != -1)
- *endLine = lineNumber;
- }
- //qDebug() << "RANGE: " << beginLine << endLine << cmd << lineNumber << m_marks;
- return cmd;
-}
-
-bool FakeVimHandler::Private::handleExSubstituteCommand(const QString &line)
+bool FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd)
// :substitute
{
- int beginLine, endLine;
- QString cmd = extractCommand(line, &beginLine, &endLine);
-
- if (cmd.startsWith(QLatin1String("substitute")))
- cmd = cmd.mid(10);
- else if (cmd.startsWith('s') && line.size() > 1
- && !isalpha(cmd.at(1).unicode()))
- cmd = cmd.mid(1);
+ QString line = cmd.cmd + ' ' + cmd.args;
+ line = line.trimmed();
+ if (line.startsWith(_("substitute")))
+ line = line.mid(10);
+ else if (line.startsWith('s') && line.size() > 1
+ && !isalpha(line.at(1).unicode()))
+ line = line.mid(1);
else
return false;
+
// we have /{pattern}/{string}/[flags] now
- if (cmd.isEmpty())
+ if (line.isEmpty())
return false;
- const QChar separator = cmd.at(0);
+ const QChar separator = line.at(0);
int pos1 = -1;
int pos2 = -1;
int i;
- for (i = 1; i < cmd.size(); ++i) {
- if (cmd.at(i) == separator && cmd.at(i - 1) != '\\') {
+ for (i = 1; i < line.size(); ++i) {
+ if (line.at(i) == separator && line.at(i - 1) != '\\') {
pos1 = i;
break;
}
}
if (pos1 == -1)
return false;
- for (++i; i < cmd.size(); ++i) {
- if (cmd.at(i) == separator && cmd.at(i - 1) != '\\') {
+ for (++i; i < line.size(); ++i) {
+ if (line.at(i) == separator && line.at(i - 1) != '\\') {
pos2 = i;
break;
}
}
if (pos2 == -1)
- pos2 = cmd.size();
+ pos2 = line.size();
- QString needle = cmd.mid(1, pos1 - 1);
- const QString replacement = cmd.mid(pos1 + 1, pos2 - pos1 - 1);
- QString flags = cmd.mid(pos2 + 1);
+ QString needle = line.mid(1, pos1 - 1);
+ const QString replacement = line.mid(pos1 + 1, pos2 - pos1 - 1);
+ QString flags = line.mid(pos2 + 1);
needle.replace('$', '\n');
needle.replace("\\\n", "\\$");
if (flags.contains('i'))
pattern.setCaseSensitivity(Qt::CaseInsensitive);
const bool global = flags.contains('g');
+ const int beginLine = lineForPosition(cmd.range.beginPos);
+ const int endLine = lineForPosition(cmd.range.endPos);
beginEditBlock();
for (int line = endLine; line >= beginLine; --line) {
QString origText = lineContents(line);
return true;
}
-bool FakeVimHandler::Private::handleExMapCommand(const QString &line) // :map
+bool FakeVimHandler::Private::handleExMapCommand(const ExCommand &cmd0) // :map
{
- int pos1 = line.indexOf(QLatin1Char(' '));
- if (pos1 == -1)
- return false;
- int pos2 = line.indexOf(QLatin1Char(' '), pos1 + 1);
- if (pos2 == -1)
- return false;
-
QByteArray modes;
enum Type { Map, Noremap, Unmap } type;
- QByteArray cmd = line.left(pos1).toLatin1();
+ QByteArray cmd = cmd0.cmd.toLatin1();
// Strange formatting. But everything else is even uglier.
if (cmd == "map") { modes = "nvo"; type = Map; } else
else
return false;
- QString lhs = line.mid(pos1 + 1, pos2 - pos1 - 1);
- QString rhs = line.mid(pos2 + 1);
+ const int pos = cmd0.args.indexOf(QLatin1Char(' '));
+ if (pos == -1) {
+ // FIXME: Dump mappings here.
+ //qDebug() << g.mappings;
+ return true;;
+ }
+
+ QString lhs = cmd0.args.left(pos);
+ QString rhs = cmd0.args.mid(pos + 1);
Inputs key;
- foreach (QChar c, lhs)
- key.append(Input(c));
+ key.parseFrom(lhs);
//qDebug() << "MAPPING: " << modes << lhs << rhs;
switch (type) {
case Unmap:
foreach (char c, modes)
- if (m_mappings.contains(c))
- m_mappings[c].remove(key);
+ if (g.mappings.contains(c))
+ g.mappings[c].remove(key);
break;
case Map:
rhs = rhs; // FIXME: expand rhs.
// Fall through.
case Noremap: {
- Inputs inputs;
- foreach (QChar c, rhs)
- inputs.append(Input(c));
+ Inputs inputs(rhs);
foreach (char c, modes)
- m_mappings[c].insert(key, inputs);
+ g.mappings[c].insert(key, inputs);
break;
}
}
return true;
}
-bool FakeVimHandler::Private::handleExHistoryCommand(const QString &cmd) // :history
+bool FakeVimHandler::Private::handleExHistoryCommand(const ExCommand &cmd)
{
- static QRegExp reHistory("^his(tory)?( (.*))?$");
- if (reHistory.indexIn(cmd) == -1)
+ // :history
+ if (cmd.cmd != "his" && cmd.cmd != "history")
return false;
- QString arg = reHistory.cap(3);
- if (arg.isEmpty()) {
+ if (cmd.args.isEmpty()) {
QString info;
info += "# command history\n";
int i = 0;
- foreach (const QString &item, m_commandHistory) {
+ foreach (const QString &item, g.commandHistory.items()) {
++i;
info += QString("%1 %2\n").arg(i, -8).arg(item);
}
return true;
}
-bool FakeVimHandler::Private::handleExSetCommand(const QString &cmd) // :set
+bool FakeVimHandler::Private::handleExRegisterCommand(const ExCommand &cmd)
+{
+ // :reg and :di[splay]
+ if (cmd.cmd != "reg" && cmd.cmd != "registers"
+ && cmd.cmd != "di" && cmd.cmd != "display")
+ return false;
+
+ QByteArray regs = cmd.args.toLatin1();
+ if (regs.isEmpty()) {
+ regs = "\"0123456789";
+ QHashIterator<int, Register> it(g.registers);
+ while (it.hasNext()) {
+ it.next();
+ if (it.key() > '9')
+ regs += char(it.key());
+ }
+ }
+ QString info;
+ info += "--- Registers ---\n";
+ foreach (char reg, regs) {
+ QString value = quoteUnprintable(g.registers[reg].contents);
+ info += QString("\"%1 %2\n").arg(reg).arg(value);
+ }
+ emit q->extraInformationChanged(info);
+ updateMiniBuffer();
+ return true;
+}
+
+bool FakeVimHandler::Private::handleExSetCommand(const ExCommand &cmd)
{
- static QRegExp reSet("^set?( (.*))?$");
- if (reSet.indexIn(cmd) == -1)
+ // :set
+ if (cmd.cmd != "se" && cmd.cmd != "set")
return false;
showBlackMessage(QString());
- QString arg = reSet.cap(2);
- SavedAction *act = theFakeVimSettings()->item(arg);
- if (arg.isEmpty()) {
- theFakeVimSetting(SettingsDialog)->trigger(QVariant());
- } else if (act && act->value().type() == QVariant::Bool) {
- // boolean config to be switched on
+ SavedAction *act = theFakeVimSettings()->item(cmd.args);
+ QTC_ASSERT(!cmd.args.isEmpty(), /**/); // Handled by plugin.
+ if (act && act->value().type() == QVariant::Bool) {
+ // Boolean config to be switched on.
bool oldValue = act->value().toBool();
if (oldValue == false)
act->setValue(true);
else if (oldValue == true)
{} // nothing to do
} else if (act) {
- // non-boolean to show
- showBlackMessage(arg + '=' + act->value().toString());
- } else if (arg.startsWith(QLatin1String("no"))
- && (act = theFakeVimSettings()->item(arg.mid(2)))) {
- // boolean config to be switched off
+ // Non-boolean to show.
+ showBlackMessage(cmd.args + '=' + act->value().toString());
+ } else if (cmd.args.startsWith(_("no"))
+ && (act = theFakeVimSettings()->item(cmd.args.mid(2)))) {
+ // Boolean config to be switched off.
bool oldValue = act->value().toBool();
if (oldValue == true)
act->setValue(false);
else if (oldValue == false)
{} // nothing to do
- } else if (arg.contains('=')) {
- // non-boolean config to set
- int p = arg.indexOf('=');
- act = theFakeVimSettings()->item(arg.left(p));
+ } else if (cmd.args.contains('=')) {
+ // Non-boolean config to set.
+ int p = cmd.args.indexOf('=');
+ act = theFakeVimSettings()->item(cmd.args.left(p));
if (act)
- act->setValue(arg.mid(p + 1));
+ act->setValue(cmd.args.mid(p + 1));
} else {
- passUnknownSetCommand(arg);
+ showRedMessage(FakeVimHandler::tr("Unknown option: ") + cmd.args);
}
updateMiniBuffer();
updateEditor();
return true;
}
-bool FakeVimHandler::Private::handleExNormalCommand(const QString &cmd) // :normal
+bool FakeVimHandler::Private::handleExNormalCommand(const ExCommand &cmd)
{
- static QRegExp reNormal("^norm(al)?( (.*))?$");
- if (reNormal.indexIn(cmd) == -1)
+ // :normal
+ if (cmd.cmd != "norm" && cmd.cmd != "normal")
return false;
- //qDebug() << "REPLAY: " << reNormal.cap(3);
- replay(reNormal.cap(3), 1);
+ //qDebug() << "REPLAY NORMAL: " << quoteUnprintable(reNormal.cap(3));
+ replay(cmd.args, 1);
return true;
}
-bool FakeVimHandler::Private::handleExDeleteCommand(const QString &line) // :d
+bool FakeVimHandler::Private::handleExDeleteCommand(const ExCommand &cmd)
{
- int beginLine, endLine;
- QString cmd = extractCommand(line, &beginLine, &endLine);
-
- static QRegExp reDelete("^d( (.*))?$");
- if (reDelete.indexIn(cmd) != -1)
+ // :delete
+ if (cmd.cmd != "d" && cmd.cmd != "delete")
return false;
- selectRange(beginLine, endLine);
- QString reg = reDelete.cap(2);
- QString text = selectedText();
- removeSelectedText();
+ setCurrentRange(cmd.range);
+ QString reg = cmd.args;
+ QString text = selectText(cmd.range);
+ removeText(currentRange());
if (!reg.isEmpty()) {
- Register &r = m_registers[reg.at(0).unicode()];
+ Register &r = g.registers[reg.at(0).unicode()];
r.contents = text;
r.rangemode = RangeLineMode;
}
return true;
}
-bool FakeVimHandler::Private::handleExWriteCommand(const QString &line)
- // :w, :x, :q, :wq, ...
+bool FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd)
{
- int beginLine, endLine;
- QString cmd = extractCommand(line, &beginLine, &endLine);
-
- static QRegExp reWrite("^[wx]q?a?!?( (.*))?$");
- if (reWrite.indexIn(cmd) == -1) // :w and :x
+ // :w, :x, :wq, ...
+ //static QRegExp reWrite("^[wx]q?a?!?( (.*))?$");
+ if (cmd.cmd != "w" && cmd.cmd != "x" && cmd.cmd != "wq")
return false;
- bool noArgs = (beginLine == -1);
+ int beginLine = lineForPosition(cmd.range.beginPos);
+ int endLine = lineForPosition(cmd.range.endPos);
+ const bool noArgs = (beginLine == -1);
if (beginLine == -1)
beginLine = 0;
if (endLine == -1)
endLine = linesInDocument();
//qDebug() << "LINES: " << beginLine << endLine;
- int indexOfSpace = cmd.indexOf(QChar(' '));
- QString prefix;
- if (indexOfSpace < 0)
- prefix = cmd;
- else
- prefix = cmd.left(indexOfSpace);
- bool forced = prefix.contains(QChar('!'));
- bool quit = prefix.contains(QChar('q')) || prefix.contains(QChar('x'));
- bool quitAll = quit && prefix.contains(QChar('a'));
- QString fileName = reWrite.cap(2);
+ QString prefix = cmd.args;
+ const bool forced = cmd.hasBang;
+ //const bool quit = prefix.contains(QChar('q')) || prefix.contains(QChar('x'));
+ //const bool quitAll = quit && prefix.contains(QChar('a'));
+ QString fileName = cmd.args;
if (fileName.isEmpty())
fileName = m_currentFileName;
QFile file1(fileName);
- bool exists = file1.exists();
+ const bool exists = file1.exists();
if (exists && !forced && !noArgs) {
showRedMessage(FakeVimHandler::tr
- ("File '%1' exists (add ! to override)").arg(fileName));
+ ("File \"%1\" exists (add ! to override)").arg(fileName));
} else if (file1.open(QIODevice::ReadWrite)) {
+ // Nobody cared, so act ourselves.
file1.close();
QTextCursor tc = m_tc;
Range range(firstPositionInLine(beginLine),
firstPositionInLine(endLine), RangeLineMode);
- QString contents = text(range);
+ QString contents = selectText(range);
m_tc = tc;
- //qDebug() << "LINES: " << beginLine << endLine;
- bool handled = false;
- emit q->writeFileRequested(&handled, fileName, contents);
- // nobody cared, so act ourselves
- if (!handled) {
- //qDebug() << "HANDLING MANUAL SAVE TO " << fileName;
- QFile::remove(fileName);
- QFile file2(fileName);
- if (file2.open(QIODevice::ReadWrite)) {
- QTextStream ts(&file2);
- ts << contents;
- } else {
- showRedMessage(FakeVimHandler::tr
- ("Cannot open file '%1' for writing").arg(fileName));
- }
+ QFile::remove(fileName);
+ QFile file2(fileName);
+ if (file2.open(QIODevice::ReadWrite)) {
+ QTextStream ts(&file2);
+ ts << contents;
+ } else {
+ showRedMessage(FakeVimHandler::tr
+ ("Cannot open file \"%1\" for writing").arg(fileName));
}
- // check result by reading back
+ // Check result by reading back.
QFile file3(fileName);
file3.open(QIODevice::ReadOnly);
QByteArray ba = file3.readAll();
showBlackMessage(FakeVimHandler::tr("\"%1\" %2 %3L, %4C written")
.arg(fileName).arg(exists ? " " : " [New] ")
.arg(ba.count('\n')).arg(ba.size()));
- if (quitAll)
- passUnknownExCommand(forced ? "qa!" : "qa");
- else if (quit)
- passUnknownExCommand(forced ? "q!" : "q");
+ //if (quitAll)
+ // passUnknownExCommand(forced ? "qa!" : "qa");
+ //else if (quit)
+ // passUnknownExCommand(forced ? "q!" : "q");
} else {
showRedMessage(FakeVimHandler::tr
- ("Cannot open file '%1' for reading").arg(fileName));
+ ("Cannot open file \"%1\" for reading").arg(fileName));
}
return true;
}
-bool FakeVimHandler::Private::handleExReadCommand(const QString &line) // :r
+bool FakeVimHandler::Private::handleExReadCommand(const ExCommand &cmd)
{
- if (!line.startsWith(QLatin1String("r ")))
+ // :read
+ if (cmd.cmd != "r" && cmd.cmd != "read")
return false;
beginEditBlock();
moveToStartOfLine();
setTargetColumn();
moveDown();
- m_currentFileName = line.mid(2);
+ m_currentFileName = cmd.args;
QFile file(m_currentFileName);
file.open(QIODevice::ReadOnly);
QTextStream ts(&file);
return true;
}
-bool FakeVimHandler::Private::handleExBangCommand(const QString &line) // :!
+bool FakeVimHandler::Private::handleExBangCommand(const ExCommand &cmd) // :!
{
- int beginLine, endLine;
- QString cmd = extractCommand(line, &beginLine, &endLine);
-
- if (!cmd.startsWith(QLatin1Char('!')))
+ if (!cmd.cmd.startsWith(QLatin1Char('!')))
return false;
- selectRange(beginLine, endLine);
- int targetPosition = firstPositionInLine(beginLine);
- QString command = cmd.mid(1).trimmed();
- QString text = selectedText();
+ setCurrentRange(cmd.range);
+ int targetPosition = firstPositionInLine(lineForPosition(cmd.range.beginPos));
+ QString command = QString(cmd.cmd.mid(1) + ' ' + cmd.args).trimmed();
+ QString text = selectText(cmd.range);
QProcess proc;
- proc.start(cmd.mid(1));
+ proc.start(command);
proc.waitForStarted();
+#ifdef Q_OS_WIN
+ text.replace(_("\n"), _("\r\n"));
+#endif
proc.write(text.toUtf8());
proc.closeWriteChannel();
proc.waitForFinished();
QString result = QString::fromUtf8(proc.readAllStandardOutput());
beginEditBlock(targetPosition);
- removeSelectedText();
- m_tc.insertText(result);
+ removeText(currentRange());
+ insertText(result);
setPosition(targetPosition);
endEditBlock();
leaveVisualMode();
return true;
}
-bool FakeVimHandler::Private::handleExShiftRightCommand(const QString &line) // :>
+bool FakeVimHandler::Private::handleExShiftCommand(const ExCommand &cmd)
{
- int beginLine, endLine;
- QString cmd = extractCommand(line, &beginLine, &endLine);
-
- if (!cmd.startsWith(QLatin1Char('>')))
+ if (cmd.cmd != "<" && cmd.cmd != ">")
return false;
- m_anchor = firstPositionInLine(beginLine);
- setPosition(firstPositionInLine(endLine));
- shiftRegionRight(1);
+ setCurrentRange(cmd.range);
+ int count = qMin(1, cmd.args.toInt());
+ if (cmd.cmd == "<")
+ shiftRegionLeft(count);
+ else
+ shiftRegionRight(count);
leaveVisualMode();
- showBlackMessage(FakeVimHandler::tr("%n lines >ed %1 time", 0,
- (endLine - beginLine + 1)).arg(1));
+ const int beginLine = lineForPosition(cmd.range.beginPos);
+ const int endLine = lineForPosition(cmd.range.endPos);
+ showBlackMessage(FakeVimHandler::tr("%n lines %1ed %2 time", 0,
+ (endLine - beginLine + 1)).arg(cmd.cmd).arg(count));
return true;
}
-bool FakeVimHandler::Private::handleExRedoCommand(const QString &line) // :redo
+bool FakeVimHandler::Private::handleExRedoCommand(const ExCommand &cmd)
{
- if (line != "red" && line != "redo")
+ // :redo
+ if (cmd.cmd != "red" && cmd.cmd != "redo")
return false;
redo();
return true;
}
-bool FakeVimHandler::Private::handleExGotoCommand(const QString &line) // :<nr>
+bool FakeVimHandler::Private::handleExGotoCommand(const ExCommand &cmd)
{
- int beginLine, endLine;
- QString cmd = extractCommand(line, &beginLine, &endLine);
-
- if (!cmd.isEmpty())
+ // :<nr>
+ if (!cmd.cmd.isEmpty())
return false;
+ const int beginLine = lineForPosition(cmd.range.beginPos);
setPosition(firstPositionInLine(beginLine));
showBlackMessage(QString());
return true;
}
-bool FakeVimHandler::Private::handleExSourceCommand(const QString &line) // :source
+bool FakeVimHandler::Private::handleExSourceCommand(const ExCommand &cmd)
{
- int pos = line.indexOf(' ');
- if (line.leftRef(pos) != "so" && line.leftRef(pos) != "source")
+ // :source
+ if (cmd.cmd != "so" && cmd.cmd != "source")
return false;
- QString fileName = line.mid(pos + 1);
+ QString fileName = cmd.args;
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
showRedMessage(FakeVimHandler::tr("Can't open file %1").arg(fileName));
void FakeVimHandler::Private::handleExCommand(const QString &line0)
{
QString line = line0; // Make sure we have a copy to prevent aliasing.
+ // FIXME: that seems to be different for %w and %s
+ if (line.startsWith(QLatin1Char('%')))
+ line = "1,$" + line.mid(1);
+
+ int beginLine = readLineCode(line);
+ int endLine = -1;
+ if (line.startsWith(',')) {
+ line = line.mid(1);
+ endLine = readLineCode(line);
+ }
+ if (beginLine != -1 && endLine == -1)
+ endLine = beginLine;
+ const int beginPos = firstPositionInLine(beginLine);
+ const int endPos = lastPositionInLine(endLine);
+ ExCommand cmd;
+ const QString arg0 = line.section(' ', 0, 0);
+ cmd.cmd = arg0;
+ cmd.args = line.mid(arg0.size() + 1).trimmed();
+ cmd.range = Range(beginPos, endPos, RangeLineMode);
+ cmd.hasBang = arg0.endsWith('!');
+ if (cmd.hasBang)
+ cmd.cmd.chop(1);
+ //qDebug() << "CMD: " << cmd;
+
enterCommandMode();
showBlackMessage(QString());
- if (handleExCommandHelper(line))
- return;
- int beginLine, endLine;
- passUnknownExCommand(extractCommand(line, &beginLine, &endLine));
+ if (!handleExCommandHelper(cmd))
+ showRedMessage(tr("Not an editor command: %1").arg(cmd.cmd));
}
-bool FakeVimHandler::Private::handleExCommandHelper(const QString &line)
+bool FakeVimHandler::Private::handleExCommandHelper(const ExCommand &cmd)
{
- return handleExGotoCommand(line)
- || handleExBangCommand(line)
- || handleExHistoryCommand(line)
- || handleExMapCommand(line)
- || handleExNormalCommand(line)
- || handleExReadCommand(line)
- || handleExRedoCommand(line)
- || handleExSetCommand(line)
- || handleExShiftRightCommand(line)
- || handleExSourceCommand(line)
- || handleExSubstituteCommand(line)
- || handleExWriteCommand(line);
+ return handleExPluginCommand(cmd)
+ || handleExGotoCommand(cmd)
+ || handleExBangCommand(cmd)
+ || handleExHistoryCommand(cmd)
+ || handleExRegisterCommand(cmd)
+ || handleExDeleteCommand(cmd)
+ || handleExMapCommand(cmd)
+ || handleExNormalCommand(cmd)
+ || handleExReadCommand(cmd)
+ || handleExRedoCommand(cmd)
+ || handleExSetCommand(cmd)
+ || handleExShiftCommand(cmd)
+ || handleExSourceCommand(cmd)
+ || handleExSubstituteCommand(cmd)
+ || handleExWriteCommand(cmd);
}
-void FakeVimHandler::Private::passUnknownExCommand(const QString &cmd)
+bool FakeVimHandler::Private::handleExPluginCommand(const ExCommand &cmd)
{
EDITOR(setTextCursor(m_tc));
- emit q->handleExCommandRequested(cmd);
+ bool handled = false;
+ emit q->handleExCommandRequested(&handled, cmd);
if (m_plaintextedit || m_textedit)
m_tc = EDITOR(textCursor());
-}
-
-void FakeVimHandler::Private::passUnknownSetCommand(const QString &arg)
-{
- bool handled = false;
- emit q->handleSetCommandRequested(&handled, arg);
- if (!handled) {
- showRedMessage(FakeVimHandler::tr("E512: Unknown option: ") + arg);
- }
+ //qDebug() << "HANDLER REQUEST: " << cmd.cmd << handled;
+ return handled;
}
static void vimPatternToQtPattern(QString *needle, QTextDocument::FindFlags *flags)
{
// FIXME: Rough mapping of a common case
- if (needle->startsWith(QLatin1String("\\<")) && needle->endsWith(QLatin1String("\\>")))
+ if (needle->startsWith(_("\\<")) && needle->endsWith(_("\\>")))
(*flags) |= QTextDocument::FindWholeWords;
- needle->remove(QLatin1String("\\<")); // start of word
- needle->remove(QLatin1String("\\>")); // end of word
+ needle->remove(_("\\<")); // start of word
+ needle->remove(_("\\>")); // end of word
//qDebug() << "NEEDLE " << needle0 << needle;
}
-void FakeVimHandler::Private::search(const QString &needle0, bool forward,
- bool incSearch)
+void FakeVimHandler::Private::search(const SearchData &sd)
{
- showBlackMessage((forward ? '/' : '?') + needle0);
- CursorPosition origPosition = cursorPosition();
+ if (sd.needle.isEmpty())
+ return;
+
+ const bool incSearch = hasConfig(ConfigIncSearch);
QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
- if (!forward)
+ if (!sd.forward)
flags |= QTextDocument::FindBackward;
- QString needle = needle0;
+ QString needle = sd.needle;
vimPatternToQtPattern(&needle, &flags);
- if (forward)
- m_tc.movePosition(Right, MoveAnchor, 1);
+ const int oldLine = cursorLineInDocument() - cursorLineOnScreen();
- int oldLine = cursorLineInDocument() - cursorLineOnScreen();
+ int startPos = position();
+ if (sd.mustMove)
+ sd.forward ? ++startPos : --startPos;
- EDITOR(setTextCursor(m_tc));
- if (EDITOR(find(needle, flags))) {
- m_tc = EDITOR(textCursor());
- m_tc.setPosition(m_tc.anchor());
- // making this unconditional feels better, but is not "vim like"
- if (oldLine != cursorLineInDocument() - cursorLineOnScreen())
- scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() / 2);
- if (!incSearch)
- highlightMatches(needle);
- } else {
- m_tc.setPosition(forward ? 0 : lastPositionInDocument());
- EDITOR(setTextCursor(m_tc));
- if (EDITOR(find(needle, flags))) {
- m_tc = EDITOR(textCursor());
- m_tc.setPosition(m_tc.anchor());
- if (oldLine != cursorLineInDocument() - cursorLineOnScreen())
- scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() / 2);
+ m_searchCursor = QTextCursor();
+ QTextCursor tc = m_tc.document()->find(needle, startPos, flags);
+ if (tc.isNull()) {
+ int startPos = sd.forward ? 0 : lastPositionInDocument();
+ tc = m_tc.document()->find(needle, startPos, flags);
+ if (tc.isNull()) {
if (!incSearch) {
- if (forward)
- showRedMessage(FakeVimHandler::tr("search hit BOTTOM, continuing at TOP"));
- else
- showRedMessage(FakeVimHandler::tr("search hit TOP, continuing at BOTTOM"));
- highlightMatches(needle);
- }
- } else {
- if (!incSearch)
highlightMatches(QString());
- setCursorPosition(origPosition);
- if (!incSearch)
showRedMessage(FakeVimHandler::tr("Pattern not found: ") + needle);
+ }
+ updateSelection();
+ return;
+ }
+ if (!incSearch) {
+ QString msg = sd.forward
+ ? FakeVimHandler::tr("search hit BOTTOM, continuing at TOP")
+ : FakeVimHandler::tr("search hit TOP, continuing at BOTTOM");
+ showRedMessage(msg);
}
}
- if (incSearch) {
- QTextEdit::ExtraSelection sel;
- sel.cursor = m_tc;
- sel.format = m_tc.blockCharFormat();
- sel.format.setForeground(Qt::white);
- sel.format.setBackground(Qt::black);
- sel.cursor.setPosition(m_tc.position(), MoveAnchor);
- sel.cursor.setPosition(m_tc.position() + needle.size(), KeepAnchor);
- QList<QTextEdit::ExtraSelection> selections = m_searchSelections;
- selections.append(sel);
- emit q->selectionChanged(selections);
+
+ // Set Cursor.
+ tc.setPosition(qMin(tc.position(), tc.anchor()), MoveAnchor);
+ tc.clearSelection();
+ m_tc = tc;
+ EDITOR(setTextCursor(m_tc));
+
+ // Making this unconditional feels better, but is not "vim like".
+ if (oldLine != cursorLineInDocument() - cursorLineOnScreen())
+ scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() / 2);
+
+ if (incSearch && sd.highlightCursor) {
+ m_searchCursor = m_tc;
+ m_searchCursor.setPosition(m_tc.position(), MoveAnchor);
+ m_searchCursor.setPosition(m_tc.position() + needle.size(), KeepAnchor);
}
setTargetColumn();
+
+ if (sd.highlightMatches)
+ highlightMatches(needle);
+ updateSelection();
+ recordJump();
}
void FakeVimHandler::Private::highlightMatches(const QString &needle0)
return;
m_oldNeedle = needle0;
m_searchSelections.clear();
-
if (!needle0.isEmpty()) {
QTextCursor tc = m_tc;
tc.movePosition(StartOfDocument, MoveAnchor);
QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
QString needle = needle0;
vimPatternToQtPattern(&needle, &flags);
-
-
- EDITOR(setTextCursor(tc));
- while (EDITOR(find(needle, flags))) {
- tc = EDITOR(textCursor());
+ while (1) {
+ tc = tc.document()->find(needle, tc.position(), flags);
+ if (tc.isNull())
+ break;
QTextEdit::ExtraSelection sel;
sel.cursor = tc;
sel.format = tc.blockCharFormat();
sel.format.setBackground(QColor(177, 177, 0));
m_searchSelections.append(sel);
tc.movePosition(Right, MoveAnchor);
- EDITOR(setTextCursor(tc));
}
}
updateSelection();
int beginLine = qMin(lineForPosition(position()), lineForPosition(anchor()));
int endLine = qMax(lineForPosition(position()), lineForPosition(anchor()));
- Range range(anchor(), position());
- range.rangemode = m_rangemode;
+ Range range(anchor(), position(), m_rangemode);
indentText(range, typedChar);
setPosition(firstPositionInLine(beginLine));
int amount = 0;
// lineForPosition has returned 1-based line numbers
- emit q->indentRegion(&amount, beginLine-1, endLine-1, typedChar);
-
+ emit q->indentRegion(&amount, beginLine - 1, endLine - 1, typedChar);
+ fixMarks(firstPositionInLine(beginLine), amount);
+ if (beginLine != endLine)
+ showBlackMessage("MARKS ARE OFF NOW");
return amount;
}
int targetPos = anchor();
if (beginLine > endLine) {
qSwap(beginLine, endLine);
- targetPos = position();
+ targetPos = position();
}
const int shift = config(ConfigShiftWidth).toInt() * repeat;
const int tab = config(ConfigTabStop).toInt();
return block.position() + block.length() - 1;
}
-QString FakeVimHandler::Private::lastSearchString() const
-{
- return m_searchHistory.empty() ? QString() : m_searchHistory.back();
-}
-
-QString FakeVimHandler::Private::text(const Range &range) const
+QString FakeVimHandler::Private::selectText(const Range &range) const
{
if (range.rangemode == RangeCharMode) {
QTextCursor tc = m_tc;
return contents;
}
-void FakeVimHandler::Private::yankSelectedText()
-{
- Range range(anchor(), position());
- range.rangemode = m_rangemode;
- yankText(range, m_register);
-}
-
void FakeVimHandler::Private::yankText(const Range &range, int toregister)
{
- Register ® = m_registers[toregister];
- reg.contents = text(range);
+ Register ® = g.registers[toregister];
+ reg.contents = selectText(range);
reg.rangemode = range.rangemode;
//qDebug() << "YANKED: " << reg.contents;
}
void FakeVimHandler::Private::transformText(const Range &range,
- Transformation transformFunc)
+ Transformation transformFunc, const QVariant &extra)
{
QTextCursor tc = m_tc;
switch (range.rangemode) {
case RangeCharMode: {
+ // This can span multiple lines.
+ beginEditBlock();
tc.setPosition(range.beginPos, MoveAnchor);
tc.setPosition(range.endPos, KeepAnchor);
- (this->*transformFunc)(range.beginPos, &tc);
+ TransformationData td(tc.selectedText(), extra);
+ (this->*transformFunc)(&td);
+ tc.removeSelectedText();
+ fixMarks(range.beginPos, td.to.size() - td.from.size());
+ tc.insertText(td.to);
+ endEditBlock();
return;
}
case RangeLineMode:
case RangeLineModeExclusive: {
+ beginEditBlock(range.beginPos);
tc.setPosition(range.beginPos, MoveAnchor);
tc.movePosition(StartOfLine, MoveAnchor);
tc.setPosition(range.endPos, KeepAnchor);
tc.movePosition(Right, KeepAnchor, 1);
}
}
- (this->*transformFunc)(range.beginPos, &tc);
+ TransformationData td(tc.selectedText(), extra);
+ (this->*transformFunc)(&td);
+ tc.removeSelectedText();
+ fixMarks(range.beginPos, td.to.size() - td.from.size());
+ tc.insertText(td.to);
+ endEditBlock();
return;
}
case RangeBlockAndTailMode:
int eCol = qMin(endColumn + 1, block.length() - 1);
tc.setPosition(block.position() + bCol, MoveAnchor);
tc.setPosition(block.position() + eCol, KeepAnchor);
- (this->*transformFunc)(block.position() + bCol, &tc);
+ TransformationData td(tc.selectedText(), extra);
+ (this->*transformFunc)(&td);
+ tc.removeSelectedText();
+ fixMarks(block.position() + bCol, td.to.size() - td.from.size());
+ tc.insertText(td.to);
block = block.previous();
}
endEditBlock();
}
}
-void FakeVimHandler::Private::removeSelectedText()
+void FakeVimHandler::Private::insertText(const Register ®)
{
- Range range(anchor(), position());
- range.rangemode = m_rangemode;
- removeText(range);
+ QTC_ASSERT(reg.rangemode == RangeCharMode,
+ qDebug() << "WRONG INSERT MODE: " << reg.rangemode; return);
+ fixMarks(position(), reg.contents.length());
+ m_tc.insertText(reg.contents);
}
void FakeVimHandler::Private::removeText(const Range &range)
transformText(range, &FakeVimHandler::Private::removeTransform);
}
-void FakeVimHandler::Private::removeTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::removeTransform(TransformationData *td)
{
- fixMarks(updateMarksAfter, tc->selectionStart() - tc->selectionEnd());
- tc->removeSelectedText();
+ Q_UNUSED(td);
}
-void FakeVimHandler::Private::downCaseSelectedText()
+void FakeVimHandler::Private::downCase(const Range &range)
{
- Range range(anchor(), position());
- range.rangemode = m_rangemode;
transformText(range, &FakeVimHandler::Private::downCaseTransform);
}
-void FakeVimHandler::Private::downCaseTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::downCaseTransform(TransformationData *td)
{
- Q_UNUSED(updateMarksAfter);
- QString str = tc->selectedText();
- tc->removeSelectedText();
- for (int i = str.size(); --i >= 0; ) {
- QChar c = str.at(i);
- str[i] = c.toLower();
- }
- tc->insertText(str);
+ td->to = td->from.toLower();
}
-void FakeVimHandler::Private::upCaseSelectedText()
+void FakeVimHandler::Private::upCase(const Range &range)
{
- Range range(anchor(), position());
- range.rangemode = m_rangemode;
transformText(range, &FakeVimHandler::Private::upCaseTransform);
}
-void FakeVimHandler::Private::upCaseTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::upCaseTransform(TransformationData *td)
{
- Q_UNUSED(updateMarksAfter);
- QString str = tc->selectedText();
- tc->removeSelectedText();
- for (int i = str.size(); --i >= 0; ) {
- QChar c = str.at(i);
- str[i] = c.toUpper();
- }
- tc->insertText(str);
+ td->to = td->from.toUpper();
}
-void FakeVimHandler::Private::invertCaseSelectedText()
+void FakeVimHandler::Private::invertCase(const Range &range)
{
- Range range(anchor(), position());
- range.rangemode = m_rangemode;
transformText(range, &FakeVimHandler::Private::invertCaseTransform);
}
-void FakeVimHandler::Private::invertCaseTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::invertCaseTransform(TransformationData *td)
{
- Q_UNUSED(updateMarksAfter);
- QString str = tc->selectedText();
- tc->removeSelectedText();
- for (int i = str.size(); --i >= 0; ) {
- QChar c = str.at(i);
- str[i] = c.isUpper() ? c.toLower() : c.toUpper();
- }
- tc->insertText(str);
+ foreach (QChar c, td->from)
+ td->to += c.isUpper() ? c.toLower() : c.toUpper();
}
-void FakeVimHandler::Private::replaceSelectedText()
+void FakeVimHandler::Private::replaceText(const Range &range, const QString &str)
{
- Range range(anchor(), position());
- range.rangemode = m_rangemode;
- transformText(range, &FakeVimHandler::Private::replaceTransform);
+ Transformation tr = &FakeVimHandler::Private::replaceByStringTransform;
+ transformText(range, tr, str);
}
-void FakeVimHandler::Private::replaceTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::replaceByStringTransform(TransformationData *td)
{
- Q_UNUSED(updateMarksAfter);
- QString str = tc->selectedText();
- tc->removeSelectedText();
- for (int i = str.size(); --i >= 0; ) {
- QChar c = str.at(i);
- str[i] = (c.toAscii() == '\n' || c.toAscii() == '\0') ? QChar('\n') : m_replacingCharacter;
- }
- tc->insertText(str);
+ td->to = td->extraData.toString();
+}
+
+void FakeVimHandler::Private::replaceByCharTransform(TransformationData *td)
+{
+ td->to = QString(td->from.size(), td->extraData.toChar());
}
void FakeVimHandler::Private::pasteText(bool afterCursor)
{
- const QString text = m_registers[m_register].contents;
+ const QString text = g.registers[m_register].contents;
const QStringList lines = text.split(QChar('\n'));
- switch (m_registers[m_register].rangemode) {
+ switch (g.registers[m_register].rangemode) {
case RangeCharMode: {
m_targetColumn = 0;
for (int i = count(); --i >= 0; ) {
if (afterCursor && rightDist() > 0)
moveRight();
- fixMarks(position(), text.length());
- m_tc.insertText(text);
+ insertText(text);
if (!afterCursor && atEndOfLine())
moveLeft();
moveLeft();
for (int i = count(); --i >= 0; ) {
if (afterCursor)
moveDown();
- fixMarks(position(), text.length());
- m_tc.insertText(text);
+ insertText(text);
moveUp(lines.size() - 1);
}
moveToFirstNonBlankOnLine();
} else {
tc.movePosition(Right, MoveAnchor, col - 1 + afterCursor);
}
- qDebug() << "INSERT " << line << " AT " << tc.position()
- << "COL: " << col;
+ //qDebug() << "INSERT " << line << " AT " << tc.position()
+ // << "COL: " << col;
fixMarks(position(), line.length());
tc.insertText(line);
tc.movePosition(StartOfLine, MoveAnchor);
}
//FIXME: This needs to called after undo/insert
-void FakeVimHandler::Private::fixMarks(int positionAction, int positionChange)
-{
- QHashIterator<int, int> i(m_marks);
- while (i.hasNext()) {
- i.next();
- if (i.value() >= positionAction) {
- if (i.value() + positionChange > 0)
- m_marks[i.key()] = i.value() + positionChange;
- else
- m_marks.remove(i.key());
+// The position 'from' is the cursor position after the change. If 'delta'
+// is positive there was a string of size 'delta' inserted after 'from'
+// and consequently all marks beyond 'from + delta' need to be incremented
+// by 'delta'. If text was removed, 'delta' is negative. All marks between
+// 'from' and 'from - delta' need to be removed, everything behing
+// 'from - delta' adjusted by 'delta'.
+void FakeVimHandler::Private::fixMarks(int from, int delta)
+{
+ //qDebug() << "ADJUSTING MARKS FROM " << from << " BY " << delta;
+ if (delta == 0)
+ return;
+ QHashIterator<int, int> it(m_marks);
+ while (it.hasNext()) {
+ it.next();
+ int pos = it.value();
+ if (delta > 0) {
+ // Inserted text.
+ if (pos >= from) {
+ //qDebug() << "MODIFIED: " << it.key() << pos;
+ setMark(it.key(), pos + delta);
+ }
+ } else {
+ // Removed text.
+ if (pos < from) {
+ // Nothing to do.
+ } else if (pos < from - delta) {
+ //qDebug() << "GONE: " << it.key();
+ m_marks.remove(it.key());
+ } else {
+ //qDebug() << "MODIFIED: " << it.key() << pos;
+ setMark(it.key(), pos + delta);
+ }
}
}
}
return m_tc.document()->findBlockByNumber(line - 1).text();
}
-void FakeVimHandler::Private::setLineContents(int line, const QString &contents) const
+void FakeVimHandler::Private::setLineContents(int line, const QString &contents)
{
QTextBlock block = m_tc.document()->findBlockByNumber(line - 1);
QTextCursor tc = m_tc;
tc.setPosition(block.position());
tc.setPosition(block.position() + block.length() - 1, KeepAnchor);
tc.removeSelectedText();
+ fixMarks(block.position(), block.length() - contents.size());
tc.insertText(contents);
}
setAnchor();
m_positionPastEnd = m_anchorPastEnd = false;
m_visualMode = visualMode;
- m_marks['<'] = m_tc.position();
- m_marks['>'] = m_tc.position();
+ setMark('<', m_tc.position());
+ setMark('>', m_tc.position());
updateMiniBuffer();
updateSelection();
}
void FakeVimHandler::Private::undo()
{
//qDebug() << " CURSOR POS: " << m_undoCursorPosition;
- int current = m_tc.document()->availableUndoSteps();
- //endEditBlock();
+ QTextDocument *doc = m_tc.document();
+ // FIXME: That's only an approximaxtion. The real solution might
+ // be to store marks and old userData with QTextBlock setUserData
+ // and retrieve them afterward.
+ const int current = doc->availableUndoSteps();
+ const int oldCount = doc->characterCount();
EDITOR(undo());
- //beginEditBlock();
- int rev = m_tc.document()->availableUndoSteps();
+ const int delta = doc->characterCount() - oldCount;
+ fixMarks(position(), delta);
+ const int rev = doc->availableUndoSteps();
if (current == rev)
showBlackMessage(FakeVimHandler::tr("Already at oldest change"));
else
if (m_undoCursorPosition.contains(rev))
m_tc.setPosition(m_undoCursorPosition[rev]);
+ setTargetColumn();
if (atEndOfLine())
moveLeft();
}
void FakeVimHandler::Private::redo()
{
- int current = m_tc.document()->availableUndoSteps();
- //endEditBlock();
+ QTextDocument *doc = m_tc.document();
+ const int current = m_tc.document()->availableUndoSteps();
+ const int oldCount = doc->characterCount();
EDITOR(redo());
- //beginEditBlock();
- int rev = m_tc.document()->availableUndoSteps();
+ const int delta = doc->characterCount() - oldCount;
+ fixMarks(position(), delta);
+ const int rev = doc->availableUndoSteps();
if (rev == current)
showBlackMessage(FakeVimHandler::tr("Already at newest change"));
else
if (m_undoCursorPosition.contains(rev))
m_tc.setPosition(m_undoCursorPosition[rev]);
+ setTargetColumn();
+}
+
+void FakeVimHandler::Private::updateCursor()
+{
+ if (m_mode == ExMode || m_subsubmode == SearchSubSubMode) {
+ EDITOR(setCursorWidth(0));
+ EDITOR(setOverwriteMode(false));
+ } else if (m_mode == InsertMode) {
+ EDITOR(setCursorWidth(m_cursorWidth));
+ EDITOR(setOverwriteMode(false));
+ } else {
+ EDITOR(setCursorWidth(m_cursorWidth));
+ EDITOR(setOverwriteMode(true));
+ }
+}
+
+void FakeVimHandler::Private::enterReplaceMode()
+{
+ m_mode = ReplaceMode;
+ m_submode = NoSubMode;
+ m_subsubmode = NoSubSubMode;
+ m_commandPrefix.clear();
+ m_lastInsertion.clear();
+ m_lastDeletion.clear();
+ updateCursor();
}
void FakeVimHandler::Private::enterInsertMode()
{
- EDITOR(setCursorWidth(m_cursorWidth));
- EDITOR(setOverwriteMode(false));
- //leaveVisualMode();
m_mode = InsertMode;
+ m_submode = NoSubMode;
+ m_subsubmode = NoSubSubMode;
+ m_commandPrefix.clear();
m_lastInsertion.clear();
- m_beginEditBlock = true;
+ m_lastDeletion.clear();
+ updateCursor();
}
void FakeVimHandler::Private::enterCommandMode()
{
- EDITOR(setCursorWidth(m_cursorWidth));
- EDITOR(setOverwriteMode(true));
if (atEndOfLine())
moveLeft();
m_mode = CommandMode;
+ m_submode = NoSubMode;
+ m_subsubmode = NoSubSubMode;
+ m_commandPrefix.clear();
+ updateCursor();
}
void FakeVimHandler::Private::enterExMode()
{
- EDITOR(setCursorWidth(0));
- EDITOR(setOverwriteMode(false));
m_mode = ExMode;
+ m_submode = NoSubMode;
+ m_subsubmode = NoSubSubMode;
+ m_commandPrefix = ":";
+ updateCursor();
}
void FakeVimHandler::Private::recordJump()
UNDO_DEBUG("jumps: " << m_jumpListUndo);
}
-void FakeVimHandler::Private::recordNewUndo()
-{
- //endEditBlock();
- UNDO_DEBUG("---- BREAK ----");
- //beginEditBlock();
-}
-
Column FakeVimHandler::Private::indentation(const QString &line) const
{
int ts = config(ConfigTabStop).toInt();
++pos;
text.truncate(pos);
// FIXME: handle 'smartindent' and 'cindent'
- m_tc.insertText(text);
+ insertText(text);
m_justAutoIndented = text.size();
}
}
return false;
m_tc.movePosition(StartOfLine, KeepAnchor);
m_tc.removeSelectedText();
+ fixMarks(m_tc.position(), -m_justAutoIndented);
m_lastInsertion.chop(m_justAutoIndented);
m_justAutoIndented = 0;
return true;
void FakeVimHandler::Private::replay(const QString &command, int n)
{
- //qDebug() << "REPLAY: " << command;
- m_inReplay = true;
+ //qDebug() << "REPLAY: " << quoteUnprintable(command);
+ g.inReplay = true;
for (int i = n; --i >= 0; ) {
foreach (QChar c, command) {
//qDebug() << " REPLAY: " << QString(c);
handleKey(Input(c));
}
}
- m_inReplay = false;
+ g.inReplay = false;
}
-
void FakeVimHandler::Private::selectWordTextObject(bool inner)
{
Q_UNUSED(inner); // FIXME
setAnchor();
// FIXME: Rework the 'anchor' concept.
if (isVisualMode())
- m_marks['<'] = m_tc.position();
+ setMark('<', m_tc.position());
moveToWordBoundary(false, true, true);
m_movetype = MoveInclusive;
}
setAnchor();
// FIXME: Rework the 'anchor' concept.
if (isVisualMode())
- m_marks['<'] = m_tc.position();
+ setMark('<', m_tc.position());
moveToWordBoundary(true, true, true);
m_movetype = MoveInclusive;
}
Q_UNUSED(type);
}
+int FakeVimHandler::Private::mark(int code) const
+{
+ // FIXME: distinguish local and global marks.
+ //qDebug() << "MARK: " << code << m_marks.value(code, -1) << m_marks;
+ return m_marks.value(code, -1);
+}
+
+void FakeVimHandler::Private::setMark(int code, int position)
+{
+ // FIXME: distinguish local and global marks.
+ m_marks[code] = position;
+}
///////////////////////////////////////////////////////////////////////
//
{
bool active = theFakeVimSetting(ConfigUseFakeVim)->value().toBool();
+ // Catch mouse events on the viewport.
+ QWidget *viewport = 0;
+ if (d->m_plaintextedit)
+ viewport = d->m_plaintextedit->viewport();
+ else if (d->m_textedit)
+ viewport = d->m_textedit->viewport();
+ if (ob == viewport) {
+ if (active && ev->type() == QEvent::MouseButtonRelease) {
+ QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
+ if (mev->button() == Qt::LeftButton) {
+ d->importSelection();
+ //return true;
+ }
+ }
+ if (active && ev->type() == QEvent::MouseButtonPress) {
+ QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
+ if (mev->button() == Qt::LeftButton) {
+ d->m_visualMode = NoVisualMode;
+ d->updateSelection();
+ }
+ }
+ return QObject::eventFilter(ob, ev);
+ }
+
if (active && ev->type() == QEvent::Shortcut) {
d->passShortcuts(false);
return false;
d->m_currentFileName = fileName;
}
+QString FakeVimHandler::currentFileName() const
+{
+ return d->m_currentFileName;
+}
+
void FakeVimHandler::showBlackMessage(const QString &msg)
{
d->showBlackMessage(msg);