Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0873fd7
implementation for automatic project import for VSvode
Jul 30, 2026
9b00265
changelog
Jul 30, 2026
0c252a4
changelog
QuangAnhLe Aug 4, 2026
de97b7c
changelog
QuangAnhLe Aug 4, 2026
e8a4755
#1167: fix missing Files import in Vscode.java
QuangAnhLe Aug 4, 2026
cf6747b
#1167: fix missing Path import in VscodeTest.java
QuangAnhLe Aug 4, 2026
4d2c243
fix seetings
QuangAnhLe Aug 4, 2026
bdccc81
Merge remote-tracking branch 'upstream/main' into feature/1167-automa…
maybeec Aug 16, 2026
7818987
Merge branch 'main' into feature/1167-automatic-project-import-vscode
QuangAnhLe Aug 24, 2026
0ae5521
deduplicate IDE repository import into IdeToolCommandlet
QuangAnhLe Aug 24, 2026
8f0feab
centralise IDE repository import template merge in IdeToolCommandlet
QuangAnhLe Aug 24, 2026
69070c3
Merge branch 'main' into feature/1167-automatic-project-import-vscode
QuangAnhLe Aug 25, 2026
dd333d0
Merge branch 'main' into feature/1167-automatic-project-import-vscode
QuangAnhLe Aug 26, 2026
cd7ad31
Merge branch 'main' into feature/1167-automatic-project-import-vscode
QuangAnhLe Sep 1, 2026
5da99fd
Adds tests for single/multi-project import, idempotency, launch targe…
QuangAnhLe Sep 2, 2026
269d403
edit changelog
QuangAnhLe Sep 2, 2026
9cda4d9
Merge branch 'main' into feature/1167-automatic-project-import-vscode
QuangAnhLe Sep 3, 2026
cc1e123
Merge branch 'main' into feature/1167-automatic-project-import-vscode
QuangAnhLe Oct 2, 2026
c04ad35
edit changelog
QuangAnhLe Oct 2, 2026
52838bd
Merge branch 'main' into feature/1167-automatic-project-import-vscode
QuangAnhLe Oct 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This file documents all notable changes to https://github.com/devonfw/IDEasy[IDE

Release with new features and bugfixes:

* https://github.com/devonfw/IDEasy/issues/1167[#1167]: Automatic project import for vs code
* https://github.com/devonfw/IDEasy/issues/2345[#2345]: DOCKER_EDITION ignored on Linux
* https://github.com/devonfw/IDEasy/issues/2381[#2381]: Make node and npm pristine versioned tools with per-project npm global prefix
* https://github.com/devonfw/IDEasy/issues/2607[#2607]: Align tool help for corepack
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
package com.devonfw.tools.ide.tool.ide;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.devonfw.tools.ide.cli.CliException;
import com.devonfw.tools.ide.commandlet.Commandlet;
import com.devonfw.tools.ide.commandlet.CommandletManager;
import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.environment.AbstractEnvironmentVariables;
import com.devonfw.tools.ide.environment.EnvironmentVariables;
import com.devonfw.tools.ide.environment.ExtensibleEnvironmentVariables;
import com.devonfw.tools.ide.tool.AbstractLocalToolCommandlet;
import com.devonfw.tools.ide.tool.LocalToolCommandlet;

/**
Expand All @@ -16,6 +26,8 @@
*/
public interface IdeToolCommandlet extends LocalToolCommandlet {

Logger LOG = LoggerFactory.getLogger(IdeToolCommandlet.class);

/**
* @return the {@link IdeWorkspaceConfigurer} that configures the workspace of this IDE. Needed so that the default {@link #configureWorkspace()} of this
* interface can delegate the shared workspace configuration logic to a single implementation.
Expand Down Expand Up @@ -61,10 +73,113 @@ default Path getIdeMetadataPath() {
/**
* Imports the repository specified by the given {@link Path} into the IDE managed by this {@link IdeToolCommandlet}.
*
* <p>
* The repository is searched for a build descriptor of any build tool that this IDE supports via {@link #getBuildTool2TemplateMap()}. The first match
* triggers a merge of the corresponding template into the workspace via {@link #mergeTemplate(Path, String)}. If no build tool of this IDE applies the
* repository is skipped.
* </p>
*
* @param repositoryPath the {@link Path} to the repository directory to import.
*/
default void importRepository(Path repositoryPath) {

throw new CliException("Repository import is not yet supported for IDE " + getName());
CommandletManager commandletManager = getContext().getCommandletManager();
for (Entry<Class<? extends AbstractLocalToolCommandlet>, String> entry : getBuildTool2TemplateMap().entrySet()) {
AbstractLocalToolCommandlet buildTool = commandletManager.getCommandlet(entry.getKey());
Path buildDescriptor = buildTool.findBuildDescriptor(repositoryPath);
if (buildDescriptor != null) {
String templateFilename = entry.getValue();
LOG.debug("Found build descriptor {} so merging template {}", buildDescriptor, templateFilename);
mergeTemplate(repositoryPath, templateFilename);
return;
}
}
LOG.warn("No supported build descriptor was found for project import in {}", repositoryPath);
}

/**
* @return the mapping of supported build tool commandlets to the template file name to be merged into the workspace (see
* {@link #mergeTemplate(Path, String)}) when the corresponding build descriptor is present in the imported repository.
* The default is an empty map meaning that no build tool is supported for repository import by this IDE.
*/
default Map<Class<? extends AbstractLocalToolCommandlet>, String> getBuildTool2TemplateMap() {

return Map.of();
}

/**
* Merges the template with the given file name into the workspace for the imported repository. This is the IDE-specific part of
* {@link #importRepository(Path)} and is called after a supported build descriptor was found.
*
* <p>
* The template location is built dynamically from the tool name (see {@link #getTemplateFolder()}) and the template file name, so no per-IDE template path
* constant is needed. The environment variables are created via {@link #getTemplateEnvironmentVariables(Path)} with the relative project path as
* {@code PROJECT_PATH}.
* </p>
*
* @param repositoryPath the {@link Path} to the imported repository directory.
* @param templateFilename the file name of the workspace-relative template to merge (as configured in {@link #getBuildTool2TemplateMap()}).
*/
default void mergeTemplate(Path repositoryPath, String templateFilename) {

String templateFolder = getTemplateFolder();
if (templateFolder == null) {
throw new UnsupportedOperationException("Repository import is not yet implemented for IDE " + getName());
}
Path templateFile = getContext().getSettingsPath()
.resolve(getName())
.resolve(IdeContext.FOLDER_WORKSPACE)
.resolve(IdeContext.FOLDER_REPOSITORY)
.resolve(templateFolder)
.resolve(templateFilename);
if (!Files.exists(templateFile)) {
throw new CliException("Cannot import project into workspace: template file not found at " + templateFile + "\n"
+ "Please do an upstream merge of your settings git repository.");
}
Path workspacesPath = getContext().getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES);
Path workspacePath = getContext().getFileAccess().findAncestor(repositoryPath, workspacesPath, 1);
if (workspacePath == null) {
throw new CliException("Cannot import project into workspace: could not find workspace from " + repositoryPath);
}
EnvironmentVariables environmentVariables = getTemplateEnvironmentVariables(workspacePath.relativize(repositoryPath));
Path workspaceFile = workspacePath.resolve(templateFolder).resolve(templateFilename);
doMergeTemplate(templateFile, workspaceFile, environmentVariables);
}

/**
* Performs the actual merge of the resolved template file into the workspace file. This is the only IDE-specific part of
* {@link #mergeTemplate(Path, String)} as the merge algorithm differs per IDE (e.g. {@code JSON} vs {@code XML}).
*
* @param templateFile the resolved {@link Path} to the template file in the settings repository.
* @param workspaceFile the {@link Path} to the target file in the workspace to merge the template into.
* @param environmentVariables the {@link EnvironmentVariables} to resolve variables (e.g. {@code PROJECT_PATH}) in the template.
*/
default void doMergeTemplate(Path templateFile, Path workspaceFile, EnvironmentVariables environmentVariables) {

throw new UnsupportedOperationException("Repository import is not yet implemented for IDE " + getName());
}

/**
* @return the name of the IDE configuration folder (e.g. {@code .vscode} or {@code .idea}) inside which the repository workspace templates
* are stored and merged, or {@code null} if this IDE does not support repository import. This folder is used both in the settings
* repository to locate the template and in the workspace to store the merged result.
*/
default String getTemplateFolder() {

return null;
}

/**
* Creates {@link EnvironmentVariables} for resolving the imported repository workspace template with the relative project path as {@code PROJECT_PATH}.
*
* @param projectPath the relative {@link Path} from the workspace root to the repository.
* @return the resolved {@link EnvironmentVariables}.
*/
default EnvironmentVariables getTemplateEnvironmentVariables(Path projectPath) {

ExtensibleEnvironmentVariables environmentVariables = new ExtensibleEnvironmentVariables(
(AbstractEnvironmentVariables) getContext().getVariables().getParent(), getContext());
environmentVariables.setValue("PROJECT_PATH", projectPath.toString().replace('\\', '/'));
return environmentVariables.resolved();
}
}
57 changes: 16 additions & 41 deletions cli/src/main/java/com/devonfw/tools/ide/tool/intellij/Intellij.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,15 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;

import com.devonfw.tools.ide.cli.CliException;
import com.devonfw.tools.ide.commandlet.CommandletManager;
import com.devonfw.tools.ide.common.Tag;
import com.devonfw.tools.ide.context.IdeContext;
import com.devonfw.tools.ide.environment.AbstractEnvironmentVariables;
import com.devonfw.tools.ide.environment.EnvironmentVariables;
import com.devonfw.tools.ide.environment.ExtensibleEnvironmentVariables;
import com.devonfw.tools.ide.merge.xml.XmlMergeDocument;
import com.devonfw.tools.ide.merge.xml.XmlMerger;
import com.devonfw.tools.ide.process.EnvironmentContext;
Expand Down Expand Up @@ -45,7 +40,6 @@ public class Intellij extends IdeaBasedIdeToolCommandlet {
private static final String IDEA_BASH_SCRIPT = IDEA + ".sh";

private static final String FOLDER_IDEA_CONFIG = ".idea";
private static final String TEMPLATE_LOCATION = "intellij/workspace/repository/" + FOLDER_IDEA_CONFIG;
private static final String GRADLE_XML = "gradle.xml";
private static final String MISC_XML = "misc.xml";
private static final String IDEA_PROPERTIES = "idea.properties";
Expand Down Expand Up @@ -125,32 +119,23 @@ protected ToolEditionAndVersion adjustRequestedEdition(ToolEditionAndVersion req
return requested;
}

private EnvironmentVariables getIntellijEnvironmentVariables(Path projectPath) {
ExtensibleEnvironmentVariables environmentVariables = new ExtensibleEnvironmentVariables(
(AbstractEnvironmentVariables) this.context.getVariables().getParent(), this.context);
@Override
public String getTemplateFolder() {

environmentVariables.setValue("PROJECT_PATH", projectPath.toString().replace('\\', '/'));
return environmentVariables.resolved();
return FOLDER_IDEA_CONFIG;
}

private void mergeConfig(Path repositoryPath, String configFilePath) {
Path templatePath = this.context.getSettingsPath().resolve(TEMPLATE_LOCATION);
Path templateFile = templatePath.resolve(configFilePath);
if (!Files.exists(templateFile)) {
throw new CliException(
"Cannot import project into workspace: template file not found at " + templateFile + "\n"
+ "Please do an upstream merge of your settings git repository.");
}
Path workspacesPath = this.context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES);
Path workspacePath = this.context.getFileAccess().findAncestor(repositoryPath, workspacesPath, 1);
if (workspacePath == null) {
throw new CliException(
"Cannot import project into workspace: could not find workspace from " + repositoryPath);
}
XmlMerger xmlMerger = new XmlMerger(this.context);
EnvironmentVariables environmentVariables = getIntellijEnvironmentVariables(workspacePath.relativize(repositoryPath));
Path workspaceFile = workspacePath.resolve(FOLDER_IDEA_CONFIG).resolve(configFilePath);
/**
* Merges the IntelliJ project template into the workspace's {@code .idea} config (e.g. {@code misc.xml} or {@code gradle.xml}).
*
* @param templateFile the resolved {@link Path} to the template file in the settings repository.
* @param workspaceFile the {@link Path} to the target {@code .idea} file to merge into.
* @param environmentVariables the {@link EnvironmentVariables} to resolve variables (e.g. {@code PROJECT_PATH}) in the template.
*/
@Override
public void doMergeTemplate(Path templateFile, Path workspaceFile, EnvironmentVariables environmentVariables) {

XmlMerger xmlMerger = new XmlMerger(this.context);
XmlMergeDocument workspaceDocument = xmlMerger.load(workspaceFile);
XmlMergeDocument templateDocument = xmlMerger.loadAndResolve(templateFile, environmentVariables);

Expand All @@ -160,18 +145,8 @@ private void mergeConfig(Path repositoryPath, String configFilePath) {
}

@Override
public void importRepository(Path repositoryPath) {
CommandletManager commandletManager = this.context.getCommandletManager();
for (Entry<Class<? extends AbstractLocalToolCommandlet>, String> entry : BUILD_TOOL_TO_IJ_TEMPLATE.entrySet()) {
AbstractLocalToolCommandlet buildTool = commandletManager.getCommandlet(entry.getKey());
Path buildDescriptor = buildTool.findBuildDescriptor(repositoryPath);
if (buildDescriptor != null) {
String templateFilename = entry.getValue();
LOG.debug("Found build descriptor {} so merging template {}", buildDescriptor, templateFilename);
mergeConfig(repositoryPath, templateFilename);
return;
}
}
LOG.warn("No supported build descriptor was found for project import in {}", repositoryPath);
public Map<Class<? extends AbstractLocalToolCommandlet>, String> getBuildTool2TemplateMap() {

return BUILD_TOOL_TO_IJ_TEMPLATE;
}
}
Loading
Loading