typescriptMain.ts 23.8 KB
Newer Older
I
isidor 已提交
1 2 3 4
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
E
Erich Gamma 已提交
5 6 7 8 9 10 11

/* --------------------------------------------------------------------------------------------
 * Includes code from typescript-sublime-plugin project, obtained from
 * https://github.com/Microsoft/TypeScript-Sublime-Plugin/blob/master/TypeScript%20Indent.tmPreferences
 * ------------------------------------------------------------------------------------------ */
'use strict';

12
import { env, languages, commands, workspace, window, ExtensionContext, Memento, IndentAction, Diagnostic, DiagnosticCollection, Range, Disposable, Uri, MessageItem, TextEditor, FileSystemWatcher } from 'vscode';
E
Erich Gamma 已提交
13

14 15 16
// This must be the first statement otherwise modules might got loaded with
// the wrong locale.
import * as nls from 'vscode-nls';
J
Johannes Rieken 已提交
17
nls.config({ locale: env.language });
18
const localize = nls.loadMessageBundle();
19

20 21
import * as path from 'path';

E
Erich Gamma 已提交
22
import * as Proto from './protocol';
D
Dirk Baeumer 已提交
23

E
Erich Gamma 已提交
24 25 26 27 28
import TypeScriptServiceClient from './typescriptServiceClient';
import { ITypescriptServiceClientHost } from './typescriptService';

import HoverProvider from './features/hoverProvider';
import DefinitionProvider from './features/definitionProvider';
M
Matt Bierner 已提交
29
import ImplementationProvider from './features/ImplementationProvider';
30
import TypeDefintionProvider from './features/typeDefinitionProvider';
E
Erich Gamma 已提交
31 32 33 34 35 36 37 38 39
import DocumentHighlightProvider from './features/documentHighlightProvider';
import ReferenceProvider from './features/referenceProvider';
import DocumentSymbolProvider from './features/documentSymbolProvider';
import SignatureHelpProvider from './features/signatureHelpProvider';
import RenameProvider from './features/renameProvider';
import FormattingProvider from './features/formattingProvider';
import BufferSyncSupport from './features/bufferSyncSupport';
import CompletionItemProvider from './features/completionItemProvider';
import WorkspaceSymbolProvider from './features/workspaceSymbolProvider';
40
import CodeActionProvider from './features/codeActionProvider';
41
import ReferenceCodeLensProvider from './features/referencesCodeLensProvider';
42
import { JsDocCompletionProvider, TryCompleteJsDocCommand } from './features/jsDocCompletionProvider';
43
import ImplementationCodeLensProvider from './features/implementationsCodeLensProvider';
E
Erich Gamma 已提交
44

D
Dirk Baeumer 已提交
45
import * as BuildStatus from './utils/buildStatus';
46
import * as ProjectStatus from './utils/projectStatus';
47
import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus';
48
import * as VersionStatus from './utils/versionStatus';
49

50 51
interface LanguageDescription {
	id: string;
52
	diagnosticSource: string;
53
	modeIds: string[];
54
	extensions: string[];
D
Dirk Baeumer 已提交
55
	configFile: string;
56 57
}

58 59 60 61 62 63 64 65 66 67
enum ProjectConfigAction {
	None,
	CreateConfig,
	LearnMore
}

interface ProjectConfigMessageItem extends MessageItem {
	id: ProjectConfigAction;
}

E
Erich Gamma 已提交
68
export function activate(context: ExtensionContext): void {
M
Matt Bierner 已提交
69 70 71 72
	const MODE_ID_TS = 'typescript';
	const MODE_ID_TSX = 'typescriptreact';
	const MODE_ID_JS = 'javascript';
	const MODE_ID_JSX = 'javascriptreact';
E
Erich Gamma 已提交
73

M
Matt Bierner 已提交
74
	const clientHost = new TypeScriptServiceClientHost([
75 76
		{
			id: 'typescript',
77
			diagnosticSource: 'ts',
78
			modeIds: [MODE_ID_TS, MODE_ID_TSX],
D
Dirk Baeumer 已提交
79 80
			extensions: ['.ts', '.tsx'],
			configFile: 'tsconfig.json'
81 82 83
		},
		{
			id: 'javascript',
84
			diagnosticSource: 'js',
85
			modeIds: [MODE_ID_JS, MODE_ID_JSX],
D
Dirk Baeumer 已提交
86 87
			extensions: ['.js', '.jsx'],
			configFile: 'jsconfig.json'
88
		}
89
	], context.storagePath, context.globalState, context.workspaceState);
90
	context.subscriptions.push(clientHost);
91

M
Matt Bierner 已提交
92
	const client = clientHost.serviceClient;
D
Dirk Baeumer 已提交
93 94 95 96

	context.subscriptions.push(commands.registerCommand('typescript.reloadProjects', () => {
		clientHost.reloadProjects();
	}));
97

98 99 100 101
	context.subscriptions.push(commands.registerCommand('javascript.reloadProjects', () => {
		clientHost.reloadProjects();
	}));

102
	context.subscriptions.push(commands.registerCommand('typescript.selectTypeScriptVersion', () => {
103 104 105
		client.onVersionStatusClicked();
	}));

106 107 108 109
	context.subscriptions.push(commands.registerCommand('typescript.openTsServerLog', () => {
		client.openTsServerLogFile();
	}));

110 111 112
	const goToProjectConfig = (isTypeScript: boolean) => {
		const editor = window.activeTextEditor;
		if (editor) {
113
			clientHost.goToProjectConfig(isTypeScript, editor.document.uri);
114 115 116 117 118
		}
	};
	context.subscriptions.push(commands.registerCommand('typescript.goToProjectConfig', goToProjectConfig.bind(null, true)));
	context.subscriptions.push(commands.registerCommand('javascript.goToProjectConfig', goToProjectConfig.bind(null, false)));

119 120 121
	const jsDocCompletionCommand = new TryCompleteJsDocCommand(client);
	context.subscriptions.push(commands.registerCommand(TryCompleteJsDocCommand.COMMAND_NAME, jsDocCompletionCommand.tryCompleteJsDoc, jsDocCompletionCommand));

122
	window.onDidChangeActiveTextEditor(VersionStatus.showHideStatus, null, context.subscriptions);
E
Erich Gamma 已提交
123
	client.onReady().then(() => {
124 125 126
		context.subscriptions.push(ProjectStatus.create(client,
			path => new Promise(resolve => setTimeout(() => resolve(clientHost.handles(path)), 750)),
			context.workspaceState));
E
Erich Gamma 已提交
127 128
	}, () => {
		// Nothing to do here. The client did show a message;
A
tslint  
Alex Dima 已提交
129
	});
D
Dirk Baeumer 已提交
130
	BuildStatus.update({ queueLength: 0 });
E
Erich Gamma 已提交
131 132
}

133
const validateSetting = 'validate.enable';
E
Erich Gamma 已提交
134

135
class LanguageProvider {
E
Erich Gamma 已提交
136

137
	private readonly extensions: ObjectMap<boolean>;
138
	private syntaxDiagnostics: ObjectMap<Diagnostic[]>;
139 140
	private readonly currentDiagnostics: DiagnosticCollection;
	private readonly bufferSyncSupport: BufferSyncSupport;
141

142 143
	private completionItemProvider: CompletionItemProvider;
	private formattingProvider: FormattingProvider;
144
	private formattingProviderRegistration: Disposable | null;
145
	private typingsStatus: TypingsStatus;
146
	private referenceCodeLensProvider: ReferenceCodeLensProvider;
147
	private implementationCodeLensProvider: ImplementationCodeLensProvider;
148
	private JsDocCompletionProvider: JsDocCompletionProvider;
149

M
Matt Bierner 已提交
150
	private _validate: boolean = true;
151

152 153
	private readonly disposables: Disposable[] = [];

M
Matt Bierner 已提交
154
	constructor(
M
Matt Bierner 已提交
155 156
		private readonly client: TypeScriptServiceClient,
		private readonly description: LanguageDescription
M
Matt Bierner 已提交
157
	) {
158 159
		this.extensions = Object.create(null);
		description.extensions.forEach(extension => this.extensions[extension] = true);
160

161 162
		this.bufferSyncSupport = new BufferSyncSupport(client, description.modeIds, {
			delete: (file: string) => {
163
				this.currentDiagnostics.delete(client.asUrl(file));
164
			}
165
		}, this.extensions);
166 167
		this.syntaxDiagnostics = Object.create(null);
		this.currentDiagnostics = languages.createDiagnosticCollection(description.id);
168

169
		this.typingsStatus = new TypingsStatus(client);
170
		new AtaProgressReporter(client);
171

172
		workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables);
173 174 175 176
		this.configurationChanged();

		client.onReady().then(() => {
			this.registerProviders(client);
177
			this.bufferSyncSupport.listen();
178 179 180 181 182
		}, () => {
			// Nothing to do here. The client did show a message;
		});
	}

183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
	public dispose(): void {
		if (this.formattingProviderRegistration) {
			this.formattingProviderRegistration.dispose();
		}

		while (this.disposables.length) {
			const obj = this.disposables.pop();
			if (obj) {
				obj.dispose();
			}
		}

		this.typingsStatus.dispose();
		this.currentDiagnostics.dispose();
		this.bufferSyncSupport.dispose();
	}

200
	private registerProviders(client: TypeScriptServiceClient): void {
M
Matt Bierner 已提交
201
		const selector = this.description.modeIds;
M
Matt Bierner 已提交
202
		const config = workspace.getConfiguration(this.id);
203

204
		this.completionItemProvider = new CompletionItemProvider(client, this.typingsStatus);
205
		this.completionItemProvider.updateConfiguration();
206
		this.disposables.push(languages.registerCompletionItemProvider(selector, this.completionItemProvider, '.'));
207 208 209

		this.formattingProvider = new FormattingProvider(client);
		this.formattingProvider.updateConfiguration(config);
210
		this.disposables.push(languages.registerOnTypeFormattingEditProvider(selector, this.formattingProvider, ';', '}', '\n'));
211
		if (this.formattingProvider.isEnabled()) {
M
Matt Bierner 已提交
212
			this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(selector, this.formattingProvider);
213
		}
214

215 216 217 218
		this.JsDocCompletionProvider = new JsDocCompletionProvider(client);
		this.JsDocCompletionProvider.updateConfiguration();
		this.disposables.push(languages.registerCompletionItemProvider(selector, this.JsDocCompletionProvider, '*'));

219 220 221 222 223 224 225
		this.disposables.push(languages.registerHoverProvider(selector, new HoverProvider(client)));
		this.disposables.push(languages.registerDefinitionProvider(selector, new DefinitionProvider(client)));
		this.disposables.push(languages.registerDocumentHighlightProvider(selector, new DocumentHighlightProvider(client)));
		this.disposables.push(languages.registerReferenceProvider(selector, new ReferenceProvider(client)));
		this.disposables.push(languages.registerDocumentSymbolProvider(selector, new DocumentSymbolProvider(client)));
		this.disposables.push(languages.registerSignatureHelpProvider(selector, new SignatureHelpProvider(client), '(', ','));
		this.disposables.push(languages.registerRenameProvider(selector, new RenameProvider(client)));
M
Matt Bierner 已提交
226

227 228 229
		this.referenceCodeLensProvider = new ReferenceCodeLensProvider(client);
		this.referenceCodeLensProvider.updateConfiguration();
		this.disposables.push(languages.registerCodeLensProvider(selector, this.referenceCodeLensProvider));
M
Matt Bierner 已提交
230

231 232 233 234 235
		this.implementationCodeLensProvider = new ImplementationCodeLensProvider(client);
		this.implementationCodeLensProvider.updateConfiguration();
		this.disposables.push(languages.registerCodeLensProvider(selector, this.implementationCodeLensProvider));

		this.disposables.push(languages.registerCodeActionsProvider(selector, new CodeActionProvider(client, this.description.id)));
M
Matt Bierner 已提交
236 237

		if (client.apiVersion.has220Features()) {
238
			this.disposables.push(languages.registerImplementationProvider(selector, new ImplementationProvider(client)));
M
Matt Bierner 已提交
239 240 241
		}

		if (client.apiVersion.has213Features()) {
242
			this.disposables.push(languages.registerTypeDefinitionProvider(selector, new TypeDefintionProvider(client)));
243 244
		}

245
		this.description.modeIds.forEach(modeId => {
246
			this.disposables.push(languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(client, modeId)));
247

248
			this.disposables.push(languages.setLanguageConfiguration(modeId, {
249 250 251 252 253 254 255 256 257 258 259 260 261
				indentationRules: {
					// ^(.*\*/)?\s*\}.*$
					decreaseIndentPattern: /^(.*\*\/)?\s*\}.*$/,
					// ^.*\{[^}"']*$
					increaseIndentPattern: /^.*\{[^}"']*$/
				},
				wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
				onEnterRules: [
					{
						// e.g. /** | */
						beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
						afterText: /^\s*\*\/$/,
						action: { indentAction: IndentAction.IndentOutdent, appendText: ' * ' }
M
Matt Bierner 已提交
262
					}, {
263 264 265
						// e.g. /** ...|
						beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
						action: { indentAction: IndentAction.None, appendText: ' * ' }
M
Matt Bierner 已提交
266
					}, {
267 268 269
						// e.g.  * ...|
						beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
						action: { indentAction: IndentAction.None, appendText: '* ' }
M
Matt Bierner 已提交
270
					}, {
271 272 273
						// e.g.  */|
						beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/,
						action: { indentAction: IndentAction.None, removeText: 1 }
274 275 276 277 278
					},
					{
						// e.g.  *-----*/|
						beforeText: /^(\t|(\ \ ))*\ \*[^/]*\*\/\s*$/,
						action: { indentAction: IndentAction.None, removeText: 1 }
279
					}
280
				]
281
			}));
282 283 284

			const EMPTY_ELEMENTS: string[] = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'];

285
			this.disposables.push(languages.setLanguageConfiguration('jsx-tags', {
286 287 288 289 290 291 292 293 294 295 296 297
				wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,
				onEnterRules: [
					{
						beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'),
						afterText: /^<\/([_:\w][_:\w-.\d]*)\s*>$/i,
						action: { indentAction: IndentAction.IndentOutdent }
					},
					{
						beforeText: new RegExp(`<(?!(?:${EMPTY_ELEMENTS.join('|')}))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$`, 'i'),
						action: { indentAction: IndentAction.Indent }
					}
				],
298
			}));
299 300 301 302
		});
	}

	private configurationChanged(): void {
M
Matt Bierner 已提交
303
		const config = workspace.getConfiguration(this.id);
304 305
		this.updateValidate(config.get(validateSetting, true));
		if (this.completionItemProvider) {
306
			this.completionItemProvider.updateConfiguration();
307
		}
308
		if (this.referenceCodeLensProvider) {
309
			this.referenceCodeLensProvider.updateConfiguration();
310
		}
311 312 313
		if (this.implementationCodeLensProvider) {
			this.implementationCodeLensProvider.updateConfiguration();
		}
314 315
		if (this.formattingProvider) {
			this.formattingProvider.updateConfiguration(config);
316 317
			if (!this.formattingProvider.isEnabled() && this.formattingProviderRegistration) {
				this.formattingProviderRegistration.dispose();
318
				this.formattingProviderRegistration = null;
319 320 321 322

			} else if (this.formattingProvider.isEnabled() && !this.formattingProviderRegistration) {
				this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(this.description.modeIds, this.formattingProvider);
			}
323
		}
324 325 326
		if (this.JsDocCompletionProvider) {
			this.JsDocCompletionProvider.updateConfiguration();
		}
327 328 329
	}

	public handles(file: string): boolean {
M
Matt Bierner 已提交
330
		const extension = path.extname(file);
D
Dirk Baeumer 已提交
331 332 333
		if ((extension && this.extensions[extension]) || this.bufferSyncSupport.handles(file)) {
			return true;
		}
M
Matt Bierner 已提交
334
		const basename = path.basename(file);
335
		return !!basename && basename === this.description.configFile;
336 337
	}

338 339 340 341
	public get id(): string {
		return this.description.id;
	}

342 343 344 345
	public get diagnosticSource(): string {
		return this.description.diagnosticSource;
	}

346
	private updateValidate(value: boolean) {
347 348 349
		if (this._validate === value) {
			return;
		}
350
		this._validate = value;
351
		this.bufferSyncSupport.validate = value;
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
		if (value) {
			this.triggerAllDiagnostics();
		} else {
			this.syntaxDiagnostics = Object.create(null);
			this.currentDiagnostics.clear();
		}
	}

	public reInitialize(): void {
		this.currentDiagnostics.clear();
		this.syntaxDiagnostics = Object.create(null);
		this.bufferSyncSupport.reOpenDocuments();
		this.bufferSyncSupport.requestAllDiagnostics();
	}

	public triggerAllDiagnostics(): void {
		this.bufferSyncSupport.requestAllDiagnostics();
	}

	public syntaxDiagnosticsReceived(file: string, diagnostics: Diagnostic[]): void {
		this.syntaxDiagnostics[file] = diagnostics;
	}

	public semanticDiagnosticsReceived(file: string, diagnostics: Diagnostic[]): void {
M
Matt Bierner 已提交
376
		const syntaxMarkers = this.syntaxDiagnostics[file];
377 378 379 380
		if (syntaxMarkers) {
			delete this.syntaxDiagnostics[file];
			diagnostics = syntaxMarkers.concat(diagnostics);
		}
381
		this.currentDiagnostics.set(this.client.asUrl(file), diagnostics);
382
	}
383 384

	public configFileDiagnosticsReceived(file: string, diagnostics: Diagnostic[]): void {
385
		this.currentDiagnostics.set(this.client.asUrl(file), diagnostics);
386
	}
387 388 389 390
}

class TypeScriptServiceClientHost implements ITypescriptServiceClientHost {
	private client: TypeScriptServiceClient;
391
	private languages: LanguageProvider[];
392
	private languagePerId: ObjectMap<LanguageProvider>;
393 394
	private configFileWatcher: FileSystemWatcher;
	private readonly disposables: Disposable[] = [];
E
Erich Gamma 已提交
395

M
Matt Bierner 已提交
396 397 398 399 400 401 402
	constructor(
		descriptions: LanguageDescription[],
		storagePath: string | undefined,
		globalState: Memento,
		workspaceState: Memento
	) {
		const handleProjectCreateOrDelete = () => {
E
Erich Gamma 已提交
403 404 405
			this.client.execute('reloadProjects', null, false);
			this.triggerAllDiagnostics();
		};
M
Matt Bierner 已提交
406
		const handleProjectChange = () => {
D
Dirk Baeumer 已提交
407 408 409
			setTimeout(() => {
				this.triggerAllDiagnostics();
			}, 1500);
A
tslint  
Alex Dima 已提交
410
		};
411 412 413 414 415
		const configFileWatcher = workspace.createFileSystemWatcher('**/[tj]sconfig.json');
		this.disposables.push(configFileWatcher);
		configFileWatcher.onDidCreate(handleProjectCreateOrDelete, this, this.disposables);
		configFileWatcher.onDidDelete(handleProjectCreateOrDelete, this, this.disposables);
		configFileWatcher.onDidChange(handleProjectChange, this, this.disposables);
E
Erich Gamma 已提交
416

417
		this.client = new TypeScriptServiceClient(this, storagePath, globalState, workspaceState, this.disposables);
418 419
		this.languages = [];
		this.languagePerId = Object.create(null);
420
		for (const description of descriptions) {
M
Matt Bierner 已提交
421
			const manager = new LanguageProvider(this.client, description);
422
			this.languages.push(manager);
423
			this.disposables.push(manager);
424
			this.languagePerId[description.id] = manager;
425 426 427 428 429 430 431 432 433 434 435
		}
	}

	public dispose(): void {
		while (this.disposables.length) {
			const obj = this.disposables.pop();
			if (obj) {
				obj.dispose();
			}
		}
		this.configFileWatcher.dispose();
E
Erich Gamma 已提交
436 437 438 439 440 441
	}

	public get serviceClient(): TypeScriptServiceClient {
		return this.client;
	}

442 443 444 445 446 447 448 449 450
	public restartTsServer(): void {
		this.client.restartTsServer();
		if (this.languages) {
			for (const provider of this.languages) {
				provider.reInitialize();
			}
		}
	}

D
Dirk Baeumer 已提交
451 452 453 454 455
	public reloadProjects(): void {
		this.client.execute('reloadProjects', null, false);
		this.triggerAllDiagnostics();
	}

D
Dirk Baeumer 已提交
456 457 458 459
	public handles(file: string): boolean {
		return !!this.findLanguage(file);
	}

460 461
	public goToProjectConfig(
		isTypeScriptProject: boolean,
462
		resource: Uri
463
	): Thenable<TextEditor | undefined> | undefined {
464 465 466 467 468 469 470 471 472 473
		const rootPath = workspace.rootPath;
		if (!rootPath) {
			window.showInformationMessage(
				localize(
					'typescript.projectConfigNoWorkspace',
					'Please open a folder in VS Code to use a TypeScript or JavaScript project'));
			return;
		}

		const file = this.client.normalizePath(resource);
474 475
		// TSServer errors when 'projectInfo' is invoked on a non js/ts file
		if (!file || !this.handles(file)) {
476 477 478 479 480 481 482
			window.showWarningMessage(
				localize(
					'typescript.projectConfigUnsupportedFile',
					'Could not determine TypeScript or JavaScript project. Unsupported file type'));
			return;
		}

483
		return this.client.execute('projectInfo', { file, needFileNameList: false } as protocol.ProjectInfoRequestArgs).then(res => {
484
			if (!res || !res.body) {
485 486
				return window.showWarningMessage(localize('typescript.projectConfigCouldNotGetInfo', 'Could not determine TypeScript or JavaScript project'))
					.then(() => void 0);
487 488 489 490 491
			}

			const { configFileName } = res.body;
			if (configFileName && configFileName.indexOf('/dev/null/') !== 0) {
				return workspace.openTextDocument(configFileName)
492 493
					.then(doc =>
						window.showTextDocument(doc, window.activeTextEditor ? window.activeTextEditor.viewColumn : undefined));
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
			}

			return window.showInformationMessage<ProjectConfigMessageItem>(
				(isTypeScriptProject
					? localize('typescript.noTypeScriptProjectConfig', 'File is not part of a TypeScript project')
					: localize('typescript.noJavaScriptProjectConfig', 'File is not part of a JavaScript project')
				), {
					title: isTypeScriptProject
						? localize('typescript.configureTsconfigQuickPick', 'Configure tsconfig.json')
						: localize('typescript.configureJsconfigQuickPick', 'Configure jsconfig.json'),
					id: ProjectConfigAction.CreateConfig
				}, {
					title: localize('typescript.projectConfigLearnMore', 'Learn More'),
					id: ProjectConfigAction.LearnMore
				}).then(selected => {
					switch (selected && selected.id) {
						case ProjectConfigAction.CreateConfig:
							const configFile = Uri.file(path.join(rootPath, isTypeScriptProject ? 'tsconfig.json' : 'jsconfig.json'));
							return workspace.openTextDocument(configFile)
								.then(undefined, _ => workspace.openTextDocument(configFile.with({ scheme: 'untitled' })))
514 515
								.then(doc =>
									window.showTextDocument(doc, window.activeTextEditor ? window.activeTextEditor.viewColumn : undefined));
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531

						case ProjectConfigAction.LearnMore:
							if (isTypeScriptProject) {
								commands.executeCommand('vscode.open', Uri.parse('https://go.microsoft.com/fwlink/?linkid=841896'));
							} else {
								commands.executeCommand('vscode.open', Uri.parse('https://go.microsoft.com/fwlink/?linkid=759670'));
							}
							return;

						default:
							return Promise.resolve(undefined);
					}
				});
		});
	}

532
	private findLanguage(file: string): LanguageProvider | null {
533 534 535 536 537 538 539
		for (let i = 0; i < this.languages.length; i++) {
			let language = this.languages[i];
			if (language.handles(file)) {
				return language;
			}
		}
		return null;
E
Erich Gamma 已提交
540 541 542
	}

	private triggerAllDiagnostics() {
543
		Object.keys(this.languagePerId).forEach(key => this.languagePerId[key].triggerAllDiagnostics());
E
Erich Gamma 已提交
544 545 546 547
	}

	/* internal */ populateService(): void {
		// See https://github.com/Microsoft/TypeScript/issues/5530
548
		workspace.saveAll(false).then(_ => {
549
			Object.keys(this.languagePerId).forEach(key => this.languagePerId[key].reInitialize());
E
Erich Gamma 已提交
550 551 552 553 554
		});
	}

	/* internal */ syntaxDiagnosticsReceived(event: Proto.DiagnosticEvent): void {
		let body = event.body;
555
		if (body && body.diagnostics) {
556 557
			let language = this.findLanguage(body.file);
			if (language) {
558
				language.syntaxDiagnosticsReceived(body.file, this.createMarkerDatas(body.diagnostics, language.diagnosticSource));
559
			}
E
Erich Gamma 已提交
560 561 562 563 564
		}
	}

	/* internal */ semanticDiagnosticsReceived(event: Proto.DiagnosticEvent): void {
		let body = event.body;
565
		if (body && body.diagnostics) {
566 567
			let language = this.findLanguage(body.file);
			if (language) {
568
				language.semanticDiagnosticsReceived(body.file, this.createMarkerDatas(body.diagnostics, language.diagnosticSource));
E
Erich Gamma 已提交
569 570
			}
		}
571
		/*
D
Dirk Baeumer 已提交
572
		if (Is.defined(body.queueLength)) {
J
Johannes Rieken 已提交
573
			BuildStatus.update({ queueLength: body.queueLength });
D
Dirk Baeumer 已提交
574
		}
575
		*/
E
Erich Gamma 已提交
576 577
	}

578
	/* internal */ configFileDiagnosticsReceived(event: Proto.ConfigFileDiagnosticEvent): void {
579
		// See https://github.com/Microsoft/TypeScript/issues/10384
580
		const body = event.body;
581
		if (!body || !body.diagnostics || !body.configFile) {
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
			return;
		}

		const language = body.triggerFile ? this.findLanguage(body.triggerFile) : this.findLanguage(body.configFile);
		if (!language) {
			return;
		}
		if (body.diagnostics.length === 0) {
			language.configFileDiagnosticsReceived(body.configFile, []);
		} else if (body.diagnostics.length >= 1) {
			workspace.openTextDocument(Uri.file(body.configFile)).then((document) => {
				let curly: [number, number, number] | undefined = undefined;
				let nonCurly: [number, number, number] | undefined = undefined;
				let diagnostic: Diagnostic;
				for (let index = 0; index < document.lineCount; index++) {
					const line = document.lineAt(index);
					const text = line.text;
					const firstNonWhitespaceCharacterIndex = line.firstNonWhitespaceCharacterIndex;
					if (firstNonWhitespaceCharacterIndex < text.length) {
						if (text.charAt(firstNonWhitespaceCharacterIndex) === '{') {
							curly = [index, firstNonWhitespaceCharacterIndex, firstNonWhitespaceCharacterIndex + 1];
							break;
604
						} else {
605 606 607 608
							const matches = /\s*([^\s]*)(?:\s*|$)/.exec(text.substr(firstNonWhitespaceCharacterIndex));
							if (matches && matches.length >= 1) {
								nonCurly = [index, firstNonWhitespaceCharacterIndex, firstNonWhitespaceCharacterIndex + matches[1].length];
							}
609
						}
610
					}
611
				}
612 613 614 615 616 617 618 619 620 621
				const match = curly || nonCurly;
				if (match) {
					diagnostic = new Diagnostic(new Range(match[0], match[1], match[0], match[2]), body.diagnostics[0].text);
				} else {
					diagnostic = new Diagnostic(new Range(0, 0, 0, 0), body.diagnostics[0].text);
				}
				if (diagnostic) {
					diagnostic.source = language.diagnosticSource;
					language.configFileDiagnosticsReceived(body.configFile, [diagnostic]);
				}
622
			}, _error => {
623 624
				language.configFileDiagnosticsReceived(body.configFile, [new Diagnostic(new Range(0, 0, 0, 0), body.diagnostics[0].text)]);
			});
625 626 627
		}
	}

628
	private createMarkerDatas(diagnostics: Proto.Diagnostic[], source: string): Diagnostic[] {
M
Matt Bierner 已提交
629
		const result: Diagnostic[] = [];
E
Erich Gamma 已提交
630
		for (let diagnostic of diagnostics) {
631
			let { start, end, text } = diagnostic;
E
Erich Gamma 已提交
632
			let range = new Range(start.line - 1, start.offset - 1, end.line - 1, end.offset - 1);
633 634
			let converted = new Diagnostic(range, text);
			converted.source = source;
635
			converted.code = '' + diagnostic.code;
636
			result.push(converted);
E
Erich Gamma 已提交
637 638 639
		}
		return result;
	}
640
}