提交 35e21324 编写于 作者: M Matt Bierner 提交者: GitHub

New tsdk flow for 19 (#19602)

* Allow using workspace typescript.tsdk setting

Allows users to opt into using their workspace typescript.tsdk setting to specify the path to their typescript install.

Also, fixes a bug when the global tsdk setting points to the workspace version of typescript, our ts selector interface can get confused on which version is currently active. The fix adds a check using the local storage value to show the correct active version.

* Flip vscode and workspace pick order

* Use shared logic when workspace tsdk setting is not used
上级 ef499350
...@@ -88,8 +88,7 @@ ...@@ -88,8 +88,7 @@
"null" "null"
], ],
"default": null, "default": null,
"description": "%typescript.tsdk.desc%", "description": "%typescript.tsdk.desc%"
"isExecutable": true
}, },
"typescript.disableAutomaticTypeAcquisition": { "typescript.disableAutomaticTypeAcquisition": {
"type": "boolean", "type": "boolean",
......
...@@ -12,7 +12,7 @@ import * as fs from 'fs'; ...@@ -12,7 +12,7 @@ import * as fs from 'fs';
import * as electron from './utils/electron'; import * as electron from './utils/electron';
import { Reader } from './utils/wireProtocol'; import { Reader } from './utils/wireProtocol';
import { workspace, window, Uri, CancellationToken, OutputChannel, Memento, MessageItem, QuickPickItem, EventEmitter, Event, commands } from 'vscode'; import { workspace, window, Uri, CancellationToken, OutputChannel, Memento, MessageItem, QuickPickItem, EventEmitter, Event, commands, WorkspaceConfiguration } from 'vscode';
import * as Proto from './protocol'; import * as Proto from './protocol';
import { ITypescriptServiceClient, ITypescriptServiceClientHost, API } from './typescriptService'; import { ITypescriptServiceClient, ITypescriptServiceClientHost, API } from './typescriptService';
...@@ -98,7 +98,8 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient ...@@ -98,7 +98,8 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
private modulePath: string | undefined; private modulePath: string | undefined;
private _onReady: { promise: Promise<void>; resolve: () => void; reject: () => void; }; private _onReady: { promise: Promise<void>; resolve: () => void; reject: () => void; };
private tsdk: string | null; private globalTsdk: string | null;
private localTsdk: string | null;
private _checkGlobalTSCVersion: boolean; private _checkGlobalTSCVersion: boolean;
private _experimentalAutoBuild: boolean; private _experimentalAutoBuild: boolean;
private trace: Trace; private trace: Trace;
...@@ -145,16 +146,23 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient ...@@ -145,16 +146,23 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
this.pendingResponses = 0; this.pendingResponses = 0;
this.callbacks = Object.create(null); this.callbacks = Object.create(null);
const configuration = workspace.getConfiguration(); const configuration = workspace.getConfiguration();
this.tsdk = configuration.get<string | null>('typescript.tsdk', null); this.globalTsdk = this.extractGlobalTsdk(configuration);
this.localTsdk = this.extractLocalTsdk(configuration);
this._experimentalAutoBuild = false; // configuration.get<boolean>('typescript.tsserver.experimentalAutoBuild', false); this._experimentalAutoBuild = false; // configuration.get<boolean>('typescript.tsserver.experimentalAutoBuild', false);
this._apiVersion = new API('1.0.0'); this._apiVersion = new API('1.0.0');
this._checkGlobalTSCVersion = true; this._checkGlobalTSCVersion = true;
this.trace = this.readTrace(); this.trace = this.readTrace();
workspace.onDidChangeConfiguration(() => { workspace.onDidChangeConfiguration(() => {
this.trace = this.readTrace(); this.trace = this.readTrace();
let oldTsdk = this.tsdk; let oldglobalTsdk = this.globalTsdk;
this.tsdk = workspace.getConfiguration().get<string | null>('typescript.tsdk', null); let oldLocalTsdk = this.localTsdk;
if (this.servicePromise === null && oldTsdk !== this.tsdk) {
const configuration = workspace.getConfiguration();
this.globalTsdk = this.extractGlobalTsdk(configuration);
this.localTsdk = this.extractLocalTsdk(configuration);
if (this.servicePromise === null && (oldglobalTsdk !== this.globalTsdk || oldLocalTsdk !== this.localTsdk)) {
this.startService(); this.startService();
} }
}); });
...@@ -164,6 +172,25 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient ...@@ -164,6 +172,25 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
this.startService(); this.startService();
} }
private extractGlobalTsdk(configuration: WorkspaceConfiguration): string | null {
let inspect = configuration.inspect('typescript.tsdk');
if (inspect && inspect.globalValue && 'string' === typeof inspect.globalValue) {
return inspect.globalValue;
}
if (inspect && inspect.defaultValue && 'string' === typeof inspect.defaultValue) {
return inspect.defaultValue;
}
return null;
}
private extractLocalTsdk(configuration: WorkspaceConfiguration): string | null {
let inspect = configuration.inspect('typescript.tsdk');
if (inspect && inspect.workspaceValue && 'string' === typeof inspect.workspaceValue) {
return inspect.workspaceValue;
}
return null;
}
get onProjectLanguageServiceStateChanged(): Event<Proto.ProjectLanguageServiceStateEventBody> { get onProjectLanguageServiceStateChanged(): Event<Proto.ProjectLanguageServiceStateEventBody> {
return this._onProjectLanguageServiceStateChanged.event; return this._onProjectLanguageServiceStateChanged.event;
} }
...@@ -306,6 +333,14 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient ...@@ -306,6 +333,14 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
return null; return null;
} }
if (this.localTsdk) {
this._checkGlobalTSCVersion = false;
if ((<any>path).isAbsolute(this.localTsdk)) {
return path.join(this.localTsdk, 'tsserver.js');
}
return path.join(workspace.rootPath, this.localTsdk, 'tsserver.js');
}
const localModulePath = path.join(workspace.rootPath, 'node_modules', 'typescript', 'lib', 'tsserver.js'); const localModulePath = path.join(workspace.rootPath, 'node_modules', 'typescript', 'lib', 'tsserver.js');
if (fs.existsSync(localModulePath) && this.getTypeScriptVersion(localModulePath)) { if (fs.existsSync(localModulePath) && this.getTypeScriptVersion(localModulePath)) {
return localModulePath; return localModulePath;
...@@ -314,12 +349,12 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient ...@@ -314,12 +349,12 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
} }
private get globalTypescriptPath(): string { private get globalTypescriptPath(): string {
if (this.tsdk) { if (this.globalTsdk) {
this._checkGlobalTSCVersion = false; this._checkGlobalTSCVersion = false;
if ((<any>path).isAbsolute(this.tsdk)) { if ((<any>path).isAbsolute(this.globalTsdk)) {
return path.join(this.tsdk, 'tsserver.js'); return path.join(this.globalTsdk, 'tsserver.js');
} else if (workspace.rootPath) { } else if (workspace.rootPath) {
return path.join(workspace.rootPath, this.tsdk, 'tsserver.js'); return path.join(workspace.rootPath, this.globalTsdk, 'tsserver.js');
} }
} }
...@@ -327,55 +362,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient ...@@ -327,55 +362,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
} }
private hasWorkspaceTsdkSetting(): boolean { private hasWorkspaceTsdkSetting(): boolean {
function stripComments(content: string): string { return !!this.localTsdk;
/**
* First capturing group matches double quoted string
* Second matches single quotes string
* Third matches block comments
* Fourth matches line comments
*/
var regexp: RegExp = /("(?:[^\\\"]*(?:\\.)?)*")|('(?:[^\\\']*(?:\\.)?)*')|(\/\*(?:\r?\n|.)*?\*\/)|(\/{2,}.*?(?:(?:\r?\n)|$))/g;
let result = content.replace(regexp, (match, m1, m2, m3, m4) => {
// Only one of m1, m2, m3, m4 matches
if (m3) {
// A block comment. Replace with nothing
return '';
} else if (m4) {
// A line comment. If it ends in \r?\n then keep it.
let length = m4.length;
if (length > 2 && m4[length - 1] === '\n') {
return m4[length - 2] === '\r' ? '\r\n' : '\n';
} else {
return '';
}
} else {
// We match a string
return match;
}
});
return result;
};
try {
let rootPath = workspace.rootPath;
if (!rootPath) {
return false;
}
let settingsFile = path.join(rootPath, '.vscode', 'settings.json');
if (!fs.existsSync(settingsFile)) {
return false;
}
let content = fs.readFileSync(settingsFile, 'utf8');
if (!content || content.length === 0) {
return false;
}
content = stripComments(content);
let json = JSON.parse(content);
let value = json['typescript.tsdk'];
return is.string(value);
} catch (error) {
}
return false;
} }
private startService(resendModels: boolean = false): void { private startService(resendModels: boolean = false): void {
...@@ -391,8 +378,8 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient ...@@ -391,8 +378,8 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
modulePath.then(modulePath => { modulePath.then(modulePath => {
if (this.workspaceState.get<boolean>(TypeScriptServiceClient.useWorkspaceTsdkStorageKey, false)) { if (this.workspaceState.get<boolean>(TypeScriptServiceClient.useWorkspaceTsdkStorageKey, false)) {
if (workspace.rootPath) { if (workspace.rootPath) {
const pathValue = './node_modules/typescript/lib'; // TODO: check if we need better error handling
return path.join(workspace.rootPath, pathValue, 'tsserver.js'); return this.localTypeScriptPath || modulePath;
} }
} }
return modulePath; return modulePath;
...@@ -501,44 +488,32 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient ...@@ -501,44 +488,32 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
const useWorkspaceVersionSetting = this.workspaceState.get<boolean>(TypeScriptServiceClient.useWorkspaceTsdkStorageKey, false); const useWorkspaceVersionSetting = this.workspaceState.get<boolean>(TypeScriptServiceClient.useWorkspaceTsdkStorageKey, false);
const shippedVersion = this.getTypeScriptVersion(this.globalTypescriptPath); const shippedVersion = this.getTypeScriptVersion(this.globalTypescriptPath);
const localModulePath = this.localTypeScriptPath; const localModulePath = this.localTypeScriptPath;
let messageShown: Thenable<MyQuickPickItem | undefined>;
const pickOptions: MyQuickPickItem[] = [];
pickOptions.push({
label: localize('useVSCodeVersionOption', 'Use VSCode\'s Version'),
description: shippedVersion || this.globalTypescriptPath,
detail: modulePath === this.globalTypescriptPath && (modulePath !== localModulePath || !useWorkspaceVersionSetting) ? localize('activeVersion', 'Currently active') : '',
id: MessageAction.useBundled,
});
if (localModulePath) { if (localModulePath) {
const localVersion = this.getTypeScriptVersion(localModulePath); const localVersion = this.getTypeScriptVersion(localModulePath);
messageShown = window.showQuickPick<MyQuickPickItem>([ pickOptions.push({
{ label: localize('useWorkspaceVersionOption', 'Use Workspace Version'),
label: localize('useWorkspaceVersionOption', 'Use Workspace Version'), description: localVersion || localModulePath,
description: localVersion || '', detail: modulePath === localModulePath && (modulePath !== this.globalTypescriptPath || useWorkspaceVersionSetting) ? localize('activeVersion', 'Currently active') : '',
detail: modulePath === localModulePath && (modulePath !== this.globalTypescriptPath || useWorkspaceVersionSetting) ? localize('activeVersion', 'Currently active') : '', id: MessageAction.useLocal
id: MessageAction.useLocal });
}, {
label: localize('useVSCodeVersionOption', 'Use VSCode\'s Version'),
description: shippedVersion || '',
detail: modulePath === this.globalTypescriptPath && (modulePath !== localModulePath || !useWorkspaceVersionSetting) ? localize('activeVersion', 'Currently active') : '',
id: MessageAction.useBundled,
}, {
label: localize('learnMore', 'Learn More'),
description: '',
id: MessageAction.learnMore
}], {
placeHolder: localize(
'selectTsVersion',
'Select the TypeScript version used for JavaScript and TypeScript language features'),
ignoreFocusOut: firstRun
});
} else {
messageShown = window.showQuickPick<MyQuickPickItem>([
{
label: localize('learnMore', 'Learn More'),
description: '',
id: MessageAction.learnMore
}], {
placeHolder: localize(
'versionCheckUsingBundledTS',
'Using VSCode\'s TypeScript version {0} for JavaScript and TypeScript language features',
shippedVersion),
});
} }
pickOptions.push({
label: localize('learnMore', 'Learn More'),
description: '',
id: MessageAction.learnMore
});
const tryShowRestart = (newModulePath: string) => { const tryShowRestart = (newModulePath: string) => {
if (firstRun || newModulePath === this.modulePath) { if (firstRun || newModulePath === this.modulePath) {
return; return;
...@@ -559,32 +534,38 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient ...@@ -559,32 +534,38 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
}); });
}; };
return messageShown.then(selected => { return window.showQuickPick<MyQuickPickItem>(pickOptions, {
if (!selected) { placeHolder: localize(
return modulePath; 'selectTsVersion',
} 'Select the TypeScript version used for JavaScript and TypeScript language features'),
switch (selected.id) { ignoreFocusOut: firstRun
case MessageAction.useLocal: })
return this.workspaceState.update(TypeScriptServiceClient.useWorkspaceTsdkStorageKey, true) .then(selected => {
.then(_ => { if (!selected) {
if (localModulePath) {
tryShowRestart(localModulePath);
}
return localModulePath;
});
case MessageAction.useBundled:
return this.workspaceState.update(TypeScriptServiceClient.useWorkspaceTsdkStorageKey, false)
.then(_ => {
tryShowRestart(this.globalTypescriptPath);
return this.globalTypescriptPath;
});
case MessageAction.learnMore:
commands.executeCommand('vscode.open', Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919'));
return modulePath;
default:
return modulePath; return modulePath;
} }
}); switch (selected.id) {
case MessageAction.useLocal:
return this.workspaceState.update(TypeScriptServiceClient.useWorkspaceTsdkStorageKey, true)
.then(_ => {
if (localModulePath) {
tryShowRestart(localModulePath);
}
return localModulePath;
});
case MessageAction.useBundled:
return this.workspaceState.update(TypeScriptServiceClient.useWorkspaceTsdkStorageKey, false)
.then(_ => {
tryShowRestart(this.globalTypescriptPath);
return this.globalTypescriptPath;
});
case MessageAction.learnMore:
commands.executeCommand('vscode.open', Uri.parse('https://go.microsoft.com/fwlink/?linkid=839919'));
return modulePath;
default:
return modulePath;
}
});
} }
private serviceStarted(resendModels: boolean): void { private serviceStarted(resendModels: boolean): void {
...@@ -912,4 +893,4 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient ...@@ -912,4 +893,4 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
} }
this.logTrace(`Event received: ${event.event} (${event.seq}).`, data); this.logTrace(`Event received: ${event.event} (${event.seq}).`, data);
} }
} }
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册