forked from qt-creator/qt-creator
fakevim: Improved mappings handling
Implemented special key names (e.g. <left>, <space>, <leader>, <backspace>). Disallow mapping with <nop>. Change-Id: I8f4cdc84cb469db0b5cddd9059484cce7aa1b17f Reviewed-by: hjk <qthjk@ovi.com>
This commit is contained in:
@@ -699,3 +699,120 @@ void FakeVimPlugin::test_vim_undo_redo()
|
|||||||
KEYS("jlllSxyz<ESC>", "abc" N "xyz" N "ghi");
|
KEYS("jlllSxyz<ESC>", "abc" N "xyz" N "ghi");
|
||||||
KEYS("u", "abc" N " " X "def" N "ghi");
|
KEYS("u", "abc" N " " X "def" N "ghi");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FakeVimPlugin::test_map()
|
||||||
|
{
|
||||||
|
TestData data;
|
||||||
|
setup(&data);
|
||||||
|
|
||||||
|
data.setText("abc def");
|
||||||
|
data.doCommand("map C i<space>x<space><esc>");
|
||||||
|
data.doCommand("map c iXXX");
|
||||||
|
data.doCommand("imap c YYY<space>");
|
||||||
|
KEYS("C", " x" X " abc def");
|
||||||
|
data.doCommand("map C <nop>");
|
||||||
|
KEYS("C", " x" X " abc def");
|
||||||
|
data.doCommand("map C i<bs><esc><right>");
|
||||||
|
KEYS("C", " " X " abc def");
|
||||||
|
KEYS("ccc<esc>", " XXXYYY YYY" X " abc def");
|
||||||
|
// unmap
|
||||||
|
KEYS(":unmap c<cr>ccc<esc>", "YYY" X " ");
|
||||||
|
KEYS(":iunmap c<cr>ccc<esc>", X "c");
|
||||||
|
data.doCommand("unmap C");
|
||||||
|
|
||||||
|
data.setText("abc def");
|
||||||
|
data.doCommand("imap x (((<space><right><right>)))<esc>");
|
||||||
|
KEYS("x", X "bc def");
|
||||||
|
KEYS("ix", "((( bc))" X ") def");
|
||||||
|
data.doCommand("iunmap x");
|
||||||
|
|
||||||
|
data.setText("abc def");
|
||||||
|
data.doCommand("map <c-right> 3l");
|
||||||
|
KEYS("<C-Right>", "abc" X " def");
|
||||||
|
KEYS("<C-Right>", "abc de" X "f");
|
||||||
|
|
||||||
|
// map vs. noremap
|
||||||
|
data.setText("abc def");
|
||||||
|
data.doCommand("map x 3l");
|
||||||
|
data.doCommand("map X x");
|
||||||
|
KEYS("X", "abc" X " def");
|
||||||
|
data.doCommand("noremap X x");
|
||||||
|
KEYS("X", "abc" X "def");
|
||||||
|
data.doCommand("unmap X");
|
||||||
|
data.doCommand("unmap x");
|
||||||
|
|
||||||
|
// limit number of recursions in mappings
|
||||||
|
data.doCommand("map X Y");
|
||||||
|
data.doCommand("map Y Z");
|
||||||
|
data.doCommand("map Z X");
|
||||||
|
KEYS("X", "abc" X "def");
|
||||||
|
data.doCommand("map Z i<space><esc>");
|
||||||
|
KEYS("X", "abc" X " def");
|
||||||
|
data.doCommand("unmap X");
|
||||||
|
data.doCommand("unmap Y");
|
||||||
|
data.doCommand("unmap Z");
|
||||||
|
|
||||||
|
// imcomplete mapping
|
||||||
|
data.setText("abc");
|
||||||
|
data.doCommand("map Xa ia<esc>");
|
||||||
|
data.doCommand("map Xb ib<esc>");
|
||||||
|
data.doCommand("map X ic<esc>");
|
||||||
|
KEYS("Xa", X "aabc");
|
||||||
|
KEYS("Xb", X "baabc");
|
||||||
|
KEYS("Xic<esc>", X "ccbaabc");
|
||||||
|
|
||||||
|
// unmap
|
||||||
|
data.doCommand("unmap Xa");
|
||||||
|
KEYS("Xa<esc>", X "cccbaabc");
|
||||||
|
data.doCommand("unmap Xb");
|
||||||
|
KEYS("Xb", X "ccccbaabc");
|
||||||
|
data.doCommand("unmap X");
|
||||||
|
KEYS("Xb", X "ccccbaabc");
|
||||||
|
KEYS("X<esc>", X "ccccbaabc");
|
||||||
|
|
||||||
|
// recursive mapping
|
||||||
|
data.setText("abc");
|
||||||
|
data.doCommand("map X Y");
|
||||||
|
data.doCommand("map XXX i1<esc>");
|
||||||
|
data.doCommand("map Y i2<esc>");
|
||||||
|
data.doCommand("map YZ i3<esc>");
|
||||||
|
data.doCommand("map _ i <esc>");
|
||||||
|
KEYS("_XXX_", X " 1 abc");
|
||||||
|
KEYS("XX_0", X " 22 1 abc");
|
||||||
|
KEYS("XXXXZ_0", X " 31 22 1 abc");
|
||||||
|
KEYS("XXXXX_0", X " 221 31 22 1 abc");
|
||||||
|
KEYS("XXZ", X "32 221 31 22 1 abc");
|
||||||
|
data.doCommand("unmap X");
|
||||||
|
data.doCommand("unmap XXX");
|
||||||
|
data.doCommand("unmap Y");
|
||||||
|
data.doCommand("unmap YZ");
|
||||||
|
data.doCommand("unmap _");
|
||||||
|
|
||||||
|
// shift modifier
|
||||||
|
data.setText("abc");
|
||||||
|
data.doCommand("map x i1<esc>");
|
||||||
|
data.doCommand("map X i2<esc>");
|
||||||
|
KEYS("x", X "1abc");
|
||||||
|
KEYS("X", X "21abc");
|
||||||
|
data.doCommand("map <S-X> i3<esc>");
|
||||||
|
KEYS("X", X "321abc");
|
||||||
|
data.doCommand("map X i4<esc>");
|
||||||
|
KEYS("X", X "4321abc");
|
||||||
|
KEYS("x", X "14321abc");
|
||||||
|
data.doCommand("unmap x");
|
||||||
|
data.doCommand("unmap X");
|
||||||
|
|
||||||
|
// undo/redo mapped input
|
||||||
|
data.setText("abc def ghi");
|
||||||
|
data.doCommand("map X dwea xyz<esc>3l");
|
||||||
|
KEYS("X", "def xyz g" X "hi");
|
||||||
|
KEYS("u", X "abc def ghi");
|
||||||
|
KEYS("<C-r>", X "def xyz ghi");
|
||||||
|
data.doCommand("unmap X");
|
||||||
|
|
||||||
|
NOT_IMPLEMENTED
|
||||||
|
// <C-o>
|
||||||
|
data.setText("abc def");
|
||||||
|
data.doCommand("imap X <c-o>:%s/def/xxx/<cr>");
|
||||||
|
KEYS("iX", "abc xxx");
|
||||||
|
}
|
||||||
|
|||||||
@@ -307,9 +307,18 @@ struct SearchData
|
|||||||
bool highlightCursor;
|
bool highlightCursor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// If string begins with given prefix remove it with trailing spaces and return true.
|
||||||
|
static bool eatString(const QString &prefix, QString *str)
|
||||||
|
{
|
||||||
|
if (!str->startsWith(prefix))
|
||||||
|
return false;
|
||||||
|
*str = str->mid(prefix.size()).trimmed();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static QRegExp vimPatternToQtPattern(QString needle, bool smartcase)
|
static QRegExp vimPatternToQtPattern(QString needle, bool smartcase)
|
||||||
{
|
{
|
||||||
/* Trasformations (Vim regexp -> QRegExp):
|
/* Transformations (Vim regexp -> QRegExp):
|
||||||
* \a -> [A-Za-z]
|
* \a -> [A-Za-z]
|
||||||
* \A -> [^A-Za-z]
|
* \A -> [^A-Za-z]
|
||||||
* \h -> [A-Za-z_]
|
* \h -> [A-Za-z_]
|
||||||
@@ -541,7 +550,11 @@ public:
|
|||||||
: m_key(0), m_xkey(0), m_modifiers(0) {}
|
: m_key(0), m_xkey(0), m_modifiers(0) {}
|
||||||
|
|
||||||
explicit Input(QChar x)
|
explicit Input(QChar x)
|
||||||
: m_key(x.unicode()), m_xkey(x.unicode()), m_modifiers(0), m_text(x) {}
|
: m_key(x.unicode()), m_xkey(x.unicode()), m_modifiers(0), m_text(x)
|
||||||
|
{
|
||||||
|
if (x.isUpper())
|
||||||
|
m_modifiers = Qt::ShiftModifier;
|
||||||
|
}
|
||||||
|
|
||||||
Input(int k, int m, const QString &t)
|
Input(int k, int m, const QString &t)
|
||||||
: m_key(k), m_modifiers(cleanModifier(m)), m_text(t)
|
: m_key(k), m_modifiers(cleanModifier(m)), m_text(t)
|
||||||
@@ -556,6 +569,11 @@ public:
|
|||||||
m_xkey = (m_text.size() == 1 ? m_text.at(0).unicode() : m_key);
|
m_xkey = (m_text.size() == 1 ? m_text.at(0).unicode() : m_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isValid() const
|
||||||
|
{
|
||||||
|
return m_key != 0 || !m_text.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
bool isDigit() const
|
bool isDigit() const
|
||||||
{
|
{
|
||||||
return m_xkey >= '0' && m_xkey <= '9';
|
return m_xkey >= '0' && m_xkey <= '9';
|
||||||
@@ -600,18 +618,17 @@ public:
|
|||||||
|
|
||||||
bool operator==(const Input &a) const
|
bool operator==(const Input &a) const
|
||||||
{
|
{
|
||||||
return a.m_key == m_key && a.m_modifiers == m_modifiers
|
return a.m_xkey == m_xkey && a.m_modifiers == m_modifiers
|
||||||
&& m_text == a.m_text;
|
&& m_text == a.m_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore e.g. ShiftModifier, which is not available in sourced data.
|
|
||||||
bool matchesForMap(const Input &a) const
|
|
||||||
{
|
|
||||||
return (a.m_key == m_key || a.m_xkey == m_xkey) && m_text == a.m_text;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const Input &a) const { return !operator==(a); }
|
bool operator!=(const Input &a) const { return !operator==(a); }
|
||||||
|
|
||||||
|
bool operator<(const Input &a) const
|
||||||
|
{
|
||||||
|
return m_key < a.m_key || m_modifiers < a.m_modifiers || m_text < a.m_text;
|
||||||
|
}
|
||||||
|
|
||||||
QString text() const { return m_text; }
|
QString text() const { return m_text; }
|
||||||
|
|
||||||
QChar asChar() const
|
QChar asChar() const
|
||||||
@@ -642,54 +659,177 @@ private:
|
|||||||
QString m_text;
|
QString m_text;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// mapping to <Nop> (do nothing)
|
||||||
|
static const Input Nop(-1, -1, QString());
|
||||||
|
|
||||||
QDebug operator<<(QDebug ts, const Input &input) { return input.dump(ts); }
|
QDebug operator<<(QDebug ts, const Input &input) { return input.dump(ts); }
|
||||||
|
|
||||||
class Inputs : public QVector<Input>
|
class Inputs : public QVector<Input>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Inputs() {}
|
Inputs() : m_noremap(true), m_silent(false) {}
|
||||||
explicit Inputs(const QString &str) { parseFrom(str); }
|
|
||||||
|
explicit Inputs(const QString &str, bool noremap = true, bool silent = false)
|
||||||
|
: m_noremap(noremap), m_silent(silent)
|
||||||
|
{
|
||||||
|
parseFrom(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool noremap() const { return m_noremap; }
|
||||||
|
|
||||||
|
bool silent() const { return m_silent; }
|
||||||
|
|
||||||
|
private:
|
||||||
void parseFrom(const QString &str);
|
void parseFrom(const QString &str);
|
||||||
|
|
||||||
|
bool m_noremap;
|
||||||
|
bool m_silent;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool iss(char a, char b)
|
static QMap<QString, int> vimKeyNames()
|
||||||
{
|
{
|
||||||
if (a >= 'a')
|
QMap<QString, int> k;
|
||||||
a -= 'a' - 'A';
|
|
||||||
if (b >= 'a')
|
// FIXME: Should be value of mapleader.
|
||||||
b -= 'a' - 'A';
|
k.insert("LEADER", Key_Backslash);
|
||||||
return a == b;
|
|
||||||
|
k.insert("SPACE", Key_Space);
|
||||||
|
k.insert("TAB", Key_Tab);
|
||||||
|
k.insert("NL", Key_Return);
|
||||||
|
k.insert("NEWLINE", Key_Return);
|
||||||
|
k.insert("LINEFEED", Key_Return);
|
||||||
|
k.insert("LF", Key_Return);
|
||||||
|
k.insert("CR", Key_Return);
|
||||||
|
k.insert("RETURN", Key_Return);
|
||||||
|
k.insert("ENTER", Key_Return);
|
||||||
|
k.insert("BS", Key_Backspace);
|
||||||
|
k.insert("BACKSPACE", Key_Backspace);
|
||||||
|
k.insert("ESC", Key_Escape);
|
||||||
|
k.insert("BAR", Key_Bar);
|
||||||
|
k.insert("BSLASH", Key_Backslash);
|
||||||
|
k.insert("DEL", Key_Delete);
|
||||||
|
k.insert("DELETE", Key_Delete);
|
||||||
|
k.insert("KDEL", Key_Delete);
|
||||||
|
k.insert("UP", Key_Up);
|
||||||
|
k.insert("DOWN", Key_Down);
|
||||||
|
k.insert("LEFT", Key_Left);
|
||||||
|
k.insert("RIGHT", Key_Right);
|
||||||
|
|
||||||
|
k.insert("F1", Key_F1);
|
||||||
|
k.insert("F2", Key_F2);
|
||||||
|
k.insert("F3", Key_F3);
|
||||||
|
k.insert("F4", Key_F4);
|
||||||
|
k.insert("F5", Key_F5);
|
||||||
|
k.insert("F6", Key_F6);
|
||||||
|
k.insert("F7", Key_F7);
|
||||||
|
k.insert("F8", Key_F8);
|
||||||
|
k.insert("F9", Key_F9);
|
||||||
|
k.insert("F10", Key_F10);
|
||||||
|
|
||||||
|
k.insert("F11", Key_F11);
|
||||||
|
k.insert("F12", Key_F12);
|
||||||
|
k.insert("F13", Key_F13);
|
||||||
|
k.insert("F14", Key_F14);
|
||||||
|
k.insert("F15", Key_F15);
|
||||||
|
k.insert("F16", Key_F16);
|
||||||
|
k.insert("F17", Key_F17);
|
||||||
|
k.insert("F18", Key_F18);
|
||||||
|
k.insert("F19", Key_F19);
|
||||||
|
k.insert("F20", Key_F20);
|
||||||
|
|
||||||
|
k.insert("F21", Key_F21);
|
||||||
|
k.insert("F22", Key_F22);
|
||||||
|
k.insert("F23", Key_F23);
|
||||||
|
k.insert("F24", Key_F24);
|
||||||
|
k.insert("F25", Key_F25);
|
||||||
|
k.insert("F26", Key_F26);
|
||||||
|
k.insert("F27", Key_F27);
|
||||||
|
k.insert("F28", Key_F28);
|
||||||
|
k.insert("F29", Key_F29);
|
||||||
|
k.insert("F30", Key_F30);
|
||||||
|
|
||||||
|
k.insert("F31", Key_F31);
|
||||||
|
k.insert("F32", Key_F32);
|
||||||
|
k.insert("F33", Key_F33);
|
||||||
|
k.insert("F34", Key_F34);
|
||||||
|
k.insert("F35", Key_F35);
|
||||||
|
|
||||||
|
k.insert("INSERT", Key_Insert);
|
||||||
|
k.insert("INS", Key_Insert);
|
||||||
|
k.insert("KINSERT", Key_Insert);
|
||||||
|
k.insert("HOME", Key_Home);
|
||||||
|
k.insert("END", Key_End);
|
||||||
|
k.insert("PAGEUP", Key_PageUp);
|
||||||
|
k.insert("PAGEDOWN", Key_PageDown);
|
||||||
|
|
||||||
|
k.insert("KPLUS", Key_Plus);
|
||||||
|
k.insert("KMINUS", Key_Minus);
|
||||||
|
k.insert("KDIVIDE", Key_Slash);
|
||||||
|
k.insert("KMULTIPLY", Key_Asterisk);
|
||||||
|
k.insert("KENTER", Key_Enter);
|
||||||
|
k.insert("KPOINT", Key_Period);
|
||||||
|
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Input parseVimKeyName(const QString &keyName)
|
||||||
|
{
|
||||||
|
if (keyName.length() == 1)
|
||||||
|
return Input(keyName.at(0));
|
||||||
|
|
||||||
|
const QStringList keys = keyName.split('-');
|
||||||
|
const int len = keys.length();
|
||||||
|
|
||||||
|
if (len == 1 && keys.at(0) == _("nop"))
|
||||||
|
return Nop;
|
||||||
|
|
||||||
|
int mods = NoModifier;
|
||||||
|
for (int i = 0; i < len - 1; ++i) {
|
||||||
|
const QString &key = keys[i].toUpper();
|
||||||
|
if (key == "S")
|
||||||
|
mods |= Qt::ShiftModifier;
|
||||||
|
else if (key == "C")
|
||||||
|
mods |= RealControlModifier;
|
||||||
|
else
|
||||||
|
return Input();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keys.isEmpty()) {
|
||||||
|
const QString key = keys.last();
|
||||||
|
if (key.length() == 1) {
|
||||||
|
// simple character
|
||||||
|
QChar c = key.at(0).toUpper();
|
||||||
|
return Input(c.unicode(), mods, QString(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
// find key name
|
||||||
|
static const QMap<QString, int> k = vimKeyNames();
|
||||||
|
QMap<QString, int>::ConstIterator it = k.constFind(key.toUpper());
|
||||||
|
if (it != k.end())
|
||||||
|
return Input(*it, mods, *it <= 0x7f ? QString(QChar::fromAscii(*it)) : QString(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Input();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inputs::parseFrom(const QString &str)
|
void Inputs::parseFrom(const QString &str)
|
||||||
{
|
{
|
||||||
const int n = str.size();
|
const int n = str.size();
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
uint c0 = str.at(i).unicode(), c1 = 0, c2 = 0, c3 = 0, c4 = 0;
|
uint c = str.at(i).unicode();
|
||||||
if (i + 1 < n)
|
if (c == '<') {
|
||||||
c1 = str.at(i + 1).unicode();
|
int j = str.indexOf('>', i);
|
||||||
if (i + 2 < n)
|
Input input;
|
||||||
c2 = str.at(i + 2).unicode();
|
if (j != -1)
|
||||||
if (i + 3 < n)
|
input = parseVimKeyName(str.mid(i+1, j - i - 1));
|
||||||
c3 = str.at(i + 3).unicode();
|
if (input.isValid()) {
|
||||||
if (i + 4 < n)
|
append(input);
|
||||||
c4 = str.at(i + 4).unicode();
|
i = j;
|
||||||
if (c0 == '<') {
|
|
||||||
if (iss(c1, 'C') && c2 == '-' && c4 == '>') {
|
|
||||||
uint c = (c3 < 90 ? c3 : c3 - 32);
|
|
||||||
append(Input(c, RealControlModifier, QString(QChar(c - 64))));
|
|
||||||
i += 4;
|
|
||||||
} else if (iss(c1, 'C') && iss(c2, 'R') && c3 == '>') {
|
|
||||||
append(Input(Key_Return, Qt::NoModifier, QString(QChar(13))));
|
|
||||||
i += 3;
|
|
||||||
} else if (iss(c1, 'E') && iss(c2, 'S') && iss(c3, 'C') && c4 == '>') {
|
|
||||||
append(Input(Key_Escape, Qt::NoModifier, QString(QChar(27))));
|
|
||||||
i += 4;
|
|
||||||
} else {
|
} else {
|
||||||
append(Input(QLatin1Char(c0)));
|
append(Input(QLatin1Char(c)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
append(Input(QLatin1Char(c0)));
|
append(Input(QLatin1Char(c)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -742,7 +882,7 @@ const QString &History::move(const QStringRef &prefix, int skip)
|
|||||||
class CommandBuffer
|
class CommandBuffer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CommandBuffer() : m_pos(0), m_userPos(0) {}
|
CommandBuffer() : m_pos(0), m_userPos(0), m_historyAutoSave(true) {}
|
||||||
|
|
||||||
void setPrompt(const QChar &prompt) { m_prompt = prompt; }
|
void setPrompt(const QChar &prompt) { m_prompt = prompt; }
|
||||||
void setContents(const QString &s) { m_buffer = s; m_pos = s.size(); }
|
void setContents(const QString &s) { m_buffer = s; m_pos = s.size(); }
|
||||||
@@ -765,6 +905,7 @@ public:
|
|||||||
void moveStart() { m_userPos = m_pos = 0; }
|
void moveStart() { m_userPos = m_pos = 0; }
|
||||||
void moveEnd() { m_userPos = m_pos = m_buffer.size(); }
|
void moveEnd() { m_userPos = m_pos = m_buffer.size(); }
|
||||||
|
|
||||||
|
void setHistoryAutoSave(bool autoSave) { m_historyAutoSave = autoSave; }
|
||||||
void historyDown() { setContents(m_history.move(userContents(), 1)); }
|
void historyDown() { setContents(m_history.move(userContents(), 1)); }
|
||||||
void historyUp() { setContents(m_history.move(userContents(), -1)); }
|
void historyUp() { setContents(m_history.move(userContents(), -1)); }
|
||||||
const QStringList &historyItems() const { return m_history.items(); }
|
const QStringList &historyItems() const { return m_history.items(); }
|
||||||
@@ -774,7 +915,13 @@ public:
|
|||||||
m_history.append(item.isNull() ? contents() : item);
|
m_history.append(item.isNull() ? contents() : item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() { historyPush(); m_buffer.clear(); m_userPos = m_pos = 0; }
|
void clear()
|
||||||
|
{
|
||||||
|
if (m_historyAutoSave)
|
||||||
|
historyPush();
|
||||||
|
m_buffer.clear();
|
||||||
|
m_userPos = m_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
QString display() const
|
QString display() const
|
||||||
{
|
{
|
||||||
@@ -820,74 +967,151 @@ private:
|
|||||||
History m_history;
|
History m_history;
|
||||||
int m_pos;
|
int m_pos;
|
||||||
int m_userPos; // last position of inserted text (for retrieving history items)
|
int m_userPos; // last position of inserted text (for retrieving history items)
|
||||||
|
bool m_historyAutoSave; // store items to history on clear()?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mappings for a specific mode.
|
// Mappings for a specific mode (trie structure)
|
||||||
class ModeMapping : public QList<QPair<Inputs, Inputs> >
|
class ModeMapping : public QMap<Input, ModeMapping>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ModeMapping() { test(); }
|
const Inputs &value() const { return m_value; }
|
||||||
|
void setValue(const Inputs &value) { m_value = value; }
|
||||||
|
private:
|
||||||
|
Inputs m_value;
|
||||||
|
};
|
||||||
|
|
||||||
void test()
|
// Mappings for all modes
|
||||||
|
typedef QHash<char, ModeMapping> Mappings;
|
||||||
|
|
||||||
|
// Iterator for mappings
|
||||||
|
class MappingsIterator : public QVector<ModeMapping::Iterator>
|
||||||
{
|
{
|
||||||
//insert(Inputs() << Input('A') << Input('A'),
|
public:
|
||||||
// Inputs() << Input('x') << Input('x'));
|
MappingsIterator(Mappings *mappings, char mode = -1, const Inputs &inputs = Inputs())
|
||||||
|
: m_parent(mappings)
|
||||||
|
{
|
||||||
|
reset(mode);
|
||||||
|
walk(inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void insert(const Inputs &from, const Inputs &to)
|
// Reset iterator state. Keep previous mode if 0.
|
||||||
|
void reset(char mode = 0)
|
||||||
{
|
{
|
||||||
for (int i = 0; i != size(); ++i)
|
clear();
|
||||||
if (at(i).first == from) {
|
m_lastValid = -1;
|
||||||
(*this)[i].second = to;
|
m_invalidInputCount = 0;
|
||||||
return;
|
if (mode != 0) {
|
||||||
}
|
m_mode = mode;
|
||||||
append(QPair<Inputs, Inputs>(from, to));
|
if (mode != -1)
|
||||||
}
|
m_modeMapping = m_parent->find(mode);
|
||||||
|
|
||||||
void remove(const Inputs &from)
|
|
||||||
{
|
|
||||||
for (int i = 0; i != size(); ++i)
|
|
||||||
if (at(i).first == from) {
|
|
||||||
removeAt(i);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns 'false' if more input is needed to decide whether a mapping
|
bool isValid() const { return !empty(); }
|
||||||
// needs to be applied. If a decision can be made, return 'true',
|
|
||||||
// and replace *input with the mapped data.
|
// Return true if mapping can be extended.
|
||||||
bool mappingDone(Inputs *inputs) const
|
bool canExtend() const { return isValid() && !last()->empty(); }
|
||||||
|
|
||||||
|
// Return true if this mapping can be used.
|
||||||
|
bool isComplete() const { return m_lastValid != -1; }
|
||||||
|
|
||||||
|
// Return size of current map.
|
||||||
|
int mapLength() const { return m_lastValid + 1; }
|
||||||
|
|
||||||
|
int invalidInputCount() const { return m_invalidInputCount; }
|
||||||
|
|
||||||
|
bool walk(const Input &input)
|
||||||
{
|
{
|
||||||
// FIXME: inefficient.
|
if (m_modeMapping == m_parent->end())
|
||||||
for (int i = 0; i != size(); ++i) {
|
return false;
|
||||||
const Inputs &haystack = at(i).first;
|
|
||||||
// A mapping
|
if (!input.isValid()) {
|
||||||
if (couldTriggerMap(haystack, *inputs)) {
|
m_invalidInputCount += 1;
|
||||||
if (haystack.size() != inputs->size())
|
|
||||||
return false; // This can be extended.
|
|
||||||
// Actual mapping.
|
|
||||||
*inputs = at(i).second;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModeMapping::Iterator it;
|
||||||
|
if (isValid()) {
|
||||||
|
it = last()->find(input);
|
||||||
|
if (it == last()->end())
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
it = m_modeMapping->find(input);
|
||||||
|
if (it == m_modeMapping->end())
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
// No extensible mapping found. Use inputs as-is.
|
|
||||||
|
if (!it->value().isEmpty())
|
||||||
|
m_lastValid = size();
|
||||||
|
append(it);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool walk(const Inputs &inputs)
|
||||||
|
{
|
||||||
|
foreach (const Input &input, inputs) {
|
||||||
|
if (!walk(input))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return current mapped value. Iterator must be valid.
|
||||||
|
const Inputs &inputs() const
|
||||||
|
{
|
||||||
|
return at(m_lastValid)->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove()
|
||||||
|
{
|
||||||
|
if (isValid()) {
|
||||||
|
if (canExtend()) {
|
||||||
|
last()->setValue(Inputs());
|
||||||
|
} else {
|
||||||
|
if (size() > 1) {
|
||||||
|
while (last()->empty()) {
|
||||||
|
at(size() - 2)->erase(last());
|
||||||
|
pop_back();
|
||||||
|
if (size() == 1 || !last()->value().isEmpty())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (last()->empty() && last()->value().isEmpty())
|
||||||
|
m_modeMapping->erase(last());
|
||||||
|
} else if (last()->empty() && !last()->value().isEmpty()) {
|
||||||
|
m_modeMapping->erase(last());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInputs(const Inputs &key, const Inputs &inputs, bool unique = false)
|
||||||
|
{
|
||||||
|
ModeMapping *current = &(*m_parent)[m_mode];
|
||||||
|
foreach (const Input &input, key)
|
||||||
|
current = &(*current)[input];
|
||||||
|
if (!unique || current->value().isEmpty())
|
||||||
|
current->setValue(inputs);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static bool couldTriggerMap(const Inputs &haystack, const Inputs &needle)
|
Mappings *m_parent;
|
||||||
{
|
Mappings::Iterator m_modeMapping;
|
||||||
// Input is already too long.
|
int m_lastValid;
|
||||||
if (needle.size() > haystack.size())
|
int m_invalidInputCount;
|
||||||
return false;
|
char m_mode;
|
||||||
for (int i = 0; i != needle.size(); ++i) {
|
|
||||||
if (!needle.at(i).matchesForMap(haystack.at(i)))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// state of current mapping
|
||||||
|
struct MappingState {
|
||||||
|
MappingState()
|
||||||
|
: maxMapDepth(1000), noremap(false), silent(false) {}
|
||||||
|
MappingState(int depth, bool noremap, bool silent)
|
||||||
|
: maxMapDepth(depth), noremap(noremap), silent(silent) {}
|
||||||
|
int maxMapDepth;
|
||||||
|
bool noremap;
|
||||||
|
bool silent;
|
||||||
|
};
|
||||||
|
|
||||||
class FakeVimHandler::Private : public QObject
|
class FakeVimHandler::Private : public QObject
|
||||||
{
|
{
|
||||||
@@ -909,8 +1133,9 @@ public:
|
|||||||
friend class FakeVimHandler;
|
friend class FakeVimHandler;
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
EventResult handleKey(const Input &);
|
EventResult handleKey(const Input &input);
|
||||||
Q_SLOT EventResult handleKey2();
|
EventResult handleDefaultKey(const Input &input);
|
||||||
|
Q_SLOT void handleMappedKeys();
|
||||||
EventResult handleInsertMode(const Input &);
|
EventResult handleInsertMode(const Input &);
|
||||||
EventResult handleReplaceMode(const Input &);
|
EventResult handleReplaceMode(const Input &);
|
||||||
EventResult handleCommandMode(const Input &);
|
EventResult handleCommandMode(const Input &);
|
||||||
@@ -1100,6 +1325,7 @@ public:
|
|||||||
bool isVisualCharMode() const { return m_visualMode == VisualCharMode; }
|
bool isVisualCharMode() const { return m_visualMode == VisualCharMode; }
|
||||||
bool isVisualLineMode() const { return m_visualMode == VisualLineMode; }
|
bool isVisualLineMode() const { return m_visualMode == VisualLineMode; }
|
||||||
bool isVisualBlockMode() const { return m_visualMode == VisualBlockMode; }
|
bool isVisualBlockMode() const { return m_visualMode == VisualBlockMode; }
|
||||||
|
char currentModeCode() const;
|
||||||
void updateEditor();
|
void updateEditor();
|
||||||
|
|
||||||
void selectTextObject(bool simple, bool inner);
|
void selectTextObject(bool simple, bool inner);
|
||||||
@@ -1300,23 +1526,26 @@ public:
|
|||||||
|
|
||||||
static struct GlobalData
|
static struct GlobalData
|
||||||
{
|
{
|
||||||
GlobalData()
|
GlobalData() : mappings(), currentMap(&mappings), inputTimer(-1)
|
||||||
{
|
{
|
||||||
inputTimer = -1;
|
// default mapping state - shouldn't be removed
|
||||||
|
mapStates << MappingState();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input.
|
|
||||||
Inputs pendingInput;
|
|
||||||
int inputTimer;
|
|
||||||
|
|
||||||
// Repetition.
|
// Repetition.
|
||||||
QString dotCommand;
|
QString dotCommand;
|
||||||
|
|
||||||
QHash<int, Register> registers;
|
QHash<int, Register> registers;
|
||||||
|
|
||||||
// All mappings.
|
// All mappings.
|
||||||
typedef QHash<char, ModeMapping> Mappings;
|
|
||||||
Mappings mappings;
|
Mappings mappings;
|
||||||
|
|
||||||
|
// Input.
|
||||||
|
Inputs pendingInput;
|
||||||
|
MappingsIterator currentMap;
|
||||||
|
int inputTimer;
|
||||||
|
int lastMapCode;
|
||||||
|
QStack<MappingState> mapStates;
|
||||||
} g;
|
} g;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1639,61 +1868,114 @@ void FakeVimHandler::Private::restoreWidget(int tabSize)
|
|||||||
EventResult FakeVimHandler::Private::handleKey(const Input &input)
|
EventResult FakeVimHandler::Private::handleKey(const Input &input)
|
||||||
{
|
{
|
||||||
KEY_DEBUG("HANDLE INPUT: " << input << " MODE: " << mode);
|
KEY_DEBUG("HANDLE INPUT: " << input << " MODE: " << mode);
|
||||||
if (m_mode == ExMode)
|
|
||||||
return handleExMode(input);
|
if (g.inputTimer != -1) {
|
||||||
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 (g.mappings.value(code).mappingDone(&g.pendingInput))
|
|
||||||
return handleKey2();
|
|
||||||
if (g.inputTimer != -1)
|
|
||||||
killTimer(g.inputTimer);
|
killTimer(g.inputTimer);
|
||||||
|
g.inputTimer = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventResult r = EventUnhandled;
|
||||||
|
if (input.isValid()) {
|
||||||
|
g.pendingInput.append(input);
|
||||||
|
if (g.currentMap.isValid()) {
|
||||||
|
if (!g.currentMap.walk(input) && g.currentMap.isComplete())
|
||||||
|
handleMappedKeys();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!g.pendingInput.isEmpty()) {
|
||||||
|
const Input &in = g.pendingInput.front();
|
||||||
|
|
||||||
|
// invalid input is used to pop mapping state
|
||||||
|
if (!in.isValid()) {
|
||||||
|
g.mapStates.pop_back();
|
||||||
|
QTC_CHECK(!g.mapStates.empty());
|
||||||
|
endEditBlock();
|
||||||
|
if (g.mapStates.size() == 1)
|
||||||
|
m_commandBuffer.setHistoryAutoSave(true);
|
||||||
|
if (m_mode == ExMode || m_subsubmode == SearchSubSubMode)
|
||||||
|
updateMiniBuffer(); // update cursor position on command line
|
||||||
|
} else {
|
||||||
|
if (!g.mapStates.last().noremap && m_subsubmode != SearchSubSubMode) {
|
||||||
|
if (!g.currentMap.isValid()) {
|
||||||
|
g.currentMap.reset(currentModeCode());
|
||||||
|
if (!g.currentMap.walk(g.pendingInput) && g.currentMap.isComplete()) {
|
||||||
|
handleMappedKeys();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle user mapping
|
||||||
|
if (g.currentMap.canExtend()) {
|
||||||
|
// wait for user to press any key or trigger mapping after interval
|
||||||
g.inputTimer = startTimer(1000);
|
g.inputTimer = startTimer(1000);
|
||||||
return EventHandled;
|
return EventHandled;
|
||||||
|
} else if (g.currentMap.isComplete()) {
|
||||||
|
handleMappedKeys();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = handleDefaultKey(in);
|
||||||
|
// TODO: Unhadled events!
|
||||||
|
}
|
||||||
|
g.pendingInput.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventResult FakeVimHandler::Private::handleDefaultKey(const Input &input)
|
||||||
|
{
|
||||||
|
if (input == Nop)
|
||||||
|
return EventHandled;
|
||||||
|
else if (m_subsubmode == SearchSubSubMode)
|
||||||
|
return handleSearchSubSubMode(input);
|
||||||
|
else if (m_mode == CommandMode)
|
||||||
|
return handleCommandMode(input);
|
||||||
|
else if (m_mode == InsertMode)
|
||||||
|
return handleInsertMode(input);
|
||||||
|
else if (m_mode == ReplaceMode)
|
||||||
|
return handleReplaceMode(input);
|
||||||
|
else if (m_mode == ExMode)
|
||||||
|
return handleExMode(input);
|
||||||
return EventUnhandled;
|
return EventUnhandled;
|
||||||
}
|
}
|
||||||
|
|
||||||
EventResult FakeVimHandler::Private::handleKey2()
|
void FakeVimHandler::Private::handleMappedKeys()
|
||||||
{
|
{
|
||||||
Inputs pendingInput = g.pendingInput;
|
int maxMapDepth = g.mapStates.last().maxMapDepth - 1;
|
||||||
|
|
||||||
|
int invalidCount = g.currentMap.invalidInputCount();
|
||||||
|
if (invalidCount > 0) {
|
||||||
|
g.mapStates.remove(g.mapStates.size() - invalidCount, invalidCount);
|
||||||
|
QTC_CHECK(!g.mapStates.empty());
|
||||||
|
for (int i = 0; i < invalidCount; ++i)
|
||||||
|
endEditBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxMapDepth <= 0) {
|
||||||
|
showRedMessage("recursive mapping");
|
||||||
|
g.pendingInput.remove(0, g.currentMap.mapLength() + invalidCount);
|
||||||
|
} else {
|
||||||
|
const Inputs &inputs = g.currentMap.inputs();
|
||||||
|
QVector<Input> rest = g.pendingInput.mid(g.currentMap.mapLength() + invalidCount);
|
||||||
g.pendingInput.clear();
|
g.pendingInput.clear();
|
||||||
if (m_mode == InsertMode) {
|
g.pendingInput << inputs << Input() << rest;
|
||||||
EventResult result = EventUnhandled;
|
g.mapStates << MappingState(maxMapDepth, inputs.noremap(), inputs.silent());
|
||||||
foreach (const Input &in, pendingInput) {
|
m_commandBuffer.setHistoryAutoSave(false);
|
||||||
EventResult r = handleInsertMode(in);
|
beginEditBlock();
|
||||||
if (r == EventHandled)
|
|
||||||
result = EventHandled;
|
|
||||||
}
|
}
|
||||||
return result;
|
g.currentMap.reset();
|
||||||
}
|
|
||||||
if (m_mode == ReplaceMode) {
|
|
||||||
EventResult result = EventUnhandled;
|
|
||||||
foreach (const Input &in, pendingInput) {
|
|
||||||
EventResult r = handleReplaceMode(in);
|
|
||||||
if (r == EventHandled)
|
|
||||||
result = EventHandled;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (m_mode == CommandMode) {
|
|
||||||
EventResult result = EventUnhandled;
|
|
||||||
foreach (const Input &in, pendingInput) {
|
|
||||||
EventResult r = handleCommandMode(in);
|
|
||||||
if (r == EventHandled)
|
|
||||||
result = EventHandled;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return EventUnhandled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FakeVimHandler::Private::timerEvent(QTimerEvent *ev)
|
void FakeVimHandler::Private::timerEvent(QTimerEvent *ev)
|
||||||
{
|
{
|
||||||
Q_UNUSED(ev);
|
if (ev->timerId() == g.inputTimer) {
|
||||||
handleKey2();
|
if (g.currentMap.isComplete())
|
||||||
|
handleMappedKeys();
|
||||||
|
handleKey(Input());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FakeVimHandler::Private::stopIncrementalFind()
|
void FakeVimHandler::Private::stopIncrementalFind()
|
||||||
@@ -2039,12 +2321,22 @@ void FakeVimHandler::Private::updateMiniBuffer()
|
|||||||
|
|
||||||
QString msg;
|
QString msg;
|
||||||
int cursorPos = -1;
|
int cursorPos = -1;
|
||||||
bool interactive = (m_mode == ExMode || m_subsubmode == SearchSubSubMode);
|
|
||||||
if (m_passing) {
|
if (m_passing) {
|
||||||
msg = "-- PASSING -- ";
|
msg = "-- PASSING -- ";
|
||||||
} else if (!m_currentMessage.isEmpty() && !interactive) {
|
} else if (m_subsubmode == SearchSubSubMode) {
|
||||||
|
msg = m_searchBuffer.display();
|
||||||
|
if (g.mapStates.size() == 1)
|
||||||
|
cursorPos = m_searchBuffer.cursorPos() + 1;
|
||||||
|
} else if (m_mode == ExMode) {
|
||||||
|
msg = m_commandBuffer.display();
|
||||||
|
if (g.mapStates.size() == 1)
|
||||||
|
cursorPos = m_commandBuffer.cursorPos() + 1;
|
||||||
|
} else if (!m_currentMessage.isEmpty()) {
|
||||||
msg = m_currentMessage;
|
msg = m_currentMessage;
|
||||||
m_currentMessage.clear();
|
} else if (g.mapStates.size() > 1 && !g.mapStates.last().silent) {
|
||||||
|
// Do not reset previous message when after running a mapped command.
|
||||||
|
return;
|
||||||
} else if (m_mode == CommandMode && isVisualMode()) {
|
} else if (m_mode == CommandMode && isVisualMode()) {
|
||||||
if (isVisualCharMode()) {
|
if (isVisualCharMode()) {
|
||||||
msg = "-- VISUAL --";
|
msg = "-- VISUAL --";
|
||||||
@@ -2057,17 +2349,13 @@ void FakeVimHandler::Private::updateMiniBuffer()
|
|||||||
msg = "-- INSERT --";
|
msg = "-- INSERT --";
|
||||||
} else if (m_mode == ReplaceMode) {
|
} else if (m_mode == ReplaceMode) {
|
||||||
msg = "-- REPLACE --";
|
msg = "-- REPLACE --";
|
||||||
} else if (m_subsubmode == SearchSubSubMode) {
|
|
||||||
msg = m_searchBuffer.display();
|
|
||||||
cursorPos = m_searchBuffer.cursorPos() + 1;
|
|
||||||
} else if (m_mode == ExMode) {
|
|
||||||
msg = m_commandBuffer.display();
|
|
||||||
cursorPos = m_commandBuffer.cursorPos() + 1;
|
|
||||||
} else {
|
} else {
|
||||||
QTC_CHECK(m_mode == CommandMode && m_subsubmode != SearchSubSubMode);
|
QTC_CHECK(m_mode == CommandMode && m_subsubmode != SearchSubSubMode);
|
||||||
msg = "-- COMMAND --";
|
msg = "-- COMMAND --";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_currentMessage.clear();
|
||||||
|
|
||||||
emit q->commandBufferChanged(msg, cursorPos);
|
emit q->commandBufferChanged(msg, cursorPos);
|
||||||
|
|
||||||
int linesInDoc = linesInDocument();
|
int linesInDoc = linesInDocument();
|
||||||
@@ -3492,8 +3780,6 @@ EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
|
|||||||
highlightMatches(needle);
|
highlightMatches(needle);
|
||||||
m_searchBuffer.clear();
|
m_searchBuffer.clear();
|
||||||
} else if (input.isKey(Key_Up) || input.isKey(Key_PageUp)) {
|
} 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.
|
|
||||||
m_searchBuffer.historyUp();
|
m_searchBuffer.historyUp();
|
||||||
} else if (input.isKey(Key_Down) || input.isKey(Key_PageDown)) {
|
} else if (input.isKey(Key_Down) || input.isKey(Key_PageDown)) {
|
||||||
m_searchBuffer.historyDown();
|
m_searchBuffer.historyDown();
|
||||||
@@ -3779,31 +4065,50 @@ bool FakeVimHandler::Private::handleExMapCommand(const ExCommand &cmd0) // :map
|
|||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const int pos = cmd0.args.indexOf(QLatin1Char(' '));
|
QString args = cmd0.args;
|
||||||
if (pos == -1) {
|
bool silent = false;
|
||||||
|
bool unique = false;
|
||||||
|
forever {
|
||||||
|
if (eatString("<silent>", &args)) {
|
||||||
|
silent = true;
|
||||||
|
} else if (eatString("<unique>", &args)) {
|
||||||
|
continue;
|
||||||
|
} else if (eatString("<special>", &args)) {
|
||||||
|
continue;
|
||||||
|
} else if (eatString("<buffer>", &args)) {
|
||||||
|
notImplementedYet();
|
||||||
|
continue;
|
||||||
|
} else if (eatString("<script>", &args)) {
|
||||||
|
notImplementedYet();
|
||||||
|
continue;
|
||||||
|
} else if (eatString("<expr>", &args)) {
|
||||||
|
notImplementedYet();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString lhs = args.section(QRegExp("\\s+"), 0, 0);
|
||||||
|
const QString rhs = args.section(QRegExp("\\s+"), 1);
|
||||||
|
if ((rhs.isNull() && type != Unmap) || (!rhs.isNull() && type == Unmap)) {
|
||||||
// FIXME: Dump mappings here.
|
// FIXME: Dump mappings here.
|
||||||
//qDebug() << g.mappings;
|
//qDebug() << g.mappings;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString lhs = cmd0.args.left(pos);
|
Inputs key(lhs);
|
||||||
QString rhs = cmd0.args.mid(pos + 1);
|
|
||||||
Inputs key;
|
|
||||||
key.parseFrom(lhs);
|
|
||||||
//qDebug() << "MAPPING: " << modes << lhs << rhs;
|
//qDebug() << "MAPPING: " << modes << lhs << rhs;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Unmap:
|
case Unmap:
|
||||||
foreach (char c, modes)
|
foreach (char c, modes)
|
||||||
if (g.mappings.contains(c))
|
MappingsIterator(&g.mappings, c, key).remove();
|
||||||
g.mappings[c].remove(key);
|
|
||||||
break;
|
break;
|
||||||
case Map:
|
case Map: // fall through
|
||||||
rhs = rhs; // FIXME: expand rhs.
|
|
||||||
// Fall through.
|
|
||||||
case Noremap: {
|
case Noremap: {
|
||||||
Inputs inputs(rhs);
|
Inputs inputs(rhs, type == Noremap, silent);
|
||||||
|
// TODO: Use MappingsIterator to insert mapping!
|
||||||
foreach (char c, modes)
|
foreach (char c, modes)
|
||||||
g.mappings[c].insert(key, inputs);
|
MappingsIterator(&g.mappings, c).setInputs(key, inputs, unique);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5309,6 +5614,18 @@ QWidget *FakeVimHandler::Private::editor() const
|
|||||||
: static_cast<QWidget *>(m_plaintextedit);
|
: static_cast<QWidget *>(m_plaintextedit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char FakeVimHandler::Private::currentModeCode() const
|
||||||
|
{
|
||||||
|
if (isVisualMode())
|
||||||
|
return 'v';
|
||||||
|
else if (m_mode == CommandMode)
|
||||||
|
return 'n';
|
||||||
|
else if (m_mode == ExMode)
|
||||||
|
return 'c';
|
||||||
|
else
|
||||||
|
return 'i';
|
||||||
|
}
|
||||||
|
|
||||||
void FakeVimHandler::Private::undo()
|
void FakeVimHandler::Private::undo()
|
||||||
{
|
{
|
||||||
// FIXME: That's only an approximaxtion. The real solution might
|
// FIXME: That's only an approximaxtion. The real solution might
|
||||||
@@ -5523,7 +5840,7 @@ void FakeVimHandler::Private::replay(const QString &command, int n)
|
|||||||
for (int i = n; --i >= 0; ) {
|
for (int i = n; --i >= 0; ) {
|
||||||
foreach (QChar c, command) {
|
foreach (QChar c, command) {
|
||||||
//qDebug() << " REPLAY: " << c.unicode();
|
//qDebug() << " REPLAY: " << c.unicode();
|
||||||
handleKey(Input(c));
|
handleDefaultKey(Input(c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5950,8 +6267,7 @@ void FakeVimHandler::handleInput(const QString &keys)
|
|||||||
{
|
{
|
||||||
Mode oldMode = d->m_mode;
|
Mode oldMode = d->m_mode;
|
||||||
d->m_mode = CommandMode;
|
d->m_mode = CommandMode;
|
||||||
Inputs inputs;
|
Inputs inputs(keys);
|
||||||
inputs.parseFrom(keys);
|
|
||||||
foreach (const Input &input, inputs)
|
foreach (const Input &input, inputs)
|
||||||
d->handleKey(input);
|
d->handleKey(input);
|
||||||
d->m_mode = oldMode;
|
d->m_mode = oldMode;
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ private slots:
|
|||||||
void test_vim_marks();
|
void test_vim_marks();
|
||||||
void test_vim_copy_paste();
|
void test_vim_copy_paste();
|
||||||
void test_vim_undo_redo();
|
void test_vim_undo_redo();
|
||||||
|
void test_map();
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user