|
| 1 | +// Copyright (c) Microsoft Corporation. All rights reserved. |
| 2 | +// Licensed under the MIT License. |
| 3 | + |
| 4 | +'use strict'; |
| 5 | + |
| 6 | +import { inject, injectable } from 'inversify'; |
| 7 | +import * as path from 'path'; |
| 8 | +import { DiagnosticSeverity, WorkspaceFolder } from 'vscode'; |
| 9 | +import { ICommandManager, IWorkspaceService } from '../../../common/application/types'; |
| 10 | +import '../../../common/extensions'; |
| 11 | +import { IFileSystem } from '../../../common/platform/types'; |
| 12 | +import { IServiceContainer } from '../../../ioc/types'; |
| 13 | +import { BaseDiagnostic, BaseDiagnosticsService } from '../base'; |
| 14 | +import { IDiagnosticsCommandFactory } from '../commands/types'; |
| 15 | +import { DiagnosticCodes } from '../constants'; |
| 16 | +import { DiagnosticCommandPromptHandlerServiceId, MessageCommandPrompt } from '../promptHandler'; |
| 17 | +import { DiagnosticScope, IDiagnostic, IDiagnosticHandlerService } from '../types'; |
| 18 | + |
| 19 | +const InvalidDebuggerTypeMessage = 'Your launch.json file needs to be updated to change the "pythonExperimental" debug ' + |
| 20 | + 'configurations to use the "python" debugger type, otherwise Python debugging may ' + |
| 21 | + 'not work. Would you like to automatically update your launch.json file now?'; |
| 22 | + |
| 23 | +export class InvalidDebuggerTypeDiagnostic extends BaseDiagnostic { |
| 24 | + constructor(message) { |
| 25 | + super(DiagnosticCodes.InvalidDebuggerTypeDiagnostic, |
| 26 | + message, DiagnosticSeverity.Error, DiagnosticScope.WorkspaceFolder); |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +export const InvalidDebuggerTypeDiagnosticsServiceId = 'InvalidDebuggerTypeDiagnosticsServiceId'; |
| 31 | + |
| 32 | +const CommandName = 'python.debugger.replaceExperimental'; |
| 33 | + |
| 34 | +@injectable() |
| 35 | +export class InvalidDebuggerTypeDiagnosticsService extends BaseDiagnosticsService { |
| 36 | + protected readonly messageService: IDiagnosticHandlerService<MessageCommandPrompt>; |
| 37 | + protected readonly fs: IFileSystem; |
| 38 | + constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) { |
| 39 | + super([DiagnosticCodes.InvalidEnvironmentPathVariableDiagnostic], serviceContainer); |
| 40 | + this.messageService = serviceContainer.get<IDiagnosticHandlerService<MessageCommandPrompt>>(IDiagnosticHandlerService, DiagnosticCommandPromptHandlerServiceId); |
| 41 | + const cmdManager = serviceContainer.get<ICommandManager>(ICommandManager); |
| 42 | + this.fs = this.serviceContainer.get<IFileSystem>(IFileSystem); |
| 43 | + cmdManager.registerCommand(CommandName, this.fixLaunchJson, this); |
| 44 | + } |
| 45 | + public async diagnose(): Promise<IDiagnostic[]> { |
| 46 | + if (await this.isExperimentalDebuggerUsed()) { |
| 47 | + return [new InvalidDebuggerTypeDiagnostic(InvalidDebuggerTypeMessage)]; |
| 48 | + } else { |
| 49 | + return []; |
| 50 | + } |
| 51 | + } |
| 52 | + public async handle(diagnostics: IDiagnostic[]): Promise<void> { |
| 53 | + // This class can only handle one type of diagnostic, hence just use first item in list. |
| 54 | + if (diagnostics.length === 0 || !this.canHandle(diagnostics[0])) { |
| 55 | + return; |
| 56 | + } |
| 57 | + const diagnostic = diagnostics[0]; |
| 58 | + const commandFactory = this.serviceContainer.get<IDiagnosticsCommandFactory>(IDiagnosticsCommandFactory); |
| 59 | + const options = [ |
| 60 | + { |
| 61 | + prompt: 'Yes, update launch.json', |
| 62 | + command: commandFactory.createCommand(diagnostic, { type: 'executeVSCCommand', options: 'python.debugger.replaceExperimental' }) |
| 63 | + }, |
| 64 | + { |
| 65 | + prompt: 'No, I will do it later' |
| 66 | + } |
| 67 | + ]; |
| 68 | + |
| 69 | + await this.messageService.handle(diagnostic, { commandPrompts: options }); |
| 70 | + } |
| 71 | + private async isExperimentalDebuggerUsed() { |
| 72 | + const workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService); |
| 73 | + if (!workspaceService.hasWorkspaceFolders) { |
| 74 | + return false; |
| 75 | + } |
| 76 | + |
| 77 | + const results = await Promise.all(workspaceService.workspaceFolders!.map(workspaceFolder => this.isExperimentalDebuggerUsedInWorkspace(workspaceFolder))); |
| 78 | + return results.filter(used => used === true).length > 0; |
| 79 | + } |
| 80 | + private getLaunchJsonFile(workspaceFolder: WorkspaceFolder) { |
| 81 | + return path.join(workspaceFolder.uri.fsPath, '.vscode', 'launch.json'); |
| 82 | + } |
| 83 | + private async isExperimentalDebuggerUsedInWorkspace(workspaceFolder: WorkspaceFolder) { |
| 84 | + const launchJson = this.getLaunchJsonFile(workspaceFolder); |
| 85 | + if (!await this.fs.fileExists(launchJson)) { |
| 86 | + return false; |
| 87 | + } |
| 88 | + |
| 89 | + const fileContents = await this.fs.readFile(launchJson); |
| 90 | + return fileContents.indexOf('"pythonExperimental"') > 0; |
| 91 | + } |
| 92 | + private async fixLaunchJson() { |
| 93 | + const workspaceService = this.serviceContainer.get<IWorkspaceService>(IWorkspaceService); |
| 94 | + if (!workspaceService.hasWorkspaceFolders) { |
| 95 | + return false; |
| 96 | + } |
| 97 | + |
| 98 | + await Promise.all(workspaceService.workspaceFolders!.map(workspaceFolder => this.fixLaunchJsonInWorkspace(workspaceFolder))); |
| 99 | + } |
| 100 | + private async fixLaunchJsonInWorkspace(workspaceFolder: WorkspaceFolder) { |
| 101 | + if (!await this.isExperimentalDebuggerUsedInWorkspace(workspaceFolder)) { |
| 102 | + return; |
| 103 | + } |
| 104 | + |
| 105 | + const launchJson = this.getLaunchJsonFile(workspaceFolder); |
| 106 | + let fileContents = await this.fs.readFile(launchJson); |
| 107 | + const debuggerType = new RegExp('"pythonExperimental"', 'g'); |
| 108 | + const debuggerLabel = new RegExp('"Python Experimental:', 'g'); |
| 109 | + |
| 110 | + fileContents = fileContents.replace(debuggerType, '"python"'); |
| 111 | + fileContents = fileContents.replace(debuggerLabel, '"Python:'); |
| 112 | + |
| 113 | + await this.fs.writeFile(launchJson, fileContents); |
| 114 | + } |
| 115 | +} |
0 commit comments