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 <num>% to goto <num>/100 percent of the document.
This commit is contained in:
Le Tan 2017-06-19 23:11:38 +08:00
parent 5953954786
commit 10a9447b96
5 changed files with 503 additions and 81 deletions

View File

@ -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<Token> &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<Key> &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<Key> replaySeq;
if (p_key == Key(Qt::Key_Y)) {
// <leader>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)) {
// <leader>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)) {
// <leader>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)) {
// <leader>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;
}

View File

@ -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<Key> &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;

View File

@ -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();

View File

@ -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;
}

View File

@ -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,