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:
Lukas Holecek
2012-09-01 07:47:25 +02:00
committed by hjk
parent fc5df4b24b
commit e767fb28fa
3 changed files with 603 additions and 169 deletions

View File

@@ -307,9 +307,18 @@ struct SearchData
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)
{
/* Trasformations (Vim regexp -> QRegExp):
/* Transformations (Vim regexp -> QRegExp):
* \a -> [A-Za-z]
* \A -> [^A-Za-z]
* \h -> [A-Za-z_]
@@ -541,7 +550,11 @@ public:
: m_key(0), m_xkey(0), m_modifiers(0) {}
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)
: 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);
}
bool isValid() const
{
return m_key != 0 || !m_text.isNull();
}
bool isDigit() const
{
return m_xkey >= '0' && m_xkey <= '9';
@@ -600,18 +618,17 @@ public:
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;
}
// 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 m_key < a.m_key || m_modifiers < a.m_modifiers || m_text < a.m_text;
}
QString text() const { return m_text; }
QChar asChar() const
@@ -642,54 +659,177 @@ private:
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); }
class Inputs : public QVector<Input>
{
public:
Inputs() {}
explicit Inputs(const QString &str) { parseFrom(str); }
Inputs() : m_noremap(true), m_silent(false) {}
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);
bool m_noremap;
bool m_silent;
};
static bool iss(char a, char b)
static QMap<QString, int> vimKeyNames()
{
if (a >= 'a')
a -= 'a' - 'A';
if (b >= 'a')
b -= 'a' - 'A';
return a == b;
QMap<QString, int> k;
// FIXME: Should be value of mapleader.
k.insert("LEADER", Key_Backslash);
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)
{
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;
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 (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;
uint c = str.at(i).unicode();
if (c == '<') {
int j = str.indexOf('>', i);
Input input;
if (j != -1)
input = parseVimKeyName(str.mid(i+1, j - i - 1));
if (input.isValid()) {
append(input);
i = j;
} else {
append(Input(QLatin1Char(c0)));
append(Input(QLatin1Char(c)));
}
} else {
append(Input(QLatin1Char(c0)));
append(Input(QLatin1Char(c)));
}
}
}
@@ -742,7 +882,7 @@ const QString &History::move(const QStringRef &prefix, int skip)
class CommandBuffer
{
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 setContents(const QString &s) { m_buffer = s; m_pos = s.size(); }
@@ -765,6 +905,7 @@ public:
void moveStart() { m_userPos = m_pos = 0; }
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 historyUp() { setContents(m_history.move(userContents(), -1)); }
const QStringList &historyItems() const { return m_history.items(); }
@@ -774,7 +915,13 @@ public:
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
{
@@ -820,74 +967,151 @@ private:
History m_history;
int m_pos;
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.
class ModeMapping : public QList<QPair<Inputs, Inputs> >
// Mappings for a specific mode (trie structure)
class ModeMapping : public QMap<Input, ModeMapping>
{
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>
{
public:
MappingsIterator(Mappings *mappings, char mode = -1, const Inputs &inputs = Inputs())
: m_parent(mappings)
{
//insert(Inputs() << Input('A') << Input('A'),
// Inputs() << Input('x') << Input('x'));
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)
if (at(i).first == from) {
(*this)[i].second = to;
return;
}
append(QPair<Inputs, Inputs>(from, to));
}
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
// needs to be applied. If a decision can be made, return 'true',
// and replace *input with the mapped data.
bool mappingDone(Inputs *inputs) const
{
// FIXME: inefficient.
for (int i = 0; i != size(); ++i) {
const Inputs &haystack = at(i).first;
// A mapping
if (couldTriggerMap(haystack, *inputs)) {
if (haystack.size() != inputs->size())
return false; // This can be extended.
// Actual mapping.
*inputs = at(i).second;
return true;
}
clear();
m_lastValid = -1;
m_invalidInputCount = 0;
if (mode != 0) {
m_mode = mode;
if (mode != -1)
m_modeMapping = m_parent->find(mode);
}
// No extensible mapping found. Use inputs as-is.
}
bool isValid() const { return !empty(); }
// Return true if mapping can be extended.
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)
{
if (m_modeMapping == m_parent->end())
return false;
if (!input.isValid()) {
m_invalidInputCount += 1;
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;
}
if (!it->value().isEmpty())
m_lastValid = size();
append(it);
return true;
}
private:
static bool couldTriggerMap(const Inputs &haystack, const Inputs &needle)
bool walk(const Inputs &inputs)
{
// Input is already too long.
if (needle.size() > haystack.size())
return false;
for (int i = 0; i != needle.size(); ++i) {
if (!needle.at(i).matchesForMap(haystack.at(i)))
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:
Mappings *m_parent;
Mappings::Iterator m_modeMapping;
int m_lastValid;
int m_invalidInputCount;
char m_mode;
};
// 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
{
@@ -909,8 +1133,9 @@ public:
friend class FakeVimHandler;
void init();
EventResult handleKey(const Input &);
Q_SLOT EventResult handleKey2();
EventResult handleKey(const Input &input);
EventResult handleDefaultKey(const Input &input);
Q_SLOT void handleMappedKeys();
EventResult handleInsertMode(const Input &);
EventResult handleReplaceMode(const Input &);
EventResult handleCommandMode(const Input &);
@@ -1100,6 +1325,7 @@ public:
bool isVisualCharMode() const { return m_visualMode == VisualCharMode; }
bool isVisualLineMode() const { return m_visualMode == VisualLineMode; }
bool isVisualBlockMode() const { return m_visualMode == VisualBlockMode; }
char currentModeCode() const;
void updateEditor();
void selectTextObject(bool simple, bool inner);
@@ -1300,23 +1526,26 @@ public:
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.
QString dotCommand;
QHash<int, Register> registers;
// All mappings.
typedef QHash<char, ModeMapping> Mappings;
Mappings mappings;
// Input.
Inputs pendingInput;
MappingsIterator currentMap;
int inputTimer;
int lastMapCode;
QStack<MappingState> mapStates;
} g;
};
@@ -1639,61 +1868,114 @@ void FakeVimHandler::Private::restoreWidget(int tabSize)
EventResult FakeVimHandler::Private::handleKey(const Input &input)
{
KEY_DEBUG("HANDLE INPUT: " << input << " MODE: " << mode);
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 (g.mappings.value(code).mappingDone(&g.pendingInput))
return handleKey2();
if (g.inputTimer != -1)
killTimer(g.inputTimer);
g.inputTimer = startTimer(1000);
return EventHandled;
if (g.inputTimer != -1) {
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);
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;
}
EventResult FakeVimHandler::Private::handleKey2()
void FakeVimHandler::Private::handleMappedKeys()
{
Inputs pendingInput = g.pendingInput;
g.pendingInput.clear();
if (m_mode == InsertMode) {
EventResult result = EventUnhandled;
foreach (const Input &in, pendingInput) {
EventResult r = handleInsertMode(in);
if (r == EventHandled)
result = EventHandled;
}
return result;
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 (m_mode == ReplaceMode) {
EventResult result = EventUnhandled;
foreach (const Input &in, pendingInput) {
EventResult r = handleReplaceMode(in);
if (r == EventHandled)
result = EventHandled;
}
return result;
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 << inputs << Input() << rest;
g.mapStates << MappingState(maxMapDepth, inputs.noremap(), inputs.silent());
m_commandBuffer.setHistoryAutoSave(false);
beginEditBlock();
}
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;
g.currentMap.reset();
}
void FakeVimHandler::Private::timerEvent(QTimerEvent *ev)
{
Q_UNUSED(ev);
handleKey2();
if (ev->timerId() == g.inputTimer) {
if (g.currentMap.isComplete())
handleMappedKeys();
handleKey(Input());
}
}
void FakeVimHandler::Private::stopIncrementalFind()
@@ -2039,12 +2321,22 @@ void FakeVimHandler::Private::updateMiniBuffer()
QString msg;
int cursorPos = -1;
bool interactive = (m_mode == ExMode || m_subsubmode == SearchSubSubMode);
if (m_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;
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()) {
if (isVisualCharMode()) {
msg = "-- VISUAL --";
@@ -2057,17 +2349,13 @@ void FakeVimHandler::Private::updateMiniBuffer()
msg = "-- INSERT --";
} else if (m_mode == ReplaceMode) {
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 {
QTC_CHECK(m_mode == CommandMode && m_subsubmode != SearchSubSubMode);
msg = "-- COMMAND --";
}
m_currentMessage.clear();
emit q->commandBufferChanged(msg, cursorPos);
int linesInDoc = linesInDocument();
@@ -3492,8 +3780,6 @@ EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
highlightMatches(needle);
m_searchBuffer.clear();
} 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();
} else if (input.isKey(Key_Down) || input.isKey(Key_PageDown)) {
m_searchBuffer.historyDown();
@@ -3779,31 +4065,50 @@ bool FakeVimHandler::Private::handleExMapCommand(const ExCommand &cmd0) // :map
else
return false;
const int pos = cmd0.args.indexOf(QLatin1Char(' '));
if (pos == -1) {
QString args = cmd0.args;
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.
//qDebug() << g.mappings;
return true;
}
QString lhs = cmd0.args.left(pos);
QString rhs = cmd0.args.mid(pos + 1);
Inputs key;
key.parseFrom(lhs);
Inputs key(lhs);
//qDebug() << "MAPPING: " << modes << lhs << rhs;
switch (type) {
case Unmap:
foreach (char c, modes)
if (g.mappings.contains(c))
g.mappings[c].remove(key);
MappingsIterator(&g.mappings, c, key).remove();
break;
case Map:
rhs = rhs; // FIXME: expand rhs.
// Fall through.
case Map: // fall through
case Noremap: {
Inputs inputs(rhs);
Inputs inputs(rhs, type == Noremap, silent);
// TODO: Use MappingsIterator to insert mapping!
foreach (char c, modes)
g.mappings[c].insert(key, inputs);
MappingsIterator(&g.mappings, c).setInputs(key, inputs, unique);
break;
}
}
@@ -5309,6 +5614,18 @@ QWidget *FakeVimHandler::Private::editor() const
: 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()
{
// 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; ) {
foreach (QChar c, command) {
//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;
d->m_mode = CommandMode;
Inputs inputs;
inputs.parseFrom(keys);
Inputs inputs(keys);
foreach (const Input &input, inputs)
d->handleKey(input);
d->m_mode = oldMode;