From 10a9447b96c92a0112b45098f4d435c25b0ed672 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Mon, 19 Jun 2017 23:11:38 +0800 Subject: [PATCH] vim-mode: support command line mode and leader key 1. We now support limited commands: :w, :q, :wq, :x, :q! ; 2. We now support fixed leader sequence: y, d, p, P ; 3. Support % to goto /100 percent of the document. --- src/utils/vvim.cpp | 508 ++++++++++++++++++++++++++++++++------ src/utils/vvim.h | 56 ++++- src/vedit.h | 8 + src/vmdeditoperations.cpp | 10 +- src/vmdtab.cpp | 2 + 5 files changed, 503 insertions(+), 81 deletions(-) diff --git a/src/utils/vvim.cpp b/src/utils/vvim.cpp index 07281224..7721dea6 100644 --- a/src/utils/vvim.cpp +++ b/src/utils/vvim.cpp @@ -18,10 +18,74 @@ const QChar VVim::c_unnamedRegister = QChar('"'); const QChar VVim::c_blackHoleRegister = QChar('_'); const QChar VVim::c_selectionRegister = QChar('+'); +#define ADDKEY(x, y) case (x): {ch = (y); break;} + +// Returns NULL QChar if invalid. +static QChar keyToChar(int p_key, int p_modifiers) +{ + if (p_modifiers == Qt::ControlModifier) { + return QChar(); + } + + if (p_key >= Qt::Key_0 && p_key <= Qt::Key_9) { + return QChar('0' + (p_key - Qt::Key_0)); + } else if (p_key >= Qt::Key_A && p_key <= Qt::Key_Z) { + if (p_modifiers == Qt::ShiftModifier) { + return QChar('A' + (p_key - Qt::Key_A)); + } else { + return QChar('a' + (p_key - Qt::Key_A)); + } + } + + QChar ch; + switch (p_key) { + ADDKEY(Qt::Key_Tab, '\t'); + ADDKEY(Qt::Key_Space, ' '); + ADDKEY(Qt::Key_Exclam, '!'); + ADDKEY(Qt::Key_QuoteDbl, '"'); + ADDKEY(Qt::Key_NumberSign, '#'); + ADDKEY(Qt::Key_Dollar, '$'); + ADDKEY(Qt::Key_Percent, '%'); + ADDKEY(Qt::Key_Ampersand, '&'); + ADDKEY(Qt::Key_Apostrophe, '\''); + ADDKEY(Qt::Key_ParenLeft, '('); + ADDKEY(Qt::Key_ParenRight, ')'); + ADDKEY(Qt::Key_Asterisk, '*'); + ADDKEY(Qt::Key_Plus, '+'); + ADDKEY(Qt::Key_Comma, ','); + ADDKEY(Qt::Key_Minus, '-'); + ADDKEY(Qt::Key_Period, '.'); + ADDKEY(Qt::Key_Slash, '/'); + ADDKEY(Qt::Key_Colon, ':'); + ADDKEY(Qt::Key_Semicolon, ';'); + ADDKEY(Qt::Key_Less, '<'); + ADDKEY(Qt::Key_Equal, '='); + ADDKEY(Qt::Key_Greater, '>'); + ADDKEY(Qt::Key_Question, '?'); + ADDKEY(Qt::Key_At, '@'); + ADDKEY(Qt::Key_BracketLeft, '['); + ADDKEY(Qt::Key_Backslash, '\\'); + ADDKEY(Qt::Key_BracketRight, ']'); + ADDKEY(Qt::Key_AsciiCircum, '^'); + ADDKEY(Qt::Key_Underscore, '_'); + ADDKEY(Qt::Key_QuoteLeft, '`'); + ADDKEY(Qt::Key_BraceLeft, '{'); + ADDKEY(Qt::Key_Bar, '|'); + ADDKEY(Qt::Key_BraceRight, '}'); + ADDKEY(Qt::Key_AsciiTilde, '~'); + + default: + break; + } + + return ch; +} + VVim::VVim(VEdit *p_editor) : QObject(p_editor), m_editor(p_editor), m_editConfig(&p_editor->getConfig()), m_mode(VimMode::Normal), - m_resetPositionInBlock(true), m_regName(c_unnamedRegister) + m_resetPositionInBlock(true), m_regName(c_unnamedRegister), + m_cmdMode(false), m_leaderKey(Key(Qt::Key_Space)), m_replayLeaderSequence(false) { initRegisters(); @@ -203,7 +267,7 @@ static void moveCursorAcrossSpaces(QTextCursor &p_cursor, // Expand the selection of @p_cursor to contain additional spaces at the two ends // within a block. -void expandSelectionAcrossSpacesWithinBlock(QTextCursor &p_cursor) +static void expandSelectionAcrossSpacesWithinBlock(QTextCursor &p_cursor) { QTextBlock block = p_cursor.block(); QString text = block.text(); @@ -261,11 +325,34 @@ static void insertChangeBlockAfterDeletion(QTextCursor &p_cursor, int p_deletion } } +// Given the percentage of the text, return the corresponding block number. +// Notice that the block number is based on 0. +// Returns -1 if it is not valid. +static int percentageToBlockNumber(const QTextDocument *p_doc, int p_percent) +{ + if (p_percent > 100 || p_percent <= 0) { + return -1; + } + + int nrBlock = p_doc->blockCount(); + int num = nrBlock * (p_percent * 1.0 / 100) - 1; + + return num >= 0 ? num : 0; +} + bool VVim::handleKeyPressEvent(QKeyEvent *p_event) +{ + bool ret = handleKeyPressEvent(p_event->key(), p_event->modifiers()); + if (ret) { + p_event->accept(); + } + + return ret; +} + +bool VVim::handleKeyPressEvent(int key, int modifiers) { bool ret = false; - int modifiers = p_event->modifiers(); - int key = p_event->key(); bool resetPositionInBlock = true; Key keyInfo(key, modifiers); bool unindent = false; @@ -290,15 +377,35 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event) goto accept; } + if (m_replayLeaderSequence) { + qDebug() << "replaying sequence" << keyToChar(key, modifiers); + } + + if (expectingCommandLineInput()) { + // All input will be treated as command line input. + // [Enter] to execute the command and exit command line mode. + if (processCommandLine(keyInfo)) { + goto clear_accept; + } else { + goto accept; + } + } + m_pendingKeys.append(keyInfo); + if (expectingLeaderSequence()) { + if (processLeaderSequence(keyInfo)) { + goto accept; + } else { + goto clear_accept; + } + } + if (expectingRegisterName()) { // Expecting a register name. QChar reg = keyToRegisterName(keyInfo); if (!reg.isNull()) { - // We should keep m_pendingKeys. m_keys.clear(); - m_tokens.clear(); setRegister(reg); if (m_registers[reg].isNamedRegister()) { m_registers[reg].m_append = (modifiers == Qt::ShiftModifier); @@ -340,12 +447,30 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event) goto clear_accept; } + // Check leader key here. If leader key conflicts with other keys, it will + // overwrite it. + // Leader sequence is just like an action. + if (keyInfo == m_leaderKey + && !hasActionToken() + && !hasNonDigitPendingKeys() + && !m_replayLeaderSequence) { + tryGetRepeatToken(m_keys, m_tokens); + + Q_ASSERT(m_keys.isEmpty()); + + m_pendingKeys.pop_back(); + m_pendingKeys.append(Key(Qt::Key_Backslash)); + m_keys.append(Key(Qt::Key_Backslash)); + goto accept; + } + // We will add key to m_keys. If all m_keys can combined to a token, add // a new token to m_tokens, clear m_keys and try to process m_tokens. switch (key) { case Qt::Key_0: { - if (modifiers == Qt::NoModifier) { + if (modifiers == Qt::NoModifier + || modifiers == Qt::KeypadModifier) { if (!m_keys.isEmpty()) { // Repeat. V_ASSERT(m_keys.last().isDigit()); @@ -376,7 +501,8 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event) case Qt::Key_8: case Qt::Key_9: { - if (modifiers == Qt::NoModifier) { + if (modifiers == Qt::NoModifier + || modifiers == Qt::KeypadModifier) { if (!m_keys.isEmpty() && numberFromKeySequence(m_keys) == -1) { // Invalid sequence. break; @@ -475,8 +601,13 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event) } // Enter Insert mode. - if (m_mode == VimMode::Normal) { - setMode(VimMode::Insert); + // Different from Vim: + // We enter Insert mode even in Visual and VisualLine mode. We + // also keep the selection after the mode change. + if (checkMode(VimMode::Normal) + || checkMode(VimMode::Visual) + || checkMode(VimMode::VisualLine)) { + setMode(VimMode::Insert, false); } } else if (modifiers == Qt::ShiftModifier) { QTextCursor cursor = m_editor->textCursor(); @@ -1045,7 +1176,8 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event) { if (modifiers == Qt::ShiftModifier) { // Specify a register. - if (!m_keys.isEmpty() || !m_tokens.isEmpty()) { + tryGetRepeatToken(m_keys, m_tokens); + if (!m_keys.isEmpty() || hasActionToken()) { // Invalid sequence. break; } @@ -1355,6 +1487,54 @@ bool VVim::handleKeyPressEvent(QKeyEvent *p_event) break; } + case Qt::Key_Colon: + { + if (modifiers == Qt::ShiftModifier) { + if (m_keys.isEmpty() + && m_tokens.isEmpty() + && checkMode(VimMode::Normal)) { + // :, enter command line mode. + // For simplicity, we do not use a standalone mode for this mode. + // Just let it be in Normal mode and use another variable to + // specify this. + m_cmdMode = true; + goto accept; + } + + break; + } + + break; + } + + case Qt::Key_Percent: + { + if (modifiers == Qt::ShiftModifier) { + tryGetRepeatToken(m_keys, m_tokens); + if (m_keys.isEmpty() && hasRepeatToken()) { + // xx%, jump to a certain line (percentage of the documents). + // Change the repeat from percentage to line number. + Token *token = getRepeatToken(); + int bn = percentageToBlockNumber(m_editor->document(), token->m_repeat); + if (bn == -1) { + break; + } else { + // Repeat of LineJump is based on 1. + token->m_repeat = bn + 1; + } + + tryAddMoveAction(); + addMovementToken(Movement::LineJump); + processCommand(m_tokens); + break; + } + + break; + } + + break; + } + default: break; } @@ -1363,7 +1543,6 @@ clear_accept: resetState(); accept: - p_event->accept(); ret = true; exit: @@ -1379,6 +1558,7 @@ void VVim::resetState() m_pendingKeys.clear(); setRegister(c_unnamedRegister); m_resetPositionInBlock = true; + m_cmdMode = false; } VimMode VVim::getMode() const @@ -1386,10 +1566,13 @@ VimMode VVim::getMode() const return m_mode; } -void VVim::setMode(VimMode p_mode) +void VVim::setMode(VimMode p_mode, bool p_clearSelection) { if (m_mode != p_mode) { - clearSelection(); + if (p_clearSelection) { + clearSelection(); + } + m_mode = p_mode; resetState(); @@ -1572,69 +1755,6 @@ void VVim::processMoveAction(QList &p_tokens) } } -#define ADDKEY(x, y) case (x): {ch = (y); break;} - -// Returns NULL QChar if invalid. -static QChar keyToChar(int p_key, int p_modifiers) -{ - if (p_modifiers == Qt::ControlModifier) { - return QChar(); - } - - if (p_key >= Qt::Key_0 && p_key <= Qt::Key_9) { - return QChar('0' + (p_key - Qt::Key_0)); - } else if (p_key >= Qt::Key_A && p_key <= Qt::Key_Z) { - if (p_modifiers == Qt::ShiftModifier) { - return QChar('A' + (p_key - Qt::Key_A)); - } else { - return QChar('a' + (p_key - Qt::Key_A)); - } - } - - QChar ch; - switch (p_key) { - ADDKEY(Qt::Key_Tab, '\t'); - ADDKEY(Qt::Key_Space, ' '); - ADDKEY(Qt::Key_Exclam, '!'); - ADDKEY(Qt::Key_QuoteDbl, '"'); - ADDKEY(Qt::Key_NumberSign, '#'); - ADDKEY(Qt::Key_Dollar, '$'); - ADDKEY(Qt::Key_Percent, '%'); - ADDKEY(Qt::Key_Ampersand, '&'); - ADDKEY(Qt::Key_Apostrophe, '\''); - ADDKEY(Qt::Key_ParenLeft, '('); - ADDKEY(Qt::Key_ParenRight, ')'); - ADDKEY(Qt::Key_Asterisk, '*'); - ADDKEY(Qt::Key_Plus, '+'); - ADDKEY(Qt::Key_Comma, ','); - ADDKEY(Qt::Key_Minus, '-'); - ADDKEY(Qt::Key_Period, '.'); - ADDKEY(Qt::Key_Slash, '/'); - ADDKEY(Qt::Key_Colon, ':'); - ADDKEY(Qt::Key_Semicolon, ';'); - ADDKEY(Qt::Key_Less, '<'); - ADDKEY(Qt::Key_Equal, '='); - ADDKEY(Qt::Key_Greater, '>'); - ADDKEY(Qt::Key_Question, '?'); - ADDKEY(Qt::Key_At, '@'); - ADDKEY(Qt::Key_BracketLeft, '['); - ADDKEY(Qt::Key_Backslash, '\\'); - ADDKEY(Qt::Key_BracketRight, ']'); - ADDKEY(Qt::Key_AsciiCircum, '^'); - ADDKEY(Qt::Key_Underscore, '_'); - ADDKEY(Qt::Key_QuoteLeft, '`'); - ADDKEY(Qt::Key_BraceLeft, '{'); - ADDKEY(Qt::Key_Bar, '|'); - ADDKEY(Qt::Key_BraceRight, '}'); - ADDKEY(Qt::Key_AsciiTilde, '~'); - - default: - break; - } - - return ch; -} - bool VVim::processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc, QTextCursor::MoveMode p_moveMode, const Token &p_token, int p_repeat) @@ -3247,6 +3367,20 @@ bool VVim::expectingCharacterTarget() const || key == Key(Qt::Key_T, Qt::ShiftModifier)); } +bool VVim::expectingCommandLineInput() const +{ + return m_cmdMode; +} + +bool VVim::expectingLeaderSequence() const +{ + if (m_replayLeaderSequence || m_keys.isEmpty()) { + return false; + } + + return m_keys.first() == Key(Qt::Key_Backslash); +} + QChar VVim::keyToRegisterName(const Key &p_key) const { if (p_key.isAlphabet()) { @@ -3301,6 +3435,24 @@ bool VVim::hasActionToken() const return has; } +bool VVim::hasRepeatToken() const +{ + // There will be only one repeat token. + bool has = false; + if (m_tokens.isEmpty()) { + return false; + } + + for (int i = 0; i < m_tokens.size(); ++i) { + if (m_tokens.at(i).isRepeat()) { + V_ASSERT(!has); + has = true; + } + } + + return has; +} + bool VVim::hasActionTokenValidForTextObject() const { if (hasActionToken()) { @@ -3350,6 +3502,19 @@ const VVim::Token *VVim::getActionToken() const return &m_tokens.first(); } +VVim::Token *VVim::getRepeatToken() +{ + V_ASSERT(hasRepeatToken()); + + for (auto & token : m_tokens) { + if (token.isRepeat()) { + return &token; + } + } + + return NULL; +} + void VVim::addRangeToken(Range p_range) { m_tokens.append(Token(p_range)); @@ -3576,3 +3741,192 @@ void VVim::setRegister(QChar p_reg) { m_regName = p_reg; } + +bool VVim::checkMode(VimMode p_mode) +{ + return m_mode == p_mode; +} + +bool VVim::processCommandLine(const Key &p_key) +{ + Q_ASSERT(m_cmdMode); + + if (p_key == Key(Qt::Key_Return) + || p_key == Key(Qt::Key_Enter, Qt::KeypadModifier)) { + // Enter, try to execute the command and exit cmd line mode. + executeCommand(m_keys); + m_cmdMode = false; + return true; + } + + if (p_key.m_key == Qt::Key_Escape + || (p_key.m_key == Qt::Key_BracketLeft && p_key.m_modifiers == Qt::ControlModifier)) { + // Go back to Normal mode. + m_keys.clear(); + m_pendingKeys.clear(); + m_cmdMode = false; + + setMode(VimMode::Normal); + return true; + } + + switch (p_key.m_key) { + case Qt::Key_Backspace: + // Delete one char backward. + if (m_keys.isEmpty()) { + // Exit command line mode. + Q_ASSERT(m_pendingKeys.size() == 1); + m_pendingKeys.pop_back(); + m_cmdMode = false; + return true; + } else { + m_keys.pop_back(); + m_pendingKeys.pop_back(); + } + + break; + + case Qt::Key_U: + { + if (p_key.m_modifiers == Qt::ControlModifier) { + // Ctrl+U, delete all input keys. + while (!m_keys.isEmpty()) { + m_keys.pop_back(); + m_pendingKeys.pop_back(); + } + } else { + // Just pend this key. + m_pendingKeys.append(p_key); + m_keys.append(p_key); + } + + break; + } + + default: + // Just pend this key. + m_pendingKeys.append(p_key); + m_keys.append(p_key); + } + + return false; +} + +void VVim::executeCommand(const QList &p_keys) +{ + bool validCommand = true; + QString msg; + + if (p_keys.isEmpty()) { + return; + } if (p_keys.size() == 1) { + const Key &key0 = p_keys.first(); + if (key0 == Key(Qt::Key_W)) { + // :w, save current file. + emit m_editor->saveNote(); + msg = tr("Note has been saved"); + } else if (key0 == Key(Qt::Key_Q)) { + // :q, quit edit mode. + emit m_editor->discardAndRead(); + msg = tr("Quit"); + } else if (key0 == Key(Qt::Key_X)) { + // :x, save if there is any change and quit edit mode. + emit m_editor->saveAndRead(); + msg = tr("Quit with note having been saved"); + } else { + validCommand = false; + } + } else if (p_keys.size() == 2) { + const Key &key0 = p_keys.first(); + const Key &key1 = p_keys.at(1); + if (key0 == Key(Qt::Key_W) && key1 == Key(Qt::Key_Q)) { + // :wq, save change and quit edit mode. + // We treat it same as :x. + emit m_editor->saveAndRead(); + msg = tr("Quit with note having been saved"); + } else if (key0 == Key(Qt::Key_Q) && key1 == Key(Qt::Key_Exclam, Qt::ShiftModifier)) { + // :q!, discard change and quit edit mode. + emit m_editor->discardAndRead(); + msg = tr("Quit"); + } else { + validCommand = false; + } + } else { + validCommand = false; + } + + if (!validCommand) { + QString str; + for (auto const & key : p_keys) { + str.append(keyToChar(key.m_key, key.m_modifiers)); + } + + message(tr("Not an editor command: %1").arg(str)); + } else { + message(msg); + } +} + +bool VVim::hasNonDigitPendingKeys() +{ + for (auto const &key : m_keys) { + if (!key.isDigit()) { + return true; + } + } + + return false; +} + +bool VVim::processLeaderSequence(const Key &p_key) +{ + // Different from Vim: + // If it is not a valid sequence, we just do nothing here. + V_ASSERT(checkPendingKey(Key(Qt::Key_Backslash))); + bool validSequence = true; + QList replaySeq; + + if (p_key == Key(Qt::Key_Y)) { + // y, "+y + replaySeq.append(Key(Qt::Key_QuoteDbl, Qt::ShiftModifier)); + replaySeq.append(Key(Qt::Key_Plus, Qt::ShiftModifier)); + replaySeq.append(Key(Qt::Key_Y)); + } else if (p_key == Key(Qt::Key_D)) { + // d, "+d + replaySeq.append(Key(Qt::Key_QuoteDbl, Qt::ShiftModifier)); + replaySeq.append(Key(Qt::Key_Plus, Qt::ShiftModifier)); + replaySeq.append(Key(Qt::Key_D)); + } else if (p_key == Key(Qt::Key_P)) { + // p, "+p + replaySeq.append(Key(Qt::Key_QuoteDbl, Qt::ShiftModifier)); + replaySeq.append(Key(Qt::Key_Plus, Qt::ShiftModifier)); + replaySeq.append(Key(Qt::Key_P)); + } else if (p_key == Key(Qt::Key_P, Qt::ShiftModifier)) { + // P, "+P + replaySeq.append(Key(Qt::Key_QuoteDbl, Qt::ShiftModifier)); + replaySeq.append(Key(Qt::Key_Plus, Qt::ShiftModifier)); + replaySeq.append(Key(Qt::Key_P, Qt::ShiftModifier)); + } else { + validSequence = false; + } + + if (!replaySeq.isEmpty()) { + // Replay the sequence. + m_replayLeaderSequence = true; + m_keys.clear(); + for (int i = 0; i < 2; ++i) { + m_pendingKeys.pop_back(); + } + + for (auto const &key : replaySeq) { + bool ret = handleKeyPressEvent(key.m_key, key.m_modifiers); + if (!ret) { + break; + } + } + + m_replayLeaderSequence = false; + } + + return validSequence; +} diff --git a/src/utils/vvim.h b/src/utils/vvim.h index 7ac0ffec..25519dfa 100644 --- a/src/utils/vvim.h +++ b/src/utils/vvim.h @@ -99,7 +99,7 @@ public: VimMode getMode() const; // Set current mode. - void setMode(VimMode p_mode); + void setMode(VimMode p_mode, bool p_clearSelection = true); // Set current register. void setRegister(QChar p_reg); @@ -146,7 +146,7 @@ private: { return m_key >= Qt::Key_0 && m_key <= Qt::Key_9 - && m_modifiers == Qt::NoModifier; + && (m_modifiers == Qt::NoModifier || m_modifiers == Qt::KeypadModifier); } int toDigit() const @@ -348,6 +348,9 @@ private: Key m_key; }; + // Returns true if the event is consumed and need no more handling. + bool handleKeyPressEvent(int key, int modifiers); + // Reset all key state info. void resetState(); @@ -416,6 +419,12 @@ private: // Check m_keys to see if we are expecting a target for f/t/F/T command. bool expectingCharacterTarget() const; + // Check if we are in command line mode. + bool expectingCommandLineInput() const; + + // Check if we are in a leader sequence. + bool expectingLeaderSequence() const; + // Return the corresponding register name of @p_key. // If @p_key is not a valid register name, return a NULL QChar. QChar keyToRegisterName(const Key &p_key) const; @@ -423,6 +432,9 @@ private: // Check if @m_tokens contains an action token. bool hasActionToken() const; + // Check if @m_tokens contains a repeat token. + bool hasRepeatToken() const; + // Try to add an Action::Move action at the front if there is no any action // token. void tryAddMoveAction(); @@ -433,6 +445,9 @@ private: // Get the action token from m_tokens. const Token *getActionToken() const; + // Get the repeat token from m_tokens. + Token *getRepeatToken(); + // Add an Range token at the end of m_tokens. void addRangeToken(Range p_range); @@ -487,6 +502,32 @@ private: void message(const QString &p_str); + // Check if m_mode equals to p_mode. + bool checkMode(VimMode p_mode); + + // In command line mode, read input @p_key and process it. + // Returns true if a command has been completed, otherwise returns false. + bool processCommandLine(const Key &p_key); + + // Execute command specified by @p_keys. + // @p_keys does not contain the leading colon. + // Following commands are supported: + // :w, :wq, :q, :q!, :x + void executeCommand(const QList &p_keys); + + // Check if m_keys has non-digit key. + bool hasNonDigitPendingKeys(); + + // Reading a leader sequence, read input @p_key and process it. + // Returns true if a sequence has been replayed or it is being read, + // otherwise returns false. + // Following sequences are supported: + // y: "+y + // d: "+d + // p: "+p + // P: "+P + bool processLeaderSequence(const Key &p_key); + VEdit *m_editor; const VEditConfig *m_editConfig; VimMode m_mode; @@ -512,6 +553,17 @@ private: // Last f/F/t/T Token. Token m_lastFindToken; + // Whether in command line mode. + bool m_cmdMode; + + // The leader key, which is Key_Space by default. + Key m_leaderKey; + + // Whether we are parsing a leader sequence. + // We will map a leader sequence to another actual sequence. When replaying + // this actual sequence, m_leaderSequence will be true. + bool m_replayLeaderSequence; + static const QChar c_unnamedRegister; static const QChar c_blackHoleRegister; static const QChar c_selectionRegister; diff --git a/src/vedit.h b/src/vedit.h index d0d725cd..fb12f578 100644 --- a/src/vedit.h +++ b/src/vedit.h @@ -86,10 +86,18 @@ public: void requestUpdateVimStatus(); signals: + // Request VEditTab to save and exit edit mode. void saveAndRead(); + + // Request VEditTab to discard and exit edit mode. void discardAndRead(); + + // Request VEditTab to edit current note. void editNote(); + // Request VEditTab to save this file. + void saveNote(); + // Emit when m_config has been updated. void configUpdated(); diff --git a/src/vmdeditoperations.cpp b/src/vmdeditoperations.cpp index 63000663..9a7b6a67 100644 --- a/src/vmdeditoperations.cpp +++ b/src/vmdeditoperations.cpp @@ -311,6 +311,8 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event) break; } + case Qt::Key_Enter: + // Fall through. case Qt::Key_Return: { if (handleKeyReturn(p_event)) { @@ -326,8 +328,12 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event) exit: // Qt::Key_Return, Qt::Key_Tab and Qt::Key_Backtab will handle m_autoIndentPos. - if (key != Qt::Key_Return && key != Qt::Key_Tab && key != Qt::Key_Backtab && - key != Qt::Key_Shift) { + if (key != Qt::Key_Return + && key != Qt::Key_Enter + && key != Qt::Key_Tab + && key != Qt::Key_Backtab + && key != Qt::Key_Shift + && key != Qt::Key_Control) { m_autoIndentPos = -1; } diff --git a/src/vmdtab.cpp b/src/vmdtab.cpp index adb09744..09ffcd1e 100644 --- a/src/vmdtab.cpp +++ b/src/vmdtab.cpp @@ -58,6 +58,8 @@ void VMdTab::setupUI() this, &VMdTab::saveAndRead); connect(m_editor, &VEdit::discardAndRead, this, &VMdTab::discardAndRead); + connect(m_editor, &VEdit::saveNote, + this, &VMdTab::saveFile); connect(m_editor, &VEdit::statusMessage, this, &VEditTab::statusMessage); connect(m_editor, &VEdit::vimStatusUpdated,