Skip to content

Commit 9701c53

Browse files
Remove DI from config launch (#20226)
closed: #19769
1 parent ad0286c commit 9701c53

16 files changed

+193
-254
lines changed

src/client/debugger/extension/configuration/dynamicdebugConfigurationService.ts

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,18 @@
44
'use strict';
55

66
import * as path from 'path';
7-
import { inject, injectable } from 'inversify';
7+
import * as fs from 'fs-extra';
8+
import { injectable } from 'inversify';
89
import { CancellationToken, DebugConfiguration, WorkspaceFolder } from 'vscode';
910
import { IDynamicDebugConfigurationService } from '../types';
10-
import { IFileSystem } from '../../../common/platform/types';
11-
import { IPathUtils } from '../../../common/types';
1211
import { DebuggerTypeName } from '../../constants';
1312
import { asyncFilter } from '../../../common/utils/arrayUtils';
1413

1514
const workspaceFolderToken = '${workspaceFolder}';
1615

1716
@injectable()
1817
export class DynamicPythonDebugConfigurationService implements IDynamicDebugConfigurationService {
19-
constructor(@inject(IFileSystem) private fs: IFileSystem, @inject(IPathUtils) private pathUtils: IPathUtils) {}
20-
18+
// eslint-disable-next-line class-methods-use-this
2119
public async provideDebugConfigurations(
2220
folder: WorkspaceFolder,
2321
_token?: CancellationToken,
@@ -32,20 +30,20 @@ export class DynamicPythonDebugConfigurationService implements IDynamicDebugConf
3230
justMyCode: true,
3331
});
3432

35-
const djangoManagePath = await this.getDjangoPath(folder);
33+
const djangoManagePath = await DynamicPythonDebugConfigurationService.getDjangoPath(folder);
3634
if (djangoManagePath) {
3735
providers.push({
3836
name: 'Python: Django',
3937
type: DebuggerTypeName,
4038
request: 'launch',
41-
program: `${workspaceFolderToken}${this.pathUtils.separator}${djangoManagePath}`,
39+
program: `${workspaceFolderToken}${path.sep}${djangoManagePath}`,
4240
args: ['runserver'],
4341
django: true,
4442
justMyCode: true,
4543
});
4644
}
4745

48-
const flaskPath = await this.getFlaskPath(folder);
46+
const flaskPath = await DynamicPythonDebugConfigurationService.getFlaskPath(folder);
4947
if (flaskPath) {
5048
providers.push({
5149
name: 'Python: Flask',
@@ -62,12 +60,9 @@ export class DynamicPythonDebugConfigurationService implements IDynamicDebugConf
6260
});
6361
}
6462

65-
let fastApiPath = await this.getFastApiPath(folder);
63+
let fastApiPath = await DynamicPythonDebugConfigurationService.getFastApiPath(folder);
6664
if (fastApiPath) {
67-
fastApiPath = path
68-
.relative(folder.uri.fsPath, fastApiPath)
69-
.replaceAll(this.pathUtils.separator, '.')
70-
.replace('.py', '');
65+
fastApiPath = path.relative(folder.uri.fsPath, fastApiPath).replaceAll(path.sep, '.').replace('.py', '');
7166
providers.push({
7267
name: 'Python: FastAPI',
7368
type: DebuggerTypeName,
@@ -82,19 +77,19 @@ export class DynamicPythonDebugConfigurationService implements IDynamicDebugConf
8277
return providers;
8378
}
8479

85-
private async getDjangoPath(folder: WorkspaceFolder) {
80+
private static async getDjangoPath(folder: WorkspaceFolder) {
8681
const regExpression = /execute_from_command_line\(/;
87-
const possiblePaths = await this.getPossiblePaths(
82+
const possiblePaths = await DynamicPythonDebugConfigurationService.getPossiblePaths(
8883
folder,
8984
['manage.py', '*/manage.py', 'app.py', '*/app.py'],
9085
regExpression,
9186
);
9287
return possiblePaths.length ? path.relative(folder.uri.fsPath, possiblePaths[0]) : null;
9388
}
9489

95-
private async getFastApiPath(folder: WorkspaceFolder) {
90+
private static async getFastApiPath(folder: WorkspaceFolder) {
9691
const regExpression = /app\s*=\s*FastAPI\(/;
97-
const fastApiPaths = await this.getPossiblePaths(
92+
const fastApiPaths = await DynamicPythonDebugConfigurationService.getPossiblePaths(
9893
folder,
9994
['main.py', 'app.py', '*/main.py', '*/app.py', '*/*/main.py', '*/*/app.py'],
10095
regExpression,
@@ -103,9 +98,9 @@ export class DynamicPythonDebugConfigurationService implements IDynamicDebugConf
10398
return fastApiPaths.length ? fastApiPaths[0] : null;
10499
}
105100

106-
private async getFlaskPath(folder: WorkspaceFolder) {
101+
private static async getFlaskPath(folder: WorkspaceFolder) {
107102
const regExpression = /app(?:lication)?\s*=\s*(?:flask\.)?Flask\(|def\s+(?:create|make)_app\(/;
108-
const flaskPaths = await this.getPossiblePaths(
103+
const flaskPaths = await DynamicPythonDebugConfigurationService.getPossiblePaths(
109104
folder,
110105
['__init__.py', 'app.py', 'wsgi.py', '*/__init__.py', '*/app.py', '*/wsgi.py'],
111106
regExpression,
@@ -114,16 +109,23 @@ export class DynamicPythonDebugConfigurationService implements IDynamicDebugConf
114109
return flaskPaths.length ? flaskPaths[0] : null;
115110
}
116111

117-
private async getPossiblePaths(folder: WorkspaceFolder, globPatterns: string[], regex: RegExp): Promise<string[]> {
112+
private static async getPossiblePaths(
113+
folder: WorkspaceFolder,
114+
globPatterns: string[],
115+
regex: RegExp,
116+
): Promise<string[]> {
118117
const foundPathsPromises = (await Promise.allSettled(
119118
globPatterns.map(
120-
async (pattern): Promise<string[]> => this.fs.search(path.join(folder.uri.fsPath, pattern)),
119+
async (pattern): Promise<string[]> =>
120+
(await fs.pathExists(path.join(folder.uri.fsPath, pattern)))
121+
? [path.join(folder.uri.fsPath, pattern)]
122+
: [],
121123
),
122124
)) as { status: string; value: [] }[];
123125
const possiblePaths: string[] = [];
124126
foundPathsPromises.forEach((result) => possiblePaths.push(...result.value));
125127
const finalPaths = await asyncFilter(possiblePaths, async (possiblePath) =>
126-
regex.exec((await this.fs.readFile(possiblePath)).toString()),
128+
regex.exec((await fs.readFile(possiblePath)).toString()),
127129
);
128130

129131
return finalPaths;

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

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,36 @@
22
// Licensed under the MIT License.
33

44
import * as path from 'path';
5+
import * as fs from 'fs-extra';
56
import { parse } from 'jsonc-parser';
6-
import { inject, injectable } from 'inversify';
77
import { DebugConfiguration, Uri, WorkspaceFolder } from 'vscode';
8-
import { IFileSystem } from '../../../../common/platform/types';
9-
import { ILaunchJsonReader } from '../types';
10-
import { IWorkspaceService } from '../../../../common/application/types';
8+
import { getWorkspaceFolder } from '../utils/workspaceFolder';
119

12-
@injectable()
13-
export class LaunchJsonReader implements ILaunchJsonReader {
14-
constructor(
15-
@inject(IFileSystem) private readonly fs: IFileSystem,
16-
@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService,
17-
) {}
10+
export async function getConfigurationsForWorkspace(workspace: WorkspaceFolder): Promise<DebugConfiguration[]> {
11+
const filename = path.join(workspace.uri.fsPath, '.vscode', 'launch.json');
1812

19-
public async getConfigurationsForWorkspace(workspace: WorkspaceFolder): Promise<DebugConfiguration[]> {
20-
const filename = path.join(workspace.uri.fsPath, '.vscode', 'launch.json');
21-
22-
if (!(await this.fs.fileExists(filename))) {
23-
return [];
24-
}
13+
if (!(await fs.pathExists(filename))) {
14+
return [];
15+
}
2516

26-
const text = await this.fs.readFile(filename);
27-
const parsed = parse(text, [], { allowTrailingComma: true, disallowComments: false });
28-
if (!parsed.configurations || !Array.isArray(parsed.configurations)) {
29-
throw Error('Missing field in launch.json: configurations');
30-
}
31-
if (!parsed.version) {
32-
throw Error('Missing field in launch.json: version');
33-
}
34-
// We do not bother ensuring each item is a DebugConfiguration...
35-
return parsed.configurations;
17+
const text = await fs.readFile(filename, 'utf-8');
18+
const parsed = parse(text, [], { allowTrailingComma: true, disallowComments: false });
19+
if (!parsed.configurations || !Array.isArray(parsed.configurations)) {
20+
throw Error('Missing field in launch.json: configurations');
21+
}
22+
if (!parsed.version) {
23+
throw Error('Missing field in launch.json: version');
3624
}
25+
// We do not bother ensuring each item is a DebugConfiguration...
26+
return parsed.configurations;
27+
}
3728

38-
public async getConfigurationsByUri(uri: Uri): Promise<DebugConfiguration[]> {
39-
const workspace = this.workspaceService.getWorkspaceFolder(uri);
29+
export async function getConfigurationsByUri(uri?: Uri): Promise<DebugConfiguration[]> {
30+
if (uri) {
31+
const workspace = getWorkspaceFolder(uri);
4032
if (workspace) {
41-
return this.getConfigurationsForWorkspace(workspace);
33+
return getConfigurationsForWorkspace(workspace);
4234
}
43-
return [];
4435
}
36+
return [];
4537
}

src/client/debugger/extension/configuration/launch.json/updaterService.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { inject, injectable } from 'inversify';
77
import { IExtensionSingleActivationService } from '../../../../activation/types';
8-
import { ICommandManager, IDocumentManager, IWorkspaceService } from '../../../../common/application/types';
8+
import { ICommandManager } from '../../../../common/application/types';
99
import { IDisposableRegistry } from '../../../../common/types';
1010
import { IDebugConfigurationService } from '../../types';
1111
import { LaunchJsonUpdaterServiceHelper } from './updaterServiceHelper';
@@ -17,18 +17,11 @@ export class LaunchJsonUpdaterService implements IExtensionSingleActivationServi
1717
constructor(
1818
@inject(ICommandManager) private readonly commandManager: ICommandManager,
1919
@inject(IDisposableRegistry) private readonly disposableRegistry: IDisposableRegistry,
20-
@inject(IWorkspaceService) private readonly workspace: IWorkspaceService,
21-
@inject(IDocumentManager) private readonly documentManager: IDocumentManager,
2220
@inject(IDebugConfigurationService) private readonly configurationProvider: IDebugConfigurationService,
2321
) {}
2422

2523
public async activate(): Promise<void> {
26-
const handler = new LaunchJsonUpdaterServiceHelper(
27-
this.commandManager,
28-
this.workspace,
29-
this.documentManager,
30-
this.configurationProvider,
31-
);
24+
const handler = new LaunchJsonUpdaterServiceHelper(this.commandManager, this.configurationProvider);
3225
this.disposableRegistry.push(
3326
this.commandManager.registerCommand(
3427
'python.SelectAndInsertDebugConfiguration',

src/client/debugger/extension/configuration/launch.json/updaterServiceHelper.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@
55

66
import { createScanner, parse, SyntaxKind } from 'jsonc-parser';
77
import { CancellationToken, DebugConfiguration, Position, Range, TextDocument, WorkspaceEdit } from 'vscode';
8-
import { ICommandManager, IDocumentManager, IWorkspaceService } from '../../../../common/application/types';
8+
import { ICommandManager } from '../../../../common/application/types';
99
import { noop } from '../../../../common/utils/misc';
1010
import { captureTelemetry } from '../../../../telemetry';
1111
import { EventName } from '../../../../telemetry/constants';
1212
import { IDebugConfigurationService } from '../../types';
13+
import { applyEdit, getActiveTextEditor } from '../utils/common';
14+
import { getWorkspaceFolder } from '../utils/workspaceFolder';
1315

1416
type PositionOfCursor = 'InsideEmptyArray' | 'BeforeItem' | 'AfterItem';
1517
type PositionOfComma = 'BeforeCursor';
1618

1719
export class LaunchJsonUpdaterServiceHelper {
1820
constructor(
1921
private readonly commandManager: ICommandManager,
20-
private readonly workspace: IWorkspaceService,
21-
private readonly documentManager: IDocumentManager,
2222
private readonly configurationProvider: IDebugConfigurationService,
2323
) {}
2424

@@ -28,8 +28,9 @@ export class LaunchJsonUpdaterServiceHelper {
2828
position: Position,
2929
token: CancellationToken,
3030
): Promise<void> {
31-
if (this.documentManager.activeTextEditor && this.documentManager.activeTextEditor.document === document) {
32-
const folder = this.workspace.getWorkspaceFolder(document.uri);
31+
const activeTextEditor = getActiveTextEditor();
32+
if (activeTextEditor && activeTextEditor.document === document) {
33+
const folder = getWorkspaceFolder(document.uri);
3334
const configs = await this.configurationProvider.provideDebugConfigurations!(folder, token);
3435

3536
if (!token.isCancellationRequested && Array.isArray(configs) && configs.length > 0) {
@@ -66,7 +67,7 @@ export class LaunchJsonUpdaterServiceHelper {
6667
const formattedJson = LaunchJsonUpdaterServiceHelper.getTextForInsertion(config, cursorPosition, commaPosition);
6768
const workspaceEdit = new WorkspaceEdit();
6869
workspaceEdit.insert(document.uri, position, formattedJson);
69-
await this.documentManager.applyEdit(workspaceEdit);
70+
await applyEdit(workspaceEdit);
7071
this.commandManager.executeCommand('editor.action.formatDocument').then(noop, noop);
7172
}
7273

src/client/debugger/extension/configuration/types.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
'use strict';
55

6-
import { CancellationToken, DebugConfiguration, Uri, WorkspaceFolder } from 'vscode';
6+
import { CancellationToken, DebugConfiguration, WorkspaceFolder } from 'vscode';
77

88
export const IDebugConfigurationResolver = Symbol('IDebugConfigurationResolver');
99
export interface IDebugConfigurationResolver<T extends DebugConfiguration> {
@@ -19,9 +19,3 @@ export interface IDebugConfigurationResolver<T extends DebugConfiguration> {
1919
token?: CancellationToken,
2020
): Promise<T | undefined>;
2121
}
22-
23-
export const ILaunchJsonReader = Symbol('ILaunchJsonReader');
24-
export interface ILaunchJsonReader {
25-
getConfigurationsForWorkspace(workspace: WorkspaceFolder): Promise<DebugConfiguration[]>;
26-
getConfigurationsByUri(uri?: Uri): Promise<DebugConfiguration[]>;
27-
}

src/client/debugger/extension/configuration/utils/common.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
'use strict';
88

9-
import { WorkspaceFolder, window, TextEditor } from 'vscode';
9+
import { WorkspaceFolder, window, TextEditor, WorkspaceEdit, workspace } from 'vscode';
1010
import { getWorkspaceFolder } from './workspaceFolder';
1111

1212
/**
@@ -26,9 +26,9 @@ export function resolveVariables(
2626
folder: WorkspaceFolder | undefined,
2727
): string | undefined {
2828
if (value) {
29-
const workspace = folder ? getWorkspaceFolder(folder.uri) : undefined;
29+
const workspaceFolder = folder ? getWorkspaceFolder(folder.uri) : undefined;
3030
const variablesObject: { [key: string]: any } = {};
31-
variablesObject.workspaceFolder = workspace ? workspace.uri.fsPath : rootFolder;
31+
variablesObject.workspaceFolder = workspaceFolder ? workspaceFolder.uri.fsPath : rootFolder;
3232

3333
const regexp = /\$\{(.*?)\}/g;
3434
return value.replace(regexp, (match: string, name: string) => {
@@ -46,3 +46,7 @@ export function getActiveTextEditor(): TextEditor | undefined {
4646
const { activeTextEditor } = window;
4747
return activeTextEditor;
4848
}
49+
50+
export function applyEdit(edit: WorkspaceEdit): Thenable<boolean> {
51+
return workspace.applyEdit(edit);
52+
}

src/client/debugger/extension/debugCommands.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import { Commands } from '../../common/constants';
1010
import { IDisposableRegistry } from '../../common/types';
1111
import { sendTelemetryEvent } from '../../telemetry';
1212
import { EventName } from '../../telemetry/constants';
13-
import { ILaunchJsonReader } from './configuration/types';
1413
import { DebugPurpose, LaunchRequestArguments } from '../types';
1514
import { IInterpreterService } from '../../interpreter/contracts';
1615
import { noop } from '../../common/utils/misc';
16+
import { getConfigurationsByUri } from './configuration/launch.json/launchJsonReader';
1717

1818
@injectable()
1919
export class DebugCommands implements IExtensionSingleActivationService {
@@ -22,7 +22,6 @@ export class DebugCommands implements IExtensionSingleActivationService {
2222
constructor(
2323
@inject(ICommandManager) private readonly commandManager: ICommandManager,
2424
@inject(IDebugService) private readonly debugService: IDebugService,
25-
@inject(ILaunchJsonReader) private readonly launchJsonReader: ILaunchJsonReader,
2625
@inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry,
2726
@inject(IInterpreterService) private readonly interpreterService: IInterpreterService,
2827
) {}
@@ -36,15 +35,15 @@ export class DebugCommands implements IExtensionSingleActivationService {
3635
this.commandManager.executeCommand(Commands.TriggerEnvironmentSelection, file).then(noop, noop);
3736
return;
3837
}
39-
const config = await this.getDebugConfiguration(file);
38+
const config = await DebugCommands.getDebugConfiguration(file);
4039
this.debugService.startDebugging(undefined, config);
4140
}),
4241
);
4342
return Promise.resolve();
4443
}
4544

46-
private async getDebugConfiguration(uri?: Uri): Promise<DebugConfiguration> {
47-
const configs = (await this.launchJsonReader.getConfigurationsByUri(uri)).filter((c) => c.request === 'launch');
45+
private static async getDebugConfiguration(uri?: Uri): Promise<DebugConfiguration> {
46+
const configs = (await getConfigurationsByUri(uri)).filter((c) => c.request === 'launch');
4847
for (const config of configs) {
4948
if ((config as LaunchRequestArguments).purpose?.includes(DebugPurpose.DebugInTerminal)) {
5049
if (!config.program && !config.module && !config.code) {

0 commit comments

Comments
 (0)