7#include "commandmode.h"
9#include "../commandrangeexpressionparser.h"
10#include "emulatedcommandbar.h"
11#include "interactivesedreplacemode.h"
12#include "searchmode.h"
14#include "../globalstate.h"
15#include "../history.h"
16#include <vimode/appcommands.h>
17#include <vimode/cmds.h>
18#include <vimode/inputmodemanager.h>
21#include "katecommandlinescript.h"
22#include "katescriptmanager.h"
25#include <KLocalizedString>
28#include <QRegularExpression>
31using namespace KateVi;
34 MatchHighlighter *matchHighlighter,
35 InputModeManager *viInputModeManager,
36 KTextEditor::ViewPrivate *view,
38 InteractiveSedReplaceMode *interactiveSedReplaceMode,
40 : ActiveMode(emulatedCommandBar, matchHighlighter, viInputModeManager, view)
42 , m_interactiveSedReplaceMode(interactiveSedReplaceMode)
43 , m_completer(completer)
46 cmds.
push_back(KateCommands::CoreCommands::self());
51 cmds.
push_back(KateCommands::EditingCommands::self());
60 for (
int z = 0; z < l.
count(); z++) {
61 m_cmdDict.insert(l[z], cmd);
64 m_cmdCompletion.insertItems(l);
68bool CommandMode::handleKeyPress(
const QKeyEvent *keyEvent)
71 CommandMode::ParsedSedExpression parsedSedExpression = parseAsSedExpression();
72 if (parsedSedExpression.parsedSuccessfully) {
75 m_edit->
setSelection(parsedSedExpression.findBeginPos, parsedSedExpression.findEndPos - parsedSedExpression.findBeginPos + 1);
79 m_edit->
setSelection(parsedSedExpression.replaceBeginPos, parsedSedExpression.replaceEndPos - parsedSedExpression.replaceBeginPos + 1);
88void CommandMode::editTextChanged(
const QString &newText)
91 if (m_completer->isCompletionActive()) {
95 if (!withoutRangeExpression().isEmpty() && !m_completer->isNextTextChangeDueToCompletionChange()) {
98 const bool commandBeforeCursorIsLeading = (commandBeforeCursorBegin() == rangeExpression().
length());
99 if (commandBeforeCursorIsLeading) {
100 CompletionStartParams completionStartParams = activateCommandCompletion();
101 startCompletion(completionStartParams);
106void CommandMode::deactivate(
bool wasAborted)
111 viInputModeManager()->globalState()->commandHistory()->append(m_edit->
text());
114 view()->clearSelection();
118CompletionStartParams CommandMode::completionInvoked(Completer::CompletionInvocation invocationType)
120 CompletionStartParams completionStartParams;
121 if (invocationType == Completer::CompletionInvocation::ExtraContext) {
122 if (isCursorInFindTermOfSed()) {
123 completionStartParams = activateSedFindHistoryCompletion();
124 }
else if (isCursorInReplaceTermOfSed()) {
125 completionStartParams = activateSedReplaceHistoryCompletion();
127 completionStartParams = activateCommandHistoryCompletion();
131 completionStartParams = activateCommandHistoryCompletion();
133 return completionStartParams;
136void CommandMode::completionChosen()
139 CommandMode::ParsedSedExpression parsedSedExpression = parseAsSedExpression();
140 if (parsedSedExpression.parsedSuccessfully) {
141 const QString originalFindTerm = sedFindTerm();
142 const QString convertedFindTerm = vimRegexToQtRegexPattern(originalFindTerm);
143 const QString commandWithSedSearchRegexConverted = withSedFindTermReplacedWith(convertedFindTerm);
144 viInputModeManager()->globalState()->searchHistory()->append(originalFindTerm);
145 const QString replaceTerm = sedReplaceTerm();
146 viInputModeManager()->globalState()->replaceHistory()->append(replaceTerm);
147 commandToExecute = commandWithSedSearchRegexConverted;
150 const QString commandResponseMessage = executeCommand(commandToExecute);
152 if (!m_interactiveSedReplaceMode->isActive()) {
153 if (commandResponseMessage.
isEmpty()) {
154 emulatedCommandBar()->hideMe();
156 closeWithStatusMessage(commandResponseMessage);
159 viInputModeManager()->globalState()->commandHistory()->append(m_edit->
text());
162QString CommandMode::executeCommand(
const QString &commandToExecute)
166 const uint textlen = commandToExecute.
length();
167 while ((n < textlen) && commandToExecute[n].isSpace()) {
175 QString commandResponseMessage;
178 KTextEditor::Range range = CommandRangeExpressionParser(viInputModeManager()).parseRange(cmd, cmd);
183 if (p == Commands::self() || p == SedReplace::self()) {
184 Commands::self()->setViInputModeManager(viInputModeManager());
185 SedReplace::self()->setViInputModeManager(viInputModeManager());
192 commandResponseMessage =
i18n(
"Error: No range allowed for command \"%1\".", cmd);
194 if (p->
exec(view(), cmd, commandResponseMessage, range)) {
195 if (commandResponseMessage.
length() > 0) {
196 commandResponseMessage =
i18n(
"Success: ") + commandResponseMessage;
199 if (commandResponseMessage.
length() > 0) {
205 commandResponseMessage =
i18n(
"Command \"%1\" failed.", cmd);
210 commandResponseMessage =
i18n(
"No such command: \"%1\"", cmd);
216 QStringLiteral(
"^(?:buffer|b|new|vnew|bp|bprev|tabp|tabprev|bn|bnext|tabn|tabnext|bf|bfirst|tabf|tabfirst"
217 "|bl|blast|tabl|tablast|e|edit|tabe|tabedit|tabnew)$"));
222 viInputModeManager()->reset();
223 return commandResponseMessage;
226QString CommandMode::withoutRangeExpression()
229 return originalCommand.
mid(rangeExpression().length());
232QString CommandMode::rangeExpression()
235 return CommandRangeExpressionParser(viInputModeManager()).parseRangeString(command);
238CommandMode::ParsedSedExpression CommandMode::parseAsSedExpression()
240 const QString commandWithoutRangeExpression = withoutRangeExpression();
241 ParsedSedExpression parsedSedExpression;
243 parsedSedExpression.parsedSuccessfully =
SedReplace::parse(commandWithoutRangeExpression,
245 parsedSedExpression.findBeginPos,
246 parsedSedExpression.findEndPos,
247 parsedSedExpression.replaceBeginPos,
248 parsedSedExpression.replaceEndPos);
249 if (parsedSedExpression.parsedSuccessfully) {
250 parsedSedExpression.delimiter = delimiter.
at(0);
251 if (parsedSedExpression.replaceBeginPos == -1) {
252 if (parsedSedExpression.findBeginPos != -1) {
255 parsedSedExpression.replaceBeginPos = commandWithoutRangeExpression.
indexOf(delimiter, parsedSedExpression.findEndPos) + 1;
256 parsedSedExpression.replaceEndPos = parsedSedExpression.replaceBeginPos - 1;
259 parsedSedExpression.replaceBeginPos = 0;
260 for (
int delimiterCount = 1; delimiterCount <= 3; delimiterCount++) {
261 parsedSedExpression.replaceBeginPos = commandWithoutRangeExpression.
indexOf(delimiter, parsedSedExpression.replaceBeginPos + 1);
263 parsedSedExpression.replaceEndPos = parsedSedExpression.replaceBeginPos - 1;
266 if (parsedSedExpression.findBeginPos == -1) {
269 parsedSedExpression.findBeginPos = commandWithoutRangeExpression.
indexOf(delimiter) + 1;
270 parsedSedExpression.findEndPos = parsedSedExpression.findBeginPos - 1;
274 if (parsedSedExpression.parsedSuccessfully) {
275 parsedSedExpression.findBeginPos += rangeExpression().
length();
276 parsedSedExpression.findEndPos += rangeExpression().
length();
277 parsedSedExpression.replaceBeginPos += rangeExpression().
length();
278 parsedSedExpression.replaceEndPos += rangeExpression().
length();
280 return parsedSedExpression;
283QString CommandMode::sedFindTerm()
286 ParsedSedExpression parsedSedExpression = parseAsSedExpression();
287 Q_ASSERT(parsedSedExpression.parsedSuccessfully);
288 return command.
mid(parsedSedExpression.findBeginPos, parsedSedExpression.findEndPos - parsedSedExpression.findBeginPos + 1);
291QString CommandMode::sedReplaceTerm()
294 ParsedSedExpression parsedSedExpression = parseAsSedExpression();
295 Q_ASSERT(parsedSedExpression.parsedSuccessfully);
296 return command.
mid(parsedSedExpression.replaceBeginPos, parsedSedExpression.replaceEndPos - parsedSedExpression.replaceBeginPos + 1);
299QString CommandMode::withSedFindTermReplacedWith(
const QString &newFindTerm)
302 ParsedSedExpression parsedSedExpression = parseAsSedExpression();
303 Q_ASSERT(parsedSedExpression.parsedSuccessfully);
305 return strView.mid(0, parsedSedExpression.findBeginPos) + newFindTerm + strView.
mid(parsedSedExpression.findEndPos + 1);
310 ParsedSedExpression parsedSedExpression = parseAsSedExpression();
311 QString delimiterEscaped = ensuredCharEscaped(text, parsedSedExpression.delimiter);
312 return delimiterEscaped;
315bool CommandMode::isCursorInFindTermOfSed()
317 ParsedSedExpression parsedSedExpression = parseAsSedExpression();
318 return parsedSedExpression.parsedSuccessfully
322bool CommandMode::isCursorInReplaceTermOfSed()
324 ParsedSedExpression parsedSedExpression = parseAsSedExpression();
325 return parsedSedExpression.parsedSuccessfully && m_edit->
cursorPosition() >= parsedSedExpression.replaceBeginPos
326 && m_edit->
cursorPosition() <= parsedSedExpression.replaceEndPos + 1;
329int CommandMode::commandBeforeCursorBegin()
331 const QString textWithoutRangeExpression = withoutRangeExpression();
332 const int cursorPositionWithoutRangeExpression = m_edit->
cursorPosition() - rangeExpression().
length();
333 int commandBeforeCursorBegin = cursorPositionWithoutRangeExpression - 1;
334 while (commandBeforeCursorBegin >= 0
335 && (textWithoutRangeExpression[commandBeforeCursorBegin].isLetterOrNumber()
336 || textWithoutRangeExpression[commandBeforeCursorBegin] ==
QLatin1Char(
'_')
337 || textWithoutRangeExpression[commandBeforeCursorBegin] ==
QLatin1Char(
'-'))) {
338 commandBeforeCursorBegin--;
340 commandBeforeCursorBegin++;
341 commandBeforeCursorBegin += rangeExpression().
length();
342 return commandBeforeCursorBegin;
345CompletionStartParams CommandMode::activateCommandCompletion()
347 return CompletionStartParams::createModeSpecific(m_cmdCompletion.
items(), commandBeforeCursorBegin());
350CompletionStartParams CommandMode::activateCommandHistoryCompletion()
352 return CompletionStartParams::createModeSpecific(reversed(viInputModeManager()->globalState()->commandHistory()->items()), 0);
355CompletionStartParams CommandMode::activateSedFindHistoryCompletion()
357 if (viInputModeManager()->globalState()->searchHistory()->isEmpty()) {
358 return CompletionStartParams::invalid();
360 CommandMode::ParsedSedExpression parsedSedExpression = parseAsSedExpression();
361 return CompletionStartParams::createModeSpecific(reversed(viInputModeManager()->globalState()->searchHistory()->items()),
362 parsedSedExpression.findBeginPos,
364 return withCaseSensitivityMarkersStripped(withSedDelimiterEscaped(completion));
368CompletionStartParams CommandMode::activateSedReplaceHistoryCompletion()
370 if (viInputModeManager()->globalState()->replaceHistory()->isEmpty()) {
371 return CompletionStartParams::invalid();
373 CommandMode::ParsedSedExpression parsedSedExpression = parseAsSedExpression();
374 return CompletionStartParams::createModeSpecific(reversed(viInputModeManager()->globalState()->replaceHistory()->items()),
375 parsedSedExpression.replaceBeginPos,
377 return withCaseSensitivityMarkersStripped(withSedDelimiterEscaped(completion));
392 return m_cmdDict.
value(QStringLiteral(
"s"));
395 for (; f < cmd.
length(); f++) {
396 if (cmd[f].isLetter()) {
QStringList items() const
An Editor command line command.
virtual bool supportsRange(const QString &cmd)
Find out if a given command can act on a range.
virtual bool exec(KTextEditor::View *view, const QString &cmd, QString &msg, const KTextEditor::Range &range=KTextEditor::Range::invalid())=0
Execute the command for the given view and cmd string.
An object representing a section of text, from one Cursor to another.
constexpr bool isValid() const noexcept
Validity check.
A specialized class for scripts that are of type ScriptType::Indentation.
static bool parse(const QString &sedReplaceString, QString &destDelim, int &destFindBeginPos, int &destFindEndPos, int &destReplaceBeginPos, int &destReplaceEndPos)
Parses sedReplaceString to see if it is a valid sed replace expression (e.g.
A KateViewBarWidget that attempts to emulate some of the features of Vim's own command bar,...
QString i18n(const char *text, const TYPE &arg...)
T value(const Key &key) const const
void insert(const QString &newText)
void setSelection(int start, int length)
qsizetype count() const const
void push_back(parameter_type value)
const QChar at(qsizetype position) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString left(qsizetype n) const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
QTextStream & left(QTextStream &stream)
void keyEvent(KeyAction action, QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier, int delay)
void showText(const QPoint &pos, const QString &text, QWidget *w)