From 5d1d4d9f54f051397ab96156d5926bb247eda207 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Thu, 10 Oct 2024 14:58:11 +0200 Subject: [PATCH] Lua: Allow creating and running user defined scripts Change-Id: I227d55105fde53f22885793b4bfae69da90e8fa6 Reviewed-by: Marcus Tillmanns --- src/plugins/coreplugin/corejsextensions.cpp | 6 ++ src/plugins/coreplugin/corejsextensions.h | 1 + src/plugins/lua/luaplugin.cpp | 91 ++++++++++++++++++++ src/plugins/lua/wizards/qcscript/script.lua | 4 + src/plugins/lua/wizards/qcscript/wizard.json | 44 ++++++++++ src/plugins/lua/wizards/wizards.qrc | 24 +++--- 6 files changed, 159 insertions(+), 11 deletions(-) create mode 100644 src/plugins/lua/wizards/qcscript/script.lua create mode 100644 src/plugins/lua/wizards/qcscript/wizard.json diff --git a/src/plugins/coreplugin/corejsextensions.cpp b/src/plugins/coreplugin/corejsextensions.cpp index ab63a813f7c..462459d3555 100644 --- a/src/plugins/coreplugin/corejsextensions.cpp +++ b/src/plugins/coreplugin/corejsextensions.cpp @@ -3,6 +3,7 @@ #include "corejsextensions.h" +#include "icore.h" #include "messagemanager.h" #include @@ -35,6 +36,11 @@ QString UtilsJsExtension::qtCreatorIdeVersion() const return QCoreApplication::applicationVersion(); } +QString UtilsJsExtension::qtCreatorSettingsPath() const +{ + return Core::ICore::userResourcePath().toString(); +} + QString UtilsJsExtension::toNativeSeparators(const QString &in) const { return QDir::toNativeSeparators(in); diff --git a/src/plugins/coreplugin/corejsextensions.h b/src/plugins/coreplugin/corejsextensions.h index 1ca8685fdb3..a6bcf5025c8 100644 --- a/src/plugins/coreplugin/corejsextensions.h +++ b/src/plugins/coreplugin/corejsextensions.h @@ -21,6 +21,7 @@ public: Q_INVOKABLE QString qtVersion() const; Q_INVOKABLE QString qtCreatorVersion() const; Q_INVOKABLE QString qtCreatorIdeVersion() const; + Q_INVOKABLE QString qtCreatorSettingsPath() const; // File name conversions: Q_INVOKABLE QString toNativeSeparators(const QString &in) const; diff --git a/src/plugins/lua/luaplugin.cpp b/src/plugins/lua/luaplugin.cpp index a1821094500..5eb2a730623 100644 --- a/src/plugins/lua/luaplugin.cpp +++ b/src/plugins/lua/luaplugin.cpp @@ -5,6 +5,8 @@ #include "luapluginspec.h" #include "luatr.h" +#include +#include #include #include #include @@ -14,6 +16,7 @@ #include #include +#include #include #include #include @@ -25,6 +28,7 @@ #include #include #include +#include #include #include @@ -34,6 +38,11 @@ using namespace ExtensionSystem; namespace Lua::Internal { +const char M_SCRIPT[] = "Lua.Script"; +const char G_SCRIPTS[] = "Lua.Scripts"; +const char ACTION_SCRIPTS_BASE[] = "Lua.Scripts."; +const char ACTION_NEW_SCRIPT[] = "Lua.NewScript"; + void setupActionModule(); void setupCoreModule(); void setupFetchModule(); @@ -270,6 +279,7 @@ class LuaPlugin : public IPlugin private: LuaPane *m_pane = nullptr; + std::unique_ptr m_userScriptsWatcher; public: LuaPlugin() {} @@ -319,6 +329,44 @@ public: }); m_pane = new LuaPane(this); + + //register actions + ActionContainer *toolsContainer = ActionManager::actionContainer(Core::Constants::M_TOOLS); + + ActionContainer *scriptContainer = ActionManager::createMenu(M_SCRIPT); + + Command *newScriptCommand = ActionBuilder(this, ACTION_NEW_SCRIPT) + .setScriptable(true) + .setText(tr("New Script...")) + .addToContainer(M_SCRIPT) + .addOnTriggered([](){ + auto command = Core::ActionManager::command(Utils::Id("Wizard.Impl.Q.QCreatorScript")); + if (command && command->action()) + command->action()->trigger(); + else + qWarning("Failed to get wizard command. UI changed?"); + }) + .command(); + + scriptContainer->addAction(newScriptCommand); + scriptContainer->addSeparator(); + scriptContainer->appendGroup(G_SCRIPTS); + + scriptContainer->menu()->setTitle(Tr::tr("Scripting")); + toolsContainer->addMenu(scriptContainer); + + const Utils::FilePath userScriptsPath = Core::ICore::userResourcePath("scripts"); + userScriptsPath.ensureWritableDir(); + if (auto watch = userScriptsPath.watch()) { + m_userScriptsWatcher.swap(*watch); + connect( + m_userScriptsWatcher.get(), + &FilePathWatcher::pathChanged, + this, + &LuaPlugin::scanForScripts); + } + + scanForScripts(); } bool delayedInitialize() final @@ -355,6 +403,49 @@ public: PluginManager::addPlugins({plugins.begin(), plugins.end()}); PluginManager::loadPluginsAtRuntime(plugins); } + + void scanForScripts() + { + const FilePath scriptsPath = Core::ICore::userResourcePath("scripts"); + if (!scriptsPath.exists()) + return; + + ActionContainer *scriptContainer = ActionManager::actionContainer(M_SCRIPT); + + const FilePaths scripts = scriptsPath.dirEntries(FileFilter({"*.lua"}, QDir::Files)); + for (const FilePath &script : scripts) { + const Id base = Id(ACTION_SCRIPTS_BASE).withSuffix(script.baseName()); + const Id menuId = base.withSuffix(".Menu"); + if (!ActionManager::actionContainer(menuId)) { + ActionContainer *container = ActionManager::createMenu(menuId); + scriptContainer->addMenu(container); + auto menu = container->menu(); + menu->setTitle(script.baseName()); + ActionBuilder(this, base) + .setText(Tr::tr("%1").arg(script.baseName())) + .setToolTip(Tr::tr("Run script '%1'").arg(script.toUserOutput())) + .addOnTriggered([this, script]() { runScript(script); }); + connect(menu->addAction(Tr::tr("Run")), &QAction::triggered, this, [this, script]() { + runScript(script); + }); + connect(menu->addAction(Tr::tr("Edit")), &QAction::triggered, this, [script]() { + Core::EditorManager::openEditor(script); + }); + } + } + } + + void runScript(const FilePath &script) + { + expected_str content = script.fileContents(); + if (content) { + Lua::runScript(QString::fromUtf8(*content), script.fileName()); + } else { + MessageManager::writeFlashing(Tr::tr("Failed to read script %1: %2") + .arg(script.toUserOutput()) + .arg(content.error())); + } + } }; } // namespace Lua::Internal diff --git a/src/plugins/lua/wizards/qcscript/script.lua b/src/plugins/lua/wizards/qcscript/script.lua new file mode 100644 index 00000000000..7f9a17eee97 --- /dev/null +++ b/src/plugins/lua/wizards/qcscript/script.lua @@ -0,0 +1,4 @@ +T = require'TextEditor' +editor = T.currentEditor() +cursor = editor:cursor() +cursor:insertText('-- Hello World!') diff --git a/src/plugins/lua/wizards/qcscript/wizard.json b/src/plugins/lua/wizards/qcscript/wizard.json new file mode 100644 index 00000000000..1c6fec4b606 --- /dev/null +++ b/src/plugins/lua/wizards/qcscript/wizard.json @@ -0,0 +1,44 @@ +{ + "version": 1, + "supportedProjectTypes": [], + "id": "Q.QCreatorScript", + "category": "R.Lua", + "trDescription": "Creates a script that can access Qt Creator internals and be triggered by an action.", + "trDisplayName": "Qt Creator Script", + "trDisplayCategory": "Lua", + "iconText": "ts", + "enabled": "%{JS: value('Plugins').indexOf('Lua') >= 0}", + "options": [ + { + "key": "DefaultSuffix", + "value": "%{JS: Util.preferredSuffix('text/x-lua')}" + }, + { + "key": "InitialPath", + "value": "%{JS: Util.qtCreatorSettingsPath()}/scripts" + }, + { + "key": "PathVisible", + "value": "false" + } + ], + "pages": [ + { + "trDisplayName": "Location", + "trShortTitle": "Location", + "typeId": "File" + } + ], + "generators": [ + { + "typeId": "File", + "data": [ + { + "source": "script.lua", + "target": "%{JS: Util.fileName(value('TargetPath'), value('DefaultSuffix'))}", + "openInEditor": true + } + ] + } + ] +} diff --git a/src/plugins/lua/wizards/wizards.qrc b/src/plugins/lua/wizards/wizards.qrc index 076550cb4a6..7f03c553c21 100644 --- a/src/plugins/lua/wizards/wizards.qrc +++ b/src/plugins/lua/wizards/wizards.qrc @@ -1,13 +1,15 @@ - - luafile/wizard.json - luafile/script.lua - plugin/init.lua.tpl - plugin/plugin.lua.tpl - plugin/project.json - plugin/wizard.json - plugin/.luarc.json - plugin/icon.png - plugin/icon@2x.png - + + luafile/wizard.json + luafile/script.lua + plugin/init.lua.tpl + plugin/plugin.lua.tpl + plugin/project.json + plugin/wizard.json + plugin/.luarc.json + plugin/icon.png + plugin/icon@2x.png + qcscript/script.lua + qcscript/wizard.json +