aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcus Tillmanns <[email protected]>2025-09-10 11:27:32 +0200
committerMarcus Tillmanns <[email protected]>2025-09-11 06:51:27 +0000
commitb462dd6b597c7f95b3fb56f7b7ebeb1664ab32eb (patch)
tree714142195bdc4c697b79c4bbbf58a97c55e1e3b4
parent8ef92a38e09f26b732f829c0ce6c9617f8ad7c09 (diff)
Devcontainer: Add (re-)start action to editor toolbar
Change-Id: I12ed3ec85207f4fbbe4242de64f2e68dd034892b Reviewed-by: David Schulz <[email protected]>
-rw-r--r--src/libs/devcontainer/devcontainerconfig.cpp31
-rw-r--r--src/libs/devcontainer/devcontainerconfig.h3
-rw-r--r--src/plugins/devcontainer/CMakeLists.txt2
-rw-r--r--src/plugins/devcontainer/devcontainerplugin.cpp80
-rw-r--r--src/plugins/devcontainer/devcontainerplugin_constants.h2
5 files changed, 115 insertions, 3 deletions
diff --git a/src/libs/devcontainer/devcontainerconfig.cpp b/src/libs/devcontainer/devcontainerconfig.cpp
index 0df9c45117b..632bb1530be 100644
--- a/src/libs/devcontainer/devcontainerconfig.cpp
+++ b/src/libs/devcontainer/devcontainerconfig.cpp
@@ -1259,4 +1259,35 @@ FilePath Config::workspaceFolder(const Config &config)
*config.containerConfig));
}
+bool Config::isValidConfigPath(
+ const Utils::FilePath &workspaceFolder, const Utils::FilePath &configPath)
+{
+ /*
+ Possible locations:
+ .devcontainer.json
+ .devcontainer/devcontainer.json
+ .devcontainer/<folder>/devcontainer.json (where <folder> is a sub-folder, one level deep)
+*/
+ // .devcontainer.json
+ if (configPath.fileName() == ".devcontainer.json" && configPath.parentDir() == workspaceFolder)
+ return true;
+
+ if (configPath.fileName() != "devcontainer.json")
+ return false;
+
+ const FilePath parentDir = configPath.parentDir();
+ const FilePath secondLvlParentDir = parentDir.parentDir();
+
+ // .devcontainer/devcontainer.json
+ if (parentDir.fileName() == ".devcontainer" && secondLvlParentDir == workspaceFolder)
+ return true;
+
+ // .devcontainer/<folder>/devcontainer.json (where <folder> is a sub-folder, one level deep)
+ if (secondLvlParentDir.fileName() == ".devcontainer"
+ && secondLvlParentDir.parentDir() == workspaceFolder)
+ return true;
+
+ return false;
+}
+
} // namespace DevContainer
diff --git a/src/libs/devcontainer/devcontainerconfig.h b/src/libs/devcontainer/devcontainerconfig.h
index 23648e680f7..d12a04e724e 100644
--- a/src/libs/devcontainer/devcontainerconfig.h
+++ b/src/libs/devcontainer/devcontainerconfig.h
@@ -241,6 +241,9 @@ struct DEVCONTAINER_EXPORT Config
const QJsonObject &json, JsonStringToString jsonStringToString);
static Utils::Result<Config> fromJson(
const QByteArray &data, const JsonStringToString &jsonStringToString);
+
+ static bool isValidConfigPath(
+ const Utils::FilePath &workspaceFolder, const Utils::FilePath &configPath);
};
//! Returns a QJsonValue for the specified path. e.g.: customization(config, "qt-creator/device/mount-cmd-bridge")
diff --git a/src/plugins/devcontainer/CMakeLists.txt b/src/plugins/devcontainer/CMakeLists.txt
index d3702513547..ed3704b4f8a 100644
--- a/src/plugins/devcontainer/CMakeLists.txt
+++ b/src/plugins/devcontainer/CMakeLists.txt
@@ -1,5 +1,5 @@
add_qtc_plugin(DevContainerPlugin
- PLUGIN_DEPENDS Core ProjectExplorer
+ PLUGIN_DEPENDS Core ProjectExplorer TextEditor
PLUGIN_TEST_DEPENDS CMakeProjectManager
DEPENDS DevContainer CmdBridgeClient
LONG_DESCRIPTION_MD README.md
diff --git a/src/plugins/devcontainer/devcontainerplugin.cpp b/src/plugins/devcontainer/devcontainerplugin.cpp
index 9b568f69f94..b6d38c5c53e 100644
--- a/src/plugins/devcontainer/devcontainerplugin.cpp
+++ b/src/plugins/devcontainer/devcontainerplugin.cpp
@@ -5,6 +5,7 @@
#include "devcontainerplugin_constants.h"
#include "devcontainerplugintr.h"
+#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
@@ -22,11 +23,14 @@
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/target.h>
+#include <texteditor/texteditor.h>
+
#include <utils/algorithm.h>
#include <utils/fsengine/fsengine.h>
#include <utils/guardedcallback.h>
#include <utils/icon.h>
#include <utils/infobar.h>
+#include <utils/theme/theme.h>
#include <QMessageBox>
@@ -35,6 +39,8 @@ using namespace Utils;
namespace DevContainer::Internal {
+const Icon DEVCONTAINER_ICON({{":/devcontainer/images/container.png", Theme::IconsBaseColor}});
+
#ifdef WITH_TESTS
QObject *createDevcontainerTest();
#endif
@@ -80,6 +86,12 @@ public:
this,
&DevContainerPlugin::onProjectRemoved);
+ connect(
+ Core::EditorManager::instance(),
+ &Core::EditorManager::editorCreated,
+ this,
+ &DevContainerPlugin::onEditorCreated);
+
for (auto project : ProjectManager::instance()->projects())
onProjectAdded(project);
@@ -91,9 +103,12 @@ public:
});
#endif
}
+
void onProjectAdded(Project *project);
void onProjectRemoved(Project *project);
+ void onEditorCreated(Core::IEditor *editor, const Utils::FilePath &filePath);
+
void startDeviceForProject(Project *project, DevContainer::InstanceConfig instanceConfig);
#ifdef WITH_TESTS
@@ -130,7 +145,7 @@ void DevContainerPlugin::onProjectRemoved(Project *project)
devices.erase(it);
}
-void DevContainerPlugin::onProjectAdded(Project *project)
+static FilePaths devContainerFilesForProject(Project *project)
{
/*
Possible locations:
@@ -142,12 +157,17 @@ void DevContainerPlugin::onProjectAdded(Project *project)
const FilePath rootDevcontainer = project->projectDirectory() / ".devcontainer.json";
const FilePaths paths{rootDevcontainer, containerFolder / ".devcontainer"};
- const FilePaths devContainerFiles = filtered(
+ return filtered(
transform(
containerFolder.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot),
[](const FilePath &path) { return path / "devcontainer.json"; })
<< rootDevcontainer << containerFolder / "devcontainer.json",
&FilePath::isFile);
+}
+
+void DevContainerPlugin::onProjectAdded(Project *project)
+{
+ const FilePaths devContainerFiles = devContainerFilesForProject(project);
if (!devContainerFiles.isEmpty()) {
const QList<DevContainer::InstanceConfig> instanceConfigs
@@ -252,6 +272,62 @@ void DevContainerPlugin::onProjectAdded(Project *project)
};
}
+void DevContainerPlugin::onEditorCreated(Core::IEditor *editor, const Utils::FilePath &filePath)
+{
+ if (filePath.fileName() != "devcontainer.json" && filePath.fileName() != ".devcontainer.json")
+ return;
+
+ auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor);
+ if (!textEditor)
+ return;
+
+ Project *project = ProjectManager::projectForFile(filePath);
+ if (!project)
+ return;
+
+ if (!DevContainer::Config::isValidConfigPath(project->rootProjectDirectory(), filePath))
+ return;
+
+ TextEditor::TextEditorWidget *textEditorWidget = textEditor->editorWidget();
+ if (!textEditorWidget)
+ return;
+
+ QAction *restartAction = new QAction(textEditorWidget);
+ restartAction->setIcon(DEVCONTAINER_ICON.icon());
+ restartAction->setText(Tr::tr("(Re-)Start Development Container"));
+ restartAction->setToolTip(Tr::tr("Start or Restart the development container."));
+
+ const FilePath workspaceFolder = project->rootProjectDirectory();
+
+ connect(restartAction, &QAction::triggered, [this, project, workspaceFolder, filePath]() {
+ auto it = devices.find(project);
+ if (it != devices.end()) {
+ std::shared_ptr<Device> existingDevice = it->second;
+ existingDevice->restart([](Result<> result) {
+ if (!result) {
+ QMessageBox box(Core::ICore::dialogParent());
+ box.setWindowTitle(Tr::tr("Development Container Error"));
+ box.setIcon(QMessageBox::Critical);
+ box.setText(result.error());
+ box.exec();
+ }
+ });
+ } else {
+ DevContainer::InstanceConfig instanceConfig{
+ .dockerCli = "docker",
+ .workspaceFolder = workspaceFolder,
+ .configFilePath = filePath,
+ .mounts = {},
+ };
+
+ startDeviceForProject(project, instanceConfig);
+ }
+ });
+
+ textEditorWidget
+ ->insertExtraToolBarAction(TextEditor::TextEditorWidget::Side::Left, restartAction);
+}
+
void DevContainerPlugin::startDeviceForProject(
Project *project, DevContainer::InstanceConfig instanceConfig)
{
diff --git a/src/plugins/devcontainer/devcontainerplugin_constants.h b/src/plugins/devcontainer/devcontainerplugin_constants.h
index e21caa4daf4..81b2abb3bdb 100644
--- a/src/plugins/devcontainer/devcontainerplugin_constants.h
+++ b/src/plugins/devcontainer/devcontainerplugin_constants.h
@@ -6,4 +6,6 @@
namespace DevContainer::Constants {
const char DEVCONTAINER_DEVICE_TYPE[] = "DevContainerDeviceType";
const char16_t DEVCONTAINER_FS_SCHEME[] = u"devcontainer";
+
+const char ACTION_START_DEVCONTAINER[] = "devcontainer.start";
} // namespace DevContainer::Constants