diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5088a065..28d35ff8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,380 +1,381 @@ # handle data files, .desktop & .cmake add_subdirectory(data) # jscripts for the part add_subdirectory( script/data ) # set right defines for libgit2 usage if(LIBGIT2_FOUND) SET (CMAKE_REQUIRED_LIBRARIES LibGit2::LibGit2) set (KTEXTEDITOR_OPTIONAL_LIBS ${KTEXTEDITOR_OPTIONAL_LIBS} LibGit2::LibGit2) endif() if(EditorConfig_FOUND) SET (CMAKE_REQUIRED_LIBRARIES ${EditorConfig_LIBRARIES}) set (KTEXTEDITOR_OPTIONAL_LIBS ${KTEXTEDITOR_OPTIONAL_LIBS} ${EditorConfig_LIBRARIES}) endif() # handle include files, both normal ones and generated ones add_subdirectory(include) # includes include_directories( # for config.h ${CMAKE_BINARY_DIR} # for generated ktexteditor headers ${CMAKE_CURRENT_BINARY_DIR}/include # for normal sources ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include/ktexteditor ${CMAKE_CURRENT_SOURCE_DIR}/buffer ${CMAKE_CURRENT_SOURCE_DIR}/completion ${CMAKE_CURRENT_SOURCE_DIR}/dialogs ${CMAKE_CURRENT_SOURCE_DIR}/document ${CMAKE_CURRENT_SOURCE_DIR}/script ${CMAKE_CURRENT_SOURCE_DIR}/mode ${CMAKE_CURRENT_SOURCE_DIR}/render ${CMAKE_CURRENT_SOURCE_DIR}/search ${CMAKE_CURRENT_SOURCE_DIR}/syntax ${CMAKE_CURRENT_SOURCE_DIR}/schema ${CMAKE_CURRENT_SOURCE_DIR}/undo ${CMAKE_CURRENT_SOURCE_DIR}/utils ${CMAKE_CURRENT_SOURCE_DIR}/inputmode ${CMAKE_CURRENT_SOURCE_DIR}/view ${CMAKE_CURRENT_SOURCE_DIR}/swapfile ${CMAKE_CURRENT_SOURCE_DIR}/variableeditor) # KTextEditor interface sources set(ktexteditor_LIB_SRCS # text buffer & buffer helpers buffer/katesecuretextbuffer.cpp buffer/katetextbuffer.cpp buffer/katetextblock.cpp buffer/katetextline.cpp buffer/katetextcursor.cpp buffer/katetextrange.cpp buffer/katetexthistory.cpp buffer/katetextfolding.cpp # completion (widget, model, delegate, ...) completion/katecompletionwidget.cpp completion/katecompletionmodel.cpp completion/katecompletiontree.cpp completion/katecompletionconfig.cpp completion/kateargumenthinttree.cpp completion/kateargumenthintmodel.cpp completion/katecompletiondelegate.cpp completion/expandingtree/expandingwidgetmodel.cpp completion/expandingtree/expandingdelegate.cpp completion/expandingtree/expandingtree.cpp # simple internal word completion completion/katewordcompletion.cpp # internal syntax-file based keyword completion completion/katekeywordcompletion.cpp # dialogs dialogs/kateconfigpage.cpp dialogs/katedialogs.cpp dialogs/kateconfigpage.cpp # document (THE document, buffer, lines/cursors/..., CORE STUFF) document/katedocument.cpp document/katebuffer.cpp # undo undo/kateundo.cpp undo/katemodifiedundo.cpp undo/kateundomanager.cpp # scripting script/katescript.cpp script/kateindentscript.cpp script/katecommandlinescript.cpp script/katescriptmanager.cpp script/katescriptaction.cpp # scripting wrappers script/katescripteditor.cpp script/katescriptdocument.cpp script/katescriptview.cpp script/katescripthelpers.cpp # mode (modemanager and co) mode/katemodemanager.cpp mode/katemodeconfigpage.cpp mode/katemodemenu.cpp mode/katewildcardmatcher.cpp mode/katemodemenulist.cpp # modeline variable editor variableeditor/variablelineedit.cpp variableeditor/variablelistview.cpp variableeditor/variableeditor.cpp variableeditor/variableitem.cpp variableeditor/katehelpbutton.cpp # printing classes printing/kateprinter.cpp printing/printpainter.cpp printing/printconfigwidgets.cpp # rendering stuff (katerenderer and helpers) render/katerenderer.cpp render/katerenderrange.cpp render/katelayoutcache.cpp render/katetextlayout.cpp render/katelinelayout.cpp # search stuff search/kateregexp.cpp search/kateplaintextsearch.cpp search/kateregexpsearch.cpp search/katematch.cpp search/katesearchbar.cpp # syntax related stuff (highlighting, xml file parsing, ...) syntax/katesyntaxmanager.cpp syntax/katehighlight.cpp syntax/katehighlightmenu.cpp syntax/katehighlightingcmds.cpp # view stuff (THE view and its helpers) view/kateview.cpp view/kateviewinternal.cpp view/kateviewhelpers.cpp view/kateannotationitemdelegate.cpp view/katemessagewidget.cpp view/katefadeeffect.cpp view/kateanimation.cpp view/katetextanimation.cpp view/katetextpreview.cpp view/katestatusbar.cpp view/wordcounter.cpp # spell checking spellcheck/prefixstore.h spellcheck/prefixstore.cpp spellcheck/ontheflycheck.h spellcheck/ontheflycheck.cpp spellcheck/spellcheck.h spellcheck/spellcheck.cpp spellcheck/spellcheckdialog.h spellcheck/spellcheckdialog.cpp spellcheck/spellcheckbar.cpp spellcheck/spellingmenu.h spellcheck/spellingmenu.cpp # generic stuff, unsorted... utils/katecmds.cpp utils/kateconfig.cpp utils/katebookmarks.cpp utils/kateautoindent.cpp utils/katetemplatehandler.cpp utils/kateglobal.cpp utils/katecmd.cpp utils/ktexteditor.cpp utils/document.cpp utils/range.cpp utils/documentcursor.cpp utils/attribute.cpp utils/codecompletioninterface.cpp utils/codecompletionmodel.cpp utils/codecompletionmodelcontrollerinterface.cpp utils/configinterface.cpp utils/movinginterface.cpp utils/movingcursor.cpp utils/movingrange.cpp utils/movingrangefeedback.cpp utils/messageinterface.cpp utils/application.cpp utils/mainwindow.cpp utils/katedefaultcolors.cpp utils/katecommandrangeexpressionparser.cpp utils/katesedcmd.cpp -utils/katemacroexpander.cpp utils/variable.cpp +utils/katevariableexpansionmanager.cpp +utils/katevariableexpansionhelpers.cpp # schema schema/kateschema.cpp schema/kateschemaconfig.cpp schema/katestyletreewidget.cpp schema/katecolortreewidget.cpp schema/katecategorydrawer.cpp # swapfile swapfile/kateswapdiffcreator.cpp swapfile/kateswapfile.cpp # export as HTML export/exporter.cpp export/htmlexporter.cpp # input modes inputmode/kateabstractinputmode.cpp inputmode/kateabstractinputmodefactory.cpp inputmode/katenormalinputmode.cpp inputmode/katenormalinputmodefactory.cpp ) # optionally compile with EditorConfig support if(EditorConfig_FOUND) set(ktexteditor_LIB_SRCS ${ktexteditor_LIB_SRCS} document/editorconfig.cpp) endif() ecm_qt_declare_logging_category(ktexteditor_LIB_SRCS HEADER katepartdebug.h IDENTIFIER LOG_KTE CATEGORY_NAME org.kde.ktexteditor DEFAULT_SEVERITY Warning) ki18n_wrap_ui(ktexteditor_LIB_SRCS dialogs/textareaappearanceconfigwidget.ui dialogs/bordersappearanceconfigwidget.ui dialogs/commandmenuconfigwidget.ui dialogs/commandmenueditwidget.ui dialogs/completionconfigtab.ui dialogs/navigationconfigwidget.ui dialogs/editconfigwidget.ui dialogs/filetypeconfigwidget.ui dialogs/indentationconfigwidget.ui dialogs/opensaveconfigwidget.ui dialogs/opensaveconfigadvwidget.ui dialogs/completionconfigwidget.ui search/searchbarincremental.ui search/searchbarpower.ui spellcheck/spellcheckbar.ui dialogs/spellcheckconfigwidget.ui schema/howtoimportschema.ui ) # add the resource files, the one with mascot + ui file and the generated ones qt5_add_resources( ktexteditor_LIB_SRCS data/ktexteditor.qrc "${CMAKE_CURRENT_BINARY_DIR}/script/data/script.qrc") if (BUILD_VIMODE) ki18n_wrap_ui(ktexteditor_LIB_SRCS vimode/config/configwidget.ui) set(ktexteditor_LIB_SRCS ${ktexteditor_LIB_SRCS} inputmode/kateviinputmode.cpp inputmode/kateviinputmodefactory.cpp # vi input mode vimode/config/configtab.cpp vimode/modes/insertvimode.cpp vimode/modes/modebase.cpp vimode/modes/normalvimode.cpp vimode/modes/replacevimode.cpp vimode/modes/visualvimode.cpp vimode/appcommands.cpp vimode/cmds.cpp vimode/inputmodemanager.cpp vimode/command.cpp vimode/motion.cpp vimode/range.cpp vimode/keyparser.cpp vimode/globalstate.cpp vimode/emulatedcommandbar/emulatedcommandbar.cpp vimode/emulatedcommandbar/matchhighlighter.cpp vimode/emulatedcommandbar/completer.cpp vimode/emulatedcommandbar/activemode.cpp vimode/emulatedcommandbar/interactivesedreplacemode.cpp vimode/emulatedcommandbar/searchmode.cpp vimode/emulatedcommandbar/commandmode.cpp vimode/commandrangeexpressionparser.cpp vimode/keymapper.cpp vimode/marks.cpp vimode/jumps.cpp vimode/history.cpp vimode/macros.cpp vimode/mappings.cpp vimode/registers.cpp vimode/searcher.cpp vimode/completion.cpp vimode/completionrecorder.cpp vimode/completionreplayer.cpp vimode/macrorecorder.cpp vimode/lastchangerecorder.cpp ) endif() add_library(KF5TextEditor ${ktexteditor_LIB_SRCS} ${KTEXTEDITOR_PUBLIC_HEADERS}) generate_export_header(KF5TextEditor BASE_NAME KTextEditor) add_library(KF5::TextEditor ALIAS KF5TextEditor) target_include_directories(KF5TextEditor INTERFACE "$") # API is more or less KParts++, other stuff is used only internally target_link_libraries(KF5TextEditor PUBLIC KF5::Parts PRIVATE Qt5::Qml Qt5::PrintSupport KF5::I18n KF5::Archive KF5::GuiAddons KF5::IconThemes KF5::ItemViews KF5::SonnetCore KF5::SyntaxHighlighting KF5::AuthCore ${KTEXTEDITOR_OPTIONAL_LIBS} ) set_target_properties(KF5TextEditor PROPERTIES VERSION ${KTEXTEDITOR_VERSION_STRING} SOVERSION ${KTEXTEDITOR_SOVERSION} EXPORT_NAME "TextEditor" ) install(TARGETS KF5TextEditor EXPORT KF5TextEditorTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ktexteditor_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KTextEditor COMPONENT Devel ) if(BUILD_QCH) ecm_add_qch( KF5TextEditor_QCH NAME KTextEditor BASE_NAME KF5TextEditor VERSION ${KF5_VERSION} ORG_DOMAIN org.kde SOURCES # using only public headers, to cover only public API ${KTEXTEDITOR_PUBLIC_HEADERS} "${CMAKE_SOURCE_DIR}/docs/apidocs-groups.dox" "${CMAKE_SOURCE_DIR}/docs/coding-guidelines.dox" "${CMAKE_SOURCE_DIR}/docs/design.dox" "${CMAKE_SOURCE_DIR}/docs/porting.dox" MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md" IMAGE_DIRS "${CMAKE_SOURCE_DIR}/docs/pics" LINK_QCHS KF5Parts_QCH BLANK_MACROS KTEXTEDITOR_EXPORT KTEXTEDITOR_DEPRECATED KTEXTEDITOR_DEPRECATED_EXPORT TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} COMPONENT Devel ) endif() include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KTextEditor LIB_NAME KF5TextEditor DEPS "KParts" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KTextEditor) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) add_executable(kauth_ktexteditor_helper buffer/katesecuretextbuffer.cpp) target_link_libraries(kauth_ktexteditor_helper KF5::AuthCore ) install(TARGETS kauth_ktexteditor_helper DESTINATION ${KAUTH_HELPER_INSTALL_DIR} ) kauth_install_helper_files(kauth_ktexteditor_helper org.kde.ktexteditor.katetextbuffer root) kauth_install_actions(org.kde.ktexteditor.katetextbuffer buffer/org.kde.ktexteditor.katetextbuffer.actions) # add part add_subdirectory(part) diff --git a/src/include/ktexteditor/editor.h b/src/include/ktexteditor/editor.h index da2cab51..4c235d10 100644 --- a/src/include/ktexteditor/editor.h +++ b/src/include/ktexteditor/editor.h @@ -1,333 +1,352 @@ /* This file is part of the KDE libraries * Copyright (C) 2005-2014 Christoph Cullmann * Copyright (C) 2005-2014 Dominik Haumann (dhaumann@kde.org) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA.*/ #ifndef KTEXTEDITOR_EDITOR_H #define KTEXTEDITOR_EDITOR_H #include #include +#include class KAboutData; class KConfig; /** * The KTextEditor namespace contains all the public API that is required * to use the KTextEditor component. Using the KTextEditor interfaces is * described in the article \ref index. * * @warning All classes that are \e not part of the KTextEditor namespace * are internal and subject to change. We mean it! */ namespace KTextEditor { class Application; class Command; class Document; class View; class EditorPrivate; class ConfigPage; /** * \brief Accessor interface for the KTextEditor framework. * * Topics: * - \ref editor_intro * - \ref editor_config * - \ref editor_commands * * \section editor_intro Introduction * * The Editor part can either be accessed through the static accessor Editor::instance() * or through the KParts component model (see \ref kte_design_part). * The Editor singleton provides general information and configuration methods * for the Editor, for example KAboutData by using aboutData(). * * The Editor has a list of all opened documents. Get this list with documents(). * To create a new Document call createDocument(). The signal documentCreated() * is emitted whenever the Editor created a new document. * * \section editor_config Editor Configuration * * The config dialog can be shown with configDialog(). * Instead of using the config dialog, the config pages can also be embedded * into the application's config dialog. To do this, configPages() returns the * number of config pages that exist and configPage() returns the requested * page. The configuration are saved automatically by the Editor. * * \note It is recommended to embed the config pages into the main application's * config dialog instead of using a separate config dialog, if the config * dialog does not look cluttered then. This way, all settings are grouped * together in one place. * * \section editor_commands Command Line Commands * * With Commands it is possible to add new commands to the command line. * These Command%s then are added to all document View%s. * Common use cases include commands like \e find or setting document variables. * The list of all registered commands can be obtained either through commandList() * or through commands(). Further, a specific command can be obtained through * queryCommand(). For further information, read the Command API documentation. * * \see KTextEditor::Document, KTextEditor::ConfigPage, KTextEditor::Command * \author Christoph Cullmann \ */ class KTEXTEDITOR_EXPORT Editor : public QObject { Q_OBJECT protected: /** * Constructor. * * Create the Editor object and pass it the internal * implementation to store a d-pointer. * * @param impl d-pointer to use */ Editor(EditorPrivate *impl); /** * Virtual destructor. */ virtual ~Editor(); public: /** * Accessor to get the Editor instance. * * @note This object will stay alive until QCoreApplication terminates. * You shall not delete it yourself. * There is only ONE Editor instance of this per process. * * \return Editor controller, after initial construction, will * live until QCoreApplication is terminating. */ static Editor *instance(); public: /** * Set the global application object. * This will allow the editor component to access * the hosting application. * @param application application object * if the argument is a nullptr, this will reset the application back to a dummy interface */ virtual void setApplication(KTextEditor::Application *application) = 0; /** * Current hosting application, if any set. * @return current application object or a dummy interface that allows you to call the functions * will never return a nullptr */ virtual KTextEditor::Application *application() const = 0; /* * Methods to create and manage the documents. */ public: /** * Create a new document object with \p parent. * * For each created document, the signal documentCreated() is emitted. * * \param parent parent object * \return new KTextEditor::Document object * \see documents(), documentCreated() */ virtual Document *createDocument(QObject *parent) = 0; /** * Get a list of all documents of this editor. * \return list of all existing documents * \see createDocument() */ virtual QList documents() = 0; Q_SIGNALS: /** * The \p editor emits this signal whenever a \p document was successfully * created. * \param editor pointer to the Editor singleton which created the new document * \param document the newly created document instance * \see createDocument() */ void documentCreated(KTextEditor::Editor *editor, KTextEditor::Document *document); /* * General Information about this editor. */ public: /** * Get the about data of this Editor part. * \return about data */ virtual const KAboutData &aboutData() const = 0; /** * Get the current default encoding for this Editor part. * \return default encoding */ QString defaultEncoding() const; /* * Configuration management. */ public: /** * Show the editor's config dialog, changes will be applied to the * editor and the configuration changes are saved. * * \note Instead of using the config dialog, the config pages can be * embedded into your own config dialog by using configPages() and * configPage(). * \param parent parent widget */ virtual void configDialog(QWidget *parent) = 0; /** * Get the number of available config pages. * If a number < 1 is returned, it does not support config pages. * \return number of config pages * \see configPage() */ virtual int configPages() const = 0; /** * Get the config page with the \p number, config pages from 0 to * configPages()-1 are available if configPages() > 0. * Configuration changes done over this widget are automatically * saved. * \param number index of config page * \param parent parent widget for config page * \return created config page or NULL, if the number is out of bounds * \see configPages() */ virtual ConfigPage *configPage(int number, QWidget *parent) = 0; public: /** * Query for the command \p cmd. * If the command \p cmd does not exist the return value is NULL. * * \param cmd name of command to query for * \return the found command or NULL if no such command exists */ virtual Command *queryCommand(const QString &cmd) const = 0; /** * Get a list of all registered commands. * \return list of all commands * \see queryCommand(), commandList() */ virtual QList commands() const = 0; /** * Get a list of available command line strings. * \return command line strings * \see commands() */ virtual QStringList commandList() const = 0; public: /** * Function that is called to expand a variable in @p text. */ using ExpandFunction = QString (*)(const QStringView& text, KTextEditor::View* view); /** * Registers a variable called @p name for exact matches. * For instance, a variable called "CurrentDocument:Path" could be * registered which then expands to the path the current document. * * @return true on success, false if the variable could not be registered, * e.g. because it already was registered previously. * * @since 5.57 */ bool registerVariableMatch(const QString& name, const QString& description, ExpandFunction expansionFunc); /** * Registers a variable for arbitrary text that matches the specified * prefix. For instance, a variable called "ENV:" could be registered * which then expands arbitrary environment variables, e.g. ENV:HOME * would expand to the user's home directory. * * @note A colon ':' is used as separator for the prefix and the text * after the colon that should be evaluated. * * @return true on success, false if a prefix could not be registered, * e.g. because it already was registered previously. * * @since 5.57 */ bool registerVariablePrefix(const QString& prefix, const QString& description, ExpandFunction expansionFunc); /** * Unregisters a variable that was previously registered with * registerVariableMatch(). * * @return true if the variable was successfully unregistered, and * false if the variable did not exist. * * @since 5.57 */ bool unregisterVariableMatch(const QString& variable); + // TODO KF6: merge "unregisterVariableMatch()" and "unregisterVariablePrefix()" into + // a single function "unregisterVariable(const QString& name)". /** * Unregisters a prefix of variable that was previously registered with * registerVariableMatch(). * * @return true if the variable was successfully unregistered, and * false if the variable did not exist. * * @since 5.57 */ bool unregisterVariablePrefix(const QString& variable); /** * Expands a single @p variable, writing the expanded value to @p output. * * @return true on success, otherwise false. * * @since 5.57 */ bool expandVariable(const QString& variable, KTextEditor::View* view, QString& output) const; + // TODO KF6: turn expandText into: QString expandText(text, view) to avoid output argument /** * Expands arbitrary @p text that may contain arbitrary many variables. * On success, the expanded text is written to @p output. * * @since 5.57 */ void expandText(const QString& text, KTextEditor::View* view, QString& output) const; + /** + * Adds a QAction to the widget in @p widgets that whenever focus is + * gained. When the action is invoked, a non-modal dialog is shown that + * lists all @p variables. If @p variables is non-empty, then only the + * variables in @p variables are listed. + * + * The supported QWidgets in the @p widgets argument currently are: + * - QLineEdit + * - QTextEdit + * + * @since 5.63 + */ + void addVariableExpansion(const QVector& widgets, + const QStringList& variables = QStringList()) const; + private: /** * private d-pointer, pointing to the internal implementation */ EditorPrivate *const d; }; } #endif diff --git a/src/utils/kateglobal.cpp b/src/utils/kateglobal.cpp index 62b709e7..f2670f74 100644 --- a/src/utils/kateglobal.cpp +++ b/src/utils/kateglobal.cpp @@ -1,649 +1,555 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2001-2010 Christoph Cullmann * Copyright (C) 2009 Erlend Hamberg * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kateglobal.h" #include "config.h" #include #include "katedocument.h" #include "kateview.h" #include "katerenderer.h" #include "katecmd.h" #include "katecmds.h" #include "katesedcmd.h" #include "katehighlightingcmds.h" #include "katemodemanager.h" #include "kateschema.h" #include "kateschemaconfig.h" #include "kateconfig.h" #include "katescriptmanager.h" #include "katebuffer.h" #include "katewordcompletion.h" #include "katekeywordcompletion.h" #include "spellcheck/spellcheck.h" #include "katepartdebug.h" #include "katedefaultcolors.h" +#include "katevariableexpansionmanager.h" #include "katenormalinputmodefactory.h" #include "kateviinputmodefactory.h" #include #include #include #include #include #include #include #include #include -#include -#include -#include #include #include -#include #if LIBGIT2_FOUND #include #endif -namespace { - -void registerVariables(KTextEditor::Editor * editor) -{ - editor->registerVariableMatch(QStringLiteral("Document:FileBaseName"), i18n("File base name without path and suffix of the current document."), [](const QStringView&, KTextEditor::View* view) { - const auto url = view ? view->document()->url().toLocalFile() : QString(); - return QFileInfo(url).baseName(); - }); - editor->registerVariableMatch(QStringLiteral("Document:FileExtension"), i18n("File extension of the current document."), [](const QStringView&, KTextEditor::View* view) { - const auto url = view ? view->document()->url().toLocalFile() : QString(); - return QFileInfo(url).completeSuffix(); - }); - editor->registerVariableMatch(QStringLiteral("Document:FileName"), i18n("File name without path of the current document."), [](const QStringView&, KTextEditor::View* view) { - const auto url = view ? view->document()->url().toLocalFile() : QString(); - return QFileInfo(url).fileName(); - }); - editor->registerVariableMatch(QStringLiteral("Document:FilePath"), i18n("Full path of the current document including the file name."), [](const QStringView&, KTextEditor::View* view) { - const auto url = view ? view->document()->url().toLocalFile() : QString(); - return QFileInfo(url).absoluteFilePath(); - }); - editor->registerVariableMatch(QStringLiteral("Document:Text"), i18n("Contents of the current document."), [](const QStringView&, KTextEditor::View* view) { - return view ? view->document()->text() : QString(); - }); - editor->registerVariableMatch(QStringLiteral("Document:Path"), i18n("Full path of the current document excluding the file name."), [](const QStringView&, KTextEditor::View* view) { - const auto url = view ? view->document()->url().toLocalFile() : QString(); - return QFileInfo(url).absolutePath(); - }); - editor->registerVariableMatch(QStringLiteral("Document:NativeFilePath"), i18n("Full document 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("Document:NativePath"), i18n("Full document 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("Document: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("Document: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("Document: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("Document: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("Document:Selection:Text"), i18n("Text selection of the current document."), [](const QStringView&, KTextEditor::View* view) { - return (view && view->selection()) ? view->selectionText() : QString(); - }); - editor->registerVariableMatch(QStringLiteral("Document:Selection:StartLine"), i18n("Start line of selected text of the current document."), [](const QStringView&, KTextEditor::View* view) { - return (view && view->selection()) ? QString::number(view->selectionRange().start().line()) : QString(); - }); - editor->registerVariableMatch(QStringLiteral("Document:Selection:StartColumn"), i18n("Start column of selected text of the current document."), [](const QStringView&, KTextEditor::View* view) { - return (view && view->selection()) ? QString::number(view->selectionRange().start().column()) : QString(); - }); - editor->registerVariableMatch(QStringLiteral("Document:Selection:EndLine"), i18n("End line of selected text of the current document."), [](const QStringView&, KTextEditor::View* view) { - return (view && view->selection()) ? QString::number(view->selectionRange().end().line()) : QString(); - }); - editor->registerVariableMatch(QStringLiteral("Document:Selection:EndColumn"), i18n("End column of selected text of the current document."), [](const QStringView&, KTextEditor::View* view) { - return (view && view->selection()) ? QString::number(view->selectionRange().end().column()) : QString(); - }); - editor->registerVariableMatch(QStringLiteral("Document:RowCount"), i18n("Number of rows of the 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.mid(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.mid(5)); - }); - - editor->registerVariablePrefix(QStringLiteral("ENV:"), i18n("Access to environment variables."), [](const QStringView& str, KTextEditor::View*) { - return QString::fromLocal8Bit(qgetenv(str.mid(4).toLocal8Bit().constData())); - }); - - editor->registerVariablePrefix(QStringLiteral("JS:"), i18n("Evaluate simple JavaScript statements."), [](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*) { - return QUuid::createUuid().toString(QUuid::WithoutBraces); - }); -} -} - //BEGIN unit test mode static bool kateUnitTestMode = false; void KTextEditor::EditorPrivate::enableUnitTestMode() { kateUnitTestMode = true; } bool KTextEditor::EditorPrivate::unitTestMode() { return kateUnitTestMode; } //END unit test mode KTextEditor::EditorPrivate::EditorPrivate(QPointer &staticInstance) : KTextEditor::Editor (this) , m_aboutData(QStringLiteral("katepart"), i18n("Kate Part"), QStringLiteral(KTEXTEDITOR_VERSION_STRING), i18n("Embeddable editor component"), KAboutLicense::LGPL_V2, i18n("(c) 2000-2019 The Kate Authors"), QString(), QStringLiteral("https://kate-editor.org")) , m_dummyApplication(nullptr) , m_application(&m_dummyApplication) , m_dummyMainWindow(nullptr) , m_defaultColors(new KateDefaultColors()) , m_searchHistoryModel(nullptr) , m_replaceHistoryModel(nullptr) { // remember this staticInstance = this; // init libgit2, we require at least 0.22 which has this function! #if LIBGIT2_FOUND git_libgit2_init(); #endif /** * register some datatypes */ qRegisterMetaType("KTextEditor::Cursor"); qRegisterMetaType("KTextEditor::Document*"); qRegisterMetaType("KTextEditor::View*"); // // fill about data // m_aboutData.addAuthor(i18n("Christoph Cullmann"), i18n("Maintainer"), QStringLiteral("cullmann@kde.org"), QStringLiteral("https://cullmann.io")); m_aboutData.addAuthor(i18n("Dominik Haumann"), i18n("Core Developer"), QStringLiteral("dhaumann@kde.org")); m_aboutData.addAuthor(i18n("Milian Wolff"), i18n("Core Developer"), QStringLiteral("mail@milianw.de"), QStringLiteral("http://milianw.de")); m_aboutData.addAuthor(i18n("Joseph Wenninger"), i18n("Core Developer"), QStringLiteral("jowenn@kde.org"), QStringLiteral("http://stud3.tuwien.ac.at/~e9925371")); m_aboutData.addAuthor(i18n("Erlend Hamberg"), i18n("Vi Input Mode"), QStringLiteral("ehamberg@gmail.com"), QStringLiteral("https://hamberg.no/erlend")); m_aboutData.addAuthor(i18n("Bernhard Beschow"), i18n("Developer"), QStringLiteral("bbeschow@cs.tu-berlin.de"), QStringLiteral("https://user.cs.tu-berlin.de/~bbeschow")); m_aboutData.addAuthor(i18n("Anders Lund"), i18n("Core Developer"), QStringLiteral("anders@alweb.dk"), QStringLiteral("https://alweb.dk")); m_aboutData.addAuthor(i18n("Michel Ludwig"), i18n("On-the-fly spell checking"), QStringLiteral("michel.ludwig@kdemail.net")); m_aboutData.addAuthor(i18n("Pascal Létourneau"), i18n("Large scale bug fixing"), QStringLiteral("pascal.letourneau@gmail.com")); m_aboutData.addAuthor(i18n("Hamish Rodda"), i18n("Core Developer"), QStringLiteral("rodda@kde.org")); m_aboutData.addAuthor(i18n("Waldo Bastian"), i18n("The cool buffersystem"), QStringLiteral("bastian@kde.org")); m_aboutData.addAuthor(i18n("Charles Samuels"), i18n("The Editing Commands"), QStringLiteral("charles@kde.org")); m_aboutData.addAuthor(i18n("Matt Newell"), i18n("Testing, ..."), QStringLiteral("newellm@proaxis.com")); m_aboutData.addAuthor(i18n("Michael Bartl"), i18n("Former Core Developer"), QStringLiteral("michael.bartl1@chello.at")); m_aboutData.addAuthor(i18n("Michael McCallum"), i18n("Core Developer"), QStringLiteral("gholam@xtra.co.nz")); m_aboutData.addAuthor(i18n("Michael Koch"), i18n("KWrite port to KParts"), QStringLiteral("koch@kde.org")); m_aboutData.addAuthor(i18n("Christian Gebauer"), QString(), QStringLiteral("gebauer@kde.org")); m_aboutData.addAuthor(i18n("Simon Hausmann"), QString(), QStringLiteral("hausmann@kde.org")); m_aboutData.addAuthor(i18n("Glen Parker"), i18n("KWrite Undo History, Kspell integration"), QStringLiteral("glenebob@nwlink.com")); m_aboutData.addAuthor(i18n("Scott Manson"), i18n("KWrite XML Syntax highlighting support"), QStringLiteral("sdmanson@alltel.net")); m_aboutData.addAuthor(i18n("John Firebaugh"), i18n("Patches and more"), QStringLiteral("jfirebaugh@kde.org")); m_aboutData.addAuthor(i18n("Andreas Kling"), i18n("Developer"), QStringLiteral("kling@impul.se")); m_aboutData.addAuthor(i18n("Mirko Stocker"), i18n("Various bugfixes"), QStringLiteral("me@misto.ch"), QStringLiteral("https://misto.ch/")); m_aboutData.addAuthor(i18n("Matthew Woehlke"), i18n("Selection, KColorScheme integration"), QStringLiteral("mw_triad@users.sourceforge.net")); m_aboutData.addAuthor(i18n("Sebastian Pipping"), i18n("Search bar back- and front-end"), QStringLiteral("webmaster@hartwork.org"), QStringLiteral("https://hartwork.org/")); m_aboutData.addAuthor(i18n("Jochen Wilhelmy"), i18n("Original KWrite Author"), QStringLiteral("digisnap@cs.tu-berlin.de")); m_aboutData.addAuthor(i18n("Gerald Senarclens de Grancy"), i18n("QA and Scripting"), QStringLiteral("oss@senarclens.eu"), QStringLiteral("http://find-santa.eu/")); m_aboutData.addCredit(i18n("Matteo Merli"), i18n("Highlighting for RPM Spec-Files, Perl, Diff and more"), QStringLiteral("merlim@libero.it")); m_aboutData.addCredit(i18n("Rocky Scaletta"), i18n("Highlighting for VHDL"), QStringLiteral("rocky@purdue.edu")); m_aboutData.addCredit(i18n("Yury Lebedev"), i18n("Highlighting for SQL"), QString()); m_aboutData.addCredit(i18n("Chris Ross"), i18n("Highlighting for Ferite"), QString()); m_aboutData.addCredit(i18n("Nick Roux"), i18n("Highlighting for ILERPG"), QString()); m_aboutData.addCredit(i18n("Carsten Niehaus"), i18n("Highlighting for LaTeX"), QString()); m_aboutData.addCredit(i18n("Per Wigren"), i18n("Highlighting for Makefiles, Python"), QString()); m_aboutData.addCredit(i18n("Jan Fritz"), i18n("Highlighting for Python"), QString()); m_aboutData.addCredit(i18n("Daniel Naber")); m_aboutData.addCredit(i18n("Roland Pabel"), i18n("Highlighting for Scheme"), QString()); m_aboutData.addCredit(i18n("Cristi Dumitrescu"), i18n("PHP Keyword/Datatype list"), QString()); m_aboutData.addCredit(i18n("Carsten Pfeiffer"), i18n("Very nice help"), QString()); m_aboutData.addCredit(i18n("Bruno Massa"), i18n("Highlighting for Lua"), QStringLiteral("brmassa@gmail.com")); m_aboutData.addCredit(i18n("All people who have contributed and I have forgotten to mention")); m_aboutData.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails")); /** * set the new Kate mascot */ m_aboutData.setProgramLogo (QImage(QStringLiteral(":/ktexteditor/mascot.png"))); // // dir watch // m_dirWatch = new KDirWatch(); // // command manager // m_cmdManager = new KateCmd(); + // + // variable expansion manager + // + m_variableExpansionManager = new KateVariableExpansionManager(this); + // // hl manager // m_hlManager = new KateHlManager(); // // mode man // m_modeManager = new KateModeManager(); // // schema man // m_schemaManager = new KateSchemaManager(); // // input mode factories // KateAbstractInputModeFactory *fact; fact = new KateNormalInputModeFactory(); m_inputModeFactories.insert(KTextEditor::View::NormalInputMode, fact); #if BUILD_VIMODE fact = new KateViInputModeFactory(); m_inputModeFactories.insert(KTextEditor::View::ViInputMode, fact); #endif // // spell check manager // m_spellCheckManager = new KateSpellCheckManager(); // config objects m_globalConfig = new KateGlobalConfig(); m_documentConfig = new KateDocumentConfig(); m_viewConfig = new KateViewConfig(); m_rendererConfig = new KateRendererConfig(); // create script manager (search scripts) m_scriptManager = KateScriptManager::self(); // // init the cmds // m_cmds.push_back(KateCommands::CoreCommands::self()); m_cmds.push_back(KateCommands::Character::self()); m_cmds.push_back(KateCommands::Date::self()); m_cmds.push_back(KateCommands::SedReplace::self()); m_cmds.push_back(KateCommands::Highlighting::self()); // global word completion model m_wordCompletionModel = new KateWordCompletionModel(this); // global keyword completion model m_keywordCompletionModel = new KateKeywordCompletionModel (this); // tap to QApplication object for color palette changes qApp->installEventFilter(this); - - // register default variables for expansion - registerVariables(this); } KTextEditor::EditorPrivate::~EditorPrivate() { delete m_globalConfig; delete m_documentConfig; delete m_viewConfig; delete m_rendererConfig; delete m_modeManager; delete m_schemaManager; delete m_dirWatch; // cu managers delete m_scriptManager; delete m_hlManager; delete m_spellCheckManager; // cu model delete m_wordCompletionModel; + // delete variable expansion manager + delete m_variableExpansionManager; + m_variableExpansionManager = nullptr; + // delete the commands before we delete the cmd manager qDeleteAll(m_cmds); delete m_cmdManager; qDeleteAll(m_inputModeFactories); // shutdown libgit2, we require at least 0.22 which has this function! #if LIBGIT2_FOUND git_libgit2_shutdown(); #endif } KTextEditor::Document *KTextEditor::EditorPrivate::createDocument(QObject *parent) { KTextEditor::DocumentPrivate *doc = new KTextEditor::DocumentPrivate(false, false, nullptr, parent); emit documentCreated(this, doc); return doc; } //END KTextEditor::Editor config stuff void KTextEditor::EditorPrivate::configDialog(QWidget *parent) { QPointer kd = new KPageDialog(parent); kd->setWindowTitle(i18n("Configure")); kd->setFaceType(KPageDialog::List); kd->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply | QDialogButtonBox::Help); QList editorPages; for (int i = 0; i < configPages(); ++i) { QFrame *page = new QFrame(); KTextEditor::ConfigPage *cp = configPage(i, page); KPageWidgetItem *item = kd->addPage(page, cp->name()); item->setHeader(cp->fullName()); item->setIcon(cp->icon()); QVBoxLayout *topLayout = new QVBoxLayout(page); topLayout->setContentsMargins(0, 0, 0, 0); connect(kd->button(QDialogButtonBox::Apply), SIGNAL(clicked()), cp, SLOT(apply())); topLayout->addWidget(cp); editorPages.append(cp); } if (kd->exec() && kd) { KateGlobalConfig::global()->configStart(); KateDocumentConfig::global()->configStart(); KateViewConfig::global()->configStart(); KateRendererConfig::global()->configStart(); for (int i = 0; i < editorPages.count(); ++i) { editorPages.at(i)->apply(); } KateGlobalConfig::global()->configEnd(); KateDocumentConfig::global()->configEnd(); KateViewConfig::global()->configEnd(); KateRendererConfig::global()->configEnd(); } delete kd; } int KTextEditor::EditorPrivate::configPages() const { return 4; } KTextEditor::ConfigPage *KTextEditor::EditorPrivate::configPage(int number, QWidget *parent) { switch (number) { case 0: return new KateViewDefaultsConfig(parent); case 1: return new KateSchemaConfigPage(parent); case 2: return new KateEditConfigTab(parent); case 3: return new KateSaveConfigTab(parent); default: break; } return nullptr; } /** * Cleanup the KTextEditor::EditorPrivate during QCoreApplication shutdown */ static void cleanupGlobal() { /** * delete if there */ delete KTextEditor::EditorPrivate::self(); } KTextEditor::EditorPrivate *KTextEditor::EditorPrivate::self() { /** * remember the static instance in a QPointer */ static bool inited = false; static QPointer staticInstance; /** * just return it, if already inited */ if (inited) { return staticInstance.data(); } /** * start init process */ inited = true; /** * now create the object and store it */ new KTextEditor::EditorPrivate(staticInstance); /** * register cleanup * let use be deleted during QCoreApplication shutdown */ qAddPostRoutine(cleanupGlobal); /** * return instance */ return staticInstance.data(); } void KTextEditor::EditorPrivate::registerDocument(KTextEditor::DocumentPrivate *doc) { Q_ASSERT (!m_documents.contains(doc)); m_documents.insert(doc, doc); } void KTextEditor::EditorPrivate::deregisterDocument(KTextEditor::DocumentPrivate *doc) { Q_ASSERT (m_documents.contains(doc)); m_documents.remove(doc); } void KTextEditor::EditorPrivate::registerView(KTextEditor::ViewPrivate *view) { Q_ASSERT (!m_views.contains(view)); m_views.insert(view); } void KTextEditor::EditorPrivate::deregisterView(KTextEditor::ViewPrivate *view) { Q_ASSERT (m_views.contains(view)); m_views.remove(view); } KTextEditor::Command *KTextEditor::EditorPrivate::queryCommand(const QString &cmd) const { return m_cmdManager->queryCommand(cmd); } QList KTextEditor::EditorPrivate::commands() const { return m_cmdManager->commands(); } QStringList KTextEditor::EditorPrivate::commandList() const { return m_cmdManager->commandList(); } +KateVariableExpansionManager *KTextEditor::EditorPrivate::variableExpansionManager() +{ + return m_variableExpansionManager; +} + void KTextEditor::EditorPrivate::updateColorPalette() { // update default color cache m_defaultColors.reset(new KateDefaultColors()); // reload the global schema (triggers reload for every view as well) m_rendererConfig->reloadSchema(); // force full update of all view caches and colors m_rendererConfig->updateConfig(); } void KTextEditor::EditorPrivate::copyToClipboard(const QString &text) { /** * empty => nop */ if (text.isEmpty()) { return; } /** * move to clipboard */ QApplication::clipboard()->setText(text, QClipboard::Clipboard); /** * LRU, kill potential duplicated, move new entry to top * cut after 10 entries */ m_clipboardHistory.removeOne(text); m_clipboardHistory.prepend(text); if (m_clipboardHistory.size() > 10) { m_clipboardHistory.removeLast(); } /** * notify about change */ emit clipboardHistoryChanged(); } bool KTextEditor::EditorPrivate::eventFilter(QObject *obj, QEvent *event) { if (obj == qApp && event->type() == QEvent::ApplicationPaletteChange) { // only update the color once for the event that belongs to the qApp updateColorPalette(); } return false; // always continue processing } QList< KateAbstractInputModeFactory *> KTextEditor::EditorPrivate::inputModeFactories() { return m_inputModeFactories.values(); } QStringListModel *KTextEditor::EditorPrivate::searchHistoryModel() { if (!m_searchHistoryModel) { KConfigGroup cg(KSharedConfig::openConfig(), "KTextEditor::Search"); const QStringList history = cg.readEntry(QStringLiteral("Search History"), QStringList()); m_searchHistoryModel = new QStringListModel(history, this); } return m_searchHistoryModel; } QStringListModel *KTextEditor::EditorPrivate::replaceHistoryModel() { if (!m_replaceHistoryModel) { KConfigGroup cg(KSharedConfig::openConfig(), "KTextEditor::Search"); const QStringList history = cg.readEntry(QStringLiteral("Replace History"), QStringList()); m_replaceHistoryModel = new QStringListModel(history, this); } return m_replaceHistoryModel; } void KTextEditor::EditorPrivate::saveSearchReplaceHistoryModels() { KConfigGroup cg(KSharedConfig::openConfig(), "KTextEditor::Search"); if (m_searchHistoryModel) { cg.writeEntry(QStringLiteral("Search History"), m_searchHistoryModel->stringList()); } if (m_replaceHistoryModel) { cg.writeEntry(QStringLiteral("Replace History"), m_replaceHistoryModel->stringList()); } } KSharedConfigPtr KTextEditor::EditorPrivate::config() { // use dummy config for unit tests! if (KTextEditor::EditorPrivate::unitTestMode()) { return KSharedConfig::openConfig(QStringLiteral("katepartrc-unittest"), KConfig::SimpleConfig, QStandardPaths::TempLocation); } // else: use application configuration, but try to transfer global settings on first use auto applicationConfig = KSharedConfig::openConfig(); if (!KConfigGroup(applicationConfig, QStringLiteral("KTextEditor Editor")).exists()) { auto globalConfig = KSharedConfig::openConfig(QStringLiteral("katepartrc")); for (auto group : {QStringLiteral("Editor"), QStringLiteral("Document"), QStringLiteral("View"), QStringLiteral("Renderer")}) { KConfigGroup origin(globalConfig, group); KConfigGroup destination(applicationConfig, QStringLiteral("KTextEditor ") + group); origin.copyTo(&destination); } } return applicationConfig; } diff --git a/src/utils/kateglobal.h b/src/utils/kateglobal.h index 40cafdd0..4324c6f5 100644 --- a/src/utils/kateglobal.h +++ b/src/utils/kateglobal.h @@ -1,573 +1,574 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2001-2010 Christoph Cullmann * Copyright (C) 2009 Erlend Hamberg * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KATE_GLOBAL_H #define KATE_GLOBAL_H #include #include "katescript.h" #include "variable.h" #include #include "ktexteditor/view.h" #include #include #include #include #include #include #include #include #include class QStringListModel; class KateCmd; class KateModeManager; class KateSchemaManager; class KateGlobalConfig; class KateDocumentConfig; class KateViewConfig; class KateRendererConfig; namespace KTextEditor { class DocumentPrivate; } namespace KTextEditor { class ViewPrivate; } class KateScriptManager; class KDirWatch; class KateHlManager; class KateSpellCheckManager; class KateWordCompletionModel; class KateAbstractInputModeFactory; class KateKeywordCompletionModel; class KateDefaultColors; +class KateVariableExpansionManager; namespace KTextEditor { /** * KTextEditor::EditorPrivate * One instance of this class is hold alive during * a kate part session, as long as any factory, document * or view stay around, here is the place to put things * which are needed and shared by all this objects ;) */ class KTEXTEDITOR_EXPORT EditorPrivate : public KTextEditor::Editor { Q_OBJECT friend class KTextEditor::Editor; // unit testing support public: /** * Calling this function internally sets a flag such that unitTestMode() * returns \p true. */ static void enableUnitTestMode(); /** * Returns \p true, if the unit test mode was enabled through a call of * enableUnitTestMode(), otherwise \p false. */ static bool unitTestMode(); // for setDefaultEncoding friend class KateDocumentConfig; private: /** * Default constructor, private, as singleton * @param staticInstance pointer to fill with content of this */ explicit EditorPrivate(QPointer &staticInstance); public: /** * Destructor */ ~EditorPrivate(); /** * Create a new document object * @param parent parent object * @return created KTextEditor::Document */ KTextEditor::Document *createDocument(QObject *parent) override; /** * Returns a list of all documents of this editor. * @return list of all existing documents */ QList documents() override { return m_documents.keys(); } /** * Set the global application object. * This will allow the editor component to access * the hosting application. * @param application application object */ void setApplication(KTextEditor::Application *application) override { // switch back to dummy application? m_application = application ? application : &m_dummyApplication; } /** * Current hosting application, if any set. * @return current application object or nullptr */ KTextEditor::Application *application() const override { return m_application; } /** * General Information about this editor */ public: /** * return the about data * @return about data of this editor part */ const KAboutData &aboutData() const override { return m_aboutData; } /** * Configuration management */ public: /** * Shows a config dialog for the part, changes will be applied * to the editor, but not saved anywhere automagically, call * writeConfig to save them */ void configDialog(QWidget *parent) override; /** * Number of available config pages * If the editor returns a number < 1, it doesn't support this * and the embedding app should use the configDialog () instead * @return number of config pages */ int configPages() const override; /** * returns config page with the given number, * config pages from 0 to configPages()-1 are available * if configPages() > 0 */ KTextEditor::ConfigPage *configPage(int number, QWidget *parent) override; /** * Kate Part Internal stuff ;) */ public: /** * singleton accessor * @return instance of the factory */ static KTextEditor::EditorPrivate *self(); /** * register document at the factory * this allows us to loop over all docs for example on config changes * @param doc document to register */ void registerDocument(KTextEditor::DocumentPrivate *doc); /** * unregister document at the factory * @param doc document to register */ void deregisterDocument(KTextEditor::DocumentPrivate *doc); /** * register view at the factory * this allows us to loop over all views for example on config changes * @param view view to register */ void registerView(KTextEditor::ViewPrivate *view); /** * unregister view at the factory * @param view view to unregister */ void deregisterView(KTextEditor::ViewPrivate *view); /** * return a list of all registered views * @return all known views */ QList views() { return m_views.toList(); } /** * global dirwatch * @return dirwatch instance */ KDirWatch *dirWatch() { return m_dirWatch; } /** * The global configuration of katepart, e.g. katepartrc * @return global shared access to katepartrc config */ static KSharedConfigPtr config(); /** * global mode manager * used to manage the modes centrally * @return mode manager */ KateModeManager *modeManager() { return m_modeManager; } /** * manager for the katepart schemas * @return schema manager */ KateSchemaManager *schemaManager() { return m_schemaManager; } /** * fallback document config * @return default config for all documents */ KateDocumentConfig *documentConfig() { return m_documentConfig; } /** * fallback view config * @return default config for all views */ KateViewConfig *viewConfig() { return m_viewConfig; } /** * fallback renderer config * @return default config for all renderers */ KateRendererConfig *rendererConfig() { return m_rendererConfig; } /** * Global script collection */ KateScriptManager *scriptManager() { return m_scriptManager; } /** * hl manager * @return hl manager */ KateHlManager *hlManager() { return m_hlManager; } /** * command manager * @return command manager */ KateCmd *cmdManager() { return m_cmdManager; } /** * spell check manager * @return spell check manager */ KateSpellCheckManager *spellCheckManager() { return m_spellCheckManager; } /** * global instance of the simple word completion mode * @return global instance of the simple word completion mode */ KateWordCompletionModel *wordCompletionModel() { return m_wordCompletionModel; } /** * Global instance of the language-aware keyword completion model * @return global instance of the keyword completion model */ KateKeywordCompletionModel *keywordCompletionModel () { return m_keywordCompletionModel; } /** * query for command * @param cmd name of command to query for * @return found command or 0 */ KTextEditor::Command *queryCommand(const QString &cmd) const override; /** * Get a list of all registered commands. * \return list of all commands */ QList commands() const override; /** * Get a list of available commandline strings. * \return commandline strings */ QStringList commandList() const override; /** * Copy text to clipboard an remember it in the history * @param text text to copy to clipboard, does nothing if empty! */ void copyToClipboard(const QString &text); /** * Clipboard history, filled with text we ever copied * to clipboard via copyToClipboard. */ const QStringList &clipboardHistory() const { return m_clipboardHistory; } /** * return a list of all registered docs * @return all known documents */ QList kateDocuments() { return m_documents.values(); } /** * Dummy main window to be null safe. * @return dummy main window */ KTextEditor::MainWindow *dummyMainWindow() { return &m_dummyMainWindow; } /** * @return list of available input mode factories */ QList inputModeFactories(); /** * Default colors, once constructed, as expensive * @return default colors */ const KateDefaultColors &defaultColors() const { return *m_defaultColors; } /** * Search pattern history shared among simple/power search instances. */ QStringListModel *searchHistoryModel(); /** * Replace pattern history shared among simple/power search instances. */ QStringListModel *replaceHistoryModel(); /** * Call this function to store the history models to the application config. */ void saveSearchReplaceHistoryModels(); + /** + * Returns the variable expansion manager. + */ + KateVariableExpansionManager *variableExpansionManager(); + Q_SIGNALS: /** * Emitted if the history of clipboard changes via copyToClipboard */ void clipboardHistoryChanged(); protected: bool eventFilter(QObject *, QEvent *) override; private Q_SLOTS: void updateColorPalette(); private: /** * about data (authors and more) */ KAboutData m_aboutData; /** * registered docs, map from general to specialized pointer */ QHash m_documents; /** * registered views */ QSet m_views; /** * global dirwatch object */ KDirWatch *m_dirWatch; /** * mode manager */ KateModeManager *m_modeManager; /** * schema manager */ KateSchemaManager *m_schemaManager; /** * global config */ KateGlobalConfig *m_globalConfig; /** * fallback document config */ KateDocumentConfig *m_documentConfig; /** * fallback view config */ KateViewConfig *m_viewConfig; /** * fallback renderer config */ KateRendererConfig *m_rendererConfig; /** * internal commands */ QList m_cmds; /** * script manager */ KateScriptManager *m_scriptManager; /** * hl manager */ KateHlManager *m_hlManager; /** * command manager */ KateCmd *m_cmdManager; + /** + * variable expansion manager + */ + KateVariableExpansionManager *m_variableExpansionManager; + /** * spell check manager */ KateSpellCheckManager *m_spellCheckManager; /** * global instance of the simple word completion mode */ KateWordCompletionModel *m_wordCompletionModel; /** * global instance of the language-specific keyword completion model */ KateKeywordCompletionModel *m_keywordCompletionModel; /** * clipboard history */ QStringList m_clipboardHistory; /** * Dummy application object to be null safe */ KTextEditor::Application m_dummyApplication; /** * access to application */ QPointer m_application; /** * Dummy main window to be null safe */ KTextEditor::MainWindow m_dummyMainWindow; /** * input modes map */ QMap m_inputModeFactories; /** * default colors */ QScopedPointer m_defaultColors; /** * Shared history models for search & replace. */ QStringListModel *m_searchHistoryModel; QStringListModel *m_replaceHistoryModel; - - /** - * Contains a lookup from the variable to the Variable instance. - */ - QHash m_variableExactMatches; - - /** - * Contains a lookup from the variable prefix to the Variable instance. - */ - QHash m_variablePrefixMatches; }; } #endif diff --git a/src/utils/katemacroexpander.cpp b/src/utils/katemacroexpander.cpp deleted file mode 100644 index 079009fd..00000000 --- a/src/utils/katemacroexpander.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright 2019 Dominik Haumann - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -#include "katemacroexpander.h" - -#include - -/** - * Find closing bracket for @p str starting a position @p pos. - */ -static int findClosing(QStringView str, int pos = 0) -{ - const int len = str.size(); - int nesting = 0; - - while (pos < len) { - ++pos; - const QChar c = str[pos]; - if (c == QLatin1Char('}')) { - if (nesting == 0) { - return pos; - } - nesting--; - } else if (c == QLatin1Char('{')) { - nesting++; - } - } - return -1; -} - -QString KateMacroExpander::expandMacro(const QString& input, KTextEditor::View* view) -{ - QString output = input; - QString oldStr; - do { - oldStr = output; - const int startIndex = output.indexOf(QLatin1String("%{")); - if (startIndex < 0) { - break; - } - - const int endIndex = findClosing(output, startIndex + 2); - if (endIndex <= startIndex) { - break; - } - - const int varLen = endIndex - (startIndex + 2); - QString variable = output.mid(startIndex + 2, varLen); - variable = expandMacro(variable, view); - if (KTextEditor::Editor::instance()->expandVariable(variable, view, variable)) { - output.replace(startIndex, endIndex - startIndex + 1, variable); - } - } while (output != oldStr); // str comparison guards against infinite loop - return output; -} - -// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/src/utils/katemacroexpander.h b/src/utils/katemacroexpander.h deleted file mode 100644 index 6b3d92f3..00000000 --- a/src/utils/katemacroexpander.h +++ /dev/null @@ -1,44 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright 2019 Dominik Haumann - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ -#ifndef KTEXTEDITOR_MACRO_EXPANDER_H -#define KTEXTEDITOR_MACRO_EXPANDER_H - -#include - -namespace KTextEditor -{ - class View; -} - -/** - * Helper for macro expansion. - */ -namespace KateMacroExpander -{ - /** - * Expands the @p input text based on the @p view. - * @return the expanded text. - */ - QString expandMacro(const QString& input, KTextEditor::View* view); -} - -#endif // KTEXTEDITOR_MACRO_EXPANDER_H - -// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/src/utils/katevariableexpansionhelpers.h b/src/utils/katevariableexpansionhelpers.h new file mode 100644 index 00000000..30ef36b8 --- /dev/null +++ b/src/utils/katevariableexpansionhelpers.h @@ -0,0 +1,101 @@ +/* This file is part of the KDE project + * + * Copyright 2019 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef KTEXTEDITOR_VARIABLE_EXPANSION_HELPERS_H +#define KTEXTEDITOR_VARIABLE_EXPANSION_HELPERS_H + +#include +#include +#include +#include + +#include "variable.h" + +class QListView; +class VariableItemModel; + +namespace KTextEditor +{ + class View; + class Variable; +} + +/** + * Helper for macro expansion. + */ +namespace KateMacroExpander +{ + /** + * Expands the @p input text based on the @p view. + * @return the expanded text. + */ + QString expandMacro(const QString& input, KTextEditor::View* view); +} + +/** + * Helper dialog that shows a non-modal dialog listing all available + * variables. If the user selects a variable, the variable is inserted + * into the respective widget. + */ +class KateVariableExpansionDialog : public QDialog +{ +public: + KateVariableExpansionDialog(QWidget *parent); + + /** + * Adds @p variable to the expansion list view. + */ + void addVariable(const KTextEditor::Variable& variable); + + /** + * Returns true if no variables were added at all to the dialog. + */ + int isEmpty() const; + + /** + * Adds @p widget to the list of widgets that trigger showing this dialog. + */ + void addWidget(QWidget *widget); + +protected: + /** + * Reimplemented for the following reasons: + * - Show this dialog if one of the widgets added with addWidget() has focus. + * - Catch the resize-event for widgets (e.g. QTextEdit) where we manually + * added a clickable action in the corner + */ + bool eventFilter(QObject *watched, QEvent *event) override; + + /** + * Called whenever a widget was deleted. If all widgets are deleted, + * this dialog deletes itself via deleteLater(). + */ + void onObjectDeleted(QObject* object); + +private: + QAction *m_showAction; + QVector m_widgets; + QVector m_variables; + VariableItemModel * m_variableModel; + QListView *m_listView; +}; + +#endif // KTEXTEDITOR_VARIABLE_EXPANSION_HELPERS_H + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/src/utils/katevariableexpansionmanager.cpp b/src/utils/katevariableexpansionmanager.cpp new file mode 100644 index 00000000..a628b5ae --- /dev/null +++ b/src/utils/katevariableexpansionmanager.cpp @@ -0,0 +1,273 @@ +/* This file is part of the KDE project + * + * Copyright 2019 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include "katevariableexpansionmanager.h" +#include "katevariableexpansionhelpers.h" + +#include +#include "kateglobal.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static void registerVariables(KateVariableExpansionManager &mng) +{ + using KTextEditor::Variable; + + mng.addVariable(Variable(QStringLiteral("Document:FileBaseName"), i18n("File base name without path and suffix of the current document."), [](const QStringView&, KTextEditor::View* view) { + const auto url = view ? view->document()->url().toLocalFile() : QString(); + return QFileInfo(url).baseName(); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document:FileExtension"), i18n("File extension of the current document."), [](const QStringView&, KTextEditor::View* view) { + const auto url = view ? view->document()->url().toLocalFile() : QString(); + return QFileInfo(url).completeSuffix(); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document:FileName"), i18n("File name without path of the current document."), [](const QStringView&, KTextEditor::View* view) { + const auto url = view ? view->document()->url().toLocalFile() : QString(); + return QFileInfo(url).fileName(); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document:FilePath"), i18n("Full path of the current document including the file name."), [](const QStringView&, KTextEditor::View* view) { + const auto url = view ? view->document()->url().toLocalFile() : QString(); + return QFileInfo(url).absoluteFilePath(); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document:Text"), i18n("Contents of the current document."), [](const QStringView&, KTextEditor::View* view) { + return view ? view->document()->text() : QString(); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document:Path"), i18n("Full path of the current document excluding the file name."), [](const QStringView&, KTextEditor::View* view) { + const auto url = view ? view->document()->url().toLocalFile() : QString(); + return QFileInfo(url).absolutePath(); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document:NativeFilePath"), i18n("Full document 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()); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document:NativePath"), i18n("Full document 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()); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document: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(); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document: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(); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document: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(); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document: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(); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document:Selection:Text"), i18n("Text selection of the current document."), [](const QStringView&, KTextEditor::View* view) { + return (view && view->selection()) ? view->selectionText() : QString(); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document:Selection:StartLine"), i18n("Start line of selected text of the current document."), [](const QStringView&, KTextEditor::View* view) { + return (view && view->selection()) ? QString::number(view->selectionRange().start().line()) : QString(); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document:Selection:StartColumn"), i18n("Start column of selected text of the current document."), [](const QStringView&, KTextEditor::View* view) { + return (view && view->selection()) ? QString::number(view->selectionRange().start().column()) : QString(); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document:Selection:EndLine"), i18n("End line of selected text of the current document."), [](const QStringView&, KTextEditor::View* view) { + return (view && view->selection()) ? QString::number(view->selectionRange().end().line()) : QString(); + }, false)); + mng.addVariable(Variable(QStringLiteral("Document:Selection:EndColumn"), i18n("End column of selected text of the current document."), [](const QStringView&, KTextEditor::View* view) { + return (view && view->selection()) ? QString::number(view->selectionRange().end().column()) : QString(); + }, false)); + mng.addVariable(Variable(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)); + + mng.addVariable(Variable(QStringLiteral("Date:Locale"), i18n("The current date in current locale format."), [](const QStringView&, KTextEditor::View*) { + return QDate::currentDate().toString(Qt::DefaultLocaleShortDate); + }, false)); + mng.addVariable(Variable(QStringLiteral("Date:ISO"), i18n("The current date (ISO)."), [](const QStringView&, KTextEditor::View*) { + return QDate::currentDate().toString(Qt::ISODate); + }, false)); + mng.addVariable(Variable(QStringLiteral("Date:"), i18n("The current date (QDate formatstring)."), [](const QStringView& str, KTextEditor::View*) { + return QDate::currentDate().toString(str.mid(5)); + }, true)); + + mng.addVariable(Variable(QStringLiteral("Time:Locale"), i18n("The current time in current locale format."), [](const QStringView&, KTextEditor::View*) { + return QTime::currentTime().toString(Qt::DefaultLocaleShortDate); + }, false)); + mng.addVariable(Variable(QStringLiteral("Time:ISO"), i18n("The current time (ISO)."), [](const QStringView&, KTextEditor::View*) { + return QTime::currentTime().toString(Qt::ISODate); + }, false)); + mng.addVariable(Variable(QStringLiteral("Time:"), i18n("The current time (QTime formatstring)."), [](const QStringView& str, KTextEditor::View*) { + return QTime::currentTime().toString(str.mid(5)); + }, true)); + + mng.addVariable(Variable(QStringLiteral("ENV:"), i18n("Access to environment variables."), [](const QStringView& str, KTextEditor::View*) { + return QString::fromLocal8Bit(qgetenv(str.mid(4).toLocal8Bit().constData())); + }, true)); + + mng.addVariable(Variable(QStringLiteral("JS:"), i18n("Evaluate simple JavaScript statements."), [](const QStringView& str, KTextEditor::View*) { + QJSEngine jsEngine; + const QJSValue out = jsEngine.evaluate(str.toString()); + return out.toString(); + }, true)); + + mng.addVariable(Variable(QStringLiteral("UUID"), i18n("Generate a new UUID."), [](const QStringView&, KTextEditor::View*) { + return QUuid::createUuid().toString(QUuid::WithoutBraces); + }, false)); +} + +KateVariableExpansionManager::KateVariableExpansionManager(QObject *parent) + : QObject(parent) +{ + // register default variables for expansion + registerVariables(*this); +} + +bool KateVariableExpansionManager::addVariable(const KTextEditor::Variable& var) +{ + if (!var.isValid()) + return false; + + // reject duplicates + const auto alreadyExists = std::any_of(m_variables.begin(), m_variables.end(), [&var](const KTextEditor::Variable &v) { + return var.name() == v.name(); + }); + if (alreadyExists) { + return false; + } + + // require a ':' in prefix matches (aka %{JS:1+1}) + if (var.isPrefixMatch() && !var.name().contains(QLatin1Char(':'))) + return false; + + m_variables.push_back(var); + return true; +} + +bool KateVariableExpansionManager::removeVariable(const QString &name) +{ + auto it = std::find_if(m_variables.begin(), m_variables.end(), [&name](const KTextEditor::Variable &var) { + return var.name() == name; + }); + if (it != m_variables.end()) { + m_variables.erase(it); + return true; + } + return false; +} + +KTextEditor::Variable KateVariableExpansionManager::variable(const QString &name) const +{ + auto it = std::find_if(m_variables.begin(), m_variables.end(), [&name](const KTextEditor::Variable &var) { + return var.name() == name; + }); + if (it != m_variables.end()) { + return *it; + } + return {}; +} + +const QVector &KateVariableExpansionManager::variables() const +{ + return m_variables; +} + +bool KateVariableExpansionManager::expandVariable(const QString& name, KTextEditor::View* view, QString& output) const +{ + // first try exact matches + auto var = variable(name); + if (!var.isValid()) { + // try prefix matching + const int colonIndex = name.indexOf(QLatin1Char(':')); + if (colonIndex >= 0) { + var = variable(name.left(colonIndex + 1)); + } + } + + if (var.isValid()) { + output = var.evaluate(name, view); + return true; + } + + return false; +} + +void KateVariableExpansionManager::expandText(const QString& text, KTextEditor::View* view, QString& output) const +{ + output = KateMacroExpander::expandMacro(text, view); +} + +void KateVariableExpansionManager::showDialog(const QVector& widgets, const QStringList& names) const +{ + // avoid any work in case no widgets or only nullptrs were provided + if (widgets.isEmpty() || std::all_of(widgets.cbegin(), widgets.cend(), [](const QWidget* w) { return w == nullptr; })) { + return; + } + + // collect variables + QVector vars; + if (!names.isEmpty()) { + for (const auto & name : names) { + const auto var = variable(name); + if (var.isValid()) { + vars.push_back(var); + } + // else: Not found, silently ignore for now + // Maybe raise a qWarning()? + } + } else { + vars = variables(); + } + + // if we have no vars at all, do nothing + if (vars.isEmpty()) { + return; + } + + // find parent dialog (for taskbar sharing, centering, ...) + QWidget *parentDialog = nullptr; + for (auto widget : widgets) { + if (widget) { + parentDialog = widget->window(); + break; + } + } + + // show dialog + auto dlg = new KateVariableExpansionDialog(parentDialog); + for (auto widget : widgets) { + if (widget) { + dlg->addWidget(widget); + } + } + + // add provided variables... + for (const auto & var : vars) { + if (var.isValid()) { + dlg->addVariable(var); + } + } +} + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/src/utils/katevariableexpansionmanager.h b/src/utils/katevariableexpansionmanager.h new file mode 100644 index 00000000..e0cdd616 --- /dev/null +++ b/src/utils/katevariableexpansionmanager.h @@ -0,0 +1,76 @@ +/* This file is part of the KDE project + * + * Copyright 2019 Dominik Haumann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef KTEXTEDITOR_VARIABLE_MANAGER +#define KTEXTEDITOR_VARIABLE_MANAGER + +#include +#include + +#include "variable.h" + +namespace KTextEditor +{ + class View; +} + +/** + * Manager class for variable expansion. + */ +class KateVariableExpansionManager : public QObject +{ +public: + /** + * Constructor with @p parent that takes ownership. + */ + KateVariableExpansionManager(QObject *parent); + + /** + * Adds @p variable to the expansion list view. + */ + bool addVariable(const KTextEditor::Variable& variable); + + /** + * Removes variable @p name. + */ + bool removeVariable(const QString &name); + + /** + * Returns the variable called @p name. + */ + KTextEditor::Variable variable(const QString &name) const; + + /** + * Returns all registered variables. + */ + const QVector &variables() const; + + bool expandVariable(const QString& variable, KTextEditor::View* view, QString& output) const; + + void expandText(const QString& text, KTextEditor::View* view, QString& output) const; + + void showDialog(const QVector& widgets, const QStringList& names) const; + +private: + QVector m_variables; +}; + +#endif // KTEXTEDITOR_VARIABLE_MANAGER + +// kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/src/utils/ktexteditor.cpp b/src/utils/ktexteditor.cpp index 24ef536a..591cb8eb 100644 --- a/src/utils/ktexteditor.cpp +++ b/src/utils/ktexteditor.cpp @@ -1,481 +1,444 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann (cullmann@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kateview.h" #include "cursor.h" #include "configpage.h" #include "editor.h" #include "document.h" #include "view.h" #include "plugin.h" #include "command.h" #include "inlinenoteinterface.h" #include "inlinenote.h" #include "inlinenotedata.h" #include "inlinenoteprovider.h" #include "markinterface.h" #include "modificationinterface.h" #include "sessionconfiginterface.h" #include "texthintinterface.h" #include "variable.h" +#include "katevariableexpansionmanager.h" #include "annotationinterface.h" #include "abstractannotationitemdelegate.h" #include "kateglobal.h" #include "kateconfig.h" #include "katecmd.h" -#include "katemacroexpander.h" using namespace KTextEditor; Cursor Cursor::fromString(const QStringRef& str) Q_DECL_NOEXCEPT { // parse format "(line, column)" const int startIndex = str.indexOf(QLatin1Char('(')); const int endIndex = str.indexOf(QLatin1Char(')')); const int commaIndex = str.indexOf(QLatin1Char(',')); if (startIndex < 0 || endIndex < 0 || commaIndex < 0 || commaIndex < startIndex || endIndex < commaIndex || endIndex < startIndex) { return invalid(); } bool ok1 = false; bool ok2 = false; const int line = str.mid(startIndex + 1, commaIndex - startIndex - 1).toInt(&ok1); const int column = str.mid(commaIndex + 1, endIndex - commaIndex - 1).toInt(&ok2); if (!ok1 || !ok2) { return invalid(); } return {line, column}; } Editor::Editor(EditorPrivate *impl) : QObject () , d (impl) { } Editor::~Editor() { } Editor *KTextEditor::Editor::instance() { /** * Just use internal KTextEditor::EditorPrivate::self() */ return KTextEditor::EditorPrivate::self(); } QString Editor::defaultEncoding () const { /** * return default encoding in global config object */ return d->documentConfig()->encoding (); } bool Editor::registerVariableMatch(const QString& name, const QString& description, ExpandFunction expansionFunc) { - if (name.isEmpty() || expansionFunc == nullptr) - return false; - - if (d->m_variableExactMatches.contains(name)) - return false; - - d->m_variableExactMatches.insert(name, Variable(name, description, expansionFunc)); - return true; + const auto var = Variable(name, description, expansionFunc, false); + return d->variableExpansionManager()->addVariable(var); } bool Editor::registerVariablePrefix(const QString& prefix, const QString& description, ExpandFunction expansionFunc) { - if (prefix.isEmpty() || expansionFunc == nullptr) - return false; - - if (d->m_variablePrefixMatches.contains(prefix)) - return false; - - if (!prefix.contains(QLatin1Char(':'))) - return false; - - d->m_variablePrefixMatches.insert(prefix, Variable(prefix, description, expansionFunc)); - return true; + const auto var = Variable(prefix, description, expansionFunc, true); + return d->variableExpansionManager()->addVariable(var); } bool Editor::unregisterVariableMatch(const QString& variable) { - auto it = d->m_variableExactMatches.find(variable); - if (it != d->m_variableExactMatches.end()) { - d->m_variableExactMatches.erase(it); - return true; - } - return false; + return d->variableExpansionManager()->removeVariable(variable); } bool Editor::unregisterVariablePrefix(const QString& variable) { - auto it = d->m_variablePrefixMatches.find(variable); - if (it != d->m_variablePrefixMatches.end()) { - d->m_variablePrefixMatches.erase(it); - return true; - } - return false; + return d->variableExpansionManager()->removeVariable(variable); } bool Editor::expandVariable(const QString& variable, KTextEditor::View* view, QString& output) const { - // first try exact matches - const auto it = d->m_variableExactMatches.find(variable); - if (it != d->m_variableExactMatches.end()) { - output = it->evaluate(variable, view); - return true; - } - - // try prefix matching - const int colonIndex = variable.indexOf(QLatin1Char(':')); - if (colonIndex >= 0) { - const QString prefix = variable.left(colonIndex + 1); - const auto itPrefix = d->m_variablePrefixMatches.find(prefix); - if (itPrefix != d->m_variablePrefixMatches.end()) { - output = itPrefix->evaluate(variable, view); - return true; - } - } - return false; + return d->variableExpansionManager()->expandVariable(variable, view, output); } void Editor::expandText(const QString& text, KTextEditor::View* view, QString& output) const { - output = KateMacroExpander::expandMacro(text, view); + d->variableExpansionManager()->expandText(text, view, output); +} + +void Editor::addVariableExpansion(const QVector& widgets, const QStringList& variables) const +{ + d->variableExpansionManager()->showDialog(widgets, variables); } bool View::insertText(const QString &text) { KTextEditor::Document *doc = document(); if (!doc) { return false; } return doc->insertText(cursorPosition(), text, blockSelection()); } bool View::isStatusBarEnabled() const { /** * is the status bar around? */ return !!d->statusBar(); } void View::setStatusBarEnabled(bool enable) { /** * no state change, do nothing */ if (enable == !!d->statusBar()) return; /** * else toggle it */ d->toggleStatusBar (); } bool View::insertTemplate(const KTextEditor::Cursor& insertPosition, const QString& templateString, const QString& script) { return d->insertTemplateInternal(insertPosition, templateString, script); } void View::setViewInputMode(InputMode inputMode) { d->setInputMode(inputMode); } ConfigPage::ConfigPage(QWidget *parent) : QWidget(parent) , d(nullptr) {} ConfigPage::~ConfigPage() {} QString ConfigPage::fullName() const { return name(); } QIcon ConfigPage::icon() const { return QIcon::fromTheme(QStringLiteral("document-properties")); } View::View (ViewPrivate *impl, QWidget *parent) : QWidget (parent), KXMLGUIClient() , d(impl) {} View::~View() {} Plugin::Plugin(QObject *parent) : QObject(parent) , d(nullptr) {} Plugin::~Plugin() {} int Plugin::configPages() const { return 0; } ConfigPage *Plugin::configPage(int, QWidget *) { return nullptr; } MarkInterface::MarkInterface() {} MarkInterface::~MarkInterface() {} ModificationInterface::ModificationInterface() {} ModificationInterface::~ModificationInterface() {} SessionConfigInterface::SessionConfigInterface() {} SessionConfigInterface::~SessionConfigInterface() {} TextHintInterface::TextHintInterface() {} TextHintInterface::~TextHintInterface() {} TextHintProvider::TextHintProvider() {} TextHintProvider::~TextHintProvider() {} InlineNoteInterface::InlineNoteInterface() {} InlineNoteInterface::~InlineNoteInterface() {} InlineNoteProvider::InlineNoteProvider() {} InlineNoteProvider::~InlineNoteProvider() {} KateInlineNoteData::KateInlineNoteData(KTextEditor::InlineNoteProvider* provider, const KTextEditor::View* view, const KTextEditor::Cursor& position, int index, bool underMouse, const QFont& font, int lineHeight) : m_provider(provider) , m_view(view) , m_position(position) , m_index(index) , m_underMouse(underMouse) , m_font(font) , m_lineHeight(lineHeight) {} InlineNote::InlineNote(const KateInlineNoteData& data) : d(data) { } qreal InlineNote::width() const { return d.m_provider->inlineNoteSize(*this).width(); } bool KTextEditor::InlineNote::underMouse() const { return d.m_underMouse; } void KTextEditor::InlineNoteProvider::inlineNoteActivated(const InlineNote& note, Qt::MouseButtons buttons, const QPoint& globalPos) { Q_UNUSED(note); Q_UNUSED(buttons); Q_UNUSED(globalPos); } void KTextEditor::InlineNoteProvider::inlineNoteFocusInEvent(const KTextEditor::InlineNote& note, const QPoint& globalPos) { Q_UNUSED(note); Q_UNUSED(globalPos); } void KTextEditor::InlineNoteProvider::inlineNoteFocusOutEvent(const KTextEditor::InlineNote& note) { Q_UNUSED(note); } void KTextEditor::InlineNoteProvider::inlineNoteMouseMoveEvent(const KTextEditor::InlineNote& note, const QPoint& globalPos) { Q_UNUSED(note); Q_UNUSED(globalPos); } KTextEditor::InlineNoteProvider* InlineNote::provider() const { return d.m_provider; } const KTextEditor::View* InlineNote::view() const { return d.m_view; } QFont InlineNote::font() const { return d.m_font; } int InlineNote::index() const { return d.m_index; } int InlineNote::lineHeight() const { return d.m_lineHeight; } KTextEditor::Cursor InlineNote::position() const { return d.m_position; } Command::Command(const QStringList &cmds, QObject *parent) : QObject(parent) , m_cmds (cmds) , d(nullptr) { // register this command static_cast (KTextEditor::Editor::instance())->cmdManager()->registerCommand (this); } Command::~Command() { // unregister this command, if instance is still there! if (KTextEditor::Editor::instance()) static_cast (KTextEditor::Editor::instance())->cmdManager()->unregisterCommand (this); } bool Command::supportsRange(const QString &) { return false; } KCompletion *Command::completionObject(KTextEditor::View *, const QString &) { return nullptr; } bool Command::wantsToProcessText(const QString &) { return false; } void Command::processText(KTextEditor::View *, const QString &) { } void View::setScrollPosition(KTextEditor::Cursor &cursor) { d->setScrollPositionInternal(cursor); } void View::setHorizontalScrollPosition(int x) { d->setHorizontalScrollPositionInternal(x); } KTextEditor::Cursor View::maxScrollPosition() const { return d->maxScrollPositionInternal(); } int View::firstDisplayedLine(LineType lineType) const { return d->firstDisplayedLineInternal(lineType); } int View::lastDisplayedLine(LineType lineType) const { return d->lastDisplayedLineInternal(lineType); } QRect View::textAreaRect() const { return d->textAreaRectInternal(); } StyleOptionAnnotationItem::StyleOptionAnnotationItem() : contentFontMetrics(QFont()) {} StyleOptionAnnotationItem::StyleOptionAnnotationItem(const StyleOptionAnnotationItem &other) : QStyleOption(Version, Type) , contentFontMetrics(QFont()) { *this = other; } StyleOptionAnnotationItem::StyleOptionAnnotationItem(int version) : QStyleOption(version, Type) , contentFontMetrics(QFont()) {} AbstractAnnotationItemDelegate::AbstractAnnotationItemDelegate(QObject *parent) : QObject(parent) {} AbstractAnnotationItemDelegate::~AbstractAnnotationItemDelegate() = default; diff --git a/src/utils/variable.cpp b/src/utils/variable.cpp index 07690782..c08d830a 100644 --- a/src/utils/variable.cpp +++ b/src/utils/variable.cpp @@ -1,50 +1,56 @@ /* This file is part of the KDE project Copyright (C) 2019 Dominik Haumann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "variable.h" namespace KTextEditor { -Variable::Variable(const QString& name, const QString& description, Variable::ExpandFunction func) +Variable::Variable(const QString& name, const QString& description, Variable::ExpandFunction func, bool isPrefixMatch) : m_name(name) , m_description(description) , m_function(func) + , m_isPrefixMatch(isPrefixMatch) {} bool Variable::isValid() const { return (!m_name.isEmpty()) && (m_function != nullptr); } +bool Variable::isPrefixMatch() const +{ + return m_isPrefixMatch; +} + QString Variable::name() const { return m_name; } QString Variable::description() const { return m_description; } QString Variable::evaluate(const QStringView& prefix, KTextEditor::View * view) const { return isValid() ? m_function(prefix, view) : QString(); } } diff --git a/src/utils/variable.h b/src/utils/variable.h index 22d02348..5bf4d0db 100644 --- a/src/utils/variable.h +++ b/src/utils/variable.h @@ -1,107 +1,130 @@ /* This file is part of the KDE project Copyright (C) 2019 Dominik Haumann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KTEXTEDITOR_VARIABLE_H #define KTEXTEDITOR_VARIABLE_H #include #include namespace KTextEditor { class View; /** * @brief Variable for variable expansion. * * @section variable_intro Introduction * * A Variable is used by the KTextEditor::Editor to expand variables, also * know as expanding macros. A Variable itself is defined by the variable * name() as well as a description() and a function that replaces the variable * by its value. * * To register a Variable in the Editor use either Editor::registerVariableMatch() * or Editor::registerPrefixMatch(). * * @see KTextEditor::Editor, KTextEditor::Editor::registerVariableMatch(), * KTextEditor::Editor::registerPrefixMatch() * @author Dominik Haumann \ */ class Variable { public: /** * Function that is called to expand a variable in @p text. * @param text */ using ExpandFunction = QString (*)(const QStringView& text, KTextEditor::View* view); + /** + * Constructs an invalid Variable, see isValid(). + */ + Variable() = default; + /** * Constructor defining a Variable by its @p name, its @p description, and * its function @p expansionFunc to expand a variable to its corresponding - * value. + * value. The parameter @p isPrefixMatch indicates whether this Variable + * represents an exact match (false) or a prefix match (true). * * @note The @p name should @e not be translated. */ - Variable(const QString& name, const QString& description, ExpandFunction expansionFunc); + Variable(const QString& name, const QString& description, ExpandFunction expansionFunc, bool isPrefixMatch); + + /** + * Copy constructor. + */ + Variable(const Variable & copy) = default; + + /** + * Assignment operator. + */ + Variable & operator=(const Variable & copy) = default; /** * Returns true, if the name is non-empty and the function provided in the * constructor is not a nullptr. */ bool isValid() const; + /** + * Returns whether this Variable represents an exact match (false) or a + * prefix match (true). + */ + bool isPrefixMatch() const; + /** * Returns the @p name that was provided in the constructor. * Depending on where the Variable is registered, this name is used to * identify an exact match or a prefix match. */ QString name() const; /** * Returns the description that was provided in the constructor. */ QString description() const; /** * Expands the Variable to its value. * * As example for an exact match, a variable "CurerntDocument:Cursor:Line" * uses the @p view to return the current line of the text cursor. In this * case @p prefix equals the text of the variable itself, i.e. * "CurerntDocument:Cursor:Line". * * As example of a prefix match, a variable "ENV:value" expands the * environment value @e value, e.g. "ENV:HOME". In this case, the @p prefix * equals the text "ENV:HOME" and @p view would be unused. * * @return the expanded variable. */ QString evaluate(const QStringView& prefix, KTextEditor::View * view) const; private: QString m_name; QString m_description; ExpandFunction m_function; + bool m_isPrefixMatch = false; }; } #endif