fakevim: Improved searching

Reset cursor position if search is canceled and always search from
initial cursor position if search expression changes.

Implemented Vim's wrapscan (ws) option.

Task-number: QTCREATORBUG-7251

Change-Id: Ic709cc4fb9dacdb94fbd17f85ac9b75738d5578c
Reviewed-by: hjk <qthjk@ovi.com>
This commit is contained in:
Lukas Holecek
2012-09-01 10:44:02 +02:00
committed by hjk
parent c4b9cff324
commit 4503ce663a
6 changed files with 131 additions and 47 deletions

View File

@@ -426,17 +426,68 @@ void FakeVimPlugin::test_vim_search()
data.setText("abc" N "def" N "ghi"); data.setText("abc" N "def" N "ghi");
KEYS("/ghi<CR>", "abc" N "def" N X "ghi"); KEYS("/ghi<CR>", "abc" N "def" N X "ghi");
KEYS("gg/\\w\\{3}<CR>", X "abc" N "def" N "ghi"); KEYS("gg/\\w\\{3}<CR>", "abc" N X "def" N "ghi");
KEYS("n", "abc" N X "def" N "ghi"); KEYS("n", "abc" N "def" N X "ghi");
KEYS("N", "abc" N X "def" N "ghi");
KEYS("N", X "abc" N "def" N "ghi"); KEYS("N", X "abc" N "def" N "ghi");
NOT_IMPLEMENTED // return to search-start position on escape or not found
KEYS("2n", "abc" N "def" N X "ghi"); KEYS("/def<ESC>", X "abc" N "def" N "ghi");
KEYS("2N", X "abc" N "def" N "ghi"); KEYS("/x", X "abc" N "def" N "ghi");
KEYS("/x<CR>", X "abc" N "def" N "ghi");
KEYS("/x<ESC>", X "abc" N "def" N "ghi");
KEYS("?def<ESC>", X "abc" N "def" N "ghi");
KEYS("?x", X "abc" N "def" N "ghi");
KEYS("?x<CR>", X "abc" N "def" N "ghi");
KEYS("?x<ESC>", X "abc" N "def" N "ghi");
// search [count] times
data.setText("abc" N "def" N "ghi");
KEYS("/\\w\\{3}<CR>", "abc" N X "def" N "ghi");
KEYS("2n", X "abc" N "def" N "ghi");
KEYS("2N", "abc" N X "def" N "ghi");
KEYS("2/\\w\\{3}<CR>", X "abc" N "def" N "ghi");
// set wrapscan (search wraps at end of file)
data.doCommand("set ws");
data.setText("abc" N "def" N "abc" N "ghi abc jkl");
KEYS("*", "abc" N "def" N X "abc" N "ghi abc jkl");
KEYS("*", "abc" N "def" N "abc" N "ghi " X "abc jkl");
KEYS("2*", "abc" N "def" N X "abc" N "ghi abc jkl");
KEYS("#", X "abc" N "def" N "abc" N "ghi abc jkl");
KEYS("#", "abc" N "def" N "abc" N "ghi " X "abc jkl");
KEYS("#", "abc" N "def" N X "abc" N "ghi abc jkl");
KEYS("2#", "abc" N "def" N "abc" N "ghi " X "abc jkl");
data.doCommand("set nows");
data.setText("abc" N "def" N "abc" N "ghi abc jkl");
KEYS("*", "abc" N "def" N X "abc" N "ghi abc jkl");
KEYS("*", "abc" N "def" N "abc" N "ghi " X "abc jkl");
KEYS("*", "abc" N "def" N "abc" N "ghi " X "abc jkl");
KEYS("#", "abc" N "def" N X "abc" N "ghi abc jkl");
KEYS("#", X "abc" N "def" N "abc" N "ghi abc jkl");
KEYS("#", X "abc" N "def" N "abc" N "ghi abc jkl");
data.setText("abc" N "def" N "ab" X "c" N "ghi abc jkl");
KEYS("#", X "abc" N "def" N "abc" N "ghi abc jkl");
/* QTCREATORBUG-7251 */ /* QTCREATORBUG-7251 */
data.setText("abc abc abc abc"); data.setText("abc abc abc abc");
KEYS("$?abc<CR>", "abc abc abc " X "abc"); KEYS("$?abc<CR>", "abc abc abc " X "abc");
KEYS("2?abc<CR>", "abc " X "abc abc abc");
KEYS("n", X "abc abc abc abc");
KEYS("N", "abc " X "abc abc abc");
NOT_IMPLEMENTED
// find same stuff forward and backward,
// i.e. '<ab>c' forward but not 'a<bc>' backward
data.setText("abc" N "def" N "ghi");
KEYS("/\\w\\{2}<CR>", X "abc" N "def" N "ghi");
KEYS("2n", "abc" N "def" N X "ghi");
KEYS("N", "abc" N X "def" N "ghi");
KEYS("N", X "abc" N "def" N "ghi");
KEYS("2n2N", X "abc" N "def" N "ghi");
} }
void FakeVimPlugin::test_vim_indent() void FakeVimPlugin::test_vim_indent()

View File

@@ -218,6 +218,13 @@ FakeVimSettings *theFakeVimSettings()
item->setCheckable(true); item->setCheckable(true);
instance->insertItem(ConfigSmartCase, item, _("smartcase"), _("scs")); instance->insertItem(ConfigSmartCase, item, _("smartcase"), _("scs"));
item = new SavedAction(instance);
item->setDefaultValue(true);
item->setValue(true);
item->setSettingsKey(group, _("WrapScan")); item->setCheckable(true);
item->setCheckable(true);
instance->insertItem(ConfigWrapScan, item, _("wrapscan"), _("ws"));
item = new SavedAction(instance); item = new SavedAction(instance);
item->setDefaultValue(_("indent,eol,start")); item->setDefaultValue(_("indent,eol,start"));
item->setSettingsKey(group, _("Backspace")); item->setSettingsKey(group, _("Backspace"));

View File

@@ -53,9 +53,11 @@ enum FakeVimSettingsCode
ConfigExpandTab, ConfigExpandTab,
ConfigAutoIndent, ConfigAutoIndent,
ConfigSmartIndent, ConfigSmartIndent,
ConfigIncSearch, ConfigIncSearch,
ConfigUseCoreSearch, ConfigUseCoreSearch,
ConfigSmartCase, ConfigSmartCase,
ConfigWrapScan,
// indent allow backspacing over autoindent // indent allow backspacing over autoindent
// eol allow backspacing over line breaks (join lines) // eol allow backspacing over line breaks (join lines)

View File

@@ -297,14 +297,12 @@ struct SearchData
SearchData() SearchData()
{ {
forward = true; forward = true;
mustMove = true;
highlightMatches = true; highlightMatches = true;
highlightCursor = true; highlightCursor = true;
} }
QString needle; QString needle;
bool forward; bool forward;
bool mustMove;
bool highlightMatches; bool highlightMatches;
bool highlightCursor; bool highlightCursor;
}; };
@@ -887,6 +885,7 @@ public:
void finishMovement(const QString &dotCommand, int count); void finishMovement(const QString &dotCommand, int count);
void resetCommandMode(); void resetCommandMode();
void search(const SearchData &sd); void search(const SearchData &sd);
void searchNext(bool forward = true);
void searchBalanced(bool forward, QChar needle, QChar other); void searchBalanced(bool forward, QChar needle, QChar other);
void highlightMatches(const QString &needle); void highlightMatches(const QString &needle);
void stopIncrementalFind(); void stopIncrementalFind();
@@ -1222,6 +1221,8 @@ public:
QList<QTextEdit::ExtraSelection> m_searchSelections; QList<QTextEdit::ExtraSelection> m_searchSelections;
QTextCursor m_searchCursor; QTextCursor m_searchCursor;
int m_searchStartPosition;
int m_searchFromScreenLine;
QString m_oldNeedle; QString m_oldNeedle;
QString m_lastSubstituteFlags; QString m_lastSubstituteFlags;
QRegExp m_lastSubstitutePattern; QRegExp m_lastSubstitutePattern;
@@ -1326,6 +1327,8 @@ void FakeVimHandler::Private::init()
m_oldPosition = -1; m_oldPosition = -1;
m_lastChangePosition = -1; m_lastChangePosition = -1;
m_breakEditBlock = false; m_breakEditBlock = false;
m_searchStartPosition = 0;
m_searchFromScreenLine = 0;
setupCharClass(); setupCharClass();
} }
@@ -2002,9 +2005,10 @@ 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()) { } else if (!m_currentMessage.isEmpty() && !interactive) {
msg = m_currentMessage; msg = m_currentMessage;
} else if (m_mode == CommandMode && isVisualMode()) { } else if (m_mode == CommandMode && isVisualMode()) {
if (isVisualCharMode()) { if (isVisualCharMode()) {
@@ -2019,10 +2023,8 @@ void FakeVimHandler::Private::updateMiniBuffer()
} else if (m_mode == ReplaceMode) { } else if (m_mode == ReplaceMode) {
msg = "-- REPLACE --"; msg = "-- REPLACE --";
} else if (!m_commandPrefix.isEmpty()) { } else if (!m_commandPrefix.isEmpty()) {
//QTC_ASSERT(m_mode == ExMode || m_subsubmode == SearchSubSubMode,
// qDebug() << "MODE: " << m_mode << m_subsubmode);
msg = m_commandPrefix + m_commandBuffer.display(); msg = m_commandPrefix + m_commandBuffer.display();
if (m_mode != CommandMode) if (interactive)
cursorPos = m_commandPrefix.size() + m_commandBuffer.cursorPos(); cursorPos = m_commandPrefix.size() + m_commandBuffer.cursorPos();
} else { } else {
QTC_CHECK(m_mode == CommandMode && m_subsubmode != SearchSubSubMode); QTC_CHECK(m_mode == CommandMode && m_subsubmode != SearchSubSubMode);
@@ -2394,6 +2396,8 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input)
m_movetype = MoveExclusive; m_movetype = MoveExclusive;
m_subsubmode = SearchSubSubMode; m_subsubmode = SearchSubSubMode;
m_commandPrefix = QLatin1Char(m_lastSearchForward ? '/' : '?'); m_commandPrefix = QLatin1Char(m_lastSearchForward ? '/' : '?');
m_searchStartPosition = position();
m_searchFromScreenLine = firstVisibleLine();
m_commandBuffer.clear(); m_commandBuffer.clear();
updateMiniBuffer(); updateMiniBuffer();
} }
@@ -2410,18 +2414,8 @@ EventResult FakeVimHandler::Private::handleCommandMode1(const Input &input)
setAnchorAndPosition(tc.position(), tc.anchor()); setAnchorAndPosition(tc.position(), tc.anchor());
g.searchHistory.append(needle); g.searchHistory.append(needle);
m_lastSearchForward = input.is('*'); m_lastSearchForward = input.is('*');
m_currentMessage.clear(); searchNext();
m_commandPrefix = QLatin1Char(m_lastSearchForward ? '/' : '?'); finishMovement();
m_commandBuffer.setContents(needle);
SearchData sd;
sd.needle = needle;
sd.forward = m_lastSearchForward;
sd.highlightCursor = false;
sd.highlightMatches = true;
search(sd);
//m_searchCursor = QTextCursor();
//updateSelection();
//updateMiniBuffer();
} else if (input.is('\'')) { } else if (input.is('\'')) {
m_subsubmode = TickSubSubMode; m_subsubmode = TickSubSubMode;
if (m_submode != NoSubMode) if (m_submode != NoSubMode)
@@ -2810,12 +2804,8 @@ EventResult FakeVimHandler::Private::handleCommandMode2(const Input &input)
} }
setPosition(cursor().selectionStart()); setPosition(cursor().selectionStart());
} else { } else {
SearchData sd; searchNext(input.is('n'));
sd.needle = g.searchHistory.current(); finishMovement();
sd.forward = input.is('n') ? m_lastSearchForward : !m_lastSearchForward;
sd.highlightCursor = false;
sd.highlightMatches = true;
search(sd);
} }
} else if (isVisualMode() && (input.is('o') || input.is('O'))) { } else if (isVisualMode() && (input.is('o') || input.is('O'))) {
int pos = position(); int pos = position();
@@ -3445,6 +3435,8 @@ EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
m_commandBuffer.clear(); m_commandBuffer.clear();
g.searchHistory.append(m_searchCursor.selectedText()); g.searchHistory.append(m_searchCursor.selectedText());
m_searchCursor = QTextCursor(); m_searchCursor = QTextCursor();
setPosition(m_searchStartPosition);
scrollToLine(m_searchFromScreenLine);
updateSelection(); updateSelection();
enterCommandMode(); enterCommandMode();
updateMiniBuffer(); updateMiniBuffer();
@@ -3477,6 +3469,8 @@ EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
search(sd); search(sd);
} }
finishMovement(m_commandPrefix + needle + '\n'); finishMovement(m_commandPrefix + needle + '\n');
} else {
finishMovement();
} }
enterCommandMode(); enterCommandMode();
highlightMatches(needle); highlightMatches(needle);
@@ -3500,7 +3494,6 @@ EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
SearchData sd; SearchData sd;
sd.needle = m_commandBuffer.contents(); sd.needle = m_commandBuffer.contents();
sd.forward = m_lastSearchForward; sd.forward = m_lastSearchForward;
sd.mustMove = false;
sd.highlightCursor = true; sd.highlightCursor = true;
sd.highlightMatches = false; sd.highlightMatches = false;
search(sd); search(sd);
@@ -4258,7 +4251,6 @@ void FakeVimHandler::Private::search(const SearchData &sd)
if (sd.needle.isEmpty()) if (sd.needle.isEmpty())
return; return;
const bool incSearch = hasConfig(ConfigIncSearch);
QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively; QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
if (!sd.forward) if (!sd.forward)
flags |= QTextDocument::FindBackward; flags |= QTextDocument::FindBackward;
@@ -4267,29 +4259,36 @@ void FakeVimHandler::Private::search(const SearchData &sd)
const int oldLine = cursorLine() - cursorLineOnScreen(); const int oldLine = cursorLine() - cursorLineOnScreen();
int startPos = position(); int startPos = m_searchStartPosition + (sd.forward ? 1 : -1);
if (sd.mustMove)
sd.forward ? ++startPos : --startPos;
m_searchCursor = QTextCursor(); m_searchCursor = QTextCursor();
int repeat = count();
QTextCursor tc = document()->find(needleExp, startPos, flags); QTextCursor tc = document()->find(needleExp, startPos, flags);
while (!tc.isNull() && --repeat >= 1)
tc = document()->find(needleExp, tc, flags);
if (tc.isNull()) { if (tc.isNull()) {
int startPos = sd.forward ? 0 : lastPositionInDocument(); if (hasConfig(ConfigWrapScan)) {
tc = document()->find(needleExp, startPos, flags); int startPos = sd.forward ? 0 : lastPositionInDocument();
if (tc.isNull()) { tc = document()->find(needleExp, startPos, flags);
if (!incSearch) { while (!tc.isNull() && --repeat >= 1)
tc = document()->find(needleExp, tc, flags);
if (tc.isNull()) {
highlightMatches(QString()); highlightMatches(QString());
showRedMessage(FakeVimHandler::tr("Pattern not found: %1") showRedMessage(FakeVimHandler::tr("Pattern not found: %1").arg(sd.needle));
.arg(needleExp.pattern())); updateSelection();
return;
} }
updateSelection();
return;
}
if (!incSearch) {
QString msg = sd.forward QString msg = sd.forward
? FakeVimHandler::tr("search hit BOTTOM, continuing at TOP") ? FakeVimHandler::tr("search hit BOTTOM, continuing at TOP")
: FakeVimHandler::tr("search hit TOP, continuing at BOTTOM"); : FakeVimHandler::tr("search hit TOP, continuing at BOTTOM");
showRedMessage(msg); showRedMessage(msg);
} else {
QString msg = sd.forward
? FakeVimHandler::tr("search hit BOTTOM without match for: %1")
: FakeVimHandler::tr("search hit TOP without match for: %1");
showRedMessage(msg.arg(sd.needle));
return;
} }
} }
@@ -4303,7 +4302,7 @@ void FakeVimHandler::Private::search(const SearchData &sd)
if (oldLine != cursorLine() - cursorLineOnScreen()) if (oldLine != cursorLine() - cursorLineOnScreen())
scrollToLine(cursorLine() - linesOnScreen() / 2); scrollToLine(cursorLine() - linesOnScreen() / 2);
if (incSearch && sd.highlightCursor) if (sd.highlightCursor)
m_searchCursor = cursor(); m_searchCursor = cursor();
setTargetColumn(); setTargetColumn();
@@ -4313,6 +4312,21 @@ void FakeVimHandler::Private::search(const SearchData &sd)
updateSelection(); updateSelection();
} }
void FakeVimHandler::Private::searchNext(bool forward)
{
SearchData sd;
sd.needle = g.searchHistory.current();
sd.forward = forward ? m_lastSearchForward : !m_lastSearchForward;
sd.highlightCursor = false;
sd.highlightMatches = true;
m_searchStartPosition = position();
m_currentMessage.clear();
search(sd);
m_commandPrefix = QLatin1Char(m_lastSearchForward ? '/' : '?');
m_commandBuffer.setContents(sd.needle);
}
void FakeVimHandler::Private::highlightMatches(const QString &needle) void FakeVimHandler::Private::highlightMatches(const QString &needle)
{ {
if (!hasConfig(ConfigHlSearch)) if (!hasConfig(ConfigHlSearch))

View File

@@ -66,6 +66,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="2">
<widget class="QCheckBox" name="checkBoxWrapScan">
<property name="text">
<string>Use wrapscan</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2"> <item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="checkBoxExpandTab"> <widget class="QCheckBox" name="checkBoxExpandTab">
<property name="text"> <property name="text">
@@ -73,7 +80,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="2"> <item row="4" column="2">
<widget class="QCheckBox" name="checkBoxShowMarks"> <widget class="QCheckBox" name="checkBoxShowMarks">
<property name="text"> <property name="text">
<string>Show position of text marks</string> <string>Show position of text marks</string>
@@ -87,7 +94,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="2"> <item row="5" column="2">
<widget class="QCheckBox" name="checkBoxPassControlKey"> <widget class="QCheckBox" name="checkBoxPassControlKey">
<property name="toolTip"> <property name="toolTip">
<string>Pass key sequences like Ctrl-S to Qt Creator core instead of interpreting them in FakeVim. This gives easier access to Qt Creator core functionality at the price of losing some features of FakeVim.</string> <string>Pass key sequences like Ctrl-S to Qt Creator core instead of interpreting them in FakeVim. This gives easier access to Qt Creator core functionality at the price of losing some features of FakeVim.</string>

View File

@@ -234,12 +234,15 @@ QWidget *FakeVimOptionPage::createPage(QWidget *parent)
m_ui.checkBoxAutoIndent); m_ui.checkBoxAutoIndent);
m_group.insert(theFakeVimSetting(ConfigSmartIndent), m_group.insert(theFakeVimSetting(ConfigSmartIndent),
m_ui.checkBoxSmartIndent); m_ui.checkBoxSmartIndent);
m_group.insert(theFakeVimSetting(ConfigIncSearch), m_group.insert(theFakeVimSetting(ConfigIncSearch),
m_ui.checkBoxIncSearch); m_ui.checkBoxIncSearch);
m_group.insert(theFakeVimSetting(ConfigUseCoreSearch), m_group.insert(theFakeVimSetting(ConfigUseCoreSearch),
m_ui.checkBoxUseCoreSearch); m_ui.checkBoxUseCoreSearch);
m_group.insert(theFakeVimSetting(ConfigSmartCase), m_group.insert(theFakeVimSetting(ConfigSmartCase),
m_ui.checkBoxSmartCase); m_ui.checkBoxSmartCase);
m_group.insert(theFakeVimSetting(ConfigWrapScan),
m_ui.checkBoxWrapScan);
connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()), connect(m_ui.pushButtonCopyTextEditorSettings, SIGNAL(clicked()),
SLOT(copyTextEditorSettings())); SLOT(copyTextEditorSettings()));