KTextEditor

katevariableexpansionmanager.cpp
1 /*
2  SPDX-FileCopyrightText: 2019 Dominik Haumann <[email protected]>
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 
27 static void registerVariables(KateVariableExpansionManager &mng)
28 {
30 
31  mng.addVariable(Variable(
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));
39  mng.addVariable(Variable(
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));
47  mng.addVariable(Variable(
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));
55  mng.addVariable(Variable(
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));
63  mng.addVariable(Variable(
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));
70  mng.addVariable(Variable(
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));
78  mng.addVariable(Variable(
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));
86  mng.addVariable(Variable(
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));
94  mng.addVariable(Variable(
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));
101  mng.addVariable(Variable(
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));
108  mng.addVariable(Variable(
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));
115  mng.addVariable(Variable(
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));
122  mng.addVariable(Variable(
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));
129  mng.addVariable(Variable(
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));
136  mng.addVariable(Variable(
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));
143  mng.addVariable(Variable(
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));
150  mng.addVariable(Variable(
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));
157  mng.addVariable(Variable(
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));
164  mng.addVariable(Variable(
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 
172  mng.addVariable(Variable(
173  QStringLiteral("Date:Locale"),
174  i18n("The current date in current locale format."),
175  [](const QStringView &, KTextEditor::View *) {
177  },
178  false));
179  mng.addVariable(Variable(
180  QStringLiteral("Date:ISO"),
181  i18n("The current date (ISO)."),
182  [](const QStringView &, KTextEditor::View *) {
184  },
185  false));
186  mng.addVariable(Variable(
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 
194  mng.addVariable(Variable(
195  QStringLiteral("Time:Locale"),
196  i18n("The current time in current locale format."),
197  [](const QStringView &, KTextEditor::View *) {
199  },
200  false));
201  mng.addVariable(Variable(
202  QStringLiteral("Time:ISO"),
203  i18n("The current time (ISO)."),
204  [](const QStringView &, KTextEditor::View *) {
206  },
207  false));
208  mng.addVariable(Variable(
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 
216  mng.addVariable(Variable(
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 
224  mng.addVariable(Variable(
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 
234  mng.addVariable(Variable(
235  QStringLiteral("PercentEncoded:"),
236  i18n("Encode text to make it URL compatible."),
237  [](const QStringView &str, KTextEditor::View *) {
239  },
240  true));
241 
242  mng.addVariable(Variable(
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 
309 bool 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 
331 QString KateVariableExpansionManager::expandText(const QString &text, KTextEditor::View *view)
332 {
333  return KateMacroExpander::expandMacro(text, view);
334 }
335 
336 void KateVariableExpansionManager::showDialog(const QVector<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;
virtual Document * document() const =0
Get the view's document, that means the view is a view of the returned document.
QWidget * window() const const
bool isEmpty() const const
Helper dialog that shows a non-modal dialog listing all available variables.
QString completeSuffix() const const
constexpr int column() const Q_DECL_NOEXCEPT
Retrieve the column on which this cursor is situated.
Definition: cursor.h:215
QString number(int n, int base)
QString fromUtf8(const char *str, int size)
QString expandMacro(const QString &input, KTextEditor::View *view)
Expands the input text based on the view.
QVector::iterator begin()
QVector::const_iterator cend() const const
virtual int lines() const =0
Get the count of lines of the document.
QStringView mid(qsizetype start) const const
void push_back(const T &value)
virtual Range selectionRange() const =0
Get the range occupied by the current selection.
QByteArray toLocal8Bit() const const
int x() const const
int y() const const
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
virtual bool selection() const =0
Query the view whether it has selected text, i.e.
constexpr Cursor end() const Q_DECL_NOEXCEPT
Get the end position of this range.
QTime currentTime()
QString toString() const const
bool removeVariable(const QString &name)
Removes variable name.
QString i18n(const char *text, const TYPE &arg...)
QString fromLocal8Bit(const char *str, int size)
bool addVariable(const KTextEditor::Variable &variable)
Adds variable to the expansion list view.
QString absoluteFilePath() const const
Manager class for variable expansion.
QVector::const_iterator cbegin() const const
QString baseName() const const
QUuid createUuid()
A text widget with KXMLGUIClient that represents a Document.
Definition: view.h:146
QString toString(qlonglong i) const const
bool isEmpty() const const
QString toLocalFile() const const
Variable for variable expansion.
Definition: variable.h:34
QDate currentDate()
QPoint mapToGlobal(const QPoint &pos) const const
QByteArray toPercentEncoding(const QString &input, const QByteArray &exclude, const QByteArray &include)
QJSValue evaluate(const QString &program, const QString &fileName, int lineNumber)
QString fileName() const const
QString toString() const const
QString absolutePath() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
virtual QString selectionText() const =0
Get the view's selected text.
QString name() const
Returns the name that was provided in the constructor.
Definition: variable.cpp:29
QVector::iterator end()
bool isPrefixMatch() const
Returns whether this Variable represents an exact match (false) or a prefix match (true).
Definition: variable.cpp:24
virtual QPoint cursorPositionCoordinates() const =0
Get the screen coordinates (x, y) of the cursor position in pixels.
constexpr Cursor start() const Q_DECL_NOEXCEPT
Get the start position of this range.
const char * constData() const const
virtual QString text() const =0
Get the document content.
QVector::iterator erase(QVector::iterator begin, QVector::iterator end)
QString toString(Qt::DateFormat format) const const
QString toString(Qt::DateFormat format) const const
constexpr int line() const Q_DECL_NOEXCEPT
Retrieve the line on which this cursor is situated.
Definition: cursor.h:197
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
virtual Cursor cursorPosition() const =0
Get the view's current cursor position.
const QVector< KTextEditor::Variable > & variables() const
Returns all registered variables.
KateVariableExpansionManager(QObject *parent)
Constructor with parent that takes ownership.
QString toString() const const
KTextEditor::Variable variable(const QString &name) const
Returns the variable called name.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Mar 26 2023 03:55:37 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.