Skip to content

Commit f813c4f

Browse files
committed
get launch config from settings.json as fallback
1 parent 13bf88f commit f813c4f

File tree

3 files changed

+148
-11
lines changed

3 files changed

+148
-11
lines changed

src/extension/debugger/configuration/launch.json/launchJsonReader.ts

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,30 @@ import * as path from 'path';
55
import * as fs from 'fs-extra';
66
import { parse } from 'jsonc-parser';
77
import { DebugConfiguration, Uri, WorkspaceFolder } from 'vscode';
8-
import { getConfiguration, getWorkspaceFolder } from '../../../common/vscodeapi';
8+
import { getWorkspaceFolder } from '../../../common/vscodeapi';
99
import { traceLog } from '../../../common/log/logging';
10+
import { getConfiguration } from './utils';
1011

1112
export async function getConfigurationsForWorkspace(workspace: WorkspaceFolder): Promise<DebugConfiguration[]> {
1213
traceLog('Getting configurations for workspace');
1314
const filename = path.join(workspace.uri.fsPath, '.vscode', 'launch.json');
1415
if (!(await fs.pathExists(filename))) {
15-
// Check launch config in the workspace file
16-
const codeWorkspaceConfig = getConfiguration('launch', workspace);
17-
if (!codeWorkspaceConfig.configurations || !Array.isArray(codeWorkspaceConfig.configurations)) {
18-
return [];
19-
}
20-
traceLog('Using configuration in workspace');
21-
return codeWorkspaceConfig.configurations;
16+
return getConfigurationsFromSettings(workspace);
2217
}
23-
2418
const text = await fs.readFile(filename, 'utf-8');
2519
const parsed = parse(text, [], { allowTrailingComma: true, disallowComments: false });
26-
if (!parsed.configurations || !Array.isArray(parsed.configurations)) {
27-
throw Error('Missing field in launch.json: configurations');
20+
// no launch.json or no configurations found in launch.json, look in settings.json
21+
if (!parsed || !parsed.configurations) {
22+
traceLog('No configurations found in launch.json, looking in settings.json.');
23+
const settingConfigs = getConfigurationsFromSettings(workspace);
24+
if (settingConfigs.length === 0) {
25+
throw Error('No configurations found in launch.json or settings.json');
26+
}
27+
return Promise.resolve(settingConfigs);
28+
}
29+
// configurations found in launch.json, verify them then return
30+
if (!Array.isArray(parsed.configurations) || parsed.configurations.length === 0) {
31+
throw Error('Invalid configurations in launch.json');
2832
}
2933
if (!parsed.version) {
3034
throw Error('Missing field in launch.json: version');
@@ -42,3 +46,14 @@ export async function getConfigurationsByUri(uri?: Uri): Promise<DebugConfigurat
4246
}
4347
return [];
4448
}
49+
50+
export function getConfigurationsFromSettings(workspace: WorkspaceFolder): DebugConfiguration[] {
51+
// look in settings.json
52+
const codeWorkspaceConfig = getConfiguration('launch', workspace);
53+
// if this includes user configs, how do I make sure it selects the workspace ones first
54+
if (!codeWorkspaceConfig.configurations || !Array.isArray(codeWorkspaceConfig.configurations)) {
55+
return [];
56+
}
57+
traceLog('Using configuration in workspace settings.json.');
58+
return codeWorkspaceConfig.configurations;
59+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { Resource } from '@vscode/python-extension';
5+
import { WorkspaceConfiguration, workspace } from 'vscode';
6+
7+
export function getConfiguration(section: string, scope?: Resource): WorkspaceConfiguration {
8+
return workspace.getConfiguration(section, scope);
9+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
'use strict';
2+
3+
import * as assert from 'assert';
4+
import * as sinon from 'sinon';
5+
import * as typemoq from 'typemoq';
6+
import * as fs from 'fs-extra';
7+
import { WorkspaceFolder, Uri, WorkspaceConfiguration } from 'vscode';
8+
import {
9+
getConfigurationsForWorkspace,
10+
getConfigurationsFromSettings,
11+
} from '../../../../extension/debugger/configuration/launch.json/launchJsonReader';
12+
import * as utils from '../../../../extension/debugger/configuration/launch.json/utils';
13+
14+
suite('Debugging - launchJsonReader', () => {
15+
let sandbox: sinon.SinonSandbox;
16+
17+
setup(() => {
18+
sandbox = sinon.createSandbox();
19+
});
20+
21+
teardown(() => {
22+
sandbox.restore();
23+
});
24+
25+
suite('getConfigurationsForWorkspace', () => {
26+
test('Should return configurations from launch.json if it exists', async () => {
27+
const workspace = typemoq.Mock.ofType<WorkspaceFolder>();
28+
workspace.setup((w) => w.uri).returns(() => Uri.file('/path/to/workspace'));
29+
30+
const launchJsonContent = `{
31+
"version": "0.2.0",
32+
"configurations": [
33+
{
34+
"name": "Launch Program",
35+
"type": "python",
36+
"request": "launch",
37+
"program": "${workspace.object.uri}/app.py"
38+
}
39+
]
40+
}`;
41+
42+
sandbox.stub(fs, 'pathExists').resolves(true);
43+
sandbox.stub(fs, 'readFile').resolves(launchJsonContent);
44+
45+
const configurations = await getConfigurationsForWorkspace(workspace.object);
46+
assert.strictEqual(configurations.length, 1);
47+
assert.strictEqual(configurations[0].name, 'Launch Program');
48+
});
49+
50+
test('Should return configurations from settings.json if launch.json does not exist', async () => {
51+
const workspace = typemoq.Mock.ofType<WorkspaceFolder>();
52+
workspace.setup((w) => w.uri).returns(() => Uri.file('/path/to/workspace'));
53+
54+
const mockConfig = typemoq.Mock.ofType<WorkspaceConfiguration>();
55+
mockConfig
56+
.setup((c) => c.configurations)
57+
.returns(() => [
58+
{
59+
name: 'Launch Program 2',
60+
type: 'python',
61+
request: 'launch',
62+
program: '${workspaceFolder}/app.py',
63+
},
64+
]);
65+
66+
sandbox.stub(fs, 'pathExists').resolves(false);
67+
sandbox.stub(utils, 'getConfiguration').returns(mockConfig.object);
68+
69+
const configurations = await getConfigurationsForWorkspace(workspace.object);
70+
assert.strictEqual(configurations.length, 1);
71+
assert.strictEqual(configurations[0].name, 'Launch Program 2');
72+
});
73+
});
74+
75+
suite('getConfigurationsFromSettings', () => {
76+
test('Should return configurations from settings.json', () => {
77+
const workspace = typemoq.Mock.ofType<WorkspaceFolder>();
78+
workspace.setup((w) => w.uri).returns(() => Uri.file('/path/to/workspace'));
79+
80+
const mockConfig = typemoq.Mock.ofType<WorkspaceConfiguration>();
81+
mockConfig
82+
.setup((c) => c.configurations)
83+
.returns(() => [
84+
{
85+
name: 'Launch Program 3',
86+
type: 'python',
87+
request: 'launch',
88+
program: '${workspaceFolder}/app.py',
89+
},
90+
]);
91+
92+
sandbox.stub(utils, 'getConfiguration').returns(mockConfig.object);
93+
94+
const configurations = getConfigurationsFromSettings(workspace.object);
95+
assert.strictEqual(configurations.length, 1);
96+
assert.strictEqual(configurations[0].name, 'Launch Program 3');
97+
});
98+
99+
test('Should return empty array if no configurations in settings.json', () => {
100+
const workspace = typemoq.Mock.ofType<WorkspaceFolder>();
101+
workspace.setup((w) => w.uri).returns(() => Uri.file('/path/to/workspace'));
102+
103+
const mockConfig = typemoq.Mock.ofType<WorkspaceConfiguration>();
104+
mockConfig.setup((c) => c.get('configurations')).returns(() => []);
105+
mockConfig.setup((c) => c.configurations).returns(() => []);
106+
107+
sandbox.stub(utils, 'getConfiguration').returns(mockConfig.object);
108+
109+
const configurations = getConfigurationsFromSettings(workspace.object);
110+
assert.strictEqual(configurations.length, 0);
111+
});
112+
});
113+
});

0 commit comments

Comments
 (0)