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

Consume languageServiceEnabled Events from TS (#16355)

* Initial work on large proejct warnings

* Update to use projectName from ts

* Use event to clean up code

* Remove unused interface

* Revert a few testing changes

* Remove languageServiceEnabled member
上级 615ea2b4
......@@ -5,7 +5,7 @@
'use strict';
import { CancellationToken, Uri } from 'vscode';
import { CancellationToken, Uri, Event } from 'vscode';
import * as Proto from './protocol';
import * as semver from 'semver';
......@@ -53,6 +53,10 @@ export class API {
public has208Features(): boolean {
return semver.gte(this._version, '2.0.8');
}
public has213Features(): boolean {
return semver.gte(this._version, '2.1.3');
}
}
export interface ITypescriptServiceClient {
......@@ -63,6 +67,8 @@ export interface ITypescriptServiceClient {
warn(message: string, data?: any): void;
error(message: string, data?: any): void;
onProjectLanguageServiceStateChanged: Event<Proto.ProjectLanguageServiceStateEventBody>;
logTelemetry(eventName: string, properties?: { [prop: string]: string });
experimentalAutoBuild: boolean;
......
......@@ -12,7 +12,7 @@ import * as fs from 'fs';
import * as electron from './utils/electron';
import { Reader } from './utils/wireProtocol';
import { workspace, window, Uri, CancellationToken, OutputChannel, Memento, MessageItem } from 'vscode';
import { workspace, window, Uri, CancellationToken, OutputChannel, Memento, MessageItem, EventEmitter, Event } from 'vscode';
import * as Proto from './protocol';
import { ITypescriptServiceClient, ITypescriptServiceClientHost, API } from './typescriptService';
......@@ -102,6 +102,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
private requestQueue: RequestItem[];
private pendingResponses: number;
private callbacks: CallbackMap;
private _onProjectLanguageServiceStateChanged = new EventEmitter<Proto.ProjectLanguageServiceStateEventBody>();
private _packageInfo: IPackageInfo | null;
private _apiVersion: API;
......@@ -148,6 +149,10 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
this.startService();
}
get onProjectLanguageServiceStateChanged(): Event<Proto.ProjectLanguageServiceStateEventBody> {
return this._onProjectLanguageServiceStateChanged.event;
}
private get output(): OutputChannel {
if (!this._output) {
this._output = window.createOutputChannel(localize('channelName', 'TypeScript'));
......@@ -684,6 +689,11 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
break;
}
this.logTelemetry(telemetryData.telemetryEventName, properties);
} else if (event.event === 'projectLanguageServiceState') {
const data = (event as Proto.ProjectLanguageServiceStateEvent).body;
if (data) {
this._onProjectLanguageServiceStateChanged.fire(data);
}
}
} else {
throw new Error('Unknown message type ' + message.type + ' recevied');
......
......@@ -8,7 +8,7 @@
import * as vscode from 'vscode';
import { ITypescriptServiceClient } from '../typescriptService';
import { loadMessageBundle } from 'vscode-nls';
import { dirname, join } from 'path';
import { dirname } from 'path';
const localize = loadMessageBundle();
const selector = ['javascript', 'javascriptreact'];
......@@ -22,37 +22,69 @@ interface Hint {
options: Option[];
}
interface ProjectHintedMap {
[k: string]: boolean;
}
const fileLimit = 500;
export function create(client: ITypescriptServiceClient, isOpen: (path: string) => Promise<boolean>, memento: vscode.Memento) {
class ExcludeHintItem {
private _item: vscode.StatusBarItem;
private _client: ITypescriptServiceClient;
private _currentHint: Hint;
constructor(client: ITypescriptServiceClient) {
this._client = client;
this._item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, Number.MIN_VALUE);
this._item.command = 'js.projectStatus.command';
}
public getCurrentHint(): Hint {
return this._currentHint;
}
public hide() {
this._item.hide();
}
public show(configFileName: string, largeRoots: string, onExecute: () => void) {
this._currentHint = {
message: largeRoots.length > 0
? localize('hintExclude', "For better performance exclude folders with many files, like: {0}", largeRoots)
: localize('hintExclude.generic', "For better performance exclude folders with many files."),
options: [{
title: localize('open', "Configure Excludes"),
execute: () => {
this._client.logTelemetry('js.hintProjectExcludes.accepted');
onExecute();
this._item.hide();
return vscode.workspace.openTextDocument(configFileName)
.then(vscode.window.showTextDocument);
}
}]
};
this._item.tooltip = this._currentHint.message;
this._item.text = localize('large.label', "Configure Excludes");
this._item.tooltip = localize('hintExclude.tooltip', "For better performance exclude folders with many files.");
this._item.color = '#A5DF3B';
this._item.show();
this._client.logTelemetry('js.hintProjectExcludes');
}
}
function createLargeProjectMonitorForProject(item: ExcludeHintItem, client: ITypescriptServiceClient, isOpen: (path: string) => Promise<boolean>, memento: vscode.Memento): vscode.Disposable[] {
const toDispose: vscode.Disposable[] = [];
const projectHinted: { [k: string]: boolean } = Object.create(null);
const projectHinted: ProjectHintedMap = Object.create(null);
const projectHintIgnoreList = memento.get<string[]>('projectHintIgnoreList', []);
for (let path of projectHintIgnoreList) {
if (!path) {
if (path === null) {
path = 'undefined';
}
projectHinted[path] = true;
}
let currentHint: Hint;
let item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, Number.MIN_VALUE);
item.command = 'js.projectStatus.command';
toDispose.push(vscode.commands.registerCommand('js.projectStatus.command', () => {
let {message, options} = currentHint;
return vscode.window.showInformationMessage(message, ...options).then(selection => {
if (selection) {
return selection.execute();
}
});
}));
toDispose.push(vscode.workspace.onDidChangeTextDocument(e => {
delete projectHinted[e.document.fileName];
}));
function onEditor(editor: vscode.TextEditor | undefined): void {
if (!editor
|| !vscode.languages.match(selector, editor.document)
......@@ -66,56 +98,26 @@ export function create(client: ITypescriptServiceClient, isOpen: (path: string)
if (!file) {
return;
}
isOpen(file).then(value => {
if (!value) {
return;
}
return client.execute('projectInfo', { file, needFileNameList: true }).then(res => {
return client.execute('projectInfo', { file, needFileNameList: true } as protocol.ProjectInfoRequestArgs).then(res => {
if (!res.body) {
return;
}
let {configFileName, fileNames} = res.body;
if (projectHinted[configFileName] === true) {
if (projectHinted[configFileName] === true || !fileNames) {
return;
}
if (fileNames && fileNames.length > fileLimit) {
if (fileNames.length > fileLimit || res.body.languageServiceDisabled) {
let largeRoots = computeLargeRoots(configFileName, fileNames).map(f => `'/${f}/'`).join(', ');
currentHint = {
message: largeRoots.length > 0
? localize('hintExclude', "For better performance exclude folders with many files, like: {0}", largeRoots)
: localize('hintExclude.generic', "For better performance exclude folders with many files."),
options: [{
title: localize('open', "Configure Excludes"),
execute: () => {
client.logTelemetry('js.hintProjectExcludes.accepted');
projectHinted[configFileName] = true;
item.hide();
let configFileUri: vscode.Uri;
let rootPath = vscode.workspace.rootPath;
if (rootPath && dirname(configFileName).indexOf('' + rootPath) === 0) {
configFileUri = vscode.Uri.file(configFileName);
} else {
configFileUri = vscode.Uri.parse('untitled://' + join(rootPath, 'jsconfig.json'));
}
return vscode.workspace.openTextDocument(configFileUri)
.then(vscode.window.showTextDocument);
}
}]
};
item.tooltip = currentHint.message;
item.text = localize('large.label', "Configure Excludes");
item.tooltip = localize('hintExclude.tooltip', "For better performance exclude folders with many files.");
item.color = '#A5DF3B';
item.show();
client.logTelemetry('js.hintProjectExcludes');
item.show(configFileName, largeRoots, () => {
projectHinted[configFileName] = true;
});
} else {
item.hide();
}
......@@ -125,9 +127,45 @@ export function create(client: ITypescriptServiceClient, isOpen: (path: string)
});
}
toDispose.push(vscode.workspace.onDidChangeTextDocument(e => {
delete projectHinted[e.document.fileName];
}));
toDispose.push(vscode.window.onDidChangeActiveTextEditor(onEditor));
onEditor(vscode.window.activeTextEditor);
return toDispose;
}
function createLargeProjectMonitorFromTypeScript(item: ExcludeHintItem, client: ITypescriptServiceClient): vscode.Disposable {
return client.onProjectLanguageServiceStateChanged(body => {
if (body.languageServiceEnabled) {
item.hide();
} else {
item.show(body.projectName, '', () => { });
}
});
}
export function create(client: ITypescriptServiceClient, isOpen: (path: string) => Promise<boolean>, memento: vscode.Memento) {
const toDispose: vscode.Disposable[] = [];
let item = new ExcludeHintItem(client);
toDispose.push(vscode.commands.registerCommand('js.projectStatus.command', () => {
let {message, options} = item.getCurrentHint();
return vscode.window.showInformationMessage(message, ...options).then(selection => {
if (selection) {
return selection.execute();
}
});
}));
if (client.apiVersion.has213Features()) {
toDispose.push(createLargeProjectMonitorFromTypeScript(item, client));
} else {
toDispose.push(...createLargeProjectMonitorForProject(item, client, isOpen, memento));
}
return vscode.Disposable.from(...toDispose);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册