mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-21 09:08:53 +01:00
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
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user