typescriptMain.ts 22.0 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, DocumentFilter, Disposable, Uri, MessageItem, TextEditor } 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';
E
Erich Gamma 已提交
42

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

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

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

interface ProjectConfigMessageItem extends MessageItem {
	id: ProjectConfigAction;
}

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

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

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

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

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

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

M
Matt Bierner 已提交
104 105 106 107 108 109 110 111 112 113 114 115 116 117
	const jsDocCompletionHelper = new JsDocCompletionHelper(client);
	context.subscriptions.push(commands.registerCommand('_typescript.tryCompleteJsDoc', () => {
		const editor = window.activeTextEditor;
		if (!editor || !editor.selection.isEmpty) {
			return commands.executeCommand('type', { text: '\n' });
		}
		return jsDocCompletionHelper.tryCompleteJsDoc(editor, editor.selection.active).then(didCompleteComment => {
			if (didCompleteComment) {
				return;
			}
			return commands.executeCommand('type', { text: '\n' });
		});
	}));

118 119 120 121 122 123 124 125 126
	const goToProjectConfig = (isTypeScript: boolean) => {
		const editor = window.activeTextEditor;
		if (editor) {
			clientHost.goToProjectConfig(isTypeScript, editor.document.uri, editor.document.languageId);
		}
	};
	context.subscriptions.push(commands.registerCommand('typescript.goToProjectConfig', goToProjectConfig.bind(null, true)));
	context.subscriptions.push(commands.registerCommand('javascript.goToProjectConfig', goToProjectConfig.bind(null, false)));

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

138
const validateSetting = 'validate.enable';
E
Erich Gamma 已提交
139

140
class LanguageProvider {
E
Erich Gamma 已提交
141

142 143
	private extensions: ObjectMap<boolean>;
	private syntaxDiagnostics: ObjectMap<Diagnostic[]>;
E
Erich Gamma 已提交
144
	private currentDiagnostics: DiagnosticCollection;
145 146
	private bufferSyncSupport: BufferSyncSupport;

147 148
	private completionItemProvider: CompletionItemProvider;
	private formattingProvider: FormattingProvider;
149
	private formattingProviderRegistration: Disposable | null;
150
	private typingsStatus: TypingsStatus;
151
	private referenceCodeLensProvider: ReferenceCodeLensProvider;
152

153 154
	private _validate: boolean;

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

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

171
		this.typingsStatus = new TypingsStatus(client);
172
		new AtaProgressReporter(client);
173

174 175 176 177 178
		workspace.onDidChangeConfiguration(this.configurationChanged, this);
		this.configurationChanged();

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

	private registerProviders(client: TypeScriptServiceClient): void {
M
Matt Bierner 已提交
186
		const config = workspace.getConfiguration(this.id);
187

188
		this.completionItemProvider = new CompletionItemProvider(client, this.typingsStatus);
189
		this.completionItemProvider.updateConfiguration();
190 191 192

		let hoverProvider = new HoverProvider(client);
		let definitionProvider = new DefinitionProvider(client);
M
Matt Bierner 已提交
193
		let implementationProvider = new ImplementationProvider(client);
194
		const typeDefinitionProvider = new TypeDefintionProvider(client);
195 196 197 198 199 200 201
		let documentHighlightProvider = new DocumentHighlightProvider(client);
		let referenceProvider = new ReferenceProvider(client);
		let documentSymbolProvider = new DocumentSymbolProvider(client);
		let signatureHelpProvider = new SignatureHelpProvider(client);
		let renameProvider = new RenameProvider(client);
		this.formattingProvider = new FormattingProvider(client);
		this.formattingProvider.updateConfiguration(config);
202
		if (this.formattingProvider.isEnabled()) {
203 204
			this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(this.description.modeIds, this.formattingProvider);
		}
205

206
		this.referenceCodeLensProvider = new ReferenceCodeLensProvider(client);
207
		this.referenceCodeLensProvider.updateConfiguration();
208 209 210 211
		if (client.apiVersion.has206Features()) {
			languages.registerCodeLensProvider(this.description.modeIds, this.referenceCodeLensProvider);
		}

212
		this.description.modeIds.forEach(modeId => {
M
Matt Bierner 已提交
213
			const selector: DocumentFilter = modeId;
214 215 216
			languages.registerCompletionItemProvider(selector, this.completionItemProvider, '.');
			languages.registerHoverProvider(selector, hoverProvider);
			languages.registerDefinitionProvider(selector, definitionProvider);
217 218
			if (client.apiVersion.has220Features()) {
				// TODO: TS 2.1.5 returns incorrect results for implementation locations.
M
Matt Bierner 已提交
219
				languages.registerImplementationProvider(selector, implementationProvider);
220
			}
221 222 223
			if (client.apiVersion.has213Features()) {
				languages.registerTypeDefinitionProvider(selector, typeDefinitionProvider);
			}
224 225 226 227 228 229
			languages.registerDocumentHighlightProvider(selector, documentHighlightProvider);
			languages.registerReferenceProvider(selector, referenceProvider);
			languages.registerDocumentSymbolProvider(selector, documentSymbolProvider);
			languages.registerSignatureHelpProvider(selector, signatureHelpProvider, '(', ',');
			languages.registerRenameProvider(selector, renameProvider);
			languages.registerOnTypeFormattingEditProvider(selector, this.formattingProvider, ';', '}', '\n');
230
			languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(client, modeId));
231 232 233
			if (client.apiVersion.has213Features()) {
				languages.registerCodeActionsProvider(selector, new CodeActionProvider(client, modeId));
			}
234

235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
			languages.setLanguageConfiguration(modeId, {
				indentationRules: {
					// ^(.*\*/)?\s*\}.*$
					decreaseIndentPattern: /^(.*\*\/)?\s*\}.*$/,
					// ^.*\{[^}"']*$
					increaseIndentPattern: /^.*\{[^}"']*$/
				},
				wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
				onEnterRules: [
					{
						// e.g. /** | */
						beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
						afterText: /^\s*\*\/$/,
						action: { indentAction: IndentAction.IndentOutdent, appendText: ' * ' }
					},
					{
						// e.g. /** ...|
						beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
						action: { indentAction: IndentAction.None, appendText: ' * ' }
					},
					{
						// e.g.  * ...|
						beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
						action: { indentAction: IndentAction.None, appendText: '* ' }
					},
					{
						// e.g.  */|
						beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/,
						action: { indentAction: IndentAction.None, removeText: 1 }
264 265 266 267 268
					},
					{
						// e.g.  *-----*/|
						beforeText: /^(\t|(\ \ ))*\ \*[^/]*\*\/\s*$/,
						action: { indentAction: IndentAction.None, removeText: 1 }
269
					}
270
				]
271
			});
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288

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

			languages.setLanguageConfiguration('jsx-tags', {
				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 }
					}
				],
			});
289 290 291 292
		});
	}

	private configurationChanged(): void {
M
Matt Bierner 已提交
293
		const config = workspace.getConfiguration(this.id);
294 295
		this.updateValidate(config.get(validateSetting, true));
		if (this.completionItemProvider) {
296
			this.completionItemProvider.updateConfiguration();
297
		}
298
		if (this.referenceCodeLensProvider) {
299
			this.referenceCodeLensProvider.updateConfiguration();
300
		}
301 302
		if (this.formattingProvider) {
			this.formattingProvider.updateConfiguration(config);
303 304
			if (!this.formattingProvider.isEnabled() && this.formattingProviderRegistration) {
				this.formattingProviderRegistration.dispose();
305
				this.formattingProviderRegistration = null;
306 307 308 309

			} else if (this.formattingProvider.isEnabled() && !this.formattingProviderRegistration) {
				this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(this.description.modeIds, this.formattingProvider);
			}
310
		}
311 312 313
	}

	public handles(file: string): boolean {
M
Matt Bierner 已提交
314
		const extension = path.extname(file);
D
Dirk Baeumer 已提交
315 316 317
		if ((extension && this.extensions[extension]) || this.bufferSyncSupport.handles(file)) {
			return true;
		}
M
Matt Bierner 已提交
318
		const basename = path.basename(file);
319
		return !!basename && basename === this.description.configFile;
320 321
	}

322 323 324 325
	public get id(): string {
		return this.description.id;
	}

326 327 328 329
	public get diagnosticSource(): string {
		return this.description.diagnosticSource;
	}

330
	private updateValidate(value: boolean) {
331 332 333
		if (this._validate === value) {
			return;
		}
334
		this._validate = value;
335
		this.bufferSyncSupport.validate = value;
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
		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 已提交
360
		const syntaxMarkers = this.syntaxDiagnostics[file];
361 362 363 364
		if (syntaxMarkers) {
			delete this.syntaxDiagnostics[file];
			diagnostics = syntaxMarkers.concat(diagnostics);
		}
365
		this.currentDiagnostics.set(this.client.asUrl(file), diagnostics);
366
	}
367 368

	public configFileDiagnosticsReceived(file: string, diagnostics: Diagnostic[]): void {
369
		this.currentDiagnostics.set(this.client.asUrl(file), diagnostics);
370
	}
371 372 373 374
}

class TypeScriptServiceClientHost implements ITypescriptServiceClientHost {
	private client: TypeScriptServiceClient;
375
	private languages: LanguageProvider[];
376
	private languagePerId: ObjectMap<LanguageProvider>;
E
Erich Gamma 已提交
377

378
	constructor(descriptions: LanguageDescription[], storagePath: string | undefined, globalState: Memento, workspaceState: Memento) {
E
Erich Gamma 已提交
379 380 381 382 383
		let handleProjectCreateOrDelete = () => {
			this.client.execute('reloadProjects', null, false);
			this.triggerAllDiagnostics();
		};
		let handleProjectChange = () => {
D
Dirk Baeumer 已提交
384 385 386
			setTimeout(() => {
				this.triggerAllDiagnostics();
			}, 1500);
A
tslint  
Alex Dima 已提交
387
		};
388
		let watcher = workspace.createFileSystemWatcher('**/[tj]sconfig.json');
E
Erich Gamma 已提交
389 390 391 392
		watcher.onDidCreate(handleProjectCreateOrDelete);
		watcher.onDidDelete(handleProjectCreateOrDelete);
		watcher.onDidChange(handleProjectChange);

393
		this.client = new TypeScriptServiceClient(this, storagePath, globalState, workspaceState);
394 395 396
		this.languages = [];
		this.languagePerId = Object.create(null);
		descriptions.forEach(description => {
397
			let manager = new LanguageProvider(this.client, description);
398 399 400
			this.languages.push(manager);
			this.languagePerId[description.id] = manager;
		});
E
Erich Gamma 已提交
401 402 403 404 405 406
	}

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

D
Dirk Baeumer 已提交
407 408 409 410 411
	public reloadProjects(): void {
		this.client.execute('reloadProjects', null, false);
		this.triggerAllDiagnostics();
	}

D
Dirk Baeumer 已提交
412 413 414 415
	public handles(file: string): boolean {
		return !!this.findLanguage(file);
	}

416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
	public goToProjectConfig(
		isTypeScriptProject: boolean,
		resource: Uri,
		languageId: string
	): Thenable<TextEditor> | undefined {
		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);
		// TODO: TSServer errors when 'projectInfo' is invoked on a non js/ts file
		if (!file || !this.languagePerId[languageId]) {
			window.showWarningMessage(
				localize(
					'typescript.projectConfigUnsupportedFile',
					'Could not determine TypeScript or JavaScript project. Unsupported file type'));
			return;
		}

		return this.client.execute('projectInfo', { file, needFileNameList: false }).then(res => {
			if (!res || !res.body) {
				return window.showWarningMessage(localize('typescript.projectConfigCouldNotGetInfo', 'Could not determine TypeScript or JavaScript project'));
			}

			const { configFileName } = res.body;
			if (configFileName && configFileName.indexOf('/dev/null/') !== 0) {
				return workspace.openTextDocument(configFileName)
					.then(window.showTextDocument);
			}

			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' })))
								.then(window.showTextDocument);

						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);
					}
				});
		});
	}

486
	private findLanguage(file: string): LanguageProvider | null {
487 488 489 490 491 492 493
		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 已提交
494 495 496
	}

	private triggerAllDiagnostics() {
497
		Object.keys(this.languagePerId).forEach(key => this.languagePerId[key].triggerAllDiagnostics());
E
Erich Gamma 已提交
498 499 500 501
	}

	/* internal */ populateService(): void {
		// See https://github.com/Microsoft/TypeScript/issues/5530
502
		workspace.saveAll(false).then(_ => {
503
			Object.keys(this.languagePerId).forEach(key => this.languagePerId[key].reInitialize());
E
Erich Gamma 已提交
504 505 506 507 508
		});
	}

	/* internal */ syntaxDiagnosticsReceived(event: Proto.DiagnosticEvent): void {
		let body = event.body;
509
		if (body && body.diagnostics) {
510 511
			let language = this.findLanguage(body.file);
			if (language) {
512
				language.syntaxDiagnosticsReceived(body.file, this.createMarkerDatas(body.diagnostics, language.diagnosticSource));
513
			}
E
Erich Gamma 已提交
514 515 516 517 518
		}
	}

	/* internal */ semanticDiagnosticsReceived(event: Proto.DiagnosticEvent): void {
		let body = event.body;
519
		if (body && body.diagnostics) {
520 521
			let language = this.findLanguage(body.file);
			if (language) {
522
				language.semanticDiagnosticsReceived(body.file, this.createMarkerDatas(body.diagnostics, language.diagnosticSource));
E
Erich Gamma 已提交
523 524
			}
		}
525
		/*
D
Dirk Baeumer 已提交
526
		if (Is.defined(body.queueLength)) {
J
Johannes Rieken 已提交
527
			BuildStatus.update({ queueLength: body.queueLength });
D
Dirk Baeumer 已提交
528
		}
529
		*/
E
Erich Gamma 已提交
530 531
	}

532
	/* internal */ configFileDiagnosticsReceived(event: Proto.ConfigFileDiagnosticEvent): void {
533
		// See https://github.com/Microsoft/TypeScript/issues/10384
534
		const body = event.body;
535
		if (!body || !body.diagnostics || !body.configFile) {
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
			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;
558
						} else {
559 560 561 562
							const matches = /\s*([^\s]*)(?:\s*|$)/.exec(text.substr(firstNonWhitespaceCharacterIndex));
							if (matches && matches.length >= 1) {
								nonCurly = [index, firstNonWhitespaceCharacterIndex, firstNonWhitespaceCharacterIndex + matches[1].length];
							}
563
						}
564
					}
565
				}
566 567 568 569 570 571 572 573 574 575
				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]);
				}
576
			}, _error => {
577 578
				language.configFileDiagnosticsReceived(body.configFile, [new Diagnostic(new Range(0, 0, 0, 0), body.diagnostics[0].text)]);
			});
579 580 581
		}
	}

582
	private createMarkerDatas(diagnostics: Proto.Diagnostic[], source: string): Diagnostic[] {
M
Matt Bierner 已提交
583
		const result: Diagnostic[] = [];
E
Erich Gamma 已提交
584
		for (let diagnostic of diagnostics) {
585
			let { start, end, text } = diagnostic;
E
Erich Gamma 已提交
586
			let range = new Range(start.line - 1, start.offset - 1, end.line - 1, end.offset - 1);
587 588
			let converted = new Diagnostic(range, text);
			converted.source = source;
589
			converted.code = '' + diagnostic.code;
590
			result.push(converted);
E
Erich Gamma 已提交
591 592 593
		}
		return result;
	}
594
}