Merge remote-tracking branch 'origin/master' into alex/semantic-exploration

This commit is contained in:
Alex Dima
2019-11-14 09:16:10 +01:00
564 changed files with 10494 additions and 7769 deletions

View File

@@ -7,6 +7,7 @@ import * as vscode from 'vscode';
import * as Proto from '../protocol';
import { ITypeScriptServiceClient } from '../typescriptService';
import API from '../utils/api';
import { coalesce } from '../utils/arrays';
import { Delayer } from '../utils/async';
import { nulToken } from '../utils/cancellation';
import { Disposable } from '../utils/dispose';
@@ -117,7 +118,11 @@ class BufferSynchronizer {
}
}
public beforeCommand(command: string) {
public reset(): void {
this._pending.clear();
}
public beforeCommand(command: string): void {
if (command === 'updateOpen') {
return;
}
@@ -149,7 +154,7 @@ class BufferSynchronizer {
}
private get supportsBatching(): boolean {
return this.client.apiVersion.gte(API.v340) && vscode.workspace.getConfiguration('typescript', null).get<boolean>('useBatchedBufferSync', true);
return this.client.apiVersion.gte(API.v340);
}
private updatePending(resource: vscode.Uri, f: (pending: ResourceMap<CloseOperation | OpenOperation | ChangeOperation>) => void): void {
@@ -264,26 +269,23 @@ class GetErrRequest {
files: ResourceMap<void>,
onDone: () => void
) {
const token = new vscode.CancellationTokenSource();
return new GetErrRequest(client, files, token, onDone);
return new GetErrRequest(client, files, onDone);
}
private _done: boolean = false;
private readonly _token: vscode.CancellationTokenSource = new vscode.CancellationTokenSource();
private constructor(
client: ITypeScriptServiceClient,
public readonly files: ResourceMap<void>,
private readonly _token: vscode.CancellationTokenSource,
onDone: () => void
) {
const args: Proto.GeterrRequestArgs = {
delay: 0,
files: Array.from(files.entries)
.map(entry => client.normalizedPath(entry.resource))
.filter(x => !!x) as string[]
files: coalesce(Array.from(files.entries).map(entry => client.normalizedPath(entry.resource)))
};
client.executeAsync('geterr', args, _token.token)
client.executeAsync('geterr', args, this._token.token)
.finally(() => {
if (this._done) {
return;
@@ -338,6 +340,9 @@ export default class BufferSyncSupport extends Disposable {
private readonly _onDelete = this._register(new vscode.EventEmitter<vscode.Uri>());
public readonly onDelete = this._onDelete.event;
private readonly _onWillChange = this._register(new vscode.EventEmitter<vscode.Uri>());
public readonly onWillChange = this._onWillChange.event;
public listen(): void {
if (this.listening) {
return;
@@ -392,7 +397,11 @@ export default class BufferSyncSupport extends Disposable {
return vscode.Uri.file(filePath);
}
public reOpenDocuments(): void {
public reset(): void {
this.pendingGetErr?.cancel();
this.pendingDiagnostics.clear();
this.synchronizer.reset();
for (const buffer of this.syncedBuffers.allBuffers) {
buffer.open();
}
@@ -425,6 +434,7 @@ export default class BufferSyncSupport extends Disposable {
return;
}
this.pendingDiagnostics.delete(resource);
this.pendingGetErr?.files.delete(resource);
this.syncedBuffers.delete(resource);
syncedBuffer.close();
this._onDelete.fire(resource);
@@ -457,6 +467,8 @@ export default class BufferSyncSupport extends Disposable {
return;
}
this._onWillChange.fire(syncedBuffer.resource);
syncedBuffer.onContentChanged(e.contentChanges);
const didTrigger = this.requestDiagnostic(syncedBuffer);
@@ -518,8 +530,10 @@ export default class BufferSyncSupport extends Disposable {
if (this.pendingGetErr) {
this.pendingGetErr.cancel();
for (const file of this.pendingGetErr.files.entries) {
orderedFileSet.set(file.resource, undefined);
for (const { resource } of this.pendingGetErr.files.entries) {
if (this.syncedBuffers.get(resource)) {
orderedFileSet.set(resource, undefined);
}
}
}

View File

@@ -66,12 +66,13 @@ class TypeScriptDocumentSymbolProvider implements vscode.DocumentSymbolProvider
const children = new Set(item.childItems || []);
for (const span of item.spans) {
const range = typeConverters.Range.fromTextSpan(span);
const selectionRange = item.nameSpan ? typeConverters.Range.fromTextSpan(item.nameSpan) : range;
const symbolInfo = new vscode.DocumentSymbol(
item.text,
'',
getSymbolKind(item.kind),
range,
range);
range.contains(selectionRange) ? selectionRange : range);
for (const child of children) {
if (child.spans.some(span => !!range.intersection(typeConverters.Range.fromTextSpan(span)))) {

View File

@@ -22,8 +22,10 @@ const autoFixableDiagnosticCodes = new Set<number>([
class TypeScriptAutoFixProvider implements vscode.CodeActionProvider {
private static readonly kind = vscode.CodeActionKind.SourceFixAll.append('ts');
public static readonly metadata: vscode.CodeActionProviderMetadata = {
providedCodeActionKinds: [vscode.CodeActionKind.SourceFixAll]
providedCodeActionKinds: [TypeScriptAutoFixProvider.kind]
};
constructor(
@@ -82,7 +84,7 @@ class TypeScriptAutoFixProvider implements vscode.CodeActionProvider {
const { edit, fixedDiagnostics } = autoFixResponse;
const codeAction = new vscode.CodeAction(
localize('autoFix.label', 'Auto fix'),
vscode.CodeActionKind.SourceFixAll);
TypeScriptAutoFixProvider.kind);
codeAction.edit = edit;
codeAction.diagnostics = fixedDiagnostics;

View File

@@ -7,6 +7,7 @@ import * as vscode from 'vscode';
import * as Proto from '../protocol';
import { ITypeScriptServiceClient } from '../typescriptService';
import API from '../utils/api';
import { coalesce } from '../utils/arrays';
import { VersionDependentRegistration } from '../utils/dependentRegistration';
import * as typeConverters from '../utils/typeConverters';
@@ -33,9 +34,7 @@ class TypeScriptFoldingProvider implements vscode.FoldingRangeProvider {
return;
}
return response.body
.map(span => this.convertOutliningSpan(span, document))
.filter(foldingRange => !!foldingRange) as vscode.FoldingRange[];
return coalesce(response.body.map(span => this.convertOutliningSpan(span, document)));
}
private convertOutliningSpan(

View File

@@ -24,7 +24,7 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip
const codeLens = inputCodeLens as ReferencesCodeLens;
const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start);
const response = await this.client.execute('implementation', args, token, { lowPriority: true });
const response = await this.client.execute('implementation', args, token, { lowPriority: true, cancelOnResourceChange: codeLens.document });
if (response.type !== 'response' || !response.body) {
codeLens.command = response.type === 'cancelled'
? TypeScriptBaseCodeLensProvider.cancelledCommand

View File

@@ -123,14 +123,77 @@ class SelectRefactorCommand implements Command {
}
}
interface CodeActionKind {
readonly kind: vscode.CodeActionKind;
matches(refactor: Proto.RefactorActionInfo): boolean;
}
const Extract_Function = Object.freeze<CodeActionKind>({
kind: vscode.CodeActionKind.RefactorExtract.append('function'),
matches: refactor => refactor.name.startsWith('function_')
});
const Extract_Constant = Object.freeze<CodeActionKind>({
kind: vscode.CodeActionKind.RefactorExtract.append('constant'),
matches: refactor => refactor.name.startsWith('constant_')
});
const Extract_Type = Object.freeze<CodeActionKind>({
kind: vscode.CodeActionKind.RefactorExtract.append('type'),
matches: refactor => refactor.name.startsWith('Extract to type alias')
});
const Extract_Interface = Object.freeze<CodeActionKind>({
kind: vscode.CodeActionKind.RefactorExtract.append('interface'),
matches: refactor => refactor.name.startsWith('Extract to interface')
});
const Move_NewFile = Object.freeze<CodeActionKind>({
kind: vscode.CodeActionKind.Refactor.append('move').append('newFile'),
matches: refactor => refactor.name.startsWith('Move to a new file')
});
const Rewrite_Import = Object.freeze<CodeActionKind>({
kind: vscode.CodeActionKind.RefactorRewrite.append('import'),
matches: refactor => refactor.name.startsWith('Convert namespace import') || refactor.name.startsWith('Convert named imports')
});
const Rewrite_Export = Object.freeze<CodeActionKind>({
kind: vscode.CodeActionKind.RefactorRewrite.append('export'),
matches: refactor => refactor.name.startsWith('Convert default export') || refactor.name.startsWith('Convert named export')
});
const Rewrite_Arrow_Braces = Object.freeze<CodeActionKind>({
kind: vscode.CodeActionKind.RefactorRewrite.append('arrow').append('braces'),
matches: refactor => refactor.name.startsWith('Convert default export') || refactor.name.startsWith('Convert named export')
});
const Rewrite_Parameters_ToDestructured = Object.freeze<CodeActionKind>({
kind: vscode.CodeActionKind.RefactorRewrite.append('parameters').append('toDestructured'),
matches: refactor => refactor.name.startsWith('Convert parameters to destructured object')
});
const Rewrite_Property_GenerateAccessors = Object.freeze<CodeActionKind>({
kind: vscode.CodeActionKind.RefactorRewrite.append('property').append('generateAccessors'),
matches: refactor => refactor.name.startsWith('Generate \'get\' and \'set\' accessors')
});
const allKnownCodeActionKinds = [
Extract_Function,
Extract_Constant,
Extract_Type,
Extract_Interface,
Move_NewFile,
Rewrite_Import,
Rewrite_Export,
Rewrite_Arrow_Braces,
Rewrite_Parameters_ToDestructured,
Rewrite_Property_GenerateAccessors
];
class TypeScriptRefactorProvider implements vscode.CodeActionProvider {
public static readonly minVersion = API.v240;
private static readonly extractFunctionKind = vscode.CodeActionKind.RefactorExtract.append('function');
private static readonly extractConstantKind = vscode.CodeActionKind.RefactorExtract.append('constant');
private static readonly extractTypeKind = vscode.CodeActionKind.RefactorExtract.append('type');
private static readonly moveKind = vscode.CodeActionKind.Refactor.append('move');
constructor(
private readonly client: ITypeScriptServiceClient,
private readonly formattingOptionsManager: FormattingOptionsManager,
@@ -142,7 +205,10 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider {
}
public static readonly metadata: vscode.CodeActionProviderMetadata = {
providedCodeActionKinds: [vscode.CodeActionKind.Refactor],
providedCodeActionKinds: [
vscode.CodeActionKind.Refactor,
...allKnownCodeActionKinds.map(x => x.kind),
],
};
public async provideCodeActions(
@@ -168,7 +234,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider {
const args: Proto.GetApplicableRefactorsRequestArgs = typeConverters.Range.toFileRangeRequestArgs(file, rangeOrSelection);
return this.client.execute('getApplicableRefactors', args, token);
});
if (!response || response.type !== 'response' || !response.body) {
if (response?.type !== 'response' || !response.body) {
return undefined;
}
@@ -224,25 +290,17 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider {
}
private static getKind(refactor: Proto.RefactorActionInfo) {
if (refactor.name.startsWith('function_')) {
return TypeScriptRefactorProvider.extractFunctionKind;
} else if (refactor.name.startsWith('constant_')) {
return TypeScriptRefactorProvider.extractConstantKind;
} else if (refactor.name.startsWith('Move')) {
return TypeScriptRefactorProvider.moveKind;
} else if (refactor.name.includes('Extract to type alias')) {
return TypeScriptRefactorProvider.extractTypeKind;
}
return vscode.CodeActionKind.Refactor;
const match = allKnownCodeActionKinds.find(kind => kind.matches(refactor));
return match ? match.kind : vscode.CodeActionKind.Refactor;
}
private static isPreferred(
action: Proto.RefactorActionInfo
): boolean {
if (action.name.startsWith('constant_')) {
if (Extract_Constant.matches(action)) {
return action.name.endsWith('scope_0');
}
if (action.name.includes('Extract to type alias')) {
if (Extract_Type.matches(action) || Extract_Interface.matches(action)) {
return true;
}
return false;

View File

@@ -19,7 +19,7 @@ class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLensProvide
public async resolveCodeLens(inputCodeLens: vscode.CodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens> {
const codeLens = inputCodeLens as ReferencesCodeLens;
const args = typeConverters.Position.toFileLocationRequestArgs(codeLens.file, codeLens.range.start);
const response = await this.client.execute('references', args, token, { lowPriority: true });
const response = await this.client.execute('references', args, token, { lowPriority: true, cancelOnResourceChange: codeLens.document });
if (response.type !== 'response' || !response.body) {
codeLens.command = response.type === 'cancelled'
? TypeScriptBaseCodeLensProvider.cancelledCommand

View File

@@ -3,12 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import * as jsonc from 'jsonc-parser';
import * as path from 'path';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { ITypeScriptServiceClient } from '../typescriptService';
import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService';
import { isTsConfigFileName } from '../utils/languageDescription';
import { Lazy } from '../utils/lazy';
import { isImplicitProjectConfigFile } from '../utils/tsconfig';
@@ -18,14 +17,14 @@ const localize = nls.loadMessageBundle();
type AutoDetect = 'on' | 'off' | 'build' | 'watch';
const exists = (file: string): Promise<boolean> =>
new Promise<boolean>((resolve, _reject) => {
fs.exists(file, (value: boolean) => {
resolve(value);
});
});
const exists = async (resource: vscode.Uri): Promise<boolean> => {
try {
const stat = await vscode.workspace.fs.stat(resource);
return stat.type === vscode.FileType.File;
} catch {
return false;
}
};
interface TypeScriptTaskDefinition extends vscode.TaskDefinition {
tsconfig: string;
@@ -36,6 +35,8 @@ interface TypeScriptTaskDefinition extends vscode.TaskDefinition {
* Provides tasks for building `tsconfig.json` files in a project.
*/
export default class TscTaskProvider implements vscode.TaskProvider {
private readonly projectInfoRequestTimeout = 2000;
private autoDetect: AutoDetect = 'on';
private readonly tsconfigProvider: TsConfigProvider;
private readonly disposables: vscode.Disposable[] = [];
@@ -62,8 +63,8 @@ export default class TscTaskProvider implements vscode.TaskProvider {
const configPaths: Set<string> = new Set();
const tasks: vscode.Task[] = [];
for (const project of await this.getAllTsConfigs(token)) {
if (!configPaths.has(project.path)) {
configPaths.add(project.path);
if (!configPaths.has(project.fsPath)) {
configPaths.add(project.fsPath);
tasks.push(...(await this.getTasksForProject(project)));
}
}
@@ -88,7 +89,8 @@ export default class TscTaskProvider implements vscode.TaskProvider {
const kind: TypeScriptTaskDefinition = (<any>_task.definition);
const tsconfigUri: vscode.Uri = _task.scope.uri.with({ path: _task.scope.uri.path + '/' + kind.tsconfig });
const tsconfig: TSConfig = {
path: tsconfigUri.fsPath,
uri: tsconfigUri,
fsPath: tsconfigUri.fsPath,
posixPath: tsconfigUri.path,
workspaceFolder: _task.scope
};
@@ -104,7 +106,7 @@ export default class TscTaskProvider implements vscode.TaskProvider {
...await this.getTsConfigsInWorkspace()
];
for (const config of configs) {
if (await exists(config.path)) {
if (await exists(config.uri)) {
out.add(config);
}
}
@@ -117,7 +119,8 @@ export default class TscTaskProvider implements vscode.TaskProvider {
if (isTsConfigFileName(editor.document.fileName)) {
const uri = editor.document.uri;
return [{
path: uri.fsPath,
uri,
fsPath: uri.fsPath,
posixPath: uri.path,
workspaceFolder: vscode.workspace.getWorkspaceFolder(uri)
}];
@@ -129,10 +132,13 @@ export default class TscTaskProvider implements vscode.TaskProvider {
return [];
}
const response = await this.client.value.execute(
'projectInfo',
{ file, needFileNameList: false },
token);
const response = await Promise.race([
this.client.value.execute(
'projectInfo',
{ file, needFileNameList: false },
token),
new Promise<typeof ServerResponse.NoContent>(resolve => setTimeout(() => resolve(ServerResponse.NoContent), this.projectInfoRequestTimeout))
]);
if (response.type !== 'response' || !response.body) {
return [];
}
@@ -143,7 +149,8 @@ export default class TscTaskProvider implements vscode.TaskProvider {
const uri = vscode.Uri.file(normalizedConfigPath);
const folder = vscode.workspace.getWorkspaceFolder(uri);
return [{
path: normalizedConfigPath,
uri,
fsPath: normalizedConfigPath,
posixPath: uri.path,
workspaceFolder: folder
}];
@@ -158,7 +165,7 @@ export default class TscTaskProvider implements vscode.TaskProvider {
private static async getCommand(project: TSConfig): Promise<string> {
if (project.workspaceFolder) {
const localTsc = await TscTaskProvider.getLocalTscAtPath(path.dirname(project.path));
const localTsc = await TscTaskProvider.getLocalTscAtPath(path.dirname(project.fsPath));
if (localTsc) {
return localTsc;
}
@@ -176,9 +183,9 @@ export default class TscTaskProvider implements vscode.TaskProvider {
private static async getLocalTscAtPath(folderPath: string): Promise<string | undefined> {
const platform = process.platform;
const bin = path.join(folderPath, 'node_modules', '.bin');
if (platform === 'win32' && await exists(path.join(bin, 'tsc.cmd'))) {
if (platform === 'win32' && await exists(vscode.Uri.file(path.join(bin, 'tsc.cmd')))) {
return path.join(bin, 'tsc.cmd');
} else if ((platform === 'linux' || platform === 'darwin') && await exists(path.join(bin, 'tsc'))) {
} else if ((platform === 'linux' || platform === 'darwin') && await exists(vscode.Uri.file(path.join(bin, 'tsc')))) {
return path.join(bin, 'tsc');
}
return undefined;
@@ -196,7 +203,7 @@ export default class TscTaskProvider implements vscode.TaskProvider {
}
private getBuildTask(workspaceFolder: vscode.WorkspaceFolder | undefined, label: string, command: string, args: string[], buildTaskidentifier: TypeScriptTaskDefinition): vscode.Task {
const buildTask = new vscode.Task(
const buildTask = new vscode.Task2(
buildTaskidentifier,
workspaceFolder || vscode.TaskScope.Workspace,
localize('buildTscLabel', 'build - {0}', label),
@@ -233,7 +240,6 @@ export default class TscTaskProvider implements vscode.TaskProvider {
}
if (this.autoDetect === 'watch' || this.autoDetect === 'on') {
tasks.push(this.getWatchTask(project.workspaceFolder, label, command, args, { type: 'typescript', tsconfig: label, option: 'watch' }));
}
@@ -256,25 +262,19 @@ export default class TscTaskProvider implements vscode.TaskProvider {
return task;
}
private getBuildShellArgs(project: TSConfig): Promise<Array<string>> {
const defaultArgs = ['-p', project.path];
return new Promise<Array<string>>((resolve) => {
fs.readFile(project.path, (error, result) => {
if (error) {
return resolve(defaultArgs);
}
try {
const tsconfig = jsonc.parse(result.toString());
if (tsconfig.references) {
return resolve(['-b', project.path]);
}
} catch {
// noop
}
return resolve(defaultArgs);
});
});
private async getBuildShellArgs(project: TSConfig): Promise<Array<string>> {
const defaultArgs = ['-p', project.fsPath];
try {
const bytes = await vscode.workspace.fs.readFile(project.uri);
const text = Buffer.from(bytes).toString('utf-8');
const tsconfig = jsonc.parse(text);
if (tsconfig?.references) {
return ['-b', project.fsPath];
}
} catch {
// noops
}
return defaultArgs;
}
private getLabelForTasks(project: TSConfig): string {

View File

@@ -4,9 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import * as jsonc from 'jsonc-parser';
import { dirname, join, basename } from 'path';
import { basename, dirname, join } from 'path';
import * as vscode from 'vscode';
import { flatten } from '../utils/arrays';
import { coalesce, flatten } from '../utils/arrays';
function mapChildren<R>(node: jsonc.Node | undefined, f: (x: jsonc.Node) => R): R[] {
return node && node.type === 'array' && node.children
@@ -25,11 +25,11 @@ class TsconfigLinkProvider implements vscode.DocumentLinkProvider {
return null;
}
return [
return coalesce([
this.getExtendsLink(document, root),
...this.getFilesLinks(document, root),
...this.getReferencesLinks(document, root)
].filter(x => !!x) as vscode.DocumentLink[];
]);
}
private getExtendsLink(document: vscode.TextDocument, root: jsonc.Node): vscode.DocumentLink | undefined {
@@ -68,7 +68,7 @@ class TsconfigLinkProvider implements vscode.DocumentLinkProvider {
}
return new vscode.DocumentLink(this.getRange(document, pathNode),
basename(pathNode.value).match('.json$')
basename(pathNode.value).endsWith('.json')
? this.getFileTarget(document, pathNode)
: this.getFolderTarget(document, pathNode));
});

View File

@@ -21,7 +21,7 @@ const testRunner = require('vscode/lib/testrunner');
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
testRunner.configure({
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
useColors: process.platform !== 'win32', // colored output from test results (only windows cannot handle)
useColors: (!process.env.BUILD_ARTIFACTSTAGINGDIRECTORY && process.platform !== 'win32'), // colored output from test results (only windows cannot handle)
timeout: 60000
});

View File

@@ -23,7 +23,7 @@ import { PluginManager } from './utils/plugins';
import * as typeConverters from './utils/typeConverters';
import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus';
import VersionStatus from './utils/versionStatus';
import { flatten } from './utils/arrays';
import { flatten, coalesce } from './utils/arrays';
// Style check diagnostics that can be reported as warnings
const styleCheckDiagnostics = [
@@ -182,7 +182,7 @@ export default class TypeScriptServiceClientHost extends Disposable {
private populateService(): void {
this.fileConfigurationManager.reset();
this.client.bufferSyncSupport.reOpenDocuments();
this.client.bufferSyncSupport.reset();
this.client.bufferSyncSupport.requestAllDiagnostics();
// See https://github.com/Microsoft/TypeScript/issues/5530
@@ -245,13 +245,13 @@ export default class TypeScriptServiceClientHost extends Disposable {
}
const relatedInformation = diagnostic.relatedInformation;
if (relatedInformation) {
converted.relatedInformation = relatedInformation.map((info: any) => {
let span = info.span;
converted.relatedInformation = coalesce(relatedInformation.map((info: any) => {
const span = info.span;
if (!span) {
return undefined;
}
return new vscode.DiagnosticRelatedInformation(typeConverters.Location.fromTextSpan(this.client.toResource(span.file), span), info.message);
}).filter((x: any) => !!x) as vscode.DiagnosticRelatedInformation[];
}));
}
if (diagnostic.reportsUnnecessary) {
converted.tags = [vscode.DiagnosticTag.Unnecessary];

View File

@@ -21,7 +21,7 @@ export namespace ServerResponse {
) { }
}
export const NoContent = new class { readonly type = 'noContent'; };
export const NoContent = { type: 'noContent' } as const;
export type Response<T extends Proto.Response> = T | Cancelled | typeof NoContent;
}
@@ -147,6 +147,7 @@ export type TypeScriptRequests = StandardTsServerRequests & NoResponseTsServerRe
export type ExecConfig = {
readonly lowPriority?: boolean;
readonly nonRecoverable?: boolean;
readonly cancelOnResourceChange?: vscode.Uri
};
export interface ITypeScriptServiceClient {

View File

@@ -35,6 +35,11 @@ export interface TsDiagnostics {
readonly diagnostics: Proto.Diagnostic[];
}
interface ToCancelOnResourceChanged {
readonly resource: vscode.Uri;
cancel(): void;
}
namespace ServerState {
export const enum Type {
None,
@@ -42,7 +47,7 @@ namespace ServerState {
Errored
}
export const None = new class { readonly type = Type.None; };
export const None = { type: Type.None } as const;
export class Running {
readonly type = Type.Running;
@@ -60,6 +65,8 @@ namespace ServerState {
public tsserverVersion: string | undefined,
public langaugeServiceEnabled: boolean,
) { }
public readonly toCancelOnResourceChange = new Set<ToCancelOnResourceChanged>();
}
export class Errored {
@@ -129,9 +136,14 @@ export default class TypeScriptServiceClient extends Disposable implements IType
this.diagnosticsManager = new DiagnosticsManager('typescript');
this.bufferSyncSupport.onDelete(resource => {
this.cancelInflightRequestsForResource(resource);
this.diagnosticsManager.delete(resource);
}, null, this._disposables);
this.bufferSyncSupport.onWillChange(resource => {
this.cancelInflightRequestsForResource(resource);
});
vscode.workspace.onDidChangeConfiguration(() => {
const oldConfiguration = this._configuration;
this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace();
@@ -173,6 +185,18 @@ export default class TypeScriptServiceClient extends Disposable implements IType
}));
}
private cancelInflightRequestsForResource(resource: vscode.Uri): void {
if (this.serverState.type !== ServerState.Type.Running) {
return;
}
for (const request of this.serverState.toCancelOnResourceChange) {
if (request.resource.toString() === resource.toString()) {
request.cancel();
}
}
}
public get configuration() {
return this._configuration;
}
@@ -609,12 +633,37 @@ export default class TypeScriptServiceClient extends Disposable implements IType
}
public execute(command: keyof TypeScriptRequests, args: any, token: vscode.CancellationToken, config?: ExecConfig): Promise<ServerResponse.Response<Proto.Response>> {
const execution = this.executeImpl(command, args, {
isAsync: false,
token,
expectsResult: true,
lowPriority: config?.lowPriority
});
let execution: Promise<ServerResponse.Response<Proto.Response>>;
if (config?.cancelOnResourceChange) {
const runningServerState = this.service();
const source = new vscode.CancellationTokenSource();
token.onCancellationRequested(() => source.cancel());
const inFlight: ToCancelOnResourceChanged = {
resource: config.cancelOnResourceChange,
cancel: () => source.cancel(),
};
runningServerState.toCancelOnResourceChange.add(inFlight);
execution = this.executeImpl(command, args, {
isAsync: false,
token: source.token,
expectsResult: true,
...config,
}).finally(() => {
runningServerState.toCancelOnResourceChange.delete(inFlight);
source.dispose();
});
} else {
execution = this.executeImpl(command, args, {
isAsync: false,
token,
expectsResult: true,
...config,
});
}
if (config?.nonRecoverable) {
execution.catch(() => this.fatalError(command));
@@ -654,10 +703,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType
private fatalError(command: string): void {
/* __GDPR__
"fatalError" : {
"${include}": [
"${TypeScriptCommonProperties}",
"command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
]
"${include}": [ "${TypeScriptCommonProperties}" ],
"command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.logTelemetry('fatalError', { command });

View File

@@ -19,6 +19,10 @@ export function equals<T>(
return a.every((x, i) => itemEquals(x, b[i]));
}
export function flatten<T>(arr: ReadonlyArray<T>[]): T[] {
return Array.prototype.concat.apply([], arr);
}
export function flatten<T>(array: ReadonlyArray<T>[]): T[] {
return Array.prototype.concat.apply([], array);
}
export function coalesce<T>(array: ReadonlyArray<T | undefined>): T[] {
return <T[]>array.filter(e => !!e);
}

View File

@@ -38,7 +38,10 @@ function getTagBodyText(tag: Proto.JSDocTagInfo): string | undefined {
function getTagDocumentation(tag: Proto.JSDocTagInfo): string | undefined {
switch (tag.name) {
case 'augments':
case 'extends':
case 'param':
case 'template':
const body = (tag.text || '').split(/^([\w\.]+)\s*-?\s*/);
if (body && body.length === 3) {
const param = body[1];

View File

@@ -2,10 +2,12 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
export interface TSConfig {
readonly path: string;
readonly uri: vscode.Uri;
readonly fsPath: string;
readonly posixPath: string;
readonly workspaceFolder?: vscode.WorkspaceFolder;
}
@@ -20,7 +22,8 @@ export default class TsConfigProvider {
const root = vscode.workspace.getWorkspaceFolder(config);
if (root) {
configs.set(config.fsPath, {
path: config.fsPath,
uri: config,
fsPath: config.fsPath,
posixPath: config.path,
workspaceFolder: root
});