diff --git a/autotests/src/variable_test.h b/autotests/src/variable_test.h --- a/autotests/src/variable_test.h +++ b/autotests/src/variable_test.h @@ -36,6 +36,7 @@ void testExactMatch(); void testPrefixMatch(); void testRecursiveMatch(); + void testBuiltins(); }; #endif // KTEXTEDITOR_VARIABLE_TEST_H diff --git a/autotests/src/variable_test.cpp b/autotests/src/variable_test.cpp --- a/autotests/src/variable_test.cpp +++ b/autotests/src/variable_test.cpp @@ -26,6 +26,7 @@ #include #include +#include using namespace KTextEditor; @@ -161,4 +162,131 @@ delete doc; } +void VariableTest::testBuiltins() +{ + auto editor = KTextEditor::Editor::instance(); + auto doc = editor->createDocument(nullptr); + doc->openUrl(QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/kate-v5.tar.gz"))); + doc->setText(QStringLiteral("get an edge in editing\n:-)")); + auto view = doc->createView(nullptr); + view->setCursorPosition(KTextEditor::Cursor(1, 2)); + view->show(); + + QString out; + + // CurrentDocument:FileBaseName + editor->expandText(QStringLiteral("%{CurrentDocument:FileBaseName}"), view, out); + QCOMPARE(out, QStringLiteral("kate-v5")); + + // CurrentDocument:FileExtension + editor->expandText(QStringLiteral("%{CurrentDocument:FileExtension}"), view, out); + QCOMPARE(out, QStringLiteral("tar.gz")); + + // CurrentDocument:FileName + editor->expandText(QStringLiteral("%{CurrentDocument:FileName}"), view, out); + QCOMPARE(out, QStringLiteral("kate-v5.tar.gz")); + + // CurrentDocument:FilePath + editor->expandText(QStringLiteral("%{CurrentDocument:FilePath}"), view, out); + QCOMPARE(out, QFileInfo(view->document()->url().toLocalFile()).absoluteFilePath()); + + // CurrentDocument:Text + editor->expandText(QStringLiteral("%{CurrentDocument:Text}"), view, out); + QCOMPARE(out, QStringLiteral("get an edge in editing\n:-)")); + + // CurrentDocument:Path + editor->expandText(QStringLiteral("%{CurrentDocument:Path}"), view, out); + QCOMPARE(out, QFileInfo(doc->url().toLocalFile()).absolutePath()); + + // CurrentDocument:NativeFilePath + editor->expandText(QStringLiteral("%{CurrentDocument:NativeFilePath}"), view, out); + QCOMPARE(out, QDir::toNativeSeparators(QFileInfo(doc->url().toLocalFile()).absoluteFilePath())); + + // CurrentDocument:NativePath + editor->expandText(QStringLiteral("%{CurrentDocument:NativePath}"), view, out); + QCOMPARE(out, QDir::toNativeSeparators(QFileInfo(doc->url().toLocalFile()).absolutePath())); + + // CurrentDocument:NativePath + editor->expandText(QStringLiteral("%{CurrentDocument:NativePath}"), view, out); + QCOMPARE(out, QDir::toNativeSeparators(QFileInfo(doc->url().toLocalFile()).absolutePath())); + + // CurrentDocument:Cursor:Line + editor->expandText(QStringLiteral("%{CurrentDocument:Cursor:Line}"), view, out); + QCOMPARE(out, QStringLiteral("1")); + + // CurrentDocument:Cursor:Column + editor->expandText(QStringLiteral("%{CurrentDocument:Cursor:Column}"), view, out); + QCOMPARE(out, QStringLiteral("2")); + + // CurrentDocument:Cursor:XPos + editor->expandText(QStringLiteral("%{CurrentDocument:Cursor:XPos}"), view, out); + QVERIFY(out.toInt() > 0); + + // CurrentDocument:Cursor:YPos + editor->expandText(QStringLiteral("%{CurrentDocument:Cursor:YPos}"), view, out); + QVERIFY(out.toInt() > 0); + + + view->setSelection(KTextEditor::Range(1, 0, 1, 3)); + // CurrentDocument:Selection:Text + editor->expandText(QStringLiteral("%{CurrentDocument:Selection:Text}"), view, out); + QCOMPARE(out, QStringLiteral(":-)")); + + // CurrentDocument:Selection:StartLine + editor->expandText(QStringLiteral("%{CurrentDocument:Selection:StartLine}"), view, out); + QCOMPARE(out, QStringLiteral("1")); + + // CurrentDocument:Selection:StartColumn + editor->expandText(QStringLiteral("%{CurrentDocument:Selection:StartColumn}"), view, out); + QCOMPARE(out, QStringLiteral("0")); + + // CurrentDocument:Selection:EndLine + editor->expandText(QStringLiteral("%{CurrentDocument:Selection:EndLine}"), view, out); + QCOMPARE(out, QStringLiteral("1")); + + // CurrentDocument:Selection:EndColumn + editor->expandText(QStringLiteral("%{CurrentDocument:Selection:EndColumn}"), view, out); + QCOMPARE(out, QStringLiteral("3")); + + // CurrentDocument:RowCount + editor->expandText(QStringLiteral("%{CurrentDocument:RowCount}"), view, out); + QCOMPARE(out, QStringLiteral("2")); + + // Date:Locale + editor->expandText(QStringLiteral("%{Date:Locale}"), view, out); + QVERIFY(!out.isEmpty()); + + // Date:ISO + editor->expandText(QStringLiteral("%{Date:ISO}"), view, out); + QVERIFY(!out.isEmpty()); + + // Date:yyyy-MM-dd + editor->expandText(QStringLiteral("%{Date:yyyy-MM-dd}"), view, out); + QVERIFY(QDate::fromString(out, QStringLiteral("yyyy-MM-dd")).isValid()); + + // Time:Locale + editor->expandText(QStringLiteral("%{Time:Locale}"), view, out); + QVERIFY(!out.isEmpty()); + + // Time:ISO + editor->expandText(QStringLiteral("%{Time:ISO}"), view, out); + QVERIFY(!out.isEmpty()); + + // Time:hh-mm-ss + editor->expandText(QStringLiteral("%{Time:hh-mm-ss}"), view, out); + QVERIFY(QTime::fromString(out, QStringLiteral("hh-mm-ss")).isValid()); + + // ENV:HOME + editor->expandText(QStringLiteral("%{ENV:HOME}"), view, out); + QCOMPARE(out, QDir::homePath()); + + // JS: + editor->expandText(QStringLiteral("%{JS:3 + %{JS:2 + 1}}"), view, out); + QCOMPARE(out, QStringLiteral("6")); + + // UUID + editor->expandText(QStringLiteral("%{UUID}"), view, out); + QCOMPARE(out.count(QLatin1Char('-')), 4); +} + // kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/src/utils/kateglobal.cpp b/src/utils/kateglobal.cpp --- a/src/utils/kateglobal.cpp +++ b/src/utils/kateglobal.cpp @@ -56,13 +56,129 @@ #include #include #include +#include +#include +#include #include #include +#include #if LIBGIT2_FOUND #include #endif +namespace { + +void registerVariables(KTextEditor::Editor * editor) +{ + editor->registerVariableMatch(QStringLiteral("CurrentDocument:FileBaseName"), i18n("Current document: File base name without path and suffix."), [](const QStringView&, KTextEditor::View* view) { + const auto url = view ? view->document()->url().toLocalFile() : QString(); + return QFileInfo(url).baseName(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:FileExtension"), i18n("Current document: File extension."), [](const QStringView&, KTextEditor::View* view) { + const auto url = view ? view->document()->url().toLocalFile() : QString(); + return QFileInfo(url).completeSuffix(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:FileName"), i18n("Current document: File name without path."), [](const QStringView&, KTextEditor::View* view) { + const auto url = view ? view->document()->url().toLocalFile() : QString(); + return QFileInfo(url).fileName(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:FilePath"), i18n("Current document: Full path including file name."), [](const QStringView&, KTextEditor::View* view) { + const auto url = view ? view->document()->url().toLocalFile() : QString(); + return QFileInfo(url).absoluteFilePath(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:Text"), i18n("Current document: Contents of entire file."), [](const QStringView&, KTextEditor::View* view) { + return view ? view->document()->text() : QString(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:Path"), i18n("Current document: Full path excluding file name."), [](const QStringView&, KTextEditor::View* view) { + const auto url = view ? view->document()->url().toLocalFile() : QString(); + return QFileInfo(url).absolutePath(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:NativeFilePath"), i18n("Current document: Full path including file name, with native path separator (backslash on Windows)."), [](const QStringView&, KTextEditor::View* view) { + const auto url = view ? view->document()->url().toLocalFile() : QString(); + return url.isEmpty() ? QString() : QDir::toNativeSeparators(QFileInfo(url).absoluteFilePath()); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:NativePath"), i18n("Current document: Full path excluding file name, with native path separator (backslash on Windows)."), [](const QStringView&, KTextEditor::View* view) { + const auto url = view ? view->document()->url().toLocalFile() : QString(); + return url.isEmpty() ? QString() : QDir::toNativeSeparators(QFileInfo(url).absolutePath()); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:Cursor:Line"), i18n("Line number of the text cursor position in current document (starts with 0)."), [](const QStringView&, KTextEditor::View* view) { + return view ? QString::number(view->cursorPosition().line()) : QString(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:Cursor:Column"), i18n("Column number of the text cursor position in current document (starts with 0)."), [](const QStringView&, KTextEditor::View* view) { + return view ? QString::number(view->cursorPosition().column()) : QString(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:Cursor:XPos"), i18n("X component in global screen coordinates of the cursor position."), [](const QStringView&, KTextEditor::View* view) { + return view ? QString::number(view->mapToGlobal(view->cursorPositionCoordinates()).x()) : QString(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:Cursor:YPos"), i18n("Y component in global screen coordinates of the cursor position."), [](const QStringView&, KTextEditor::View* view) { + return view ? QString::number(view->mapToGlobal(view->cursorPositionCoordinates()).y()) : QString(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:Selection:Text"), i18n("Selection of current document."), [](const QStringView&, KTextEditor::View* view) { + return (view && view->selection()) ? view->selectionText() : QString(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:Selection:StartLine"), i18n("Start line of selected text of current document."), [](const QStringView&, KTextEditor::View* view) { + return (view && view->selection()) ? QString::number(view->selectionRange().start().line()) : QString(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:Selection:StartColumn"), i18n("Start column of selected text of current document."), [](const QStringView&, KTextEditor::View* view) { + return (view && view->selection()) ? QString::number(view->selectionRange().start().column()) : QString(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:Selection:EndLine"), i18n("End line of selected text of current document."), [](const QStringView&, KTextEditor::View* view) { + return (view && view->selection()) ? QString::number(view->selectionRange().end().line()) : QString(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:Selection:EndColumn"), i18n("End column of selected text of current document."), [](const QStringView&, KTextEditor::View* view) { + return (view && view->selection()) ? QString::number(view->selectionRange().end().column()) : QString(); + }); + editor->registerVariableMatch(QStringLiteral("CurrentDocument:RowCount"), i18n("Number of rows of current document."), [](const QStringView&, KTextEditor::View* view) { + return view ? QString::number(view->document()->lines()) : QString(); + }); + + editor->registerVariableMatch(QStringLiteral("Date:Locale"), i18n("The current date in current locale format."), [](const QStringView&, KTextEditor::View*) { + return QDate::currentDate().toString(Qt::DefaultLocaleShortDate); + }); + editor->registerVariableMatch(QStringLiteral("Date:ISO"), i18n("The current date (ISO)."), [](const QStringView&, KTextEditor::View*) { + return QDate::currentDate().toString(Qt::ISODate); + }); + editor->registerVariablePrefix(QStringLiteral("Date:"), i18n("The current date (QDate formatstring)."), [](const QStringView& str, KTextEditor::View*) { + return QDate::currentDate().toString(str.right(str.length() - 5)); + }); + + editor->registerVariableMatch(QStringLiteral("Time:Locale"), i18n("The current time in current locale format."), [](const QStringView&, KTextEditor::View*) { + return QTime::currentTime().toString(Qt::DefaultLocaleShortDate); + }); + editor->registerVariableMatch(QStringLiteral("Time:ISO"), i18n("The current time (ISO)."), [](const QStringView&, KTextEditor::View*) { + return QTime::currentTime().toString(Qt::ISODate); + }); + editor->registerVariablePrefix(QStringLiteral("Time:"), i18n("The current time (QTime formatstring)."), [](const QStringView& str, KTextEditor::View*) { + return QTime::currentTime().toString(str.right(str.length() - 5)); + }); + + editor->registerVariablePrefix(QStringLiteral("ENV:"), i18n("Access environment variables."), [](const QStringView& str, KTextEditor::View*) { + return QString::fromLocal8Bit(qgetenv(str.right(str.size() - 4).toLocal8Bit().constData())); + }); + + editor->registerVariablePrefix(QStringLiteral("JS:"), i18n("Evaluate simple JavaScript statements. The statements may not contain '{' nor '}' characters."), [](const QStringView& str, KTextEditor::View*) { + QJSEngine jsEngine; + const QJSValue out = jsEngine.evaluate(str.toString()); + return out.toString(); + }); + + editor->registerVariableMatch(QStringLiteral("UUID"), i18n("Generate a new UUID."), [](const QStringView&, KTextEditor::View*) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) + return QUuid::createUuid().toString(QUuid::WithoutBraces); +#else + // LEGACY + QString uuid = QUuid::createUuid().toString(); + if (uuid.startsWith(QLatin1Char('{'))) + uuid.remove(0, 1); + if (uuid.endsWith(QLatin1Char('}'))) + uuid.chop(1); + return uuid; +#endif + }); +} +} + //BEGIN unit test mode static bool kateUnitTestMode = false; @@ -226,6 +342,9 @@ // tap to QApplication object for color palette changes qApp->installEventFilter(this); + + // register default variables for expansion + registerVariables(this); } KTextEditor::EditorPrivate::~EditorPrivate()