KTextEditor

katevariableexpansionmanager.cpp
1 /* SPDX-License-Identifier: LGPL-2.0-or-later
2 
3  Copyright 2019 Dominik Haumann <[email protected]>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 */
20 
21 #include "katevariableexpansionmanager.h"
22 #include "katevariableexpansionhelpers.h"
23 
24 #include "kateglobal.h"
25 #include <KTextEditor/Editor>
26 
27 #include <KLocalizedString>
28 
29 #include <QAbstractItemModel>
30 #include <QListView>
31 #include <QVBoxLayout>
32 
33 #include <QDate>
34 #include <QDir>
35 #include <QFileInfo>
36 #include <QJSEngine>
37 #include <QTime>
38 #include <QUuid>
39 
40 static void registerVariables(KateVariableExpansionManager &mng)
41 {
43 
44  mng.addVariable(Variable(
45  QStringLiteral("Document:FileBaseName"),
46  i18n("File base name without path and suffix of the current document."),
47  [](const QStringView &, KTextEditor::View *view) {
48  const auto url = view ? view->document()->url().toLocalFile() : QString();
49  return QFileInfo(url).baseName();
50  },
51  false));
52  mng.addVariable(Variable(
53  QStringLiteral("Document:FileExtension"),
54  i18n("File extension of the current document."),
55  [](const QStringView &, KTextEditor::View *view) {
56  const auto url = view ? view->document()->url().toLocalFile() : QString();
57  return QFileInfo(url).completeSuffix();
58  },
59  false));
60  mng.addVariable(Variable(
61  QStringLiteral("Document:FileName"),
62  i18n("File name without path of the current document."),
63  [](const QStringView &, KTextEditor::View *view) {
64  const auto url = view ? view->document()->url().toLocalFile() : QString();
65  return QFileInfo(url).fileName();
66  },
67  false));
68  mng.addVariable(Variable(
69  QStringLiteral("Document:FilePath"),
70  i18n("Full path of the current document including the file name."),
71  [](const QStringView &, KTextEditor::View *view) {
72  const auto url = view ? view->document()->url().toLocalFile() : QString();
73  return QFileInfo(url).absoluteFilePath();
74  },
75  false));
76  mng.addVariable(Variable(
77  QStringLiteral("Document:Text"), i18n("Contents of the current document."), [](const QStringView &, KTextEditor::View *view) { return view ? view->document()->text() : QString(); }, false));
78  mng.addVariable(Variable(
79  QStringLiteral("Document:Path"),
80  i18n("Full path of the current document excluding the file name."),
81  [](const QStringView &, KTextEditor::View *view) {
82  const auto url = view ? view->document()->url().toLocalFile() : QString();
83  return QFileInfo(url).absolutePath();
84  },
85  false));
86  mng.addVariable(Variable(
87  QStringLiteral("Document:NativeFilePath"),
88  i18n("Full document path including 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).absoluteFilePath());
92  },
93  false));
94  mng.addVariable(Variable(
95  QStringLiteral("Document:NativePath"),
96  i18n("Full document path excluding file name, with native path separator (backslash on Windows)."),
97  [](const QStringView &, KTextEditor::View *view) {
98  const auto url = view ? view->document()->url().toLocalFile() : QString();
99  return url.isEmpty() ? QString() : QDir::toNativeSeparators(QFileInfo(url).absolutePath());
100  },
101  false));
102  mng.addVariable(Variable(
103  QStringLiteral("Document:Cursor:Line"),
104  i18n("Line number of the text cursor position in current document (starts with 0)."),
105  [](const QStringView &, KTextEditor::View *view) { return view ? QString::number(view->cursorPosition().line()) : QString(); },
106  false));
107  mng.addVariable(Variable(
108  QStringLiteral("Document:Cursor:Column"),
109  i18n("Column number of the text cursor position in current document (starts with 0)."),
110  [](const QStringView &, KTextEditor::View *view) { return view ? QString::number(view->cursorPosition().column()) : QString(); },
111  false));
112  mng.addVariable(Variable(
113  QStringLiteral("Document:Cursor:XPos"),
114  i18n("X component in global screen coordinates of the cursor position."),
115  [](const QStringView &, KTextEditor::View *view) { return view ? QString::number(view->mapToGlobal(view->cursorPositionCoordinates()).x()) : QString(); },
116  false));
117  mng.addVariable(Variable(
118  QStringLiteral("Document:Cursor:YPos"),
119  i18n("Y component in global screen coordinates of the cursor position."),
120  [](const QStringView &, KTextEditor::View *view) { return view ? QString::number(view->mapToGlobal(view->cursorPositionCoordinates()).y()) : QString(); },
121  false));
122  mng.addVariable(Variable(
123  QStringLiteral("Document:Selection:Text"), i18n("Text selection of the current document."), [](const QStringView &, KTextEditor::View *view) { return (view && view->selection()) ? view->selectionText() : QString(); }, false));
124  mng.addVariable(Variable(
125  QStringLiteral("Document:Selection:StartLine"),
126  i18n("Start line of selected text of the current document."),
127  [](const QStringView &, KTextEditor::View *view) { return (view && view->selection()) ? QString::number(view->selectionRange().start().line()) : QString(); },
128  false));
129  mng.addVariable(Variable(
130  QStringLiteral("Document:Selection:StartColumn"),
131  i18n("Start column of selected text of the current document."),
132  [](const QStringView &, KTextEditor::View *view) { return (view && view->selection()) ? QString::number(view->selectionRange().start().column()) : QString(); },
133  false));
134  mng.addVariable(Variable(
135  QStringLiteral("Document:Selection:EndLine"),
136  i18n("End line of selected text of the current document."),
137  [](const QStringView &, KTextEditor::View *view) { return (view && view->selection()) ? QString::number(view->selectionRange().end().line()) : QString(); },
138  false));
139  mng.addVariable(Variable(
140  QStringLiteral("Document:Selection:EndColumn"),
141  i18n("End column of selected text of the current document."),
142  [](const QStringView &, KTextEditor::View *view) { return (view && view->selection()) ? QString::number(view->selectionRange().end().column()) : QString(); },
143  false));
144  mng.addVariable(Variable(
145  QStringLiteral("Document:RowCount"), i18n("Number of rows of the current document."), [](const QStringView &, KTextEditor::View *view) { return view ? QString::number(view->document()->lines()) : QString(); }, false));
146 
147  mng.addVariable(Variable(
148  QStringLiteral("Date:Locale"), i18n("The current date in current locale format."), [](const QStringView &, KTextEditor::View *) { return QDate::currentDate().toString(Qt::DefaultLocaleShortDate); }, false));
149  mng.addVariable(Variable(
150  QStringLiteral("Date:ISO"), i18n("The current date (ISO)."), [](const QStringView &, KTextEditor::View *) { return QDate::currentDate().toString(Qt::ISODate); }, false));
151  mng.addVariable(Variable(
152  QStringLiteral("Date:"), i18n("The current date (QDate formatstring)."), [](const QStringView &str, KTextEditor::View *) { return QDate::currentDate().toString(str.mid(5)); }, true));
153 
154  mng.addVariable(Variable(
155  QStringLiteral("Time:Locale"), i18n("The current time in current locale format."), [](const QStringView &, KTextEditor::View *) { return QTime::currentTime().toString(Qt::DefaultLocaleShortDate); }, false));
156  mng.addVariable(Variable(
157  QStringLiteral("Time:ISO"), i18n("The current time (ISO)."), [](const QStringView &, KTextEditor::View *) { return QTime::currentTime().toString(Qt::ISODate); }, false));
158  mng.addVariable(Variable(
159  QStringLiteral("Time:"), i18n("The current time (QTime formatstring)."), [](const QStringView &str, KTextEditor::View *) { return QTime::currentTime().toString(str.mid(5)); }, true));
160 
161  mng.addVariable(Variable(
162  QStringLiteral("ENV:"), i18n("Access to environment variables."), [](const QStringView &str, KTextEditor::View *) { return QString::fromLocal8Bit(qgetenv(str.mid(4).toLocal8Bit().constData())); }, true));
163 
164  mng.addVariable(Variable(
165  QStringLiteral("JS:"),
166  i18n("Evaluate simple JavaScript statements."),
167  [](const QStringView &str, KTextEditor::View *) {
168  QJSEngine jsEngine;
169  const QJSValue out = jsEngine.evaluate(str.toString());
170  return out.toString();
171  },
172  true));
173 
174  mng.addVariable(Variable(
175  QStringLiteral("PercentEncoded:"), i18n("Encode text to make it URL compatible."), [](const QStringView &str, KTextEditor::View *) {
177  }, true));
178 
179  mng.addVariable(Variable(
180  QStringLiteral("UUID"), i18n("Generate a new UUID."), [](const QStringView &, KTextEditor::View *) { return QUuid::createUuid().toString(QUuid::WithoutBraces); }, false));
181 }
182 
184  : QObject(parent)
185 {
186  // register default variables for expansion
187  registerVariables(*this);
188 }
189 
191 {
192  if (!var.isValid())
193  return false;
194 
195  // reject duplicates
196  const auto alreadyExists = std::any_of(m_variables.begin(), m_variables.end(), [&var](const KTextEditor::Variable &v) { return var.name() == v.name(); });
197  if (alreadyExists) {
198  return false;
199  }
200 
201  // require a ':' in prefix matches (aka %{JS:1+1})
202  if (var.isPrefixMatch() && !var.name().contains(QLatin1Char(':')))
203  return false;
204 
205  m_variables.push_back(var);
206  return true;
207 }
208 
210 {
211  auto it = std::find_if(m_variables.begin(), m_variables.end(), [&name](const KTextEditor::Variable &var) { return var.name() == name; });
212  if (it != m_variables.end()) {
213  m_variables.erase(it);
214  return true;
215  }
216  return false;
217 }
218 
220 {
221  auto it = std::find_if(m_variables.begin(), m_variables.end(), [&name](const KTextEditor::Variable &var) { return var.name() == name; });
222  if (it != m_variables.end()) {
223  return *it;
224  }
225  return {};
226 }
227 
229 {
230  return m_variables;
231 }
232 
233 bool KateVariableExpansionManager::expandVariable(const QString &name, KTextEditor::View *view, QString &output) const
234 {
235  // first try exact matches
236  auto var = variable(name);
237  if (!var.isValid()) {
238  // try prefix matching
239  const int colonIndex = name.indexOf(QLatin1Char(':'));
240  if (colonIndex >= 0) {
241  var = variable(name.left(colonIndex + 1));
242  }
243  }
244 
245  if (var.isValid()) {
246  output = var.evaluate(name, view);
247  return true;
248  }
249 
250  return false;
251 }
252 
253 QString KateVariableExpansionManager::expandText(const QString &text, KTextEditor::View *view) const
254 {
255  return KateMacroExpander::expandMacro(text, view);
256 }
257 
258 void KateVariableExpansionManager::showDialog(const QVector<QWidget *> &widgets, const QStringList &names) const
259 {
260  // avoid any work in case no widgets or only nullptrs were provided
261  if (widgets.isEmpty() || std::all_of(widgets.cbegin(), widgets.cend(), [](const QWidget *w) { return w == nullptr; })) {
262  return;
263  }
264 
265  // collect variables
267  if (!names.isEmpty()) {
268  for (const auto &name : names) {
269  const auto var = variable(name);
270  if (var.isValid()) {
271  vars.push_back(var);
272  }
273  // else: Not found, silently ignore for now
274  // Maybe raise a qWarning()?
275  }
276  } else {
277  vars = variables();
278  }
279 
280  // if we have no vars at all, do nothing
281  if (vars.isEmpty()) {
282  return;
283  }
284 
285  // find parent dialog (for taskbar sharing, centering, ...)
286  QWidget *parentDialog = nullptr;
287  for (auto widget : widgets) {
288  if (widget) {
289  parentDialog = widget->window();
290  break;
291  }
292  }
293 
294  // show dialog
295  auto dlg = new KateVariableExpansionDialog(parentDialog);
296  for (auto widget : widgets) {
297  if (widget) {
298  dlg->addWidget(widget);
299  }
300  }
301 
302  // add provided variables...
303  for (const auto &var : vars) {
304  if (var.isValid()) {
305  dlg->addVariable(var);
306  }
307  }
308 }
309 
310 // kate: space-indent on; indent-width 4; replace-tabs on;
QString completeSuffix() const const
Helper dialog that shows a non-modal dialog listing all available variables.
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
bool addVariable(const KTextEditor::Variable &variable)
Adds variable to the expansion list view.
Variable for variable expansion.
Definition: variable.h:49
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:33
QString toString(Qt::DateFormat format) const const
virtual int lines() const =0
Get the count of lines of the document.
QString toString(Qt::DateFormat format) const const
const QVector< KTextEditor::Variable > & variables() const
Returns all registered variables.
QWidget * window() const const
QVector::iterator begin()
virtual Document * document() const =0
Get the view&#39;s document, that means the view is a view of the returned document.
virtual QString text() const =0
Get the document content.
QString toString() const const
QVector::iterator erase(QVector::iterator begin, QVector::iterator end)
QPoint mapToGlobal(const QPoint &pos) const const
QVector::const_iterator cend() const const
QByteArray toLocal8Bit() const const
bool isPrefixMatch() const
Returns whether this Variable represents an exact match (false) or a prefix match (true)...
Definition: variable.cpp:38
QString number(int n, int base)
QString fromLocal8Bit(const char *str, int size)
QString fromUtf8(const char *str, int size)
QString fileName() const const
bool isEmpty() const const
QString absoluteFilePath() const const
Manager class for variable expansion.
const char * constData() const const
virtual Cursor cursorPosition() const =0
Get the view&#39;s current cursor position.
constexpr int column() const Q_DECL_NOEXCEPT
Retrieve the column on which this cursor is situated.
Definition: cursor.h:215
QVector::const_iterator cbegin() const const
QJSValue evaluate(const QString &program, const QString &fileName, int lineNumber)
constexpr Cursor start() const Q_DECL_NOEXCEPT
Get the start position of this range.
virtual QString selectionText() const =0
Get the view&#39;s selected text.
QString toLocalFile() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool removeVariable(const QString &name)
Removes variable name.
QString expandMacro(const QString &input, KTextEditor::View *view)
Expands the input text based on the view.
QTime currentTime()
QString i18n(const char *text, const TYPE &arg...)
virtual Range selectionRange() const =0
Get the range occupied by the current selection.
KateVariableExpansionManager(QObject *parent)
Constructor with parent that takes ownership.
QString toString() const const
QString name() const
Returns the name that was provided in the constructor.
Definition: variable.cpp:43
bool isEmpty() const const
constexpr Cursor end() const Q_DECL_NOEXCEPT
Get the end position of this range.
QDate currentDate()
QByteArray toPercentEncoding(const QString &input, const QByteArray &exclude, const QByteArray &include)
constexpr int line() const Q_DECL_NOEXCEPT
Retrieve the line on which this cursor is situated.
Definition: cursor.h:197
QString left(int n) const const
void push_back(const T &value)
KTextEditor::Variable variable(const QString &name) const
Returns the variable called name.
QUrl url() const
virtual QPoint cursorPositionCoordinates() const =0
Get the screen coordinates (x, y) of the cursor position in pixels.
virtual bool selection() const =0
Query the view whether it has selected text, i.e.
A text widget with KXMLGUIClient that represents a Document.
Definition: view.h:153
QString absolutePath() const const
QVector::iterator end()
QString toString() const const
QString baseName() const const
QUuid createUuid()
QStringView mid(qsizetype start) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon May 25 2020 23:10:44 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.