forked from microsoft/vscode-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathupdateTestSettings.ts
More file actions
158 lines (140 loc) · 7.04 KB
/
updateTestSettings.ts
File metadata and controls
158 lines (140 loc) · 7.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
'use strict';
import { inject, injectable } from 'inversify';
import { applyEdits, findNodeAtLocation, getNodeValue, ModificationOptions, modify, parseTree } from 'jsonc-parser';
import * as path from 'path';
import { IExtensionActivationService, LanguageServerType } from '../../activation/types';
import { IApplicationEnvironment, IWorkspaceService } from '../../common/application/types';
import '../../common/extensions';
import { IFileSystem } from '../../common/platform/types';
import { Resource } from '../../common/types';
import { swallowExceptions } from '../../common/utils/decorators';
import { traceDecoratorError, traceError } from '../../logging';
// TODO: rename the class since it is not used just for test settings
@injectable()
export class UpdateTestSettingService implements IExtensionActivationService {
public readonly supportedWorkspaceTypes = { untrustedWorkspace: false, virtualWorkspace: false };
constructor(
@inject(IFileSystem) private readonly fs: IFileSystem,
@inject(IApplicationEnvironment) private readonly application: IApplicationEnvironment,
@inject(IWorkspaceService) private readonly workspace: IWorkspaceService,
) {}
public async activate(resource: Resource): Promise<void> {
this.updateTestSettings(resource).ignoreErrors();
}
@traceDecoratorError('Failed to update test settings')
public async updateTestSettings(resource: Resource): Promise<void> {
const filesToBeFixed = await this.getFilesToBeFixed(resource);
await Promise.all(filesToBeFixed.map((file) => this.fixSettingInFile(file)));
}
public getSettingsFiles(resource: Resource): string[] {
const settingsFiles: string[] = [];
if (this.application.userSettingsFile) {
settingsFiles.push(this.application.userSettingsFile);
}
const workspaceFolder = this.workspace.getWorkspaceFolder(resource);
if (workspaceFolder) {
settingsFiles.push(path.join(workspaceFolder.uri.fsPath, '.vscode', 'settings.json'));
}
return settingsFiles;
}
public async getFilesToBeFixed(resource: Resource): Promise<string[]> {
const files = this.getSettingsFiles(resource);
const result = await Promise.all(
files.map(async (file) => {
const needsFixing = await this.doesFileNeedToBeFixed(file);
return { file, needsFixing };
}),
);
return result.filter((item) => item.needsFixing).map((item) => item.file);
}
// fixLanguageServerSetting provided for tests so not all tests have to
// deal with potential whitespace changes.
@swallowExceptions('Failed to update settings.json')
public async fixSettingInFile(filePath: string, fixLanguageServerSetting = true): Promise<string> {
let fileContents = await this.fs.readFile(filePath);
const setting = new RegExp('"python\\.unitTest', 'g');
fileContents = fileContents.replace(setting, '"python.testing');
const setting_pytest_enabled = new RegExp('\\.pyTestEnabled"', 'g');
const setting_pytest_args = new RegExp('\\.pyTestArgs"', 'g');
const setting_pytest_path = new RegExp('\\.pyTestPath"', 'g');
fileContents = fileContents.replace(setting_pytest_enabled, '.pytestEnabled"');
fileContents = fileContents.replace(setting_pytest_args, '.pytestArgs"');
fileContents = fileContents.replace(setting_pytest_path, '.pytestPath"');
const setting_pep8_args = new RegExp('\\.(?<!auto)pep8Args', 'g');
const setting_pep8_cat_severity = new RegExp('\\.pep8CategorySeverity\\.', 'g');
const setting_pep8_enabled = new RegExp('\\.pep8Enabled', 'g');
const setting_pep8_path = new RegExp('\\.(?<!auto)pep8Path', 'g');
fileContents = fileContents.replace(setting_pep8_args, '.pycodestyleArgs');
fileContents = fileContents.replace(setting_pep8_cat_severity, '.pycodestyleCategorySeverity.');
fileContents = fileContents.replace(setting_pep8_enabled, '.pycodestyleEnabled');
fileContents = fileContents.replace(setting_pep8_path, '.pycodestylePath');
// TODO: remove when python.jediEnabled is no longer in typical user settings.
if (fixLanguageServerSetting) {
fileContents = this.fixLanguageServerSettings(fileContents);
}
await this.fs.writeFile(filePath, fileContents);
return fileContents;
}
public async doesFileNeedToBeFixed(filePath: string): Promise<boolean> {
try {
if (!(await this.fs.fileExists(filePath))) {
return false;
}
const contents = await this.fs.readFile(filePath);
return (
contents.indexOf('python.jediEnabled') > 0 ||
contents.indexOf('python.unitTest.') > 0 ||
contents.indexOf('.pyTest') > 0 ||
contents.indexOf('.pep8') > 0
);
} catch (ex) {
traceError('Failed to check if file needs to be fixed', ex);
return false;
}
}
private fixLanguageServerSettings(fileContent: string): string {
// `python.jediEnabled` is deprecated:
// - If missing, do nothing.
// - `true`, then set to `languageServer: Jedi`.
// - `false` and `languageServer` is present, do nothing.
// - `false` and `languageServer` is NOT present, set `languageServer` to `None`.
// There is no other language server so set it to None.
// `jediEnabled` is NOT removed since JSONC parser may also remove comments.
const jediEnabledPath = ['python.jediEnabled'];
const languageServerPath = ['python.languageServer'];
try {
const ast = parseTree(fileContent);
const jediEnabledNode = findNodeAtLocation(ast, jediEnabledPath);
const languageServerNode = findNodeAtLocation(ast, languageServerPath);
// If missing, do nothing.
if (!jediEnabledNode) {
return fileContent;
}
const jediEnabled = getNodeValue(jediEnabledNode);
const modificationOptions: ModificationOptions = {
formattingOptions: {
tabSize: 4,
insertSpaces: true,
},
};
// `jediEnabled` is true, set it to Jedi.
if (jediEnabled) {
return applyEdits(
fileContent,
modify(fileContent, languageServerPath, LanguageServerType.Jedi, modificationOptions),
);
}
// `jediEnabled` is false, and Pylance(Node) is missing then.
// Then set it to None.
if (!languageServerNode) {
return applyEdits(
fileContent,
modify(fileContent, languageServerPath, LanguageServerType.None, modificationOptions),
);
}
} catch {}
return fileContent;
}
}