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

devonfw / IDEasy / 43020566664

11 Nov 2026 12:27PM UTC coverage: 75.347% (+0.1%) from 75.242%
43020566664

push

github

web-flow
#2393: open a project folder (--project) instead of the main workspace (#2565)

* #2491: add --project flag with path resolution/validation and getOpenPath() foundation

Introduces an optional --project flag on IDE launch commands (e.g. `ide intellij
--project <path>`). The flag is consumed by IDEasy and never passed through to the
IDE binary; its value becomes the folder the IDE opens.

Key parts:
- getOpenPath() is the single source of truth for "which folder does this launch
  open?": the explicit --project folder when given (relative paths resolved against
  the current working directory), otherwise getWorkspacePath() (today's default).
- A launch with a missing or not-a-folder --project path aborts with a clear error
  and the IDE is not started.
- extractProjectOption() strips the flag from the trailing passthrough args, which
  the framework would otherwise forward verbatim to the IDE binary.

Default (no-flag) behavior is unchanged.

* #2492: open the folder given by getOpenPath() in IntelliJ/VS Code launch args

Point the IDE launch-argument seams at getOpenPath() (sub-task #2 of #2393)
instead of getWorkspacePath(), so a launch with an explicit --project flag opens
exactly that folder:

- IdeaBasedIdeToolCommandlet.runTool appends getOpenPath() (covers all JetBrains /
  IDEA-based commandlets: IntelliJ, Android Studio, PyCharm, via the shared base).
- Vscode.configureToolArgs appends getOpenPath(); --user-data-dir / --profile stay
  on the main profile (sharing is decided behavior per #2393).

Without a --project flag getOpenPath() falls back to getWorkspacePath(), so the
default (main workspace) behavior is unchanged. Eclipse is left out (different
workspace model). Supersedes the no-flag CWD approach of #2434 / #2236.

Tests (AssertJ):
- IntelliJ with --project -> launch args target the selected folder, not the workspace.
- VS Code with --project -> configureToolArgs appends the folder; profile stays on main.
- VS Code no-flag -> configure... (continued)

6452 of 9489 branches covered (67.99%)

Branch coverage included in aggregate %.

16482 of 20949 relevant lines covered (78.68%)

3.39 hits per line

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

89.58
cli/src/main/java/com/devonfw/tools/ide/commandlet/AbstractCommandlet.java
1
package com.devonfw.tools.ide.commandlet;
2

3
import java.util.ArrayList;
4
import java.util.Collections;
5
import java.util.HashMap;
6
import java.util.List;
7
import java.util.Map;
8

9
import com.devonfw.tools.ide.context.AbstractIdeContext;
10
import com.devonfw.tools.ide.context.IdeContext;
11
import com.devonfw.tools.ide.function.AbstractParameterizedFunction;
12
import com.devonfw.tools.ide.nls.NlsBundle;
13
import com.devonfw.tools.ide.property.KeywordProperty;
14
import com.devonfw.tools.ide.property.Property;
15
import com.devonfw.tools.ide.tool.AbstractToolCommandlet;
16
import com.devonfw.tools.ide.version.VersionIdentifier;
17

18
/**
19
 * A {@link AbstractCommandlet} is a sub-command of the IDE CLI.
20
 */
21
public abstract class AbstractCommandlet extends AbstractParameterizedFunction implements Commandlet {
22

23
  /** The {@link IdeContext} instance. */
24
  protected final IdeContext context;
25

26
  private final List<Property<?>> valuesList;
27

28
  private final List<Property<?>> values;
29

30
  private final Map<String, Property<?>> optionMap;
31

32
  private KeywordProperty firstKeyword;
33

34
  /**
35
   * The constructor.
36
   *
37
   * @param context the {@link IdeContext}.
38
   */
39
  public AbstractCommandlet(IdeContext context) {
40

41
    super();
2 ✔
42
    this.context = context;
3 ✔
43
    this.valuesList = new ArrayList<>();
5 ✔
44
    this.values = Collections.unmodifiableList(this.valuesList);
5 ✔
45
    this.optionMap = new HashMap<>();
5 ✔
46
  }
1 ✔
47

48
  /**
49
   * @return the {@link List} of {@link Property properties} that are {@link Property#isValue() values}.
50
   */
51
  public List<Property<?>> getValues() {
52

53
    return this.values;
3 ✔
54
  }
55

56
  /**
57
   * Initializes this commandlet. Unfortunately Java has some lifecycle flaw that constructors are called linear across the inheritance hierarchy while fields
58
   * can only be initialized after super-constructor is fully executed. So unlike with overriding methods where you can put code before AND after the
59
   * super-call, for constructors this is not possible. Hence, an initializer triggered by some super-class cannot access fields only declared in a subclass
60
   * even if that class overrides some init method called from the constructor. As a result we have to declare this method and call it from the outside
61
   * ({@link CommandletManager}) after the constructor completed.
62
   */
63
  protected void initialize() {
64

65
  }
1 ✔
66

67
  /**
68
   * @param nameOrAlias the potential {@link Property#getName() name} or {@link Property#getAlias() alias} of the requested {@link Property}.
69
   * @return the requested {@link Property property} or {@code null} if not found.
70
   */
71
  public Property<?> getOption(String nameOrAlias) {
72

73
    return this.optionMap.get(nameOrAlias);
6 ✔
74
  }
75

76
  @Override
77
  protected <P extends Property<?>> P add(P property) {
78

79
    super.add(property);
4 ✔
80
    if (property.isValue()) {
3 ✔
81
      this.valuesList.add(property);
5 ✔
82
    }
83
    return property;
2 ✔
84
  }
85

86
  @Override
87
  protected Property<?> registerOption(String name, Property<?> property) {
88

89
    return this.optionMap.put(name, property);
7 ✔
90
  }
91

92
  /**
93
   * @param keyword the {@link KeywordProperty keyword} to {@link #add(Property) add}.
94
   */
95
  protected void addKeyword(String keyword) {
96

97
    addKeyword(keyword, null);
4 ✔
98
  }
1 ✔
99

100
  /**
101
   * @param keyword the {@link KeywordProperty keyword} to {@link #add(Property) add}.
102
   * @param alias the optional {@link KeywordProperty#getAlias() alias}.
103
   */
104
  protected void addKeyword(String keyword, String alias) {
105

106
    KeywordProperty property = new KeywordProperty(keyword, true, alias);
7 ✔
107
    if (this.firstKeyword == null) {
3 ✔
108
      if (!getProperties().isEmpty()) {
4 !
109
        throw new IllegalStateException(property + " must be first property in " + getClass().getSimpleName());
×
110
      }
111
      this.firstKeyword = property;
3 ✔
112
    }
113
    add(property);
4 ✔
114
  }
1 ✔
115

116
  @Override
117
  public IdeContext getContext() {
118

119
    return this.context;
3 ✔
120
  }
121

122
  /**
123
   * @return the first keyword of this {@link AbstractCommandlet}. Typically, the same as {@link #getName() name} but may also differ (e.g. "set" vs.
124
   *     "set-version").
125
   */
126
  public KeywordProperty getFirstKeyword() {
127

128
    return this.firstKeyword;
3 ✔
129
  }
130

131
  /**
132
   * @param <C> type of the {@link AbstractCommandlet}.
133
   * @param commandletType the {@link Class} reflecting the requested {@link AbstractCommandlet}.
134
   * @return the requested {@link AbstractCommandlet}.
135
   * @see CommandletManager#getCommandlet(Class)
136
   */
137
  protected <C extends AbstractCommandlet> C getCommandlet(Class<C> commandletType) {
138

139
    return this.context.getCommandletManager().getCommandlet(commandletType);
6 ✔
140
  }
141

142
  /**
143
   * @return {@code true} if {@link IdeContext#getIdeHome() IDE_HOME} is required for this commandlet, {@code false} otherwise.
144
   */
145
  public boolean isIdeHomeRequired() {
146

147
    return isIdeRootRequired();
3 ✔
148
  }
149

150
  /**
151
   * @return {@code true} if {@link IdeContext#getIdeRoot() IDE_ROOT} is required for this commandlet, {@code false} otherwise.
152
   */
153
  public boolean isIdeRootRequired() {
154

155
    return true;
2 ✔
156
  }
157

158
  /**
159
   * @return {@code true} to suppress the {@link com.devonfw.tools.ide.step.StepImpl#logSummary(boolean) step summary success message}.
160
   */
161
  public boolean isSuppressStepSuccess() {
162

163
    return false;
2 ✔
164
  }
165

166
  /**
167
   * @return {@code true} if the output of this commandlet is (potentially) processed automatically from outside, {@code false} otherwise. For example
168
   *     {@link CompleteCommandlet} logs the suggestions for auto-completion to a bash script. Also, the {@link EnvironmentCommandlet} logs the environment
169
   *     variables for the {@code ide} wrapper script. In such scenarios these logs shall not be spammed with warnings like "IDE_ROOT is not set" that would
170
   *     break the processing of the output.
171
   */
172
  public boolean isProcessableOutput() {
173

174
    return false;
2 ✔
175
  }
176

177
  /**
178
   * @return {@code true} to write a logfile (unless disabled via {@link com.devonfw.tools.ide.variable.IdeVariables#IDE_WRITE_LOGFILE}), {@code false}
179
   *     otherwise.
180
   */
181
  public boolean isWriteLogFile() {
182
    return !isProcessableOutput();
×
183
  }
184

185
  /**
186
   * Runs this {@link AbstractCommandlet}.
187
   */
188
  public final void run() {
189

190
    if (this.context != null) { // for ContextCommandlet we do not have a context yet
3 ✔
191
      ((AbstractIdeContext) this.context).configureJavaUtilLogging(this);
5 ✔
192
    }
193
    doRun();
2 ✔
194
  }
1 ✔
195

196
  protected abstract void doRun();
197

198
  /**
199
   * Provide additional usage help of this {@link AbstractCommandlet} to the user.
200
   *
201
   * @param bundle the {@link NlsBundle} to get I18N messages from.
202
   */
203
  public void printHelp(NlsBundle bundle) {
204

205
  }
1 ✔
206

207
  @Override
208
  public String toString() {
209

210
    return getClass().getSimpleName() + "[" + getName() + "]";
7 ✔
211
  }
212

213
  /**
214
   * @return the {@link AbstractToolCommandlet} set in a {@link Property} of this commandlet used for auto-completion of a {@link VersionIdentifier} or
215
   *     {@link com.devonfw.tools.ide.tool.plugin.ToolPluginDescriptor}, otherwise {@code null} if not exists or not configured.
216
   */
217
  public AbstractToolCommandlet getToolForCompletion() {
218
    return null;
2 ✔
219
  }
220
}
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