diff --git a/src/plugins/lua/CMakeLists.txt b/src/plugins/lua/CMakeLists.txt index ca1117aeebc..fa2b21d8efd 100644 --- a/src/plugins/lua/CMakeLists.txt +++ b/src/plugins/lua/CMakeLists.txt @@ -1,6 +1,6 @@ add_qtc_plugin(Lua PLUGIN_DEPENDS Core - PUBLIC_DEPENDS lua546 sol2 TextEditor + PUBLIC_DEPENDS lua546 sol2 TextEditor ProjectExplorer PUBLIC_DEFINES LUA_AVAILABLE SOURCES bindings/action.cpp @@ -15,6 +15,7 @@ add_qtc_plugin(Lua bindings/localsocket.cpp bindings/macro.cpp bindings/messagemanager.cpp + bindings/project.cpp bindings/qt.cpp bindings/qtcprocess.cpp bindings/settings.cpp diff --git a/src/plugins/lua/bindings/project.cpp b/src/plugins/lua/bindings/project.cpp new file mode 100644 index 00000000000..a7dffa2ec2a --- /dev/null +++ b/src/plugins/lua/bindings/project.cpp @@ -0,0 +1,166 @@ +// Copyright (C) The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "../luaengine.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace ProjectExplorer; +using namespace Utils; + +namespace Lua::Internal { + +void setupProjectModule() +{ + registerProvider("Project", [](sol::state_view lua) -> sol::object { + const ScriptPluginSpec *pluginSpec = lua.get("PluginSpec"); + QObject *guard = pluginSpec->connectionGuard.get(); + + sol::table result = lua.create_table(); + + result.new_usertype( + "RunConfiguration", + sol::no_constructor, + "runnable", + sol::property(&RunConfiguration::runnable)); + + result.new_usertype( + "Project", + sol::no_constructor, + "directory", + sol::property(&Project::projectDirectory), + "activeRunConfiguration", + [](Project *project) { return project->activeTarget()->activeRunConfiguration(); }); + + result["startupProject"] = [] { return ProjectManager::instance()->startupProject(); }; + + result["canRunStartupProject"] = + [](const QString &mode) -> std::pair> { + auto result = ProjectExplorerPlugin::canRunStartupProject(Utils::Id::fromString(mode)); + if (result) + return std::make_pair(true, sol::lua_nil); + return std::make_pair(false, result.error()); + }; + + result["runStartupProject"] = + [guard](const sol::optional &runnable) { + auto project = ProjectManager::instance()->startupProject(); + if (!project) + throw sol::error("No startup project"); + + auto runConfiguration = project->activeTarget()->activeRunConfiguration(); + + if (!runConfiguration) + throw sol::error("No active run configuration"); + + auto rc = std::make_unique(ProjectExplorer::Constants::NORMAL_RUN_MODE); + rc->copyDataFromRunConfiguration(runConfiguration); + + if (runnable) { + rc->setCommandLine(runnable->command); + rc->setWorkingDirectory(runnable->workingDirectory); + rc->setEnvironment(runnable->environment); + } + + BuildForRunConfigStatus status = BuildManager::potentiallyBuildForRunConfig( + runConfiguration); + + auto startRun = [rc = std::move(rc)]() mutable { + if (!rc->createMainWorker()) + return; + ProjectExplorerPlugin::startRunControl(rc.release()); + }; + + if (status == BuildForRunConfigStatus::Building) { + QObject::connect( + BuildManager::instance(), + &BuildManager::buildQueueFinished, + guard, + [startRun = std::move(startRun)](bool success) mutable { + if (success) + startRun(); + }, + Qt::SingleShotConnection); + } else { + startRun(); + } + }; + + result["RunMode"] = lua.create_table_with( + "Normal", Constants::NORMAL_RUN_MODE, "Debug", Constants::DEBUG_RUN_MODE); + + return result; + }); + + // startupProjectChanged + registerHook("projects.startupProjectChanged", [](sol::function func, QObject *guard) { + QObject::connect( + ProjectManager::instance(), + &ProjectManager::startupProjectChanged, + guard, + [func](ProjectExplorer::Project *project) { + Utils::expected_str res = void_safe_call(func, project); + QTC_CHECK_EXPECTED(res); + }); + }); + + // projectAdded + registerHook("projects.projectAdded", [](sol::function func, QObject *guard) { + QObject::connect( + ProjectManager::instance(), + &ProjectManager::projectAdded, + guard, + [func](ProjectExplorer::Project *project) { + Utils::expected_str res = void_safe_call(func, project); + QTC_CHECK_EXPECTED(res); + }); + }); + + // projectRemoved + registerHook("projects.projectRemoved", [](sol::function func, QObject *guard) { + QObject::connect( + ProjectManager::instance(), + &ProjectManager::projectRemoved, + guard, + [func](ProjectExplorer::Project *project) { + Utils::expected_str res = void_safe_call(func, project); + QTC_CHECK_EXPECTED(res); + }); + }); + + // aboutToRemoveProject + registerHook("projects.aboutToRemoveProject", [](sol::function func, QObject *guard) { + QObject::connect( + ProjectManager::instance(), + &ProjectManager::aboutToRemoveProject, + guard, + [func](ProjectExplorer::Project *project) { + Utils::expected_str res = void_safe_call(func, project); + QTC_CHECK_EXPECTED(res); + }); + }); + + // runActionsUpdated + registerHook("projects.runActionsUpdated", [](sol::function func, QObject *guard) { + QObject::connect( + ProjectExplorerPlugin::instance(), + &ProjectExplorerPlugin::runActionsUpdated, + guard, + [func]() { + Utils::expected_str res = void_safe_call(func); + QTC_CHECK_EXPECTED(res); + }); + }); +} + +} // namespace Lua::Internal diff --git a/src/plugins/lua/luaplugin.cpp b/src/plugins/lua/luaplugin.cpp index d91391ca175..b1ec613c0d4 100644 --- a/src/plugins/lua/luaplugin.cpp +++ b/src/plugins/lua/luaplugin.cpp @@ -45,6 +45,7 @@ void setupLocalSocketModule(); void setupMacroModule(); void setupMessageManagerModule(); void setupProcessModule(); +void setupProjectModule(); void setupQtModule(); void setupSettingsModule(); void setupTextEditorModule(); @@ -263,6 +264,7 @@ public: setupMacroModule(); setupMessageManagerModule(); setupProcessModule(); + setupProjectModule(); setupQtModule(); setupSettingsModule(); setupTextEditorModule(); diff --git a/src/plugins/lua/meta/project.lua b/src/plugins/lua/meta/project.lua new file mode 100644 index 00000000000..9254e7a7ef6 --- /dev/null +++ b/src/plugins/lua/meta/project.lua @@ -0,0 +1,38 @@ +---@meta Project +local project = {} + +---@module 'Utils' + +---@enum RunMode +project.RunMode { + Normal = "RunConfiguration.NormalRunMode", + Debug = "RunConfiguration.DebugRunMode", +} + +---@class RunConfiguration +---@field runnable ProcessRunData +project.RunConfiguration = {} + +---@class Project +---@field directory FilePath The directory of the project. +project.Project = {} + +---Returns the active run configuration of the project. +---@return RunConfiguration|nil The active run configuration of the project, or nil if there is no active run configuration. +function project.Project:activeRunConfiguration() end + +---Returns the startup project. +---@return Project|nil The startup project, or nil if there is no startup project. +function project.startupProject() end + +---Test whether the current startup project can be started using the specified run mode. +---@param runMode RunMode The run mode to test. +---@return boolean True if the current startup project can be started using the specified run mode; otherwise, false. +---@return string|nil If the project cannot be started, a message explaining why; otherwise, nil. +function project.canRunStartupProject(runMode) end + +---Starts the active run configuration of the current startup project. It will be build first if necessary. +---@param runnable? ProcessRunData Override the run configuration with the specified runnable. +function project.runStartupProject(runnable) end + +return project diff --git a/src/plugins/lua/meta/qtc.lua b/src/plugins/lua/meta/qtc.lua index 073828368d1..f5eb17ab3e1 100644 --- a/src/plugins/lua/meta/qtc.lua +++ b/src/plugins/lua/meta/qtc.lua @@ -52,6 +52,14 @@ EditorHooks = {} ---@field contentsChanged? function function(document: TextDocument, position: integer, charsRemoved: integer, charsAdded: integer) ---@field cursorChanged? function function(editor: TextEditor, cursor: MultiTextCursor) +---@class ProjectHooks +---@field startupProjectChanged? function function(project: Project) +---@field projectAdded? function function(project: Project) +---@field projectRemoved? function function(project: Project) +---@field aboutToRemoveProject? function function(project: Project) +---@field runActionsUpdated? function function() Called when Project.canRunStartupProject() might have changed. + ---@class Hooks ---@field editors? EditorHooks +---@field projects? ProjectHooks Hooks = {}