Git commit 765d8e684c37432c17c0bde30473f472b64ea0f3 by Simon St James. Committed on 17/06/2016 at 08:14. Pushed by sstjames into branch 'master'. Move CommandMode into its own source files. M +1 -0 src/CMakeLists.txt M +5 -3 src/include/ktexteditor/command.h A +394 -0 src/vimode/emulatedcommandbar/commandmode.cpp [License: = UNKNOWN] * A +80 -0 src/vimode/emulatedcommandbar/commandmode.h [License: UN= KNOWN] * M +1 -378 src/vimode/emulatedcommandbar/emulatedcommandbar.cpp M +1 -57 src/vimode/emulatedcommandbar/emulatedcommandbar.h The files marked with a * at the end have a non valid license. Please read:= http://techbase.kde.org/Policies/Licensing_Policy and use the headers whic= h are listed at that page. http://commits.kde.org/ktexteditor/765d8e684c37432c17c0bde30473f472b64ea0f3 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51374c7..25ac61a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -281,6 +281,7 @@ if (BUILD_VIMODE) vimode/emulatedcommandbar/activemode.cpp vimode/emulatedcommandbar/interactivesedreplacemode.cpp vimode/emulatedcommandbar/searchmode.cpp + vimode/emulatedcommandbar/commandmode.cpp vimode/commandrangeexpressionparser.cpp vimode/keymapper.cpp vimode/marks.cpp diff --git a/src/include/ktexteditor/command.h b/src/include/ktexteditor/co= mmand.h index f65d11b..3d57ef4 100644 --- a/src/include/ktexteditor/command.h +++ b/src/include/ktexteditor/command.h @@ -24,6 +24,8 @@ = #include #include +#include + #include = class QStringList; @@ -112,7 +114,7 @@ public: { return m_cmds; } - = + /** * Find out if a given command can act on a range. This is used for ch= ecking * if a command should be called when the user also gave a range or if= an @@ -130,7 +132,7 @@ public: * of replaced strings as \p msg, like "16 replacements made." If an e= rror * occurred in the usage it would return \e false and set the \p msg to * something like "missing argument." or such. - * = + * * If a non-invalid range is given, the command shall be executed on t= hat range. * supportsRange() tells if the command supports that. * @@ -198,7 +200,7 @@ private: * the command list this command got constructed with */ const QStringList m_cmds; - = + /** * Private d-pointer */ diff --git a/src/vimode/emulatedcommandbar/commandmode.cpp b/src/vimode/emu= latedcommandbar/commandmode.cpp new file mode 100644 index 0000000..215b5d0 --- /dev/null +++ b/src/vimode/emulatedcommandbar/commandmode.cpp @@ -0,0 +1,394 @@ +#include "commandmode.h" + +#include "emulatedcommandbar.h" +#include "interactivesedreplacemode.h" +#include "searchmode.h" +#include "../commandrangeexpressionparser.h" + +#include +#include +#include +#include "../globalstate.h" +#include "../history.h" + +#include "katescriptmanager.h" +#include "katecmds.h" + +#include + +#include +#include + +using namespace KateVi; + +CommandMode::CommandMode ( EmulatedCommandBar* emulatedCommandBar, MatchHi= ghlighter* matchHighlighter, KTextEditor::ViewPrivate* view, QLineEdit* ed= it, InteractiveSedReplaceMode *interactiveSedReplaceMode, Completer* comple= ter) + : ActiveMode ( emulatedCommandBar, matchHighlighter ), + m_edit(edit), + m_view(view), + m_interactiveSedReplaceMode(interactiveSedReplaceMode), + m_completer(completer) +{ + QList cmds; + + cmds.push_back(KateCommands::CoreCommands::self()); + cmds.push_back(Commands::self()); + cmds.push_back(AppCommands::self()); + cmds.push_back(SedReplace::self()); + cmds.push_back(BufferCommands::self()); + + Q_FOREACH (KTextEditor::Command *cmd, KateScriptManager::self()->comma= ndLineScripts()) { + cmds.push_back(cmd); + } + + Q_FOREACH (KTextEditor::Command *cmd, cmds) { + QStringList l =3D cmd->cmds(); + + for (int z =3D 0; z < l.count(); z++) { + m_cmdDict.insert(l[z], cmd); + } + + m_cmdCompletion.insertItems(l); + } +} + +void CommandMode::setViInputModeManager ( InputModeManager* viInputModeMan= ager ) +{ + m_viInputModeManager =3D viInputModeManager; +} + +bool CommandMode::handleKeyPress ( const QKeyEvent* keyEvent ) +{ + if (keyEvent->modifiers() =3D=3D Qt::ControlModifier && (keyEvent->key= () =3D=3D Qt::Key_D || keyEvent->key() =3D=3D Qt::Key_F)) { + CommandMode::ParsedSedExpression parsedSedExpression =3D parseAsSe= dExpression(); + if (parsedSedExpression.parsedSuccessfully) { + const bool clearFindTerm =3D (keyEvent->key() =3D=3D Qt::Key_D= ); + if (clearFindTerm) { + m_edit->setSelection(parsedSedExpression.findBeginPos, par= sedSedExpression.findEndPos - parsedSedExpression.findBeginPos + 1); + m_edit->insert(QString()); + } else { + // Clear replace term. + m_edit->setSelection(parsedSedExpression.replaceBeginPos, = parsedSedExpression.replaceEndPos - parsedSedExpression.replaceBeginPos + 1= ); + m_edit->insert(QString()); + } + } + return true; + } + return false; +} + +void CommandMode::editTextChanged ( const QString& newText ) +{ + Q_UNUSED(newText); // We read the current text from m_edit. + if (m_completer->isCompletionActive()) + return; + // Command completion doesn't need to be manually invoked. + if (!withoutRangeExpression().isEmpty() && !m_completer->isNextTextCha= ngeDueToCompletionChange()) { + // ... However, command completion mode should not be automaticall= y invoked if this is not the current leading + // word in the text edit (it gets annoying if completion pops up a= fter ":s/se" etc). + const bool commandBeforeCursorIsLeading =3D (commandBeforeCursorBe= gin() =3D=3D rangeExpression().length()); + if (commandBeforeCursorIsLeading) { + CompletionStartParams completionStartParams =3D activateComman= dCompletion(); + startCompletion(completionStartParams); + } + } +} + +void CommandMode::deactivate ( bool wasAborted ) +{ + if (wasAborted) { + // Appending the command to the history when it is executed is han= dled elsewhere; we can't + // do it inside closed() as we may still be showing the command re= sponse display. + m_viInputModeManager->globalState()->commandHistory()->append(m_ed= it->text()); + // With Vim, aborting a command returns us to Normal mode, even if= we were in Visual Mode. + // If we switch from Visual to Normal mode, we need to clear the s= election. + m_view->clearSelection(); + } + +} + +CompletionStartParams CommandMode::completionInvoked(Completer::Completion= Invocation invocationType) +{ + CompletionStartParams completionStartParams; + if (invocationType =3D=3D Completer::CompletionInvocation::ExtraContex= t) + { + if (isCursorInFindTermOfSed()) { + completionStartParams =3D activateSedFindHistoryCompletion(); + } else if (isCursorInReplaceTermOfSed()) { + completionStartParams =3D activateSedReplaceHistoryCompletion(= ); + } else { + completionStartParams =3D activateCommandHistoryCompletion(); + } + } + else + { + // Normal context, so boring, ordinary History completion. + completionStartParams =3D activateCommandHistoryCompletion(); + } + return completionStartParams; +} + +void CommandMode::completionChosen() +{ + QString commandToExecute =3D m_edit->text(); + CommandMode::ParsedSedExpression parsedSedExpression =3D parseAsSedExp= ression(); + if (parsedSedExpression.parsedSuccessfully) { + const QString originalFindTerm =3D sedFindTerm(); + const QString convertedFindTerm =3D vimRegexToQtRegexPattern(origi= nalFindTerm); + const QString commandWithSedSearchRegexConverted =3D withSedFindTe= rmReplacedWith(convertedFindTerm); + m_viInputModeManager->globalState()->searchHistory()->append(origi= nalFindTerm); + const QString replaceTerm =3D sedReplaceTerm(); + m_viInputModeManager->globalState()->replaceHistory()->append(repl= aceTerm); + commandToExecute =3D commandWithSedSearchRegexConverted; + } + + const QString commandResponseMessage =3D executeCommand(commandToExecu= te); + // Don't close the bar if executing the command switched us to Interac= tive Sed Replace mode. + if (!m_interactiveSedReplaceMode->isActive()) { + if (commandResponseMessage.isEmpty()) { + m_emulatedCommandBar->hideMe(); + } else { + closeWithStatusMessage(commandResponseMessage); + } + } + m_viInputModeManager->globalState()->commandHistory()->append(m_edit->= text()); + +} + +QString CommandMode::executeCommand ( const QString& commandToExecute ) +{ + // Silently ignore leading space characters and colon characters (for = vi-heads). + uint n =3D 0; + const uint textlen =3D commandToExecute.length(); + while ((n < textlen) && commandToExecute[n].isSpace()) { + n++; + } + + if (n >=3D textlen) { + return QString(); + } + + QString commandResponseMessage; + QString cmd =3D commandToExecute.mid(n); + + KTextEditor::Range range =3D CommandRangeExpressionParser(m_viInputMod= eManager).parseRange(cmd, cmd); + + if (cmd.length() > 0) { + KTextEditor::Command *p =3D queryCommand(cmd); + KateViCommandInterface *ci =3D dynamic_cast(p); + + if (ci) { + ci->setViInputModeManager(m_viInputModeManager); + ci->setViGlobal(m_viInputModeManager->globalState()); + } + + // The following commands changes the focus themselves, so bar sho= uld be hidden before execution. + + // We got a range and a valid command, but the command does not in= herit the RangeCommand + // extension. Bail out. + if (range.isValid() && !p->supportsRange(cmd)) { + commandResponseMessage =3D i18n("Error: No range allowed for c= ommand \"%1\".", cmd); + } else { + if (p) { + if (p->exec(m_view, cmd, commandResponseMessage, range)) { + + if (commandResponseMessage.length() > 0) { + commandResponseMessage =3D i18n("Success: ") + com= mandResponseMessage; + } + } else { + if (commandResponseMessage.length() > 0) { + if (commandResponseMessage.contains(QLatin1Char('\= n'))) { + // multiline error, use widget with more space + QWhatsThis::showText(m_emulatedCommandBar->map= ToGlobal(QPoint(0, 0)), commandResponseMessage); + } + } else { + commandResponseMessage =3D i18n("Command \"%1\" fa= iled.", cmd); + } + } + } else { + commandResponseMessage =3D i18n("No such command: \"%1\"",= cmd); + } + } + } + + // the following commands change the focus themselves + if (!QRegExp(QLatin1String("buffer|b|new|vnew|bp|bprev|bn|bnext|bf|bfi= rst|bl|blast|edit|e")).exactMatch(cmd.split(QLatin1String(" ")).at(0))) { + m_view->setFocus(); + } + + m_viInputModeManager->reset(); + return commandResponseMessage; + +} + + +QString CommandMode::withoutRangeExpression() +{ + const QString originalCommand =3D m_edit->text(); + return originalCommand.mid(rangeExpression().length()); +} + +QString CommandMode::rangeExpression() +{ + const QString command =3D m_edit->text(); + return CommandRangeExpressionParser(m_viInputModeManager).parseRangeSt= ring(command); +} + +CommandMode::ParsedSedExpression CommandMode::parseAsSedExpression() +{ + const QString commandWithoutRangeExpression =3D withoutRangeExpression= (); + ParsedSedExpression parsedSedExpression; + QString delimiter; + parsedSedExpression.parsedSuccessfully =3D SedReplace::parse(commandWi= thoutRangeExpression, delimiter, parsedSedExpression.findBeginPos, parsedSe= dExpression.findEndPos, parsedSedExpression.replaceBeginPos, parsedSedExpre= ssion.replaceEndPos); + if (parsedSedExpression.parsedSuccessfully) { + parsedSedExpression.delimiter =3D delimiter.at(0); + if (parsedSedExpression.replaceBeginPos =3D=3D -1) { + if (parsedSedExpression.findBeginPos !=3D -1) { + // The replace term was empty, and a quirk of the regex us= ed is that replaceBeginPos will be -1. + // It's actually the position after the first occurrence o= f the delimiter after the end of the find pos. + parsedSedExpression.replaceBeginPos =3D commandWithoutRang= eExpression.indexOf(delimiter, parsedSedExpression.findEndPos) + 1; + parsedSedExpression.replaceEndPos =3D parsedSedExpression.= replaceBeginPos - 1; + } else { + // Both find and replace terms are empty; replace term is = at the third occurrence of the delimiter. + parsedSedExpression.replaceBeginPos =3D 0; + for (int delimiterCount =3D 1; delimiterCount <=3D 3; deli= miterCount++) { + parsedSedExpression.replaceBeginPos =3D commandWithout= RangeExpression.indexOf(delimiter, parsedSedExpression.replaceBeginPos + 1); + } + parsedSedExpression.replaceEndPos =3D parsedSedExpression.= replaceBeginPos - 1; + } + } + if (parsedSedExpression.findBeginPos =3D=3D -1) { + // The find term was empty, and a quirk of the regex used is t= hat findBeginPos will be -1. + // It's actually the position after the first occurrence of th= e delimiter. + parsedSedExpression.findBeginPos =3D commandWithoutRangeExpres= sion.indexOf(delimiter) + 1; + parsedSedExpression.findEndPos =3D parsedSedExpression.findBeg= inPos - 1; + } + + } + + if (parsedSedExpression.parsedSuccessfully) { + parsedSedExpression.findBeginPos +=3D rangeExpression().length(); + parsedSedExpression.findEndPos +=3D rangeExpression().length(); + parsedSedExpression.replaceBeginPos +=3D rangeExpression().length(= ); + parsedSedExpression.replaceEndPos +=3D rangeExpression().length(); + } + return parsedSedExpression; + +} + +QString CommandMode::sedFindTerm() +{ + const QString command =3D m_edit->text(); + ParsedSedExpression parsedSedExpression =3D parseAsSedExpression(); + Q_ASSERT(parsedSedExpression.parsedSuccessfully); + return command.mid(parsedSedExpression.findBeginPos, parsedSedExpressi= on.findEndPos - parsedSedExpression.findBeginPos + 1); +} + +QString CommandMode::sedReplaceTerm() +{ + const QString command =3D m_edit->text(); + ParsedSedExpression parsedSedExpression =3D parseAsSedExpression(); + Q_ASSERT(parsedSedExpression.parsedSuccessfully); + return command.mid(parsedSedExpression.replaceBeginPos, parsedSedExpre= ssion.replaceEndPos - parsedSedExpression.replaceBeginPos + 1); +} + +QString CommandMode::withSedFindTermReplacedWith ( const QString& newFindT= erm ) +{ + const QString command =3D m_edit->text(); + ParsedSedExpression parsedSedExpression =3D parseAsSedExpression(); + Q_ASSERT(parsedSedExpression.parsedSuccessfully); + return command.mid(0, parsedSedExpression.findBeginPos) + + newFindTerm + + command.mid(parsedSedExpression.findEndPos + 1); +} + +QString CommandMode::withSedDelimiterEscaped ( const QString& text ) +{ + ParsedSedExpression parsedSedExpression =3D parseAsSedExpression(); + QString delimiterEscaped =3D ensuredCharEscaped(text, parsedSedExpress= ion.delimiter); + return delimiterEscaped; +} + +bool CommandMode::isCursorInFindTermOfSed() +{ + ParsedSedExpression parsedSedExpression =3D parseAsSedExpression(); + return parsedSedExpression.parsedSuccessfully && (m_edit->cursorPositi= on() >=3D parsedSedExpression.findBeginPos && m_edit->cursorPosition() <=3D= parsedSedExpression.findEndPos + 1); +} + +bool CommandMode::isCursorInReplaceTermOfSed() +{ + ParsedSedExpression parsedSedExpression =3D parseAsSedExpression(); + return parsedSedExpression.parsedSuccessfully && m_edit->cursorPositio= n() >=3D parsedSedExpression.replaceBeginPos && m_edit->cursorPosition() <= =3D parsedSedExpression.replaceEndPos + 1; +} + +int CommandMode::commandBeforeCursorBegin() +{ + const QString textWithoutRangeExpression =3D withoutRangeExpression(); + const int cursorPositionWithoutRangeExpression =3D m_edit->cursorPosit= ion() - rangeExpression().length(); + int commandBeforeCursorBegin =3D cursorPositionWithoutRangeExpression = - 1; + while (commandBeforeCursorBegin >=3D 0 && (textWithoutRangeExpression[= commandBeforeCursorBegin].isLetterOrNumber() || textWithoutRangeExpression[= commandBeforeCursorBegin] =3D=3D QLatin1Char('_') || textWithoutRangeExpres= sion[commandBeforeCursorBegin] =3D=3D QLatin1Char('-'))) { + commandBeforeCursorBegin--; + } + commandBeforeCursorBegin++; + commandBeforeCursorBegin +=3D rangeExpression().length(); + return commandBeforeCursorBegin; +} + +CompletionStartParams CommandMode::activateCommandCompletion() +{ + return CompletionStartParams::createModeSpecific(m_cmdCompletion.items= (), commandBeforeCursorBegin()); +} + +CompletionStartParams CommandMode::activateCommandHistoryCompletion() +{ + return CompletionStartParams::createModeSpecific(reversed(m_viInputMod= eManager->globalState()->commandHistory()->items()), 0); +} + +CompletionStartParams CommandMode::activateSedFindHistoryCompletion() +{ + if (m_viInputModeManager->globalState()->searchHistory()->isEmpty()) + { + return CompletionStartParams::invalid(); + } + CommandMode::ParsedSedExpression parsedSedExpression =3D parseAsSedExp= ression(); + return CompletionStartParams::createModeSpecific(reversed(m_viInputMod= eManager->globalState()->searchHistory()->items()), + parsedSedExpression.f= indBeginPos, + [this] (const QStrin= g& completion) -> QString { return withCaseSensitivityMarkersStripped(withS= edDelimiterEscaped(completion)); }); +} + +CompletionStartParams CommandMode::activateSedReplaceHistoryCompletion() +{ + if (m_viInputModeManager->globalState()->replaceHistory()->isEmpty()) + { + return CompletionStartParams::invalid(); + } + CommandMode::ParsedSedExpression parsedSedExpression =3D parseAsSedExp= ression(); + return CompletionStartParams::createModeSpecific(reversed(m_viInputMod= eManager->globalState()->replaceHistory()->items()), + parsedSedExpression.r= eplaceBeginPos, + [this] (const QStrin= g& completion) -> QString { return withCaseSensitivityMarkersStripped(withS= edDelimiterEscaped(completion)); }); +} + +KTextEditor::Command* CommandMode::queryCommand ( const QString& cmd ) con= st +{ + // a command can be named ".*[\w\-]+" with the constrain that it must + // contain at least one letter. + int f =3D 0; + bool b =3D false; + + // special case: '-' and '_' can be part of a command name, but if the + // command is 's' (substitute), it should be considered the delimiter = and + // should not be counted as part of the command name + if (cmd.length() >=3D 2 && cmd.at(0) =3D=3D QLatin1Char('s') && (cmd.a= t(1) =3D=3D QLatin1Char('-') || cmd.at(1) =3D=3D QLatin1Char('_'))) { + return m_cmdDict.value(QStringLiteral("s")); + } + + for (; f < cmd.length(); f++) { + if (cmd[f].isLetter()) { + b =3D true; + } + if (b && (! cmd[f].isLetterOrNumber() && cmd[f] !=3D QLatin1Char('= -') && cmd[f] !=3D QLatin1Char('_'))) { + break; + } + } + return m_cmdDict.value(cmd.left(f)); + +} diff --git a/src/vimode/emulatedcommandbar/commandmode.h b/src/vimode/emula= tedcommandbar/commandmode.h new file mode 100644 index 0000000..f160315 --- /dev/null +++ b/src/vimode/emulatedcommandbar/commandmode.h @@ -0,0 +1,80 @@ +#ifndef KATEVI_EMULATED_COMMAND_BAR_COMMANDMODE_H +#define KATEVI_EMULATED_COMMAND_BAR_COMMANDMODE_H + +#include "activemode.h" + +#include + +#include + +namespace KTextEditor { + class ViewPrivate; +} + +namespace KateVi +{ +class EmulatedCommandBar; +class MatchHighlighter; +class InteractiveSedReplaceMode; +class Completer; +class InputModeManager; + +class CommandMode : public ActiveMode +{ +public: + CommandMode(EmulatedCommandBar* emulatedCommandBar, MatchHighlighter* = matchHighlighter, KTextEditor::ViewPrivate* view, QLineEdit* edit, Interac= tiveSedReplaceMode *interactiveSedReplaceMode, Completer* completer); + virtual ~CommandMode() + { + } + void setViInputModeManager(InputModeManager *viInputModeManager); + virtual bool handleKeyPress ( const QKeyEvent* keyEvent ); + virtual void editTextChanged(const QString &newText); + virtual CompletionStartParams completionInvoked(Completer::CompletionI= nvocation invocationType); + virtual void completionChosen(); + void deactivate(bool wasAborted); + QString executeCommand(const QString &commandToExecute); +private: + CompletionStartParams activateCommandCompletion(); + CompletionStartParams activateCommandHistoryCompletion(); + CompletionStartParams activateSedFindHistoryCompletion(); + CompletionStartParams activateSedReplaceHistoryCompletion(); + QString withoutRangeExpression(); + QString rangeExpression(); + QString withSedFindTermReplacedWith(const QString &newFindTerm); + QString withSedDelimiterEscaped(const QString &text); + bool isCursorInFindTermOfSed(); + bool isCursorInReplaceTermOfSed(); + QString sedFindTerm(); + QString sedReplaceTerm(); + /** + * Stuff to do with expressions of the form: + * + * s/find/replace/ + */ + struct ParsedSedExpression { + bool parsedSuccessfully; + int findBeginPos; + int findEndPos; + int replaceBeginPos; + int replaceEndPos; + QChar delimiter; + }; + /** + * The "range expression" is the (optional) expression before the c= ommand that describes + * the range over which the command should be run e.g. '<,'>. @see= CommandRangeExpressionParser + */ + CommandMode::ParsedSedExpression parseAsSedExpression(); + void replaceCommandBeforeCursorWith(const QString &newCommand); + int commandBeforeCursorBegin(); + QLineEdit *m_edit; + InputModeManager *m_viInputModeManager =3D nullptr; + KTextEditor::ViewPrivate *m_view; + InteractiveSedReplaceMode *m_interactiveSedReplaceMode; + Completer *m_completer; + KCompletion m_cmdCompletion; + QHash m_cmdDict; + KTextEditor::Command *queryCommand(const QString &cmd) const; +}; +} + +#endif diff --git a/src/vimode/emulatedcommandbar/emulatedcommandbar.cpp b/src/vim= ode/emulatedcommandbar/emulatedcommandbar.cpp index 513de48..7ee21a1 100644 --- a/src/vimode/emulatedcommandbar/emulatedcommandbar.cpp +++ b/src/vimode/emulatedcommandbar/emulatedcommandbar.cpp @@ -31,24 +31,18 @@ #include "matchhighlighter.h" #include "interactivesedreplacemode.h" #include "searchmode.h" +#include "commandmode.h" = -#include #include -#include #include "../history.h" = -#include "katecmds.h" -#include "katescriptmanager.h" #include "../registers.h" #include "../searcher.h" = -#include - #include #include #include #include -#include = #include = @@ -435,374 +429,3 @@ void EmulatedCommandBar::hideAllWidgetsExcept(QWidget= * widgetToKeepVisible) = } = -EmulatedCommandBar::CommandMode::CommandMode ( EmulatedCommandBar* emulate= dCommandBar, MatchHighlighter* matchHighlighter, KTextEditor::ViewPrivate* = view, QLineEdit* edit, InteractiveSedReplaceMode *interactiveSedReplaceMod= e, Completer* completer) - : ActiveMode ( emulatedCommandBar, matchHighlighter ), - m_edit(edit), - m_view(view), - m_interactiveSedReplaceMode(interactiveSedReplaceMode), - m_completer(completer) -{ - QList cmds; - - cmds.push_back(KateCommands::CoreCommands::self()); - cmds.push_back(Commands::self()); - cmds.push_back(AppCommands::self()); - cmds.push_back(SedReplace::self()); - cmds.push_back(BufferCommands::self()); - - Q_FOREACH (KTextEditor::Command *cmd, KateScriptManager::self()->comma= ndLineScripts()) { - cmds.push_back(cmd); - } - - Q_FOREACH (KTextEditor::Command *cmd, cmds) { - QStringList l =3D cmd->cmds(); - - for (int z =3D 0; z < l.count(); z++) { - m_cmdDict.insert(l[z], cmd); - } - - m_cmdCompletion.insertItems(l); - } -} - -void EmulatedCommandBar::CommandMode::setViInputModeManager ( InputModeMan= ager* viInputModeManager ) -{ - m_viInputModeManager =3D viInputModeManager; -} - -bool EmulatedCommandBar::CommandMode::handleKeyPress ( const QKeyEvent* ke= yEvent ) -{ - if (keyEvent->modifiers() =3D=3D Qt::ControlModifier && (keyEvent->key= () =3D=3D Qt::Key_D || keyEvent->key() =3D=3D Qt::Key_F)) { - CommandMode::ParsedSedExpression parsedSedExpression =3D parseAsSe= dExpression(); - if (parsedSedExpression.parsedSuccessfully) { - const bool clearFindTerm =3D (keyEvent->key() =3D=3D Qt::Key_D= ); - if (clearFindTerm) { - m_edit->setSelection(parsedSedExpression.findBeginPos, par= sedSedExpression.findEndPos - parsedSedExpression.findBeginPos + 1); - m_edit->insert(QString()); - } else { - // Clear replace term. - m_edit->setSelection(parsedSedExpression.replaceBeginPos, = parsedSedExpression.replaceEndPos - parsedSedExpression.replaceBeginPos + 1= ); - m_edit->insert(QString()); - } - } - return true; - } - return false; -} - -void EmulatedCommandBar::CommandMode::editTextChanged ( const QString& new= Text ) -{ - Q_UNUSED(newText); // We read the current text from m_edit. - if (m_completer->isCompletionActive()) - return; - // Command completion doesn't need to be manually invoked. - if (!withoutRangeExpression().isEmpty() && !m_completer->isNextTextCha= ngeDueToCompletionChange()) { - // ... However, command completion mode should not be automaticall= y invoked if this is not the current leading - // word in the text edit (it gets annoying if completion pops up a= fter ":s/se" etc). - const bool commandBeforeCursorIsLeading =3D (commandBeforeCursorBe= gin() =3D=3D rangeExpression().length()); - if (commandBeforeCursorIsLeading) { - CompletionStartParams completionStartParams =3D activateComman= dCompletion(); - startCompletion(completionStartParams); - } - } -} - -void EmulatedCommandBar::CommandMode::deactivate ( bool wasAborted ) -{ - if (wasAborted) { - // Appending the command to the history when it is executed is han= dled elsewhere; we can't - // do it inside closed() as we may still be showing the command re= sponse display. - m_viInputModeManager->globalState()->commandHistory()->append(m_ed= it->text()); - // With Vim, aborting a command returns us to Normal mode, even if= we were in Visual Mode. - // If we switch from Visual to Normal mode, we need to clear the s= election. - m_view->clearSelection(); - } - -} - -CompletionStartParams EmulatedCommandBar::CommandMode::completionInvoked(C= ompleter::CompletionInvocation invocationType) -{ - CompletionStartParams completionStartParams; - if (invocationType =3D=3D Completer::CompletionInvocation::ExtraContex= t) - { - if (isCursorInFindTermOfSed()) { - completionStartParams =3D activateSedFindHistoryCompletion(); - } else if (isCursorInReplaceTermOfSed()) { - completionStartParams =3D activateSedReplaceHistoryCompletion(= ); - } else { - completionStartParams =3D activateCommandHistoryCompletion(); - } - } - else - { - // Normal context, so boring, ordinary History completion. - completionStartParams =3D activateCommandHistoryCompletion(); - } - return completionStartParams; -} - -void EmulatedCommandBar::CommandMode::completionChosen() -{ - QString commandToExecute =3D m_edit->text(); - CommandMode::ParsedSedExpression parsedSedExpression =3D parseAsSedExp= ression(); - if (parsedSedExpression.parsedSuccessfully) { - const QString originalFindTerm =3D sedFindTerm(); - const QString convertedFindTerm =3D vimRegexToQtRegexPattern(origi= nalFindTerm); - const QString commandWithSedSearchRegexConverted =3D withSedFindTe= rmReplacedWith(convertedFindTerm); - m_viInputModeManager->globalState()->searchHistory()->append(origi= nalFindTerm); - const QString replaceTerm =3D sedReplaceTerm(); - m_viInputModeManager->globalState()->replaceHistory()->append(repl= aceTerm); - commandToExecute =3D commandWithSedSearchRegexConverted; - } - - const QString commandResponseMessage =3D executeCommand(commandToExecu= te); - // Don't close the bar if executing the command switched us to Interac= tive Sed Replace mode. - if (!m_interactiveSedReplaceMode->isActive()) { - if (commandResponseMessage.isEmpty()) { - m_emulatedCommandBar->hideMe(); - } else { - closeWithStatusMessage(commandResponseMessage); - } - } - m_viInputModeManager->globalState()->commandHistory()->append(m_edit->= text()); - -} - -QString EmulatedCommandBar::CommandMode::executeCommand ( const QString& c= ommandToExecute ) -{ - // Silently ignore leading space characters and colon characters (for = vi-heads). - uint n =3D 0; - const uint textlen =3D commandToExecute.length(); - while ((n < textlen) && commandToExecute[n].isSpace()) { - n++; - } - - if (n >=3D textlen) { - return QString(); - } - - QString commandResponseMessage; - QString cmd =3D commandToExecute.mid(n); - - KTextEditor::Range range =3D CommandRangeExpressionParser(m_viInputMod= eManager).parseRange(cmd, cmd); - - if (cmd.length() > 0) { - KTextEditor::Command *p =3D queryCommand(cmd); - KateViCommandInterface *ci =3D dynamic_cast(p); - - if (ci) { - ci->setViInputModeManager(m_viInputModeManager); - ci->setViGlobal(m_viInputModeManager->globalState()); - } - - // The following commands changes the focus themselves, so bar sho= uld be hidden before execution. - - // We got a range and a valid command, but the command does not in= herit the RangeCommand - // extension. Bail out. - if (range.isValid() && !p->supportsRange(cmd)) { - commandResponseMessage =3D i18n("Error: No range allowed for c= ommand \"%1\".", cmd); - } else { - if (p) { - if (p->exec(m_view, cmd, commandResponseMessage, range)) { - - if (commandResponseMessage.length() > 0) { - commandResponseMessage =3D i18n("Success: ") + com= mandResponseMessage; - } - } else { - if (commandResponseMessage.length() > 0) { - if (commandResponseMessage.contains(QLatin1Char('\= n'))) { - // multiline error, use widget with more space - QWhatsThis::showText(m_emulatedCommandBar->map= ToGlobal(QPoint(0, 0)), commandResponseMessage); - } - } else { - commandResponseMessage =3D i18n("Command \"%1\" fa= iled.", cmd); - } - } - } else { - commandResponseMessage =3D i18n("No such command: \"%1\"",= cmd); - } - } - } - - // the following commands change the focus themselves - if (!QRegExp(QLatin1String("buffer|b|new|vnew|bp|bprev|bn|bnext|bf|bfi= rst|bl|blast|edit|e")).exactMatch(cmd.split(QLatin1String(" ")).at(0))) { - m_view->setFocus(); - } - - m_viInputModeManager->reset(); - return commandResponseMessage; - -} - - -QString EmulatedCommandBar::CommandMode::withoutRangeExpression() -{ - const QString originalCommand =3D m_edit->text(); - return originalCommand.mid(rangeExpression().length()); -} - -QString EmulatedCommandBar::CommandMode::rangeExpression() -{ - const QString command =3D m_edit->text(); - return CommandRangeExpressionParser(m_viInputModeManager).parseRangeSt= ring(command); -} - -EmulatedCommandBar::CommandMode::ParsedSedExpression EmulatedCommandBar::C= ommandMode::parseAsSedExpression() -{ - const QString commandWithoutRangeExpression =3D withoutRangeExpression= (); - ParsedSedExpression parsedSedExpression; - QString delimiter; - parsedSedExpression.parsedSuccessfully =3D SedReplace::parse(commandWi= thoutRangeExpression, delimiter, parsedSedExpression.findBeginPos, parsedSe= dExpression.findEndPos, parsedSedExpression.replaceBeginPos, parsedSedExpre= ssion.replaceEndPos); - if (parsedSedExpression.parsedSuccessfully) { - parsedSedExpression.delimiter =3D delimiter.at(0); - if (parsedSedExpression.replaceBeginPos =3D=3D -1) { - if (parsedSedExpression.findBeginPos !=3D -1) { - // The replace term was empty, and a quirk of the regex us= ed is that replaceBeginPos will be -1. - // It's actually the position after the first occurrence o= f the delimiter after the end of the find pos. - parsedSedExpression.replaceBeginPos =3D commandWithoutRang= eExpression.indexOf(delimiter, parsedSedExpression.findEndPos) + 1; - parsedSedExpression.replaceEndPos =3D parsedSedExpression.= replaceBeginPos - 1; - } else { - // Both find and replace terms are empty; replace term is = at the third occurrence of the delimiter. - parsedSedExpression.replaceBeginPos =3D 0; - for (int delimiterCount =3D 1; delimiterCount <=3D 3; deli= miterCount++) { - parsedSedExpression.replaceBeginPos =3D commandWithout= RangeExpression.indexOf(delimiter, parsedSedExpression.replaceBeginPos + 1); - } - parsedSedExpression.replaceEndPos =3D parsedSedExpression.= replaceBeginPos - 1; - } - } - if (parsedSedExpression.findBeginPos =3D=3D -1) { - // The find term was empty, and a quirk of the regex used is t= hat findBeginPos will be -1. - // It's actually the position after the first occurrence of th= e delimiter. - parsedSedExpression.findBeginPos =3D commandWithoutRangeExpres= sion.indexOf(delimiter) + 1; - parsedSedExpression.findEndPos =3D parsedSedExpression.findBeg= inPos - 1; - } - - } - - if (parsedSedExpression.parsedSuccessfully) { - parsedSedExpression.findBeginPos +=3D rangeExpression().length(); - parsedSedExpression.findEndPos +=3D rangeExpression().length(); - parsedSedExpression.replaceBeginPos +=3D rangeExpression().length(= ); - parsedSedExpression.replaceEndPos +=3D rangeExpression().length(); - } - return parsedSedExpression; - -} - -QString EmulatedCommandBar::CommandMode::sedFindTerm() -{ - const QString command =3D m_edit->text(); - ParsedSedExpression parsedSedExpression =3D parseAsSedExpression(); - Q_ASSERT(parsedSedExpression.parsedSuccessfully); - return command.mid(parsedSedExpression.findBeginPos, parsedSedExpressi= on.findEndPos - parsedSedExpression.findBeginPos + 1); -} - -QString EmulatedCommandBar::CommandMode::sedReplaceTerm() -{ - const QString command =3D m_edit->text(); - ParsedSedExpression parsedSedExpression =3D parseAsSedExpression(); - Q_ASSERT(parsedSedExpression.parsedSuccessfully); - return command.mid(parsedSedExpression.replaceBeginPos, parsedSedExpre= ssion.replaceEndPos - parsedSedExpression.replaceBeginPos + 1); -} - -QString EmulatedCommandBar::CommandMode::withSedFindTermReplacedWith ( con= st QString& newFindTerm ) -{ - const QString command =3D m_edit->text(); - ParsedSedExpression parsedSedExpression =3D parseAsSedExpression(); - Q_ASSERT(parsedSedExpression.parsedSuccessfully); - return command.mid(0, parsedSedExpression.findBeginPos) + - newFindTerm + - command.mid(parsedSedExpression.findEndPos + 1); -} - -QString EmulatedCommandBar::CommandMode::withSedDelimiterEscaped ( const Q= String& text ) -{ - ParsedSedExpression parsedSedExpression =3D parseAsSedExpression(); - QString delimiterEscaped =3D ensuredCharEscaped(text, parsedSedExpress= ion.delimiter); - return delimiterEscaped; -} - -bool EmulatedCommandBar::CommandMode::isCursorInFindTermOfSed() -{ - ParsedSedExpression parsedSedExpression =3D parseAsSedExpression(); - return parsedSedExpression.parsedSuccessfully && (m_edit->cursorPositi= on() >=3D parsedSedExpression.findBeginPos && m_edit->cursorPosition() <=3D= parsedSedExpression.findEndPos + 1); -} - -bool EmulatedCommandBar::CommandMode::isCursorInReplaceTermOfSed() -{ - ParsedSedExpression parsedSedExpression =3D parseAsSedExpression(); - return parsedSedExpression.parsedSuccessfully && m_edit->cursorPositio= n() >=3D parsedSedExpression.replaceBeginPos && m_edit->cursorPosition() <= =3D parsedSedExpression.replaceEndPos + 1; -} - -int EmulatedCommandBar::CommandMode::commandBeforeCursorBegin() -{ - const QString textWithoutRangeExpression =3D withoutRangeExpression(); - const int cursorPositionWithoutRangeExpression =3D m_edit->cursorPosit= ion() - rangeExpression().length(); - int commandBeforeCursorBegin =3D cursorPositionWithoutRangeExpression = - 1; - while (commandBeforeCursorBegin >=3D 0 && (textWithoutRangeExpression[= commandBeforeCursorBegin].isLetterOrNumber() || textWithoutRangeExpression[= commandBeforeCursorBegin] =3D=3D QLatin1Char('_') || textWithoutRangeExpres= sion[commandBeforeCursorBegin] =3D=3D QLatin1Char('-'))) { - commandBeforeCursorBegin--; - } - commandBeforeCursorBegin++; - commandBeforeCursorBegin +=3D rangeExpression().length(); - return commandBeforeCursorBegin; -} - -CompletionStartParams EmulatedCommandBar::CommandMode::activateCommandComp= letion() -{ - return CompletionStartParams::createModeSpecific(m_cmdCompletion.items= (), commandBeforeCursorBegin()); -} - -CompletionStartParams EmulatedCommandBar::CommandMode::activateCommandHist= oryCompletion() -{ - return CompletionStartParams::createModeSpecific(reversed(m_viInputMod= eManager->globalState()->commandHistory()->items()), 0); -} - -CompletionStartParams EmulatedCommandBar::CommandMode::activateSedFindHist= oryCompletion() -{ - if (m_viInputModeManager->globalState()->searchHistory()->isEmpty()) - { - return CompletionStartParams::invalid(); - } - CommandMode::ParsedSedExpression parsedSedExpression =3D parseAsSedExp= ression(); - return CompletionStartParams::createModeSpecific(reversed(m_viInputMod= eManager->globalState()->searchHistory()->items()), - parsedSedExpression.f= indBeginPos, - [this] (const QStrin= g& completion) -> QString { return withCaseSensitivityMarkersStripped(withS= edDelimiterEscaped(completion)); }); -} - -CompletionStartParams EmulatedCommandBar::CommandMode::activateSedReplaceH= istoryCompletion() -{ - if (m_viInputModeManager->globalState()->replaceHistory()->isEmpty()) - { - return CompletionStartParams::invalid(); - } - CommandMode::ParsedSedExpression parsedSedExpression =3D parseAsSedExp= ression(); - return CompletionStartParams::createModeSpecific(reversed(m_viInputMod= eManager->globalState()->replaceHistory()->items()), - parsedSedExpression.r= eplaceBeginPos, - [this] (const QStrin= g& completion) -> QString { return withCaseSensitivityMarkersStripped(withS= edDelimiterEscaped(completion)); }); -} - -KTextEditor::Command* EmulatedCommandBar::CommandMode::queryCommand ( cons= t QString& cmd ) const -{ - // a command can be named ".*[\w\-]+" with the constrain that it must - // contain at least one letter. - int f =3D 0; - bool b =3D false; - - // special case: '-' and '_' can be part of a command name, but if the - // command is 's' (substitute), it should be considered the delimiter = and - // should not be counted as part of the command name - if (cmd.length() >=3D 2 && cmd.at(0) =3D=3D QLatin1Char('s') && (cmd.a= t(1) =3D=3D QLatin1Char('-') || cmd.at(1) =3D=3D QLatin1Char('_'))) { - return m_cmdDict.value(QStringLiteral("s")); - } - - for (; f < cmd.length(); f++) { - if (cmd[f].isLetter()) { - b =3D true; - } - if (b && (! cmd[f].isLetterOrNumber() && cmd[f] !=3D QLatin1Char('= -') && cmd[f] !=3D QLatin1Char('_'))) { - break; - } - } - return m_cmdDict.value(cmd.left(f)); - -} diff --git a/src/vimode/emulatedcommandbar/emulatedcommandbar.h b/src/vimod= e/emulatedcommandbar/emulatedcommandbar.h index 3831f0b..09d4485 100644 --- a/src/vimode/emulatedcommandbar/emulatedcommandbar.h +++ b/src/vimode/emulatedcommandbar/emulatedcommandbar.h @@ -41,6 +41,7 @@ namespace KateVi class MatchHighlighter; class InteractiveSedReplaceMode; class SearchMode; +class CommandMode; = /** * A KateViewBarWidget that attempts to emulate some of the features of Vi= m's own command bar, @@ -87,63 +88,6 @@ private: friend class ActiveMode; QScopedPointer m_matchHighligher; QScopedPointer m_completer; - - class CommandMode : public ActiveMode - { - public: - CommandMode(EmulatedCommandBar* emulatedCommandBar, MatchHighlight= er* matchHighlighter, KTextEditor::ViewPrivate* view, QLineEdit* edit, Int= eractiveSedReplaceMode *interactiveSedReplaceMode, Completer* completer); - virtual ~CommandMode() - { - } - void setViInputModeManager(InputModeManager *viInputModeManager); - virtual bool handleKeyPress ( const QKeyEvent* keyEvent ); - virtual void editTextChanged(const QString &newText); - virtual CompletionStartParams completionInvoked(Completer::Complet= ionInvocation invocationType); - virtual void completionChosen(); - void deactivate(bool wasAborted); - QString executeCommand(const QString &commandToExecute); - private: - CompletionStartParams activateCommandCompletion(); - CompletionStartParams activateCommandHistoryCompletion(); - CompletionStartParams activateSedFindHistoryCompletion(); - CompletionStartParams activateSedReplaceHistoryCompletion(); - QString withoutRangeExpression(); - QString rangeExpression(); - QString withSedFindTermReplacedWith(const QString &newFindTerm); - QString withSedDelimiterEscaped(const QString &text); - bool isCursorInFindTermOfSed(); - bool isCursorInReplaceTermOfSed(); - QString sedFindTerm(); - QString sedReplaceTerm(); - /** - * Stuff to do with expressions of the form: - * - * s/find/replace/ - */ - struct ParsedSedExpression { - bool parsedSuccessfully; - int findBeginPos; - int findEndPos; - int replaceBeginPos; - int replaceEndPos; - QChar delimiter; - }; - /** - * The "range expression" is the (optional) expression before the c= ommand that describes - * the range over which the command should be run e.g. '<,'>. @see= CommandRangeExpressionParser - */ - CommandMode::ParsedSedExpression parseAsSedExpression(); - void replaceCommandBeforeCursorWith(const QString &newCommand); - int commandBeforeCursorBegin(); - QLineEdit *m_edit; - InputModeManager *m_viInputModeManager =3D nullptr; - KTextEditor::ViewPrivate *m_view; - InteractiveSedReplaceMode *m_interactiveSedReplaceMode; - Completer *m_completer; - KCompletion m_cmdCompletion; - QHash m_cmdDict; - KTextEditor::Command *queryCommand(const QString &cmd) const; - }; QScopedPointer m_interactiveSedReplaceMode; QScopedPointer m_searchMode; QScopedPointer m_commandMode;