11#include <KLocalizedString>
12#include <KTextEditor/Application>
13#include <KTextEditor/Document>
14#include <KTextEditor/Editor>
15#include <KTextEditor/MainWindow>
16#include <KTextEditor/View>
18#include <vimode/appcommands.h>
20using namespace KateVi;
23AppCommands *AppCommands::m_instance =
nullptr;
25AppCommands::AppCommands()
26 :
KTextEditor::Command({QStringLiteral(
"q"), QStringLiteral(
"qa"), QStringLiteral(
"qall"), QStringLiteral(
"q!"), QStringLiteral(
"qa!"),
27 QStringLiteral(
"qall!"), QStringLiteral(
"w"), QStringLiteral(
"wq"), QStringLiteral(
"wa"), QStringLiteral(
"wqa"),
28 QStringLiteral(
"x"), QStringLiteral(
"xa"), QStringLiteral(
"new"), QStringLiteral(
"vnew"), QStringLiteral(
"e"),
29 QStringLiteral(
"edit"), QStringLiteral(
"enew"), QStringLiteral(
"sp"), QStringLiteral(
"split"), QStringLiteral(
"vs"),
30 QStringLiteral(
"vsplit"), QStringLiteral(
"only"), QStringLiteral(
"tabe"), QStringLiteral(
"tabedit"), QStringLiteral(
"tabnew"),
31 QStringLiteral(
"bd"), QStringLiteral(
"bdelete"), QStringLiteral(
"tabc"), QStringLiteral(
"tabclose"), QStringLiteral(
"clo"),
32 QStringLiteral(
"close")})
33 , re_write(QStringLiteral(
"^w(a)?$"))
34 , re_close(QStringLiteral(
"^bd(elete)?|tabc(lose)?$"))
35 , re_quit(QStringLiteral(
"^(w)?q(a|all)?(!)?$"))
36 , re_exit(QStringLiteral(
"^x(a)?$"))
37 , re_edit(QStringLiteral(
"^e(dit)?|tabe(dit)?|tabnew$"))
38 , re_tabedit(QStringLiteral(
"^tabe(dit)?|tabnew$"))
39 , re_new(QStringLiteral(
"^(v)?new$"))
40 , re_split(QStringLiteral(
"^sp(lit)?$"))
41 , re_vsplit(QStringLiteral(
"^vs(plit)?$"))
42 , re_vclose(QStringLiteral(
"^clo(se)?$"))
43 , re_only(QStringLiteral(
"^on(ly)?$"))
47AppCommands::~AppCommands()
52bool AppCommands::exec(KTextEditor::View *view,
const QString &cmd, QString &msg,
const KTextEditor::Range &)
55 QString command(args.takeFirst());
57 KTextEditor::MainWindow *mainWin = view->
mainWindow();
60 QRegularExpressionMatch
match;
61 if ((match = re_write.match(command)).hasMatch()) {
62 if (!
match.captured(1).isEmpty()) {
64 for (KTextEditor::Document *doc : docs) {
67 msg =
i18n(
"All documents written to disk");
70 msg =
i18n(
"Document written to disk");
74 else if ((match = re_close.match(command)).hasMatch()) {
78 }
else if ((match = re_quit.match(command)).hasMatch()) {
79 const bool save = !
match.captured(1).isEmpty();
80 const bool allDocuments = !
match.captured(2).isEmpty();
81 const bool doNotPromptForSave = !
match.captured(3).isEmpty();
86 for (KTextEditor::Document *doc : docs) {
91 if (doNotPromptForSave) {
93 for (KTextEditor::Document *doc : docs) {
94 if (doc->isModified()) {
95 doc->setModified(
false);
108 if (doNotPromptForSave) {
119 }
else if ((match = re_exit.match(command)).hasMatch()) {
120 if (!
match.captured(1).isEmpty()) {
122 for (KTextEditor::Document *doc : docs) {
137 }
else if ((match = re_edit.match(command)).hasMatch()) {
138 QString argument = args.join(QLatin1Char(
' '));
139 if (argument.
isEmpty() || argument == QLatin1String(
"!")) {
140 if ((match = re_tabedit.match(command)).hasMatch()) {
141 if (
auto doc = app->
openUrl(QUrl())) {
152 QUrl arg2path(argument);
158 .resolved(arg2path));
162 KTextEditor::Document *doc = app->
findUrl(url);
174 }
else if ((match = re_new.match(command)).hasMatch()) {
175 if (
match.captured(1) == QLatin1String(
"v")) {
181 }
else if (command == QLatin1String(
"enew")) {
183 }
else if ((match = re_split.match(command)).hasMatch()) {
185 }
else if ((match = re_vsplit.match(command)).hasMatch()) {
187 }
else if ((match = re_vclose.match(command)).hasMatch()) {
189 }
else if ((match = re_only.match(command)).hasMatch()) {
196bool AppCommands::help(KTextEditor::View *view,
const QString &cmd, QString &msg)
200 if (re_write.match(cmd).hasMatch()) {
202 "<p><b>w/wa — write document(s) to disk</b></p>"
203 "<p>Usage: <tt><b>w[a]</b></tt></p>"
204 "<p>Writes the current document(s) to disk. "
205 "It can be called in two ways:<br />"
206 " <tt>w</tt> — writes the current document to disk<br />"
207 " <tt>wa</tt> — writes all documents to disk.</p>"
208 "<p>If no file name is associated with the document, "
209 "a file dialog will be shown.</p>");
211 }
else if (re_quit.match(cmd).hasMatch()) {
213 "<p><b>q/qa/wq/wqa — [write and] quit</b></p>"
214 "<p>Usage: <tt><b>[w]q[a]</b></tt></p>"
215 "<p>Quits the application. If <tt>w</tt> is prepended, it also writes"
216 " the document(s) to disk. This command "
217 "can be called in several ways:<br />"
218 " <tt>q</tt> — closes the current view.<br />"
219 " <tt>qa</tt> — closes all views, effectively quitting the application.<br />"
220 " <tt>wq</tt> — writes the current document to disk and closes its view.<br />"
221 " <tt>wqa</tt> — writes all documents to disk and quits.</p>"
222 "<p>In all cases, if the view being closed is the last view, the application quits. "
223 "If no file name is associated with the document and it should be written to disk, "
224 "a file dialog will be shown.</p>");
226 }
else if (re_exit.match(cmd).hasMatch()) {
228 "<p><b>x/xa — write and quit</b></p>"
229 "<p>Usage: <tt><b>x[a]</b></tt></p>"
230 "<p>Saves document(s) and quits (e<b>x</b>its). This command "
231 "can be called in two ways:<br />"
232 " <tt>x</tt> — closes the current view.<br />"
233 " <tt>xa</tt> — closes all views, effectively quitting the application.</p>"
234 "<p>In all cases, if the view being closed is the last view, the application quits. "
235 "If no file name is associated with the document and it should be written to disk, "
236 "a file dialog will be shown.</p>"
237 "<p>Unlike the 'w' commands, this command only writes the document if it is modified."
240 }
else if (re_split.match(cmd).hasMatch()) {
242 "<p><b>sp,split— Split horizontally the current view into two</b></p>"
243 "<p>Usage: <tt><b>sp[lit]</b></tt></p>"
244 "<p>The result is two views on the same document.</p>");
246 }
else if (re_vsplit.match(cmd).hasMatch()) {
248 "<p><b>vs,vsplit— Split vertically the current view into two</b></p>"
249 "<p>Usage: <tt><b>vs[plit]</b></tt></p>"
250 "<p>The result is two views on the same document.</p>");
252 }
else if (re_vclose.match(cmd).hasMatch()) {
254 "<p><b>clo[se]— Close the current view</b></p>"
255 "<p>Usage: <tt><b>clo[se]</b></tt></p>"
256 "<p>After executing it, the current view will be closed.</p>");
258 }
else if (re_new.match(cmd).hasMatch()) {
260 "<p><b>[v]new — split view and create new document</b></p>"
261 "<p>Usage: <tt><b>[v]new</b></tt></p>"
262 "<p>Splits the current view and opens a new document in the new view."
263 " This command can be called in two ways:<br />"
264 " <tt>new</tt> — splits the view horizontally and opens a new document.<br />"
265 " <tt>vnew</tt> — splits the view vertically and opens a new document.<br />"
268 }
else if (re_edit.match(cmd).hasMatch()) {
270 "<p><b>e[dit] — reload current document</b></p>"
271 "<p>Usage: <tt><b>e[dit]</b></tt></p>"
272 "<p>Starts <b>e</b>diting the current document again. This is useful to re-edit"
273 " the current file, when it was modified on disk.</p>");
280KTextEditor::View *AppCommands::findViewInDifferentSplitView(KTextEditor::MainWindow *window, KTextEditor::View *view)
282 const auto views =
window->views();
283 for (
auto it : views) {
284 if (!
window->viewsInSameSplitView(it, view)) {
291void AppCommands::closeDocuments(
const QList<KTextEditor::Document *> &documents)
299void AppCommands::closeCurrentDocument()
303 if (
auto view = mw->activeView()) {
311void AppCommands::closeCurrentView()
315 if (
auto view = mw->activeView()) {
320void AppCommands::closeCurrentSplitView()
324 if (
auto view = mw->activeView()) {
325 mw->closeSplitView(view);
329void AppCommands::closeOtherSplitViews()
333 if (
auto view = mw->activeView()) {
334 while (KTextEditor::View *viewToRemove = findViewInDifferentSplitView(mw, view)) {
335 mw->closeSplitView(viewToRemove);
340void AppCommands::quit()
348BufferCommands *BufferCommands::m_instance =
nullptr;
350BufferCommands::BufferCommands()
351 : KTextEditor::Command({QStringLiteral(
"ls"),
353 QStringLiteral(
"buffer"),
354 QStringLiteral(
"bn"),
355 QStringLiteral(
"bnext"),
356 QStringLiteral(
"bp"),
357 QStringLiteral(
"bprevious"),
358 QStringLiteral(
"tabn"),
359 QStringLiteral(
"tabnext"),
360 QStringLiteral(
"tabp"),
361 QStringLiteral(
"tabprevious"),
362 QStringLiteral(
"bf"),
363 QStringLiteral(
"bfirst"),
364 QStringLiteral(
"bl"),
365 QStringLiteral(
"blast"),
366 QStringLiteral(
"tabf"),
367 QStringLiteral(
"tabfirst"),
368 QStringLiteral(
"tabl"),
369 QStringLiteral(
"tablast")})
373BufferCommands::~BufferCommands()
375 m_instance =
nullptr;
378bool BufferCommands::exec(KTextEditor::View *view,
const QString &cmd, QString &,
const KTextEditor::Range &)
382 QString command = args.takeFirst();
383 QString argument = args.join(QLatin1Char(
' '));
385 if (command == QLatin1String(
"ls")) {
387 }
else if (command == QLatin1String(
"b") || command == QLatin1String(
"buffer")) {
388 switchDocument(view, argument);
389 }
else if (command == QLatin1String(
"bp") || command == QLatin1String(
"bprevious")) {
391 }
else if (command == QLatin1String(
"bn") || command == QLatin1String(
"bnext")) {
393 }
else if (command == QLatin1String(
"bf") || command == QLatin1String(
"bfirst")) {
395 }
else if (command == QLatin1String(
"bl") || command == QLatin1String(
"blast")) {
397 }
else if (command == QLatin1String(
"tabn") || command == QLatin1String(
"tabnext")) {
399 }
else if (command == QLatin1String(
"tabp") || command == QLatin1String(
"tabprevious")) {
401 }
else if (command == QLatin1String(
"tabf") || command == QLatin1String(
"tabfirst")) {
403 }
else if (command == QLatin1String(
"tabl") || command == QLatin1String(
"tablast")) {
409void BufferCommands::switchDocument(KTextEditor::View *view,
const QString &address)
417 const int idx =
address.toInt();
418 QList<KTextEditor::Document *> docs = documents();
420 if (idx > 0 && idx <= docs.
size()) {
422 activateDocument(view, docs.
at(idx - 1));
425 KTextEditor::Document *doc =
nullptr;
427 for (KTextEditor::Document *it : docs) {
428 if (it->documentName() == address) {
435 activateDocument(view, doc);
440void BufferCommands::prevBuffer(KTextEditor::View *view)
442 const QList<KTextEditor::Document *> docs = documents();
446 activateDocument(view, docs.
at(idx - 1));
448 activateDocument(view, docs.
last());
452void BufferCommands::nextBuffer(KTextEditor::View *view)
454 QList<KTextEditor::Document *> docs = documents();
457 if (idx + 1 < docs.
size()) {
458 activateDocument(view, docs.
at(idx + 1));
460 activateDocument(view, docs.
first());
464void BufferCommands::firstBuffer(KTextEditor::View *view)
466 auto docs = documents();
468 activateDocument(view, documents().at(0));
472void BufferCommands::lastBuffer(KTextEditor::View *view)
474 auto docs = documents();
476 activateDocument(view, documents().last());
480void BufferCommands::prevTab(KTextEditor::View *view)
485void BufferCommands::nextTab(KTextEditor::View *view)
490void BufferCommands::firstTab(KTextEditor::View *view)
495void BufferCommands::lastTab(KTextEditor::View *view)
500void BufferCommands::activateDocument(KTextEditor::View *view, KTextEditor::Document *doc)
502 KTextEditor::MainWindow *mainWindow = view->
mainWindow();
508QList<KTextEditor::Document *> BufferCommands::documents()
514bool BufferCommands::help(KTextEditor::View * ,
const QString &cmd, QString &msg)
516 if (cmd == QLatin1String(
"b") || cmd == QLatin1String(
"buffer")) {
518 "<p><b>b,buffer — Edit document N from the document list</b></p>"
519 "<p>Usage: <tt><b>b[uffer] [N]</b></tt></p>");
521 }
else if (cmd == QLatin1String(
"bp") || cmd == QLatin1String(
"bprevious") || cmd == QLatin1String(
"tabp") || cmd == QLatin1String(
"tabprevious")) {
523 "<p><b>bp,bprev — previous buffer</b></p>"
524 "<p>Usage: <tt><b>bp[revious] [N]</b></tt></p>"
525 "<p>Goes to <b>[N]</b>th previous document (\"<b>b</b>uffer\") in document list. </p>"
526 "<p> <b>[N]</b> defaults to one. </p>"
527 "<p>Wraps around the start of the document list.</p>");
529 }
else if (cmd == QLatin1String(
"bn") || cmd == QLatin1String(
"bnext") || cmd == QLatin1String(
"tabn") || cmd == QLatin1String(
"tabnext")) {
531 "<p><b>bn,bnext — switch to next document</b></p>"
532 "<p>Usage: <tt><b>bn[ext] [N]</b></tt></p>"
533 "<p>Goes to <b>[N]</b>th next document (\"<b>b</b>uffer\") in document list."
534 "<b>[N]</b> defaults to one. </p>"
535 "<p>Wraps around the end of the document list.</p>");
537 }
else if (cmd == QLatin1String(
"bf") || cmd == QLatin1String(
"bfirst") || cmd == QLatin1String(
"tabf") || cmd == QLatin1String(
"tabfirst")) {
539 "<p><b>bf,bfirst — first document</b></p>"
540 "<p>Usage: <tt><b>bf[irst]</b></tt></p>"
541 "<p>Goes to the <b>f</b>irst document (\"<b>b</b>uffer\") in document list.</p>");
543 }
else if (cmd == QLatin1String(
"bl") || cmd == QLatin1String(
"blast") || cmd == QLatin1String(
"tabl") || cmd == QLatin1String(
"tablast")) {
545 "<p><b>bl,blast — last document</b></p>"
546 "<p>Usage: <tt><b>bl[ast]</b></tt></p>"
547 "<p>Goes to the <b>l</b>ast document (\"<b>b</b>uffer\") in document list.</p>");
549 }
else if (cmd == QLatin1String(
"ls")) {
552 "<p>list current buffers<p>");
KTextEditor::Document * openUrl(const QUrl &url, const QString &encoding=QString())
Open the document url with the given encoding.
bool closeDocuments(const QList< KTextEditor::Document * > &documents)
Close a list of documents.
KTextEditor::Document * findUrl(const QUrl &url)
Get the document with the URL url.
bool closeDocument(KTextEditor::Document *document)
Close the given document.
KTextEditor::MainWindow * activeMainWindow()
Accessor to the active main window.
bool quit()
Ask app to quit.
QList< KTextEditor::Document * > documents()
Get a list of all documents that are managed by the application.
virtual bool documentReload()=0
Reload the current file.
virtual bool documentSave()=0
Save the current file.
static Editor * instance()
Accessor to get the Editor instance.
virtual KTextEditor::Application * application() const =0
Current hosting application, if any set.
KTextEditor::View * openUrl(const QUrl &url, const QString &encoding=QString())
Open the document url with the given encoding.
QList< KTextEditor::View * > views()
Get a list of all views for this main window.
KTextEditor::View * activateView(KTextEditor::Document *document)
Activate the view with the corresponding document.
void splitView(Qt::Orientation orientation)
Split current view space according to orientation.
virtual Document * document() const =0
Get the view's document, that means the view is a view of the returned document.
virtual KTextEditor::MainWindow * mainWindow() const =0
Get the view's main window, if any.
QString i18n(const char *text, const TYPE &arg...)
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
PostalAddress address(const QVariant &location)
KGUIADDONS_EXPORT QWindow * window(QObject *job)
QAction * save(const QObject *recvr, const char *slot, QObject *parent)
The KTextEditor namespace contains all the public API that is required to use the KTextEditor compone...
const_reference at(qsizetype i) const const
qsizetype indexOf(const AT &value, qsizetype from) const const
bool isEmpty() const const
qsizetype size() const const
bool isEmpty() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QUrl fromLocalFile(const QString &localFile)
bool isValid() const const
QUrl resolved(const QUrl &relative) const const