Skip to content

Commit c4f7da5

Browse files
committed
Runconfig settings now support vscode variables resolution and refactored the runconfig settings merge logic with launch.json for better testiblity
1 parent 86aa3aa commit c4f7da5

File tree

6 files changed

+342
-62
lines changed

6 files changed

+342
-62
lines changed

vscode/src/configurations/listener.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
*/
1616

1717
import { ConfigurationChangeEvent, ExtensionContext, workspace } from "vscode";
18-
import { userConfigsListened } from "./configuration";
18+
import { configKeys, userConfigsListened } from "./configuration";
1919
import { Disposable } from "vscode-languageclient";
2020
import { globalState } from "../globalState";
21+
import { runConfigurationUpdateAll } from "../views/runConfiguration";
22+
import { appendPrefixToCommand } from "../utils";
2123

2224
const configChangeHandler = (params: ConfigurationChangeEvent) => {
2325
userConfigsListened.forEach((config: string) => {
@@ -26,6 +28,11 @@ const configChangeHandler = (params: ConfigurationChangeEvent) => {
2628
globalState.getClientPromise().restartExtension(globalState.getNbProcessManager(), true);
2729
}
2830
});
31+
32+
const runConfigSection = appendPrefixToCommand(configKeys.runConfigArguments.split('.')[0]);
33+
if (params.affectsConfiguration(runConfigSection)) {
34+
runConfigurationUpdateAll();
35+
}
2936
}
3037

3138
const configChangeListener = workspace.onDidChangeConfiguration(configChangeHandler);

vscode/src/debugger/debugger.ts

Lines changed: 5 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ import { extConstants } from '../constants';
2323
import { l10n } from '../localiser';
2424
import { StreamDebugAdapter } from './streamDebugAdapter';
2525
import { extCommands, nbCommands } from '../commands/commands';
26-
import { argumentsNode, environmentVariablesNode, vmOptionsNode, workingDirectoryNode } from '../views/runConfiguration';
27-
import { initializeRunConfiguration, parseArguments } from '../utils';
26+
import { getRunConfigurationValues } from '../views/runConfiguration';
27+
import { initializeRunConfiguration } from '../utils';
2828
import { globalState } from '../globalState';
29+
import { applyRunConfigurationOverrides } from '../views/runConfigurationUtils';
2930

3031
export function registerDebugger(context: ExtensionContext): void {
3132
let debugTrackerFactory = new NetBeansDebugAdapterTrackerFactory();
@@ -217,59 +218,6 @@ class NetBeansConfigurationResolver implements vscode.DebugConfigurationProvider
217218
class RunConfigurationProvider implements vscode.DebugConfigurationProvider {
218219

219220
resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> {
220-
return new Promise<vscode.DebugConfiguration>(resolve => {
221-
resolve(config);
222-
});
223-
}
224-
225-
resolveDebugConfigurationWithSubstitutedVariables?(_folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> {
226-
return new Promise<vscode.DebugConfiguration>(resolve => {
227-
const args = argumentsNode.getValue();
228-
if (args) {
229-
if (!config.args) {
230-
config.args = args;
231-
} else {
232-
config.args = `${config.args} ${args}`;
233-
}
234-
}
235-
236-
const vmArgs = vmOptionsNode.getValue();
237-
if (vmArgs) {
238-
if (!config.vmArgs) {
239-
config.vmArgs = vmArgs;
240-
} else if (Array.isArray(config.vmArgs)) {
241-
let cfg: string[] = config.vmArgs;
242-
243-
const result = parseArguments(vmArgs);
244-
cfg.push(...result);
245-
} else {
246-
// assume the config is a string
247-
config.vmArgs = `${config.vmArgs} ${vmArgs}`;
248-
}
249-
}
250-
251-
const env = environmentVariablesNode.getValue();
252-
if (env) {
253-
const envs = env.split(',');
254-
if (!config.env) {
255-
config.env = {};
256-
}
257-
for (let val of envs) {
258-
val = val.trim();
259-
const div = val.indexOf('=');
260-
if (div > 0) { // div === 0 means bad format (no ENV name)
261-
config.env[val.substring(0, div)] = val.substring(div + 1, val.length);
262-
}
263-
}
264-
}
265-
266-
const cwd = workingDirectoryNode.getValue();
267-
if (cwd) {
268-
config.cwd = cwd;
269-
}
270-
271-
resolve(config);
272-
});
221+
return applyRunConfigurationOverrides(config, getRunConfigurationValues());
273222
}
274-
275-
}
223+
}

vscode/src/lsp/listeners/requests/handlers.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,6 @@ const updateConfigRequestHandler = async (param: any) => {
9999
wsConfig.update(param.key, param.value, wsFile ? null : true)
100100
.then(() => {
101101
LOGGER.log("Updated configuration: " + param.section + "." + param.key + "=" + param.value + "; in: " + (wsFile ? wsFile.toString() : "Global"));
102-
})
103-
.then(() => {
104-
runConfigurationUpdateAll();
105102
});
106103
} catch (err) {
107104
LOGGER.error("Failed to update configuration. Reason: " + (isString(err) ? err : isError(err) ? err.message : "error"));
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/*
2+
Copyright (c) 2026, Oracle and/or its affiliates.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
import { expect } from 'chai';
17+
import { describe, it } from 'mocha';
18+
import { applyRunConfigurationOverrides } from '../../../views/runConfigurationUtils';
19+
import { DebugConfiguration } from 'vscode';
20+
21+
describe('runConfigurationUtils', () => {
22+
it('leaves the configuration unchanged when no overrides are provided', () => {
23+
const config = createDebugConfiguration({
24+
args: '--existing',
25+
vmArgs: '-Xmx512m',
26+
env: { EXISTING: '1' },
27+
cwd: '/existing'
28+
});
29+
30+
const updated = applyRunConfigurationOverrides(config, {});
31+
32+
expect(updated).to.deep.equal(config);
33+
});
34+
35+
it('appends launch arguments to an existing string value', () => {
36+
const config = createDebugConfiguration({
37+
args: '--existing'
38+
});
39+
40+
const updated = applyRunConfigurationOverrides(config, {
41+
args: '--added'
42+
});
43+
44+
expect(updated.args).to.equal('--existing --added');
45+
});
46+
47+
it('adds parsed vm arguments into an existing array', () => {
48+
const existingVmArgs = ['-Xmx512m'];
49+
const config = createDebugConfiguration({
50+
vmArgs: existingVmArgs
51+
});
52+
53+
const updated = applyRunConfigurationOverrides(config, {
54+
vmArgs: '-Dfoo=bar "-Dquoted=value here"'
55+
});
56+
57+
expect(updated.vmArgs).to.deep.equal([
58+
'-Xmx512m',
59+
'-Dfoo=bar',
60+
'"-Dquoted=value here"'
61+
]);
62+
63+
expect(existingVmArgs).to.deep.equal(['-Xmx512m']);
64+
});
65+
66+
it('appends vm arguments to an existing string value', () => {
67+
const config = createDebugConfiguration({
68+
vmArgs: '-Xmx512m'
69+
});
70+
71+
const updated = applyRunConfigurationOverrides(config, {
72+
vmArgs: '-Dfoo=bar'
73+
});
74+
75+
expect(updated.vmArgs).to.equal('-Xmx512m -Dfoo=bar');
76+
});
77+
78+
it('uses new vm arguments as-is when the config has none yet', () => {
79+
const config = createDebugConfiguration();
80+
81+
const updated = applyRunConfigurationOverrides(config, {
82+
vmArgs: '-Dfoo=bar'
83+
});
84+
85+
expect(updated.vmArgs).to.equal('-Dfoo=bar');
86+
});
87+
88+
it('merges environment variables and ignores malformed entries', () => {
89+
const config = createDebugConfiguration({
90+
env: { EXISTING: '1' }
91+
});
92+
93+
const updated = applyRunConfigurationOverrides(config, {
94+
env: ' FIRST = one , invalid, =missingName, SECOND=two=2 '
95+
});
96+
97+
expect(updated.env).to.deep.equal({
98+
EXISTING: '1',
99+
FIRST: 'one',
100+
SECOND: 'two=2'
101+
});
102+
});
103+
104+
it('parses environment variables through applyRunConfigurationOverrides', () => {
105+
const config = createDebugConfiguration();
106+
107+
const updated = applyRunConfigurationOverrides(config, {
108+
env: 'A=1, B=two'
109+
});
110+
111+
expect(updated.env).to.deep.equal({
112+
A: '1',
113+
B: 'two'
114+
});
115+
});
116+
117+
it('overrides existing environment variable keys with new values', () => {
118+
const config = createDebugConfiguration({
119+
env: { A: '1', B: '2' }
120+
});
121+
122+
const updated = applyRunConfigurationOverrides(config, {
123+
env: 'B=updated, C=3'
124+
});
125+
126+
expect(updated.env).to.deep.equal({
127+
A: '1',
128+
B: 'updated',
129+
C: '3'
130+
});
131+
});
132+
133+
it('creates a fresh env record when the current env is not an object', () => {
134+
const config = createDebugConfiguration({
135+
env: 'invalid-env-shape'
136+
});
137+
138+
const updated = applyRunConfigurationOverrides(config, {
139+
env: 'A=1'
140+
});
141+
142+
expect(updated.env).to.deep.equal({
143+
A: '1'
144+
});
145+
});
146+
147+
it('overrides cwd when provided', () => {
148+
const config = createDebugConfiguration();
149+
150+
const updated = applyRunConfigurationOverrides(config, {
151+
cwd: '/tmp/workdir'
152+
});
153+
154+
expect(updated.cwd).to.equal('/tmp/workdir');
155+
});
156+
157+
it('does not replace existing values with empty-string overrides', () => {
158+
const config = createDebugConfiguration({
159+
args: '--existing',
160+
vmArgs: '-Xmx512m',
161+
env: { EXISTING: '1' },
162+
cwd: '/existing'
163+
});
164+
165+
const updated = applyRunConfigurationOverrides(config, {
166+
args: '',
167+
vmArgs: '',
168+
env: '',
169+
cwd: ''
170+
});
171+
172+
expect(updated).to.deep.equal({
173+
type: 'java+',
174+
name: 'Test Config',
175+
request: 'launch',
176+
args: '--existing',
177+
vmArgs: '-Xmx512m',
178+
env: { EXISTING: '1' },
179+
cwd: '/existing'
180+
});
181+
});
182+
183+
it('ignores whitespace-only overrides', () => {
184+
const config = createDebugConfiguration({
185+
args: '--existing',
186+
vmArgs: '-Xmx512m',
187+
env: { EXISTING: '1' },
188+
cwd: '/existing'
189+
});
190+
191+
const updated = applyRunConfigurationOverrides(config, {
192+
args: ' ',
193+
vmArgs: ' ',
194+
env: ' ',
195+
cwd: ' '
196+
});
197+
198+
expect(updated).to.deep.equal(config);
199+
});
200+
201+
it('replaces a non-string args value with the new override', () => {
202+
const config = createDebugConfiguration({
203+
args: ['unexpected-array']
204+
});
205+
206+
const updated = applyRunConfigurationOverrides(config, {
207+
args: '--added'
208+
});
209+
210+
expect(updated.args).to.equal('--added');
211+
});
212+
});
213+
214+
const createDebugConfiguration = (overrides: Record<string, unknown> = {}): DebugConfiguration => {
215+
return {
216+
type: 'java+',
217+
name: 'Test Config',
218+
request: 'launch',
219+
...overrides
220+
};
221+
}

vscode/src/views/runConfiguration.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class RunConfigurationNode extends vscode.TreeItem {
9898

9999
updateNode(reload?: boolean) {
100100
if (reload) {
101-
this.value = getConfigurationValue(this.settingsKey) as string;
101+
this.value = getConfigurationValue(this.settingsKey);
102102
}
103103
this.description = this.value ? this.value : l10n.value("jdk.extension.runConfig.default.label");
104104
this.tooltip = `${this.label} ${this.description}`;
@@ -158,3 +158,16 @@ export function runConfigurationUpdateAll() {
158158
environmentVariablesNode.updateNode(true);
159159
workingDirectoryNode.updateNode(true);
160160
}
161+
export interface RunConfigurationNodes {
162+
readonly args?: string;
163+
readonly vmArgs?: string;
164+
readonly env?: string;
165+
readonly cwd?: string;
166+
}
167+
168+
export const getRunConfigurationValues = (): RunConfigurationNodes => ({
169+
args: argumentsNode.getValue(),
170+
vmArgs: vmOptionsNode.getValue(),
171+
env: environmentVariablesNode.getValue(),
172+
cwd: workingDirectoryNode.getValue(),
173+
});

0 commit comments

Comments
 (0)