KTextEditor

katevariableexpansionmanager.cpp
1/*
2 SPDX-FileCopyrightText: 2019 Dominik Haumann <dhaumann@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "katevariableexpansionmanager.h"
8#include "katevariableexpansionhelpers.h"
9
10#include "katedocument.h"
11#include "kateglobal.h"
12
13#include <KLocalizedString>
14
15#include <QAbstractItemModel>
16#include <QListView>
17#include <QVBoxLayout>
18
19#include <QDate>
20#include <QDir>
21#include <QFileInfo>
22#include <QJSEngine>
23#include <QLocale>
24#include <QTime>
25#include <QUuid>
26
27static void registerVariables(KateVariableExpansionManager &mng)
28{
30
32 QStringLiteral("Document:FileBaseName"),
33 i18n("File base name without path and suffix of the current document."),
34 [](const QStringView &, KTextEditor::View *view) {
35 const auto url = view ? view->document()->url().toLocalFile() : QString();
36 return QFileInfo(url).baseName();
37 },
38 false));
40 QStringLiteral("Document:FileExtension"),
41 i18n("File extension of the current document."),
42 [](const QStringView &, KTextEditor::View *view) {
43 const auto url = view ? view->document()->url().toLocalFile() : QString();
44 return QFileInfo(url).completeSuffix();
45 },
46 false));
48 QStringLiteral("Document:FileName"),
49 i18n("File name without path of the current document."),
50 [](const QStringView &, KTextEditor::View *view) {
51 const auto url = view ? view->document()->url().toLocalFile() : QString();
52 return QFileInfo(url).fileName();
53 },
54 false));
56 QStringLiteral("Document:FilePath"),
57 i18n("Full path of the current document including the file name."),
58 [](const QStringView &, KTextEditor::View *view) {
59 const auto url = view ? view->document()->url().toLocalFile() : QString();
60 return QFileInfo(url).absoluteFilePath();
61 },
62 false));
64 QStringLiteral("Document:Text"),
65 i18n("Contents of the current document."),
66 [](const QStringView &, KTextEditor::View *view) {
67 return view ? view->document()->text() : QString();
68 },
69 false));
71 QStringLiteral("Document:Path"),
72 i18n("Full path of the current document excluding the file name."),
73 [](const QStringView &, KTextEditor::View *view) {
74 const auto url = view ? view->document()->url().toLocalFile() : QString();
75 return QFileInfo(url).absolutePath();
76 },
77 false));
79 QStringLiteral("Document:NativeFilePath"),
80 i18n("Full document path including file name, with native path separator (backslash on Windows)."),
81 [](const QStringView &, KTextEditor::View *view) {
82 const auto url = view ? view->document()->url().toLocalFile() : QString();
83 return url.isEmpty() ? QString() : QDir::toNativeSeparators(QFileInfo(url).absoluteFilePath());
84 },
85 false));
87 QStringLiteral("Document:NativePath"),
88 i18n("Full document path excluding file name, with native path separator (backslash on Windows)."),
89 [](const QStringView &, KTextEditor::View *view) {
90 const auto url = view ? view->document()->url().toLocalFile() : QString();
91 return url.isEmpty() ? QString() : QDir::toNativeSeparators(QFileInfo(url).absolutePath());
92 },
93 false));
95 QStringLiteral("Document:Cursor:Line"),
96 i18n("Line number of the text cursor position in current document (starts with 0)."),
97 [](const QStringView &, KTextEditor::View *view) {
98 return view ? QString::number(view->cursorPosition().line()) : QString();
99 },
100 false));
102 QStringLiteral("Document:Cursor:Column"),
103 i18n("Column number of the text cursor position in current document (starts with 0)."),
104 [](const QStringView &, KTextEditor::View *view) {
105 return view ? QString::number(view->cursorPosition().column()) : QString();
106 },
107 false));
109 QStringLiteral("Document:Cursor:XPos"),
110 i18n("X component in global screen coordinates of the cursor position."),
111 [](const QStringView &, KTextEditor::View *view) {
112 return view ? QString::number(view->mapToGlobal(view->cursorPositionCoordinates()).x()) : QString();
113 },
114 false));
116 QStringLiteral("Document:Cursor:YPos"),
117 i18n("Y component in global screen coordinates of the cursor position."),
118 [](const QStringView &, KTextEditor::View *view) {
119 return view ? QString::number(view->mapToGlobal(view->cursorPositionCoordinates()).y()) : QString();
120 },
121 false));
123 QStringLiteral("Document:Selection:Text"),
124 i18n("Text selection of the current document."),
125 [](const QStringView &, KTextEditor::View *view) {
126 return (view && view->selection()) ? view->selectionText() : QString();
127 },
128 false));
130 QStringLiteral("Document:Selection:StartLine"),
131 i18n("Start line of selected text of the current document."),
132 [](const QStringView &, KTextEditor::View *view) {
133 return (view && view->selection()) ? QString::number(view->selectionRange().start().line()) : QString();
134 },
135 false));
137 QStringLiteral("Document:Selection:StartColumn"),
138 i18n("Start column of selected text of the current document."),
139 [](const QStringView &, KTextEditor::View *view) {
140 return (view && view->selection()) ? QString::number(view->selectionRange().start().column()) : QString();
141 },
142 false));
144 QStringLiteral("Document:Selection:EndLine"),
145 i18n("End line of selected text of the current document."),
146 [](const QStringView &, KTextEditor::View *view) {
147 return (view && view->selection()) ? QString::number(view->selectionRange().end().line()) : QString();
148 },
149 false));
151 QStringLiteral("Document:Selection:EndColumn"),
152 i18n("End column of selected text of the current document."),
153 [](const QStringView &, KTextEditor::View *view) {
154 return (view && view->selection()) ? QString::number(view->selectionRange().end().column()) : QString();
155 },
156 false));
158 QStringLiteral("Document:RowCount"),
159 i18n("Number of rows of the current document."),
160 [](const QStringView &, KTextEditor::View *view) {
161 return view ? QString::number(view->document()->lines()) : QString();
162 },
163 false));
165 QStringLiteral("Document:Variable:"),
166 i18n("Read a document variable."),
167 [](const QStringView &str, KTextEditor::View *view) {
168 return view ? qobject_cast<KTextEditor::DocumentPrivate *>(view->document())->variable(str.mid(18).toString()) : QString();
169 },
170 true));
171
173 QStringLiteral("Date:Locale"),
174 i18n("The current date in current locale format."),
175 [](const QStringView &, KTextEditor::View *) {
177 },
178 false));
180 QStringLiteral("Date:ISO"),
181 i18n("The current date (ISO)."),
182 [](const QStringView &, KTextEditor::View *) {
184 },
185 false));
187 QStringLiteral("Date:"),
188 i18n("The current date (QDate formatstring)."),
189 [](const QStringView &str, KTextEditor::View *) {
190 return QDate::currentDate().toString(str.mid(5));
191 },
192 true));
193
195 QStringLiteral("Time:Locale"),
196 i18n("The current time in current locale format."),
197 [](const QStringView &, KTextEditor::View *) {
199 },
200 false));
202 QStringLiteral("Time:ISO"),
203 i18n("The current time (ISO)."),
204 [](const QStringView &, KTextEditor::View *) {
206 },
207 false));
209 QStringLiteral("Time:"),
210 i18n("The current time (QTime formatstring)."),
211 [](const QStringView &str, KTextEditor::View *) {
212 return QTime::currentTime().toString(str.mid(5));
213 },
214 true));
215
217 QStringLiteral("ENV:"),
218 i18n("Access to environment variables."),
219 [](const QStringView &str, KTextEditor::View *) {
220 return QString::fromLocal8Bit(qgetenv(str.mid(4).toLocal8Bit().constData()));
221 },
222 true));
223
225 QStringLiteral("JS:"),
226 i18n("Evaluate simple JavaScript statements."),
227 [](const QStringView &str, KTextEditor::View *) {
228 QJSEngine jsEngine;
229 const QJSValue out = jsEngine.evaluate(str.toString());
230 return out.toString();
231 },
232 true));
233
235 QStringLiteral("PercentEncoded:"),
236 i18n("Encode text to make it URL compatible."),
237 [](const QStringView &str, KTextEditor::View *) {
239 },
240 true));
241
243 QStringLiteral("UUID"),
244 i18n("Generate a new UUID."),
245 [](const QStringView &, KTextEditor::View *) {
247 },
248 false));
249}
250
252 : QObject(parent)
253{
254 // register default variables for expansion
255 registerVariables(*this);
256}
257
259{
260 if (!var.isValid()) {
261 return false;
262 }
263
264 // reject duplicates
265 const auto alreadyExists = std::any_of(m_variables.begin(), m_variables.end(), [&var](const KTextEditor::Variable &v) {
266 return var.name() == v.name();
267 });
268 if (alreadyExists) {
269 return false;
270 }
271
272 // require a ':' in prefix matches (aka %{JS:1+1})
273 if (var.isPrefixMatch() && !var.name().contains(QLatin1Char(':'))) {
274 return false;
275 }
276
277 m_variables.push_back(var);
278 return true;
279}
280
282{
283 auto it = std::find_if(m_variables.begin(), m_variables.end(), [&name](const KTextEditor::Variable &var) {
284 return var.name() == name;
285 });
286 if (it != m_variables.end()) {
287 m_variables.erase(it);
288 return true;
289 }
290 return false;
291}
292
294{
295 auto it = std::find_if(m_variables.begin(), m_variables.end(), [&name](const KTextEditor::Variable &var) {
296 return var.name() == name;
297 });
298 if (it != m_variables.end()) {
299 return *it;
300 }
301 return {};
302}
303
305{
306 return m_variables;
307}
308
309bool KateVariableExpansionManager::expandVariable(const QString &name, KTextEditor::View *view, QString &output) const
310{
311 // first try exact matches
312 auto var = variable(name);
313 if (!var.isValid()) {
314 // try prefix matching
315 for (auto &v : m_variables) {
316 if (v.isPrefixMatch() && name.startsWith(v.name())) {
317 var = v;
318 break;
319 }
320 }
321 }
322
323 if (var.isValid()) {
324 output = var.evaluate(name, view);
325 return true;
326 }
327
328 return false;
329}
330
331QString KateVariableExpansionManager::expandText(const QString &text, KTextEditor::View *view)
332{
333 return KateMacroExpander::expandMacro(text, view);
334}
335
336void KateVariableExpansionManager::showDialog(const QList<QWidget *> &widgets, const QStringList &names) const
337{
338 // avoid any work in case no widgets or only nullptrs were provided
339 if (widgets.isEmpty() || std::all_of(widgets.cbegin(), widgets.cend(), [](const QWidget *w) {
340 return w == nullptr;
341 })) {
342 return;
343 }
344
345 // collect variables
347 if (!names.isEmpty()) {
348 for (const auto &name : names) {
349 const auto var = variable(name);
350 if (var.isValid()) {
351 vars.push_back(var);
352 }
353 // else: Not found, silently ignore for now
354 // Maybe raise a qCWarning(LOG_KTE)?
355 }
356 } else {
357 vars = variables();
358 }
359
360 // if we have no vars at all, do nothing
361 if (vars.isEmpty()) {
362 return;
363 }
364
365 // find parent dialog (for taskbar sharing, centering, ...)
366 QWidget *parentDialog = nullptr;
367 for (auto widget : widgets) {
368 if (widget) {
369 parentDialog = widget->window();
370 break;
371 }
372 }
373
374 // show dialog
375 auto dlg = new KateVariableExpansionDialog(parentDialog);
376 for (auto widget : widgets) {
377 if (widget) {
378 dlg->addWidget(widget);
379 }
380 }
381
382 // add provided variables...
383 for (const auto &var : std::as_const(vars)) {
384 if (var.isValid()) {
385 dlg->addVariable(var);
386 }
387 }
388}
389
390// kate: space-indent on; indent-width 4; replace-tabs on;
constexpr int column() const noexcept
Retrieve the column on which this cursor is situated.
Definition cursor.h:192
constexpr int line() const noexcept
Retrieve the line on which this cursor is situated.
Definition cursor.h:174
virtual QString text() const =0
Get the document content.
virtual int lines() const =0
Get the count of lines of the document.
constexpr Cursor end() const noexcept
Get the end position of this range.
constexpr Cursor start() const noexcept
Get the start position of this range.
Variable for variable expansion.
Definition variable.h:35
bool isValid() const
Returns true, if the name is non-empty and the function provided in the constructor is not a nullptr.
Definition variable.cpp:19
bool isPrefixMatch() const
Returns whether this Variable represents an exact match (false) or a prefix match (true).
Definition variable.cpp:24
QString name() const
Returns the name that was provided in the constructor.
Definition variable.cpp:29
A text widget with KXMLGUIClient that represents a Document.
Definition view.h:244
virtual Document * document() const =0
Get the view's document, that means the view is a view of the returned document.
virtual QPoint cursorPositionCoordinates() const =0
Get the screen coordinates (x, y) of the cursor position in pixels.
virtual Cursor cursorPosition() const =0
Get the view's current cursor position.
virtual bool selection() const =0
Query the view whether it has selected text, i.e.
virtual Range selectionRange() const =0
Get the range occupied by the current selection.
virtual QString selectionText() const =0
Get the view's selected text.
Helper dialog that shows a non-modal dialog listing all available variables.
Manager class for variable expansion.
KTextEditor::Variable variable(const QString &name) const
Returns the variable called name.
KateVariableExpansionManager(QObject *parent)
Constructor with parent that takes ownership.
bool addVariable(const KTextEditor::Variable &variable)
Adds variable to the expansion list view.
bool removeVariable(const QString &name)
Removes variable name.
const QList< KTextEditor::Variable > & variables() const
Returns all registered variables.
QString i18n(const char *text, const TYPE &arg...)
QString expandMacro(const QString &input, KTextEditor::View *view)
Expands the input text based on the view.
const char * constData() const const
QDate currentDate()
QString toString(QStringView format, QCalendar cal) const const
QString absoluteFilePath() const const
QString absolutePath() const const
QString baseName() const const
QString completeSuffix() const const
QString fileName() const const
QJSValue evaluate(const QString &program, const QString &fileName, int lineNumber, QStringList *exceptionStackTrace)
QString toString() const const
iterator begin()
const_iterator cbegin() const const
const_iterator cend() const const
iterator end()
iterator erase(const_iterator begin, const_iterator end)
bool isEmpty() const const
void push_back(parameter_type value)
QString toString(QDate date, FormatType format) const const
int x() const const
int y() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromLocal8Bit(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
QString number(double n, char format, int precision)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QStringView mid(qsizetype start, qsizetype length) const const
QByteArray toLocal8Bit() const const
QString toString() const const
QTime currentTime()
QString toString(QStringView format) const const
QString toLocalFile() const const
QByteArray toPercentEncoding(const QString &input, const QByteArray &exclude, const QByteArray &include)
QUuid createUuid()
QString toString(StringFormat mode) const const
QPoint mapToGlobal(const QPoint &pos) const const
QWidget * window() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:15:44 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.