Skip to content

Commit

Permalink
Remove DI from config launch (#20226)
Browse files Browse the repository at this point in the history
closed: #19769
  • Loading branch information
paulacamargo25 authored Nov 15, 2022
1 parent ad0286c commit 9701c53
Show file tree
Hide file tree
Showing 16 changed files with 193 additions and 254 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,18 @@
'use strict';

import * as path from 'path';
import { inject, injectable } from 'inversify';
import * as fs from 'fs-extra';
import { injectable } from 'inversify';
import { CancellationToken, DebugConfiguration, WorkspaceFolder } from 'vscode';
import { IDynamicDebugConfigurationService } from '../types';
import { IFileSystem } from '../../../common/platform/types';
import { IPathUtils } from '../../../common/types';
import { DebuggerTypeName } from '../../constants';
import { asyncFilter } from '../../../common/utils/arrayUtils';

const workspaceFolderToken = '${workspaceFolder}';

@injectable()
export class DynamicPythonDebugConfigurationService implements IDynamicDebugConfigurationService {
constructor(@inject(IFileSystem) private fs: IFileSystem, @inject(IPathUtils) private pathUtils: IPathUtils) {}

// eslint-disable-next-line class-methods-use-this
public async provideDebugConfigurations(
folder: WorkspaceFolder,
_token?: CancellationToken,
Expand All @@ -32,20 +30,20 @@ export class DynamicPythonDebugConfigurationService implements IDynamicDebugConf
justMyCode: true,
});

const djangoManagePath = await this.getDjangoPath(folder);
const djangoManagePath = await DynamicPythonDebugConfigurationService.getDjangoPath(folder);
if (djangoManagePath) {
providers.push({
name: 'Python: Django',
type: DebuggerTypeName,
request: 'launch',
program: `${workspaceFolderToken}${this.pathUtils.separator}${djangoManagePath}`,
program: `${workspaceFolderToken}${path.sep}${djangoManagePath}`,
args: ['runserver'],
django: true,
justMyCode: true,
});
}

const flaskPath = await this.getFlaskPath(folder);
const flaskPath = await DynamicPythonDebugConfigurationService.getFlaskPath(folder);
if (flaskPath) {
providers.push({
name: 'Python: Flask',
Expand All @@ -62,12 +60,9 @@ export class DynamicPythonDebugConfigurationService implements IDynamicDebugConf
});
}

let fastApiPath = await this.getFastApiPath(folder);
let fastApiPath = await DynamicPythonDebugConfigurationService.getFastApiPath(folder);
if (fastApiPath) {
fastApiPath = path
.relative(folder.uri.fsPath, fastApiPath)
.replaceAll(this.pathUtils.separator, '.')
.replace('.py', '');
fastApiPath = path.relative(folder.uri.fsPath, fastApiPath).replaceAll(path.sep, '.').replace('.py', '');
providers.push({
name: 'Python: FastAPI',
type: DebuggerTypeName,
Expand All @@ -82,19 +77,19 @@ export class DynamicPythonDebugConfigurationService implements IDynamicDebugConf
return providers;
}

private async getDjangoPath(folder: WorkspaceFolder) {
private static async getDjangoPath(folder: WorkspaceFolder) {
const regExpression = /execute_from_command_line\(/;
const possiblePaths = await this.getPossiblePaths(
const possiblePaths = await DynamicPythonDebugConfigurationService.getPossiblePaths(
folder,
['manage.py', '*/manage.py', 'app.py', '*/app.py'],
regExpression,
);
return possiblePaths.length ? path.relative(folder.uri.fsPath, possiblePaths[0]) : null;
}

private async getFastApiPath(folder: WorkspaceFolder) {
private static async getFastApiPath(folder: WorkspaceFolder) {
const regExpression = /app\s*=\s*FastAPI\(/;
const fastApiPaths = await this.getPossiblePaths(
const fastApiPaths = await DynamicPythonDebugConfigurationService.getPossiblePaths(
folder,
['main.py', 'app.py', '*/main.py', '*/app.py', '*/*/main.py', '*/*/app.py'],
regExpression,
Expand All @@ -103,9 +98,9 @@ export class DynamicPythonDebugConfigurationService implements IDynamicDebugConf
return fastApiPaths.length ? fastApiPaths[0] : null;
}

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

private async getPossiblePaths(folder: WorkspaceFolder, globPatterns: string[], regex: RegExp): Promise<string[]> {
private static async getPossiblePaths(
folder: WorkspaceFolder,
globPatterns: string[],
regex: RegExp,
): Promise<string[]> {
const foundPathsPromises = (await Promise.allSettled(
globPatterns.map(
async (pattern): Promise<string[]> => this.fs.search(path.join(folder.uri.fsPath, pattern)),
async (pattern): Promise<string[]> =>
(await fs.pathExists(path.join(folder.uri.fsPath, pattern)))
? [path.join(folder.uri.fsPath, pattern)]
: [],
),
)) as { status: string; value: [] }[];
const possiblePaths: string[] = [];
foundPathsPromises.forEach((result) => possiblePaths.push(...result.value));
const finalPaths = await asyncFilter(possiblePaths, async (possiblePath) =>
regex.exec((await this.fs.readFile(possiblePath)).toString()),
regex.exec((await fs.readFile(possiblePath)).toString()),
);

return finalPaths;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,36 @@
// Licensed under the MIT License.

import * as path from 'path';
import * as fs from 'fs-extra';
import { parse } from 'jsonc-parser';
import { inject, injectable } from 'inversify';
import { DebugConfiguration, Uri, WorkspaceFolder } from 'vscode';
import { IFileSystem } from '../../../../common/platform/types';
import { ILaunchJsonReader } from '../types';
import { IWorkspaceService } from '../../../../common/application/types';
import { getWorkspaceFolder } from '../utils/workspaceFolder';

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

public async getConfigurationsForWorkspace(workspace: WorkspaceFolder): Promise<DebugConfiguration[]> {
const filename = path.join(workspace.uri.fsPath, '.vscode', 'launch.json');

if (!(await this.fs.fileExists(filename))) {
return [];
}
if (!(await fs.pathExists(filename))) {
return [];
}

const text = await this.fs.readFile(filename);
const parsed = parse(text, [], { allowTrailingComma: true, disallowComments: false });
if (!parsed.configurations || !Array.isArray(parsed.configurations)) {
throw Error('Missing field in launch.json: configurations');
}
if (!parsed.version) {
throw Error('Missing field in launch.json: version');
}
// We do not bother ensuring each item is a DebugConfiguration...
return parsed.configurations;
const text = await fs.readFile(filename, 'utf-8');
const parsed = parse(text, [], { allowTrailingComma: true, disallowComments: false });
if (!parsed.configurations || !Array.isArray(parsed.configurations)) {
throw Error('Missing field in launch.json: configurations');
}
if (!parsed.version) {
throw Error('Missing field in launch.json: version');
}
// We do not bother ensuring each item is a DebugConfiguration...
return parsed.configurations;
}

public async getConfigurationsByUri(uri: Uri): Promise<DebugConfiguration[]> {
const workspace = this.workspaceService.getWorkspaceFolder(uri);
export async function getConfigurationsByUri(uri?: Uri): Promise<DebugConfiguration[]> {
if (uri) {
const workspace = getWorkspaceFolder(uri);
if (workspace) {
return this.getConfigurationsForWorkspace(workspace);
return getConfigurationsForWorkspace(workspace);
}
return [];
}
return [];
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { inject, injectable } from 'inversify';
import { IExtensionSingleActivationService } from '../../../../activation/types';
import { ICommandManager, IDocumentManager, IWorkspaceService } from '../../../../common/application/types';
import { ICommandManager } from '../../../../common/application/types';
import { IDisposableRegistry } from '../../../../common/types';
import { IDebugConfigurationService } from '../../types';
import { LaunchJsonUpdaterServiceHelper } from './updaterServiceHelper';
Expand All @@ -17,18 +17,11 @@ export class LaunchJsonUpdaterService implements IExtensionSingleActivationServi
constructor(
@inject(ICommandManager) private readonly commandManager: ICommandManager,
@inject(IDisposableRegistry) private readonly disposableRegistry: IDisposableRegistry,
@inject(IWorkspaceService) private readonly workspace: IWorkspaceService,
@inject(IDocumentManager) private readonly documentManager: IDocumentManager,
@inject(IDebugConfigurationService) private readonly configurationProvider: IDebugConfigurationService,
) {}

public async activate(): Promise<void> {
const handler = new LaunchJsonUpdaterServiceHelper(
this.commandManager,
this.workspace,
this.documentManager,
this.configurationProvider,
);
const handler = new LaunchJsonUpdaterServiceHelper(this.commandManager, this.configurationProvider);
this.disposableRegistry.push(
this.commandManager.registerCommand(
'python.SelectAndInsertDebugConfiguration',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@

import { createScanner, parse, SyntaxKind } from 'jsonc-parser';
import { CancellationToken, DebugConfiguration, Position, Range, TextDocument, WorkspaceEdit } from 'vscode';
import { ICommandManager, IDocumentManager, IWorkspaceService } from '../../../../common/application/types';
import { ICommandManager } from '../../../../common/application/types';
import { noop } from '../../../../common/utils/misc';
import { captureTelemetry } from '../../../../telemetry';
import { EventName } from '../../../../telemetry/constants';
import { IDebugConfigurationService } from '../../types';
import { applyEdit, getActiveTextEditor } from '../utils/common';
import { getWorkspaceFolder } from '../utils/workspaceFolder';

type PositionOfCursor = 'InsideEmptyArray' | 'BeforeItem' | 'AfterItem';
type PositionOfComma = 'BeforeCursor';

export class LaunchJsonUpdaterServiceHelper {
constructor(
private readonly commandManager: ICommandManager,
private readonly workspace: IWorkspaceService,
private readonly documentManager: IDocumentManager,
private readonly configurationProvider: IDebugConfigurationService,
) {}

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

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

Expand Down
8 changes: 1 addition & 7 deletions src/client/debugger/extension/configuration/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

'use strict';

import { CancellationToken, DebugConfiguration, Uri, WorkspaceFolder } from 'vscode';
import { CancellationToken, DebugConfiguration, WorkspaceFolder } from 'vscode';

export const IDebugConfigurationResolver = Symbol('IDebugConfigurationResolver');
export interface IDebugConfigurationResolver<T extends DebugConfiguration> {
Expand All @@ -19,9 +19,3 @@ export interface IDebugConfigurationResolver<T extends DebugConfiguration> {
token?: CancellationToken,
): Promise<T | undefined>;
}

export const ILaunchJsonReader = Symbol('ILaunchJsonReader');
export interface ILaunchJsonReader {
getConfigurationsForWorkspace(workspace: WorkspaceFolder): Promise<DebugConfiguration[]>;
getConfigurationsByUri(uri?: Uri): Promise<DebugConfiguration[]>;
}
10 changes: 7 additions & 3 deletions src/client/debugger/extension/configuration/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

'use strict';

import { WorkspaceFolder, window, TextEditor } from 'vscode';
import { WorkspaceFolder, window, TextEditor, WorkspaceEdit, workspace } from 'vscode';
import { getWorkspaceFolder } from './workspaceFolder';

/**
Expand All @@ -26,9 +26,9 @@ export function resolveVariables(
folder: WorkspaceFolder | undefined,
): string | undefined {
if (value) {
const workspace = folder ? getWorkspaceFolder(folder.uri) : undefined;
const workspaceFolder = folder ? getWorkspaceFolder(folder.uri) : undefined;
const variablesObject: { [key: string]: any } = {};
variablesObject.workspaceFolder = workspace ? workspace.uri.fsPath : rootFolder;
variablesObject.workspaceFolder = workspaceFolder ? workspaceFolder.uri.fsPath : rootFolder;

const regexp = /\$\{(.*?)\}/g;
return value.replace(regexp, (match: string, name: string) => {
Expand All @@ -46,3 +46,7 @@ export function getActiveTextEditor(): TextEditor | undefined {
const { activeTextEditor } = window;
return activeTextEditor;
}

export function applyEdit(edit: WorkspaceEdit): Thenable<boolean> {
return workspace.applyEdit(edit);
}
9 changes: 4 additions & 5 deletions src/client/debugger/extension/debugCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import { Commands } from '../../common/constants';
import { IDisposableRegistry } from '../../common/types';
import { sendTelemetryEvent } from '../../telemetry';
import { EventName } from '../../telemetry/constants';
import { ILaunchJsonReader } from './configuration/types';
import { DebugPurpose, LaunchRequestArguments } from '../types';
import { IInterpreterService } from '../../interpreter/contracts';
import { noop } from '../../common/utils/misc';
import { getConfigurationsByUri } from './configuration/launch.json/launchJsonReader';

@injectable()
export class DebugCommands implements IExtensionSingleActivationService {
Expand All @@ -22,7 +22,6 @@ export class DebugCommands implements IExtensionSingleActivationService {
constructor(
@inject(ICommandManager) private readonly commandManager: ICommandManager,
@inject(IDebugService) private readonly debugService: IDebugService,
@inject(ILaunchJsonReader) private readonly launchJsonReader: ILaunchJsonReader,
@inject(IDisposableRegistry) private readonly disposables: IDisposableRegistry,
@inject(IInterpreterService) private readonly interpreterService: IInterpreterService,
) {}
Expand All @@ -36,15 +35,15 @@ export class DebugCommands implements IExtensionSingleActivationService {
this.commandManager.executeCommand(Commands.TriggerEnvironmentSelection, file).then(noop, noop);
return;
}
const config = await this.getDebugConfiguration(file);
const config = await DebugCommands.getDebugConfiguration(file);
this.debugService.startDebugging(undefined, config);
}),
);
return Promise.resolve();
}

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

0 comments on commit 9701c53

Please sign in to comment.