FakeVim: Add emulation for vim-commentary

Change-Id: I34f222182835ae160e6c4c66ad0bada79d8abeff
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Tom Praschan
2021-01-25 21:06:53 +01:00
committed by Tom Praschan
parent b1f1ecb1b5
commit f626e27370
8 changed files with 274 additions and 77 deletions

View File

@@ -110,6 +110,18 @@
\li \c :<, \c :> \li \c :<, \c :>
\endlist \endlist
\section2 Plugin Emulation
FakeVim also emulates some popular vim plugins. To enable plugin emulation
for particular plugins, select \uicontrol Tools > \uicontrol Options >
\uicontrol FakeVim > \uicontrol General > \uicontrol {Plugin Emulation}.
Currently emulated plugins:
\list
\li \l{https://github.com/tpope/vim-commentary}{vim-commentary}: \c gc
action to comment code regions. For example, \c gcc, \c gc2j, \c gcip
\endlist
\section2 Insert Mode \section2 Insert Mode
\list \list

View File

@@ -4178,6 +4178,59 @@ void FakeVimPlugin::test_vim_visual_block_D()
KEYS(".", X "a" N "g" N "" N "j"); KEYS(".", X "a" N "g" N "" N "j");
} }
void FakeVimPlugin::test_vim_commentary_emulation()
{
TestData data;
setup(&data);
data.doCommand("set commentary");
// Commenting a single line
data.setText("abc" N "def");
KEYS("gcc", X "// abc" N "def");
KEYS("gcc", X "abc" N "def");
KEYS(".", X "// abc" N "def");
// Multiple lines
data.setText("abc" N " def" N "ghi");
KEYS("gcj", X "// abc" N " // def" N "ghi");
KEYS("gcj", X "abc" N " def" N "ghi");
KEYS("gc2j", X "// abc" N " // def" N "// ghi");
KEYS("gcj", X "abc" N " def" N "// ghi");
KEYS(".", X "// abc" N " // def" N "// ghi");
// Visual mode
data.setText("abc" N "def");
KEYS("Vjgc", X "// abc" N "// def");
KEYS(".", X "abc" N "def");
}
void FakeVimPlugin::test_vim_commentary_file_names()
{
TestData data;
setup(&data);
data.doCommand("set commentary");
// Default is "//"
data.setText("abc");
KEYS("gcc", X "// abc");
// pri and pro
data.handler->setCurrentFileName("Test.pri");
data.setText("abc");
KEYS("gcc", X "# abc");
data.handler->setCurrentFileName("Test.pro");
KEYS("gcc", X "abc");
// .h .hpp .cpp
data.handler->setCurrentFileName("Test.h");
data.setText("abc");
KEYS("gcc", X "// abc");
data.handler->setCurrentFileName("Test.hpp");
KEYS("gcc", X "abc");
data.handler->setCurrentFileName("Test.cpp");
KEYS("gcc", X "// abc");
}
void FakeVimPlugin::test_macros() void FakeVimPlugin::test_macros()
{ {
TestData data; TestData data;

View File

@@ -113,6 +113,9 @@ FakeVimSettings::FakeVimSettings()
createAction(ConfigBackspace, QString("indent,eol,start"), "ConfigBackspace", "bs"); createAction(ConfigBackspace, QString("indent,eol,start"), "ConfigBackspace", "bs");
createAction(ConfigIsKeyword, QString("@,48-57,_,192-255,a-z,A-Z"), "IsKeyword", "isk"); createAction(ConfigIsKeyword, QString("@,48-57,_,192-255,a-z,A-Z"), "IsKeyword", "isk");
createAction(ConfigClipboard, QString(), "Clipboard", "cb"); createAction(ConfigClipboard, QString(), "Clipboard", "cb");
// Emulated plugins
createAction(ConfigEmulateVimCommentary, false, "commentary");
} }
FakeVimSettings::~FakeVimSettings() FakeVimSettings::~FakeVimSettings()

View File

@@ -108,6 +108,9 @@ enum FakeVimSettingsCode
ConfigScrollOff, ConfigScrollOff,
ConfigRelativeNumber, ConfigRelativeNumber,
// Plugin emulation
ConfigEmulateVimCommentary,
ConfigBlinkingCursor ConfigBlinkingCursor
}; };

View File

@@ -173,6 +173,7 @@ enum SubMode
RegisterSubMode, // Used for " RegisterSubMode, // Used for "
ShiftLeftSubMode, // Used for < ShiftLeftSubMode, // Used for <
ShiftRightSubMode, // Used for > ShiftRightSubMode, // Used for >
CommentSubMode, // Used for gc
InvertCaseSubMode, // Used for g~ InvertCaseSubMode, // Used for g~
DownCaseSubMode, // Used for gu DownCaseSubMode, // Used for gu
UpCaseSubMode, // Used for gU UpCaseSubMode, // Used for gU
@@ -1342,6 +1343,8 @@ QString dotCommandFromSubMode(SubMode submode)
return QLatin1String("c"); return QLatin1String("c");
if (submode == DeleteSubMode) if (submode == DeleteSubMode)
return QLatin1String("d"); return QLatin1String("d");
if (submode == CommentSubMode)
return QLatin1String("gc");
if (submode == InvertCaseSubMode) if (submode == InvertCaseSubMode)
return QLatin1String("g~"); return QLatin1String("g~");
if (submode == DownCaseSubMode) if (submode == DownCaseSubMode)
@@ -1822,6 +1825,7 @@ public:
bool handleChangeDeleteYankSubModes(const Input &); bool handleChangeDeleteYankSubModes(const Input &);
void handleChangeDeleteYankSubModes(); void handleChangeDeleteYankSubModes();
bool handleReplaceSubMode(const Input &); bool handleReplaceSubMode(const Input &);
bool handleCommentSubMode(const Input &);
bool handleFilterSubMode(const Input &); bool handleFilterSubMode(const Input &);
bool handleRegisterSubMode(const Input &); bool handleRegisterSubMode(const Input &);
bool handleShiftSubMode(const Input &); bool handleShiftSubMode(const Input &);
@@ -2084,6 +2088,7 @@ public:
bool isOperatorPending() const { bool isOperatorPending() const {
return g.submode == ChangeSubMode return g.submode == ChangeSubMode
|| g.submode == DeleteSubMode || g.submode == DeleteSubMode
|| g.submode == CommentSubMode
|| g.submode == FilterSubMode || g.submode == FilterSubMode
|| g.submode == IndentSubMode || g.submode == IndentSubMode
|| g.submode == ShiftLeftSubMode || g.submode == ShiftLeftSubMode
@@ -2159,6 +2164,8 @@ public:
void invertCase(const Range &range); void invertCase(const Range &range);
void toggleComment(const Range &range);
void upCase(const Range &range); void upCase(const Range &range);
void downCase(const Range &range); void downCase(const Range &range);
@@ -3582,6 +3589,7 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
if (g.submode == ChangeSubMode if (g.submode == ChangeSubMode
|| g.submode == DeleteSubMode || g.submode == DeleteSubMode
|| g.submode == CommentSubMode
|| g.submode == YankSubMode || g.submode == YankSubMode
|| g.submode == InvertCaseSubMode || g.submode == InvertCaseSubMode
|| g.submode == DownCaseSubMode || g.submode == DownCaseSubMode
@@ -3608,6 +3616,11 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement)
insertAutomaticIndentation(true); insertAutomaticIndentation(true);
endEditBlock(); endEditBlock();
setTargetColumn(); setTargetColumn();
} else if (g.submode == CommentSubMode) {
pushUndoState(false);
beginEditBlock();
toggleComment(currentRange());
endEditBlock();
} else if (g.submode == DeleteSubMode) { } else if (g.submode == DeleteSubMode) {
pushUndoState(false); pushUndoState(false);
beginEditBlock(); beginEditBlock();
@@ -4254,6 +4267,8 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input)
|| g.submode == DeleteSubMode || g.submode == DeleteSubMode
|| g.submode == YankSubMode) { || g.submode == YankSubMode) {
handled = handleChangeDeleteYankSubModes(input); handled = handleChangeDeleteYankSubModes(input);
} else if (g.submode == CommentSubMode && hasConfig(ConfigEmulateVimCommentary)) {
handled = handleCommentSubMode(input);
} else if (g.submode == ReplaceSubMode) { } else if (g.submode == ReplaceSubMode) {
handled = handleReplaceSubMode(input); handled = handleReplaceSubMode(input);
} else if (g.submode == FilterSubMode) { } else if (g.submode == FilterSubMode) {
@@ -4395,6 +4410,31 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input)
setTargetColumn(); setTargetColumn();
} else if (input.isControl('a')) { } else if (input.isControl('a')) {
changeNumberTextObject(count()); changeNumberTextObject(count());
} else if (g.gflag && input.is('c') && hasConfig(ConfigEmulateVimCommentary)) {
if (isVisualMode()) {
pushUndoState();
QTextCursor start(m_cursor);
QTextCursor end(start);
end.setPosition(end.anchor());
const int count = qAbs(start.blockNumber() - end.blockNumber());
if (count == 0) {
dotCommand = "gcc";
} else {
dotCommand = QString("gc%1j").arg(count);
}
leaveVisualMode();
toggleComment(currentRange());
g.submode = NoSubMode;
} else {
g.movetype = MoveLineWise;
g.submode = CommentSubMode;
pushUndoState();
setAnchor();
}
} else if ((input.is('c') || input.is('d') || input.is('y')) && isNoVisualMode()) { } else if ((input.is('c') || input.is('d') || input.is('y')) && isNoVisualMode()) {
setAnchor(); setAnchor();
g.opcount = g.mvcount; g.opcount = g.mvcount;
@@ -4733,6 +4773,27 @@ bool FakeVimHandler::Private::handleReplaceSubMode(const Input &input)
return handled; return handled;
} }
bool FakeVimHandler::Private::handleCommentSubMode(const Input &input)
{
if (!input.is('c'))
return false;
g.movetype = MoveLineWise;
const int anc = firstPositionInLine(cursorLine() + 1);
moveDown(count() - 1);
const int pos = lastPositionInLine(cursorLine() + 1);
setAnchorAndPosition(anc, pos);
setDotCommand(QString("%1gcc").arg(count()));
finishMovement();
g.submode = NoSubMode;
return true;
}
bool FakeVimHandler::Private::handleFilterSubMode(const Input &) bool FakeVimHandler::Private::handleFilterSubMode(const Input &)
{ {
return false; return false;
@@ -7331,7 +7392,47 @@ void FakeVimHandler::Private::invertCase(const Range &range)
result[i] = c.isUpper() ? c.toLower() : c.toUpper(); result[i] = c.isUpper() ? c.toLower() : c.toUpper();
} }
return result; return result;
}); });
}
void FakeVimHandler::Private::toggleComment(const Range &range)
{
static const QMap<QString, QString> extensionToCommentString {
{"pri", "#"},
{"pro", "#"},
{"h", "//"},
{"hpp", "//"},
{"cpp", "//"},
};
const QString commentString = extensionToCommentString.value(QFileInfo(m_currentFileName).suffix(), "//");
transformText(range,
[&commentString] (const QString &text) -> QString {
QStringList lines = text.split('\n');
const QRegExp checkForComment("^\\s*" + QRegExp::escape(commentString));
const bool firstLineIsComment
= !lines.empty() && lines.front().contains(checkForComment);
for (auto& line : lines) {
if (!line.isEmpty()) {
if (firstLineIsComment) {
const bool hasSpaceAfterCommentString
= line.contains(QRegExp(checkForComment.pattern() + "\\s"));
const int sizeToReplace = hasSpaceAfterCommentString ? commentString.size() + 1
: commentString.size();
line.replace(line.indexOf(commentString), sizeToReplace, "");
} else {
const int indexOfFirstNonSpace = line.indexOf(QRegExp("[^\\s]"));
line = line.left(indexOfFirstNonSpace) + commentString + " " + line.right(line.size() - indexOfFirstNonSpace);
}
}
}
return lines.size() == 1 ? lines.front() : lines.join("\n");
});
} }
void FakeVimHandler::Private::replaceText(const Range &range, const QString &str) void FakeVimHandler::Private::replaceText(const Range &range, const QString &str)

View File

@@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>580</width> <width>580</width>
<height>474</height> <height>531</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
@@ -26,52 +26,10 @@
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<layout class="QGridLayout" name="gridLayout_3"> <layout class="QGridLayout" name="gridLayout_3">
<item row="6" column="0"> <item row="2" column="1">
<widget class="QCheckBox" name="checkBoxStartOfLine"> <widget class="QCheckBox" name="checkBoxIgnoreCase">
<property name="text"> <property name="text">
<string>Start of line</string> <string>Use ignorecase</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="checkBoxHlSearch">
<property name="text">
<string>Highlight search results</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBoxSmartIndent">
<property name="text">
<string>Smart indentation</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxSmartTab">
<property name="text">
<string>Smart tabulators</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkBoxExpandTab">
<property name="text">
<string>Expand tabulators</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkBoxIncSearch">
<property name="text">
<string>Incremental search</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBoxUseCoreSearch">
<property name="text">
<string>Use search dialog</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -82,10 +40,55 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="6" column="0">
<widget class="QCheckBox" name="checkBoxWrapScan"> <widget class="QCheckBox" name="checkBoxStartOfLine">
<property name="text"> <property name="text">
<string>Use wrapscan</string> <string>Start of line</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxSmartTab">
<property name="text">
<string>Smart tabulators</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBoxUseCoreSearch">
<property name="text">
<string>Use search dialog</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="checkBoxRelativeNumber">
<property name="toolTip">
<string>Displays line numbers relative to the line containing text cursor.</string>
</property>
<property name="text">
<string>Show line numbers relative to cursor</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="checkBoxHlSearch">
<property name="text">
<string>Highlight search results</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkBoxIncSearch">
<property name="text">
<string>Incremental search</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="checkBoxShowMarks">
<property name="text">
<string>Show position of text marks</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -106,13 +109,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1">
<widget class="QCheckBox" name="checkBoxIgnoreCase">
<property name="text">
<string>Use ignorecase</string>
</property>
</widget>
</item>
<item row="5" column="0"> <item row="5" column="0">
<widget class="QCheckBox" name="checkBoxShowCmd"> <widget class="QCheckBox" name="checkBoxShowCmd">
<property name="text"> <property name="text">
@@ -120,6 +116,27 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="0">
<widget class="QCheckBox" name="checkBoxBlinkingCursor">
<property name="text">
<string>Blinking cursor</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkBoxExpandTab">
<property name="text">
<string>Expand tabulators</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="checkBoxWrapScan">
<property name="text">
<string>Use wrapscan</string>
</property>
</widget>
</item>
<item row="7" column="0"> <item row="7" column="0">
<widget class="QCheckBox" name="checkBoxPassKeys"> <widget class="QCheckBox" name="checkBoxPassKeys">
<property name="toolTip"> <property name="toolTip">
@@ -130,32 +147,34 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="0"> <item row="1" column="0">
<widget class="QCheckBox" name="checkBoxBlinkingCursor"> <widget class="QCheckBox" name="checkBoxSmartIndent">
<property name="text"> <property name="text">
<string>Blinking cursor</string> <string>Smart indentation</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="checkBoxShowMarks">
<property name="text">
<string>Show position of text marks</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="checkBoxRelativeNumber">
<property name="toolTip">
<string>Displays line numbers relative to the line containing text cursor.</string>
</property>
<property name="text">
<string>Show line numbers relative to cursor</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Plugin Emulation</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxVimCommentary">
<property name="text">
<string>Vim-commentary</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_5"> <layout class="QHBoxLayout" name="horizontalLayout_5">
<item> <item>

View File

@@ -429,6 +429,8 @@ QWidget *FakeVimOptionPage::widget()
m_group.insert(theFakeVimSetting(ConfigRelativeNumber), m_ui.checkBoxRelativeNumber); m_group.insert(theFakeVimSetting(ConfigRelativeNumber), m_ui.checkBoxRelativeNumber);
m_group.insert(theFakeVimSetting(ConfigBlinkingCursor), m_ui.checkBoxBlinkingCursor); m_group.insert(theFakeVimSetting(ConfigBlinkingCursor), m_ui.checkBoxBlinkingCursor);
m_group.insert(theFakeVimSetting(ConfigEmulateVimCommentary), m_ui.checkBoxVimCommentary);
connect(m_ui.pushButtonCopyTextEditorSettings, &QAbstractButton::clicked, connect(m_ui.pushButtonCopyTextEditorSettings, &QAbstractButton::clicked,
this, &FakeVimOptionPage::copyTextEditorSettings); this, &FakeVimOptionPage::copyTextEditorSettings);
connect(m_ui.pushButtonSetQtStyle, &QAbstractButton::clicked, connect(m_ui.pushButtonSetQtStyle, &QAbstractButton::clicked,

View File

@@ -157,6 +157,10 @@ private slots:
void test_vim_Visual_d(); void test_vim_Visual_d();
void test_vim_visual_block_D(); void test_vim_visual_block_D();
// Plugin emulation
void test_vim_commentary_emulation();
void test_vim_commentary_file_names();
void test_macros(); void test_macros();
void test_vim_qtcreator(); void test_vim_qtcreator();