• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

devonfw / IDEasy / 30911412186

04 Aug 2026 12:56PM UTC coverage: 72.622% (+0.04%) from 72.587%
30911412186

Pull #2263

github

web-flow
Merge 4d2c2437b into cd9770b9c
Pull Request #2263: Feature/1167 automatic project import vscode

5017 of 7657 branches covered (65.52%)

Branch coverage included in aggregate %.

13097 of 17286 relevant lines covered (75.77%)

3.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.87
cli/src/main/java/com/devonfw/tools/ide/tool/vscode/Vscode.java
1
package com.devonfw.tools.ide.tool.vscode;
2

3
import java.nio.file.Files;
4
import java.nio.file.Path;
5
import java.util.ArrayList;
6
import java.util.List;
7
import java.util.Map;
8
import java.util.Map.Entry;
9
import java.util.Set;
10

11
import org.slf4j.Logger;
12
import org.slf4j.LoggerFactory;
13

14
import com.devonfw.tools.ide.cli.CliException;
15
import com.devonfw.tools.ide.commandlet.CommandletManager;
16
import com.devonfw.tools.ide.common.Tag;
17
import com.devonfw.tools.ide.context.IdeContext;
18
import com.devonfw.tools.ide.environment.AbstractEnvironmentVariables;
19
import com.devonfw.tools.ide.environment.EnvironmentVariables;
20
import com.devonfw.tools.ide.environment.ExtensibleEnvironmentVariables;
21
import com.devonfw.tools.ide.log.IdeLogLevel;
22
import com.devonfw.tools.ide.merge.JsonMerger;
23
import com.devonfw.tools.ide.process.ProcessContext;
24
import com.devonfw.tools.ide.process.ProcessMode;
25
import com.devonfw.tools.ide.process.ProcessResult;
26
import com.devonfw.tools.ide.step.Step;
27
import com.devonfw.tools.ide.tool.LocalToolCommandlet;
28
import com.devonfw.tools.ide.tool.ToolCommandlet;
29
import com.devonfw.tools.ide.tool.gradle.Gradle;
30
import com.devonfw.tools.ide.tool.ide.IdeToolCommandlet;
31
import com.devonfw.tools.ide.tool.mvn.Mvn;
32
import com.devonfw.tools.ide.tool.plugin.ToolPluginDescriptor;
33

34
/**
35
 * {@link ToolCommandlet} for <a href="https://code.visualstudio.com/">vscode</a>.
36
 */
37
public class Vscode extends IdeToolCommandlet {
38

39
  private static final Logger LOG = LoggerFactory.getLogger(Vscode.class);
3✔
40

41
  /** The {@link #getConfiguredEdition() edition} for VSCodium. */
42
  private static final String EDITION_VSCODIUM = "vscodium";
43

44
  /** Folder name for VSCode workspace configuration. */
45
  private static final String FOLDER_VSCODE = ".vscode";
46

47
  /** Settings file name for VSCode. */
48
  private static final String SETTINGS_JSON = "settings.json";
49

50
  /** Template location for VSCode repository workspace settings. */
51
  private static final String TEMPLATE_LOCATION = "vscode/workspace/repository/" + FOLDER_VSCODE;
52

53
  /** Map of build tool classes to their corresponding VSCode settings template. */
54
  private static final Map<Class<? extends LocalToolCommandlet>, String> BUILD_TOOL_TO_TEMPLATE =
5✔
55
      Map.of(Mvn.class, SETTINGS_JSON, Gradle.class, SETTINGS_JSON);
2✔
56

57
  /**
58
   * The constructor.
59
   *
60
   * @param context the {@link IdeContext}.
61
   */
62
  public Vscode(IdeContext context) {
63

64
    super(context, "vscode", Set.of(Tag.VS_CODE));
6✔
65
  }
1✔
66

67
  @Override
68
  protected String getBinaryName() {
69

70
    if (EDITION_VSCODIUM.equals(getConfiguredEdition())) {
5✔
71
      return "codium";
2✔
72
    }
73
    return "code";
2✔
74
  }
75

76

77
  @Override
78
  public boolean installPlugin(ToolPluginDescriptor plugin, Step step, ProcessContext pc) {
79

80
    List<String> extensionsCommands = new ArrayList<>();
4✔
81
    extensionsCommands.add("--force");
4✔
82
    extensionsCommands.add("--install-extension");
4✔
83
    String extensionInstallTarget = plugin.id();
3✔
84
    // If a version number was specified, add it to the extension identifier with the format "extensionId@version"
85
    boolean versionSpecified = (plugin.version() != null) && !plugin.version().isBlank();
11!
86
    if (versionSpecified) {
2✔
87
      extensionInstallTarget = extensionInstallTarget + "@" + plugin.version();
5✔
88
    }
89
    extensionsCommands.add(extensionInstallTarget);
4✔
90
    ProcessResult result = runTool(pc, ProcessMode.DEFAULT_CAPTURE, extensionsCommands);
6✔
91
    if (result.isSuccessful()) {
3!
92
      if (versionSpecified) {
2✔
93
        IdeLogLevel.SUCCESS.log(LOG, "Successfully installed plugin: {} with version: {}", plugin.name(), plugin.version());
17✔
94
      } else {
95
        IdeLogLevel.SUCCESS.log(LOG, "Successfully installed plugin: {}", plugin.name());
11✔
96
      }
97
      step.success();
2✔
98
      return true;
2✔
99
    }
100
    if (versionSpecified) {
×
101
      IdeLogLevel.ERROR.log(LOG, "Failed to install plugin: {} with version: {}", plugin.name(), plugin.version());
×
102
    } else {
103
      IdeLogLevel.ERROR.log(LOG, "Failed to install plugin: {}", plugin.name());
×
104
    }
105
    return false;
×
106
  }
107

108
  @Override
109
  protected void configureToolArgs(ProcessContext pc, ProcessMode processMode, List<String> args) {
110

111
    if (this.context.getSystemInfo().isWsl()) {
5✔
112
      pc.withEnvVar("DONT_PROMPT_WSL_INSTALL", "1");
5✔
113
    }
114
    Path vsCodeConf = this.context.getWorkspacePath().resolve(".vscode/.userdata");
6✔
115
    pc.addArg("--new-window");
4✔
116
    pc.addArg("--user-data-dir=" + vsCodeConf);
6✔
117
    Path vsCodeExtensionFolder = this.context.getIdeHome().resolve("plugins/vscode");
6✔
118
    pc.addArg("--extensions-dir=" + vsCodeExtensionFolder);
6✔
119
    pc.addArg(this.context.getWorkspacePath());
6✔
120
    super.configureToolArgs(pc, processMode, args);
5✔
121
  }
1✔
122

123
  @Override
124
  public void importRepository(Path repositoryPath) {
125
    CommandletManager commandletManager = this.context.getCommandletManager();
4✔
126
    for (Entry<Class<? extends LocalToolCommandlet>, String> entry : BUILD_TOOL_TO_TEMPLATE.entrySet()) {
11!
127
      LocalToolCommandlet buildTool = commandletManager.getCommandlet(entry.getKey());
7✔
128
      Path buildDescriptor = buildTool.findBuildDescriptor(repositoryPath);
4✔
129
      if (buildDescriptor != null) {
2✔
130
        String templateFilename = entry.getValue();
4✔
131
        LOG.debug("Found build descriptor {} so merging template {}", buildDescriptor, templateFilename);
5✔
132
        mergeSettings(repositoryPath, templateFilename);
4✔
133
        return;
1✔
134
      }
135
    }
1✔
136
    LOG.warn("No supported build descriptor was found for project import in {}", repositoryPath);
×
137
  }
×
138

139
  /**
140
   * Creates {@link EnvironmentVariables} for resolving VSCode workspace templates with the relative project path.
141
   *
142
   * @param projectPath the relative {@link Path} from workspace to repository.
143
   * @return the resolved {@link EnvironmentVariables}.
144
   */
145
  private EnvironmentVariables getVscodeEnvironmentVariables(Path projectPath) {
146
    ExtensibleEnvironmentVariables environmentVariables = new ExtensibleEnvironmentVariables(
4✔
147
        (AbstractEnvironmentVariables) this.context.getVariables().getParent(), this.context);
7✔
148

149
    environmentVariables.setValue("PROJECT_PATH", projectPath.toString().replace('\\', '/'));
8✔
150
    return environmentVariables.resolved();
3✔
151
  }
152

153
  /**
154
   * Merges the VSCode settings template into the workspace's {@code .vscode/settings.json}.
155
   *
156
   * @param repositoryPath the {@link Path} to the repository to import.
157
   * @param configFilePath the filename of the config file (e.g. {@code settings.json}).
158
   */
159
  private void mergeSettings(Path repositoryPath, String configFilePath) {
160
    Path templatePath = this.context.getSettingsPath().resolve(TEMPLATE_LOCATION);
6✔
161
    Path templateFile = templatePath.resolve(configFilePath);
4✔
162
    if (!Files.exists(templateFile)) {
5!
163
      throw new CliException(
×
164
          "Cannot import project into workspace: template file not found at " + templateFile + "\n"
165
              + "Please do an upstream merge of your settings git repository.");
166
    }
167
    Path workspacesPath = this.context.getIdeHome().resolve(IdeContext.FOLDER_WORKSPACES);
6✔
168
    Path workspacePath = this.context.getFileAccess().findAncestor(repositoryPath, workspacesPath, 1);
8✔
169
    if (workspacePath == null) {
2!
170
      throw new CliException(
×
171
          "Cannot import project into workspace: could not find workspace from " + repositoryPath);
172
    }
173
    JsonMerger jsonMerger = new JsonMerger(this.context);
6✔
174
    EnvironmentVariables environmentVariables = getVscodeEnvironmentVariables(workspacePath.relativize(repositoryPath));
6✔
175
    Path vscodeFolder = workspacePath.resolve(FOLDER_VSCODE);
4✔
176
    Path workspaceFile = vscodeFolder.resolve(configFilePath);
4✔
177

178
    // Ensure .vscode folder exists
179
    this.context.getFileAccess().mkdirs(vscodeFolder);
5✔
180

181
    // Merge template into workspace settings (template acts as "setup" for creation, also as "update" for variable resolution)
182
    jsonMerger.merge(templateFile, templateFile, environmentVariables, workspaceFile);
7✔
183

184
    LOG.debug("Merged VSCode settings into {} for repository at {}", workspaceFile, repositoryPath);
5✔
185
  }
1✔
186

187
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc