提交 1505b3dc 编写于 作者: M Matt Bierner 提交者: GitHub

Monitor Typings Acqusition Events for TS Completion Item Provider (#17098)

Monitors the status of typings acqusition from TS. If we are acquiring typings, in the completion item provider, return an error result that informs the user that the results are incomplete. Typings acqusition is currently capped at 30 seconds per typings install event so that we don't block intellisense forever if something goes wrong
上级 fb619701
......@@ -8,11 +8,15 @@
import { CompletionItem, TextDocument, Position, CompletionItemKind, CompletionItemProvider, CancellationToken, WorkspaceConfiguration, TextEdit, Range, SnippetString, workspace, ProviderResult } from 'vscode';
import { ITypescriptServiceClient } from '../typescriptService';
import TypingsStatus from '../utils/typingsStatus';
import * as PConst from '../protocol.const';
import { CompletionEntry, CompletionsRequestArgs, CompletionDetailsRequestArgs, CompletionEntryDetails, FileLocationRequestArgs } from '../protocol';
import * as Previewer from './previewer';
import * as nls from 'vscode-nls';
let localize = nls.loadMessageBundle();
class MyCompletionItem extends CompletionItem {
document: TextDocument;
......@@ -96,10 +100,12 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
public sortBy = [{ type: 'reference', partSeparator: '/' }];
private client: ITypescriptServiceClient;
private typingsStatus: TypingsStatus;
private config: Configuration;
constructor(client: ITypescriptServiceClient) {
constructor(client: ITypescriptServiceClient, typingsStatus: TypingsStatus) {
this.client = client;
this.typingsStatus = typingsStatus;
this.config = { useCodeSnippetsOnMethodSuggest: false };
}
......@@ -110,6 +116,13 @@ export default class TypeScriptCompletionItemProvider implements CompletionItemP
}
public provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken): Promise<CompletionItem[]> {
if (this.typingsStatus.isAcquiringTypings) {
return Promise.reject({
label: localize('acquiringTypingsLabel', 'Acquiring typings...'),
detail: localize('acquiringTypingsDetail', 'Acquiring typings definitions for IntelliSense.')
});
}
let filepath = this.client.asAbsolutePath(document.uri);
if (!filepath) {
return Promise.resolve<CompletionItem[]>([]);
......
......@@ -36,9 +36,10 @@ import CompletionItemProvider from './features/completionItemProvider';
import WorkspaceSymbolProvider from './features/workspaceSymbolProvider';
import CodeActionProvider from './features/codeActionProvider';
import * as VersionStatus from './utils/versionStatus';
import * as ProjectStatus from './utils/projectStatus';
import * as BuildStatus from './utils/buildStatus';
import * as ProjectStatus from './utils/projectStatus';
import TypingsStatus from './utils/typingsStatus';
import * as VersionStatus from './utils/versionStatus';
interface LanguageDescription {
id: string;
......@@ -105,6 +106,7 @@ class LanguageProvider {
private completionItemProvider: CompletionItemProvider;
private formattingProvider: FormattingProvider;
private formattingProviderRegistration: Disposable | null;
private typingsStatus: TypingsStatus;
private _validate: boolean;
......@@ -122,6 +124,7 @@ class LanguageProvider {
this.syntaxDiagnostics = Object.create(null);
this.currentDiagnostics = languages.createDiagnosticCollection(description.id);
this.typingsStatus = new TypingsStatus(client);
workspace.onDidChangeConfiguration(this.configurationChanged, this);
this.configurationChanged();
......@@ -137,7 +140,7 @@ class LanguageProvider {
private registerProviders(client: TypeScriptServiceClient): void {
let config = workspace.getConfiguration(this.id);
this.completionItemProvider = new CompletionItemProvider(client);
this.completionItemProvider = new CompletionItemProvider(client, this.typingsStatus);
this.completionItemProvider.updateConfiguration(config);
let hoverProvider = new HoverProvider(client);
......
......@@ -68,6 +68,8 @@ export interface ITypescriptServiceClient {
error(message: string, data?: any): void;
onProjectLanguageServiceStateChanged: Event<Proto.ProjectLanguageServiceStateEventBody>;
onDidBeginInstallTypings: Event<Proto.BeginInstallTypesEventBody>;
onDidEndInstallTypings: Event<Proto.EndInstallTypesEventBody>;
logTelemetry(eventName: string, properties?: { [prop: string]: string }): void;
......
......@@ -103,6 +103,8 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
private pendingResponses: number;
private callbacks: CallbackMap;
private _onProjectLanguageServiceStateChanged = new EventEmitter<Proto.ProjectLanguageServiceStateEventBody>();
private _onDidBeginInstallTypings = new EventEmitter<Proto.BeginInstallTypesEventBody>();
private _onDidEndInstallTypings = new EventEmitter<Proto.EndInstallTypesEventBody>();
private _packageInfo: IPackageInfo | null;
private _apiVersion: API;
......@@ -153,6 +155,14 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
return this._onProjectLanguageServiceStateChanged.event;
}
get onDidBeginInstallTypings(): Event<Proto.BeginInstallTypesEventBody> {
return this._onDidBeginInstallTypings.event;
}
get onDidEndInstallTypings(): Event<Proto.EndInstallTypesEventBody> {
return this._onDidEndInstallTypings.event;
}
private get output(): OutputChannel {
if (!this._output) {
this._output = window.createOutputChannel(localize('channelName', 'TypeScript'));
......@@ -695,6 +705,16 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
if (data) {
this._onProjectLanguageServiceStateChanged.fire(data);
}
} else if (event.event === 'beginInstallTypes') {
const data = (event as Proto.BeginInstallTypesEvent).body;
if (data) {
this._onDidBeginInstallTypings.fire(data);
}
} else if (event.event === 'endInstallTypes') {
const data = (event as Proto.EndInstallTypesEvent).body;
if (data) {
this._onDidEndInstallTypings.fire(data);
}
}
} else {
throw new Error('Unknown message type ' + message.type + ' recevied');
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import { ITypescriptServiceClient } from '../typescriptService';
const typingsInstallTimeout = 30 * 1000;
export default class TypingsStatus extends vscode.Disposable {
private _acquiringTypings: Map<NodeJS.Timer> = Object.create({});
private _client: ITypescriptServiceClient;
private _subscriptions: vscode.Disposable[] = [];
constructor(client: ITypescriptServiceClient) {
super(() => this.dispose());
this._client = client;
this._subscriptions.push(
this._client.onDidBeginInstallTypings(event => this.onBeginInstallTypings(event.eventId)));
this._subscriptions.push(
this._client.onDidEndInstallTypings(event => this.onEndInstallTypings(event.eventId)));
}
public dispose(): void {
this._subscriptions.forEach(x => x.dispose());
for (const eventId of Object.keys(this._acquiringTypings)) {
clearTimeout(this._acquiringTypings[eventId]);
}
}
public get isAcquiringTypings(): boolean {
return Object.keys(this._acquiringTypings).length > 0;
}
private onBeginInstallTypings(eventId: number): void {
if (this._acquiringTypings[eventId]) {
return;
}
this._acquiringTypings[eventId] = setTimeout(() => {
this.onEndInstallTypings(eventId);
}, typingsInstallTimeout);
}
private onEndInstallTypings(eventId: number): void {
const timer = this._acquiringTypings[eventId];
if (timer) {
clearTimeout(timer);
}
delete this._acquiringTypings[eventId];
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册