瀏覽代碼

Adopt strict checking (fixes microsoft/monaco-editor#1715)

Alex Dima 5 年之前
父節點
當前提交
e4ab9e4360
共有 10 個文件被更改,包括 362 次插入328 次删除
  1. 3 3
      package-lock.json
  2. 1 1
      package.json
  3. 308 295
      src/languageFeatures.ts
  4. 8 0
      src/lib/editor.worker.d.ts
  5. 8 4
      src/monaco.contribution.ts
  6. 2 2
      src/ts.worker.ts
  7. 12 11
      src/tsWorker.ts
  8. 3 2
      src/tsconfig.esm.json
  9. 3 2
      src/tsconfig.json
  10. 14 8
      src/workerManager.ts

+ 3 - 3
package-lock.json

@@ -11,9 +11,9 @@
       "dev": true
     },
     "monaco-editor-core": {
-      "version": "0.18.1",
-      "resolved": "https://registry.npmjs.org/monaco-editor-core/-/monaco-editor-core-0.18.1.tgz",
-      "integrity": "sha512-euzXzmwjZFG0oAPGjICMwINcZBzQDyfGDYlAR5YNMBJZO9Bmkqq1xpTTze/qQ0KKbVmawFXiwgUbg7WVgebP9Q==",
+      "version": "0.19.0",
+      "resolved": "https://registry.npmjs.org/monaco-editor-core/-/monaco-editor-core-0.19.0.tgz",
+      "integrity": "sha512-wD60zpYDhsuJbzzLQigAteK2cA5fkuaDn+4c7NLwm/526OX5eL6MMvLhfvRgrDvLO00SYhFf6vz1y1C8M1hTpQ==",
       "dev": true
     },
     "monaco-languages": {

+ 1 - 1
package.json

@@ -20,7 +20,7 @@
     "url": "https://github.com/Microsoft/monaco-typescript/issues"
   },
   "devDependencies": {
-    "monaco-editor-core": "^0.18.1",
+    "monaco-editor-core": "^0.19.0",
     "monaco-languages": "^1.8.0",
     "monaco-plugin-helpers": "^1.0.2",
     "requirejs": "^2.3.6",

+ 308 - 295
src/languageFeatures.ts

@@ -11,7 +11,6 @@ import { TypeScriptWorker } from './tsWorker';
 import Uri = monaco.Uri;
 import Position = monaco.Position;
 import Range = monaco.Range;
-import Thenable = monaco.Thenable;
 import CancellationToken = monaco.CancellationToken;
 import IDisposable = monaco.IDisposable;
 
@@ -48,7 +47,7 @@ export function flattenDiagnosticMessageText(diag: string | ts.DiagnosticMessage
 	return result;
 }
 
-function displayPartsToString(displayParts: ts.SymbolDisplayPart[]): string {
+function displayPartsToString(displayParts: ts.SymbolDisplayPart[] | undefined): string {
 	if (displayParts) {
 		return displayParts.map((displayPart) => displayPart.text).join("");
 	}
@@ -62,19 +61,17 @@ export abstract class Adapter {
 	constructor(protected _worker: (first: Uri, ...more: Uri[]) => Promise<TypeScriptWorker>) {
 	}
 
-	protected _positionToOffset(uri: Uri, position: monaco.IPosition): number {
-		let model = monaco.editor.getModel(uri);
-		return model.getOffsetAt(position);
-	}
+	// protected _positionToOffset(model: monaco.editor.ITextModel, position: monaco.IPosition): number {
+	// 	return model.getOffsetAt(position);
+	// }
 
-	protected _offsetToPosition(uri: Uri, offset: number): monaco.IPosition {
-		let model = monaco.editor.getModel(uri);
-		return model.getPositionAt(offset);
-	}
+	// protected _offsetToPosition(model: monaco.editor.ITextModel, offset: number): monaco.IPosition {
+	// 	return model.getPositionAt(offset);
+	// }
 
-	protected _textSpanToRange(uri: Uri, span: ts.TextSpan): monaco.IRange {
-		let p1 = this._offsetToPosition(uri, span.start);
-		let p2 = this._offsetToPosition(uri, span.start + span.length);
+	protected _textSpanToRange(model: monaco.editor.ITextModel, span: ts.TextSpan): monaco.IRange {
+		let p1 = model.getPositionAt(span.start);
+		let p2 = model.getPositionAt(span.start + span.length);
 		let { lineNumber: startLineNumber, column: startColumn } = p1;
 		let { lineNumber: endLineNumber, column: endColumn } = p2;
 		return { startLineNumber, startColumn, endLineNumber, endColumn };
@@ -108,7 +105,7 @@ export class DiagnosticsAdapter extends Adapter {
 			let handle: number;
 			const changeSubscription = model.onDidChangeContent(() => {
 				clearTimeout(handle);
-				handle = setTimeout(() => this._doValidate(model.uri), 500);
+				handle = setTimeout(() => this._doValidate(model), 500);
 			});
 
 			this._listener[model.uri.toString()] = {
@@ -118,7 +115,7 @@ export class DiagnosticsAdapter extends Adapter {
 				}
 			};
 
-			this._doValidate(model.uri);
+			this._doValidate(model);
 		};
 
 		const onModelRemoved = (model: monaco.editor.IModel): void => {
@@ -163,43 +160,46 @@ export class DiagnosticsAdapter extends Adapter {
 		this._disposables = [];
 	}
 
-	private _doValidate(resource: Uri): void {
-		this._worker(resource).then(worker => {
-			if (!monaco.editor.getModel(resource)) {
-				// model was disposed in the meantime
-				return null;
-			}
-			const promises: Promise<ts.Diagnostic[]>[] = [];
-			const { noSyntaxValidation, noSemanticValidation, noSuggestionDiagnostics } = this._defaults.getDiagnosticsOptions();
-			if (!noSyntaxValidation) {
-				promises.push(worker.getSyntacticDiagnostics(resource.toString()));
-			}
-			if (!noSemanticValidation) {
-				promises.push(worker.getSemanticDiagnostics(resource.toString()));
-			}
-			if (!noSuggestionDiagnostics) {
-				promises.push(worker.getSuggestionDiagnostics(resource.toString()));
-			}
-			return Promise.all(promises);
-		}).then(diagnostics => {
-			if (!diagnostics || !monaco.editor.getModel(resource)) {
-				// model was disposed in the meantime
-				return null;
-			}
-			const markers = diagnostics
-				.reduce((p, c) => c.concat(p), [])
-				.filter(d => (this._defaults.getDiagnosticsOptions().diagnosticCodesToIgnore || []).indexOf(d.code) === -1)
-				.map(d => this._convertDiagnostics(resource, d));
-
-			monaco.editor.setModelMarkers(monaco.editor.getModel(resource), this._selector, markers);
-		}).then(undefined, err => {
-			console.error(err);
-		});
+	private async _doValidate(model: monaco.editor.ITextModel): Promise<void> {
+		const worker = await this._worker(model.uri);
+
+		if (model.isDisposed()) {
+			// model was disposed in the meantime
+			return;
+		}
+
+		const promises: Promise<ts.Diagnostic[]>[] = [];
+		const { noSyntaxValidation, noSemanticValidation, noSuggestionDiagnostics } = this._defaults.getDiagnosticsOptions();
+		if (!noSyntaxValidation) {
+			promises.push(worker.getSyntacticDiagnostics(model.uri.toString()));
+		}
+		if (!noSemanticValidation) {
+			promises.push(worker.getSemanticDiagnostics(model.uri.toString()));
+		}
+		if (!noSuggestionDiagnostics) {
+			promises.push(worker.getSuggestionDiagnostics(model.uri.toString()));
+		}
+
+		const diagnostics = await Promise.all(promises);
+
+		if (!diagnostics || model.isDisposed()) {
+			// model was disposed in the meantime
+			return;
+		}
+
+		const markers = diagnostics
+			.reduce((p, c) => c.concat(p), [])
+			.filter(d => (this._defaults.getDiagnosticsOptions().diagnosticCodesToIgnore || []).indexOf(d.code) === -1)
+			.map(d => this._convertDiagnostics(model, d));
+
+		monaco.editor.setModelMarkers(model, this._selector, markers);
 	}
 
-	private _convertDiagnostics(resource: Uri, diag: ts.Diagnostic): monaco.editor.IMarkerData {
-		const { lineNumber: startLineNumber, column: startColumn } = this._offsetToPosition(resource, diag.start);
-		const { lineNumber: endLineNumber, column: endColumn } = this._offsetToPosition(resource, diag.start + diag.length);
+	private _convertDiagnostics(model: monaco.editor.ITextModel, diag: ts.Diagnostic): monaco.editor.IMarkerData {
+		const diagStart = diag.start || 0;
+		const diagLength = diag.length || 1;
+		const { lineNumber: startLineNumber, column: startColumn } = model.getPositionAt(diagStart);
+		const { lineNumber: endLineNumber, column: endColumn } = model.getPositionAt(diagStart + diagLength);
 
 		return {
 			severity: this._tsDiagnosticCategoryToMarkerSeverity(diag.category),
@@ -210,28 +210,41 @@ export class DiagnosticsAdapter extends Adapter {
 			message: flattenDiagnosticMessageText(diag.messageText, '\n'),
 			code: diag.code.toString(),
 			tags: diag.reportsUnnecessary ? [monaco.MarkerTag.Unnecessary] : [],
-			relatedInformation: this._convertRelatedInformation(resource, diag.relatedInformation),
+			relatedInformation: this._convertRelatedInformation(model, diag.relatedInformation),
 		};
 	}
 
-	private _convertRelatedInformation(resource: Uri, relatedInformation?: ts.DiagnosticRelatedInformation[]): monaco.editor.IRelatedInformation[] {
-		if (relatedInformation === undefined)
-			return undefined;
+	private _convertRelatedInformation(model: monaco.editor.ITextModel, relatedInformation?: ts.DiagnosticRelatedInformation[]): monaco.editor.IRelatedInformation[] | undefined {
+		if (!relatedInformation) {
+			return;
+		}
 
-		return relatedInformation.map(info => {
-			const relatedResource = info.file === undefined ? resource : monaco.Uri.parse(info.file.fileName);
-			const { lineNumber: startLineNumber, column: startColumn } = this._offsetToPosition(relatedResource, info.start);
-			const { lineNumber: endLineNumber, column: endColumn } = this._offsetToPosition(relatedResource, info.start + info.length);
+		const result: monaco.editor.IRelatedInformation[] = [];
+		relatedInformation.forEach((info) => {
+			let relatedResource: monaco.editor.ITextModel | null = model;
+			if (info.file) {
+				const relatedResourceUri = monaco.Uri.parse(info.file.fileName);
+				relatedResource = monaco.editor.getModel(relatedResourceUri);
+			}
 
-			return {
-				resource: relatedResource,
+			if (!relatedResource) {
+				return;
+			}
+			const infoStart = info.start || 0;
+			const infoLength = info.length || 1;
+			const { lineNumber: startLineNumber, column: startColumn } = relatedResource.getPositionAt(infoStart);
+			const { lineNumber: endLineNumber, column: endColumn } = relatedResource.getPositionAt(infoStart + infoLength);
+
+			result.push({
+				resource: relatedResource.uri,
 				startLineNumber,
 				startColumn,
 				endLineNumber,
 				endColumn,
 				message: flattenDiagnosticMessageText(info.messageText, '\n')
-			};
+			});
 		});
+		return result;
 	}
 
 	private _tsDiagnosticCategoryToMarkerSeverity(category: ts.DiagnosticCategory): monaco.MarkerSeverity {
@@ -241,6 +254,7 @@ export class DiagnosticsAdapter extends Adapter {
 			case DiagnosticCategory.Warning: return monaco.MarkerSeverity.Warning
 			case DiagnosticCategory.Suggestion: return monaco.MarkerSeverity.Hint
 		}
+		return monaco.MarkerSeverity.Info;
 	}
 }
 
@@ -257,68 +271,64 @@ export class SuggestAdapter extends Adapter implements monaco.languages.Completi
 		return ['.'];
 	}
 
-	provideCompletionItems(model: monaco.editor.IReadOnlyModel, position: Position, _context: monaco.languages.CompletionContext, token: CancellationToken): Thenable<monaco.languages.CompletionList> {
+	public async provideCompletionItems(model: monaco.editor.ITextModel, position: Position, _context: monaco.languages.CompletionContext, token: CancellationToken): Promise<monaco.languages.CompletionList | undefined> {
 		const wordInfo = model.getWordUntilPosition(position);
 		const wordRange = new Range(position.lineNumber, wordInfo.startColumn, position.lineNumber, wordInfo.endColumn);
 		const resource = model.uri;
-		const offset = this._positionToOffset(resource, position);
+		const offset = model.getOffsetAt(position);
 
-		return this._worker(resource).then(worker => {
-			return worker.getCompletionsAtPosition(resource.toString(), offset);
-		}).then(info => {
-			if (!info) {
-				return;
-			}
-			let suggestions: MyCompletionItem[] = info.entries.map(entry => {
-				let range = wordRange;
-				if (entry.replacementSpan) {
-					const p1 = model.getPositionAt(entry.replacementSpan.start);
-					const p2 = model.getPositionAt(entry.replacementSpan.start + entry.replacementSpan.length);
-					range = new Range(p1.lineNumber, p1.column, p2.lineNumber, p2.column);
-				}
+		const worker = await this._worker(resource);
+		const info = await worker.getCompletionsAtPosition(resource.toString(), offset);
 
-				return {
-					uri: resource,
-					position: position,
-					range: range,
-					label: entry.name,
-					insertText: entry.name,
-					sortText: entry.sortText,
-					kind: SuggestAdapter.convertKind(entry.kind)
-				};
-			});
+		if (!info || model.isDisposed()) {
+			return;
+		}
+
+		const suggestions: MyCompletionItem[] = info.entries.map(entry => {
+			let range = wordRange;
+			if (entry.replacementSpan) {
+				const p1 = model.getPositionAt(entry.replacementSpan.start);
+				const p2 = model.getPositionAt(entry.replacementSpan.start + entry.replacementSpan.length);
+				range = new Range(p1.lineNumber, p1.column, p2.lineNumber, p2.column);
+			}
 
 			return {
-				suggestions
+				uri: resource,
+				position: position,
+				range: range,
+				label: entry.name,
+				insertText: entry.name,
+				sortText: entry.sortText,
+				kind: SuggestAdapter.convertKind(entry.kind)
 			};
 		});
+
+		return {
+			suggestions
+		};
 	}
 
-	resolveCompletionItem(_model: monaco.editor.IReadOnlyModel, _position: Position, item: monaco.languages.CompletionItem, token: CancellationToken): Thenable<monaco.languages.CompletionItem> {
-		let myItem = <MyCompletionItem>item;
+	public async resolveCompletionItem(model: monaco.editor.ITextModel, _position: Position, item: monaco.languages.CompletionItem, token: CancellationToken): Promise<monaco.languages.CompletionItem> {
+		const myItem = <MyCompletionItem>item;
 		const resource = myItem.uri;
 		const position = myItem.position;
+		const offset = model.getOffsetAt(position);
 
-		return this._worker(resource).then(worker => {
-			return worker.getCompletionEntryDetails(resource.toString(),
-				this._positionToOffset(resource, position),
-				myItem.label);
-
-		}).then(details => {
-			if (!details) {
-				return myItem;
+		const worker = await this._worker(resource);
+		const details = await worker.getCompletionEntryDetails(resource.toString(), offset, myItem.label);
+		if (!details || model.isDisposed()) {
+			return myItem;
+		}
+		return <MyCompletionItem>{
+			uri: resource,
+			position: position,
+			label: details.name,
+			kind: SuggestAdapter.convertKind(details.kind),
+			detail: displayPartsToString(details.displayParts),
+			documentation: {
+				value: displayPartsToString(details.documentation)
 			}
-			return <MyCompletionItem>{
-				uri: resource,
-				position: position,
-				label: details.name,
-				kind: SuggestAdapter.convertKind(details.kind),
-				detail: displayPartsToString(details.displayParts),
-				documentation: {
-					value: displayPartsToString(details.documentation)
-				}
-			};
-		});
+		};
 	}
 
 	private static convertKind(kind: string): monaco.languages.CompletionItemKind {
@@ -359,50 +369,51 @@ export class SignatureHelpAdapter extends Adapter implements monaco.languages.Si
 
 	public signatureHelpTriggerCharacters = ['(', ','];
 
-	provideSignatureHelp(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.SignatureHelpResult> {
-		let resource = model.uri;
-		return this._worker(resource).then(worker => worker.getSignatureHelpItems(resource.toString(), this._positionToOffset(resource, position))).then(info => {
+	public async provideSignatureHelp(model: monaco.editor.ITextModel, position: Position, token: CancellationToken): Promise<monaco.languages.SignatureHelpResult | undefined> {
+		const resource = model.uri;
+		const offset = model.getOffsetAt(position);
+		const worker = await this._worker(resource);
+		const info = await worker.getSignatureHelpItems(resource.toString(), offset);
 
-			if (!info) {
-				return;
-			}
+		if (!info || model.isDisposed()) {
+			return;
+		}
 
-			let ret: monaco.languages.SignatureHelp = {
-				activeSignature: info.selectedItemIndex,
-				activeParameter: info.argumentIndex,
-				signatures: []
-			};
+		const ret: monaco.languages.SignatureHelp = {
+			activeSignature: info.selectedItemIndex,
+			activeParameter: info.argumentIndex,
+			signatures: []
+		};
 
-			info.items.forEach(item => {
+		info.items.forEach(item => {
 
-				let signature: monaco.languages.SignatureInformation = {
-					label: '',
-					parameters: []
-				};
+			const signature: monaco.languages.SignatureInformation = {
+				label: '',
+				parameters: []
+			};
 
-				signature.documentation = displayPartsToString(item.documentation);
-				signature.label += displayPartsToString(item.prefixDisplayParts);
-				item.parameters.forEach((p, i, a) => {
-					let label = displayPartsToString(p.displayParts);
-					let parameter: monaco.languages.ParameterInformation = {
-						label: label,
-						documentation: displayPartsToString(p.documentation)
-					};
-					signature.label += label;
-					signature.parameters.push(parameter);
-					if (i < a.length - 1) {
-						signature.label += displayPartsToString(item.separatorDisplayParts);
-					}
-				});
-				signature.label += displayPartsToString(item.suffixDisplayParts);
-				ret.signatures.push(signature);
+			signature.documentation = displayPartsToString(item.documentation);
+			signature.label += displayPartsToString(item.prefixDisplayParts);
+			item.parameters.forEach((p, i, a) => {
+				const label = displayPartsToString(p.displayParts);
+				const parameter: monaco.languages.ParameterInformation = {
+					label: label,
+					documentation: displayPartsToString(p.documentation)
+				};
+				signature.label += label;
+				signature.parameters.push(parameter);
+				if (i < a.length - 1) {
+					signature.label += displayPartsToString(item.separatorDisplayParts);
+				}
 			});
-
-			return {
-				value: ret,
-				dispose() { }
-			};
+			signature.label += displayPartsToString(item.suffixDisplayParts);
+			ret.signatures.push(signature);
 		});
+
+		return {
+			value: ret,
+			dispose() { }
+		};
 	}
 }
 
@@ -410,34 +421,33 @@ export class SignatureHelpAdapter extends Adapter implements monaco.languages.Si
 
 export class QuickInfoAdapter extends Adapter implements monaco.languages.HoverProvider {
 
-	provideHover(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.Hover> {
-		let resource = model.uri;
+	public async provideHover(model: monaco.editor.ITextModel, position: Position, token: CancellationToken): Promise<monaco.languages.Hover | undefined> {
+		const resource = model.uri;
+		const offset = model.getOffsetAt(position);
+		const worker = await this._worker(resource);
+		const info = await worker.getQuickInfoAtPosition(resource.toString(), offset);
 
-		return this._worker(resource).then(worker => {
-			return worker.getQuickInfoAtPosition(resource.toString(), this._positionToOffset(resource, position));
-		}).then(info => {
-			if (!info) {
-				return;
+		if (!info || model.isDisposed()) {
+			return;
+		}
+
+		const documentation = displayPartsToString(info.documentation);
+		const tags = info.tags ? info.tags.map(tag => {
+			const label = `*@${tag.name}*`;
+			if (!tag.text) {
+				return label;
 			}
-			let documentation = displayPartsToString(info.documentation);
-			let tags = info.tags ? info.tags.map(tag => {
-				const label = `*@${tag.name}*`;
-				if (!tag.text) {
-					return label;
-				}
-				return label + (tag.text.match(/\r\n|\n/g) ? ' \n' + tag.text : ` - ${tag.text}`);
-			})
-				.join('  \n\n') : '';
-			let contents = displayPartsToString(info.displayParts);
-			return {
-				range: this._textSpanToRange(resource, info.textSpan),
-				contents: [{
-					value: '```js\n' + contents + '\n```\n'
-				}, {
-					value: documentation + (tags ? '\n\n' + tags : '')
-				}]
-			};
-		});
+			return label + (tag.text.match(/\r\n|\n/g) ? ' \n' + tag.text : ` - ${tag.text}`);
+		}).join('  \n\n') : '';
+		const contents = displayPartsToString(info.displayParts);
+		return {
+			range: this._textSpanToRange(model, info.textSpan),
+			contents: [{
+				value: '```js\n' + contents + '\n```\n'
+			}, {
+				value: documentation + (tags ? '\n\n' + tags : '')
+			}]
+		};
 	}
 }
 
@@ -445,21 +455,21 @@ export class QuickInfoAdapter extends Adapter implements monaco.languages.HoverP
 
 export class OccurrencesAdapter extends Adapter implements monaco.languages.DocumentHighlightProvider {
 
-	public provideDocumentHighlights(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.DocumentHighlight[]> {
+	public async provideDocumentHighlights(model: monaco.editor.ITextModel, position: Position, token: CancellationToken): Promise<monaco.languages.DocumentHighlight[] | undefined> {
 		const resource = model.uri;
+		const offset = model.getOffsetAt(position)
+		const worker = await this._worker(resource);
+		const entries = await worker.getOccurrencesAtPosition(resource.toString(), offset);
 
-		return this._worker(resource).then(worker => {
-			return worker.getOccurrencesAtPosition(resource.toString(), this._positionToOffset(resource, position));
-		}).then(entries => {
-			if (!entries) {
-				return;
-			}
-			return entries.map(entry => {
-				return <monaco.languages.DocumentHighlight>{
-					range: this._textSpanToRange(resource, entry.textSpan),
-					kind: entry.isWriteAccess ? monaco.languages.DocumentHighlightKind.Write : monaco.languages.DocumentHighlightKind.Text
-				};
-			});
+		if (!entries || model.isDisposed()) {
+			return;
+		}
+
+		return entries.map(entry => {
+			return <monaco.languages.DocumentHighlight>{
+				range: this._textSpanToRange(model, entry.textSpan),
+				kind: entry.isWriteAccess ? monaco.languages.DocumentHighlightKind.Write : monaco.languages.DocumentHighlightKind.Text
+			};
 		});
 	}
 }
@@ -468,27 +478,28 @@ export class OccurrencesAdapter extends Adapter implements monaco.languages.Docu
 
 export class DefinitionAdapter extends Adapter {
 
-	public provideDefinition(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.Definition> {
+	public async provideDefinition(model: monaco.editor.ITextModel, position: Position, token: CancellationToken): Promise<monaco.languages.Definition | undefined> {
 		const resource = model.uri;
+		const offset = model.getOffsetAt(position);
+		const worker = await this._worker(resource);
+		const entries = await worker.getDefinitionAtPosition(resource.toString(), offset);
 
-		return this._worker(resource).then(worker => {
-			return worker.getDefinitionAtPosition(resource.toString(), this._positionToOffset(resource, position));
-		}).then(entries => {
-			if (!entries) {
-				return;
-			}
-			const result: monaco.languages.Location[] = [];
-			for (let entry of entries) {
-				const uri = Uri.parse(entry.fileName);
-				if (monaco.editor.getModel(uri)) {
-					result.push({
-						uri: uri,
-						range: this._textSpanToRange(uri, entry.textSpan)
-					});
-				}
+		if (!entries || model.isDisposed()) {
+			return;
+		}
+
+		const result: monaco.languages.Location[] = [];
+		for (let entry of entries) {
+			const uri = Uri.parse(entry.fileName);
+			const refModel = monaco.editor.getModel(uri);
+			if (refModel) {
+				result.push({
+					uri: uri,
+					range: this._textSpanToRange(refModel, entry.textSpan)
+				});
 			}
-			return result;
-		});
+		}
+		return result;
 	}
 }
 
@@ -496,27 +507,28 @@ export class DefinitionAdapter extends Adapter {
 
 export class ReferenceAdapter extends Adapter implements monaco.languages.ReferenceProvider {
 
-	provideReferences(model: monaco.editor.IReadOnlyModel, position: Position, context: monaco.languages.ReferenceContext, token: CancellationToken): Thenable<monaco.languages.Location[]> {
+	public async provideReferences(model: monaco.editor.ITextModel, position: Position, context: monaco.languages.ReferenceContext, token: CancellationToken): Promise<monaco.languages.Location[] | undefined> {
 		const resource = model.uri;
+		const offset = model.getOffsetAt(position);
+		const worker = await this._worker(resource);
+		const entries = await worker.getReferencesAtPosition(resource.toString(), offset);
 
-		return this._worker(resource).then(worker => {
-			return worker.getReferencesAtPosition(resource.toString(), this._positionToOffset(resource, position));
-		}).then(entries => {
-			if (!entries) {
-				return;
-			}
-			const result: monaco.languages.Location[] = [];
-			for (let entry of entries) {
-				const uri = Uri.parse(entry.fileName);
-				if (monaco.editor.getModel(uri)) {
-					result.push({
-						uri: uri,
-						range: this._textSpanToRange(uri, entry.textSpan)
-					});
-				}
+		if (!entries || model.isDisposed()) {
+			return;
+		}
+
+		const result: monaco.languages.Location[] = [];
+		for (let entry of entries) {
+			const uri = Uri.parse(entry.fileName);
+			const refModel = monaco.editor.getModel(uri);
+			if (refModel) {
+				result.push({
+					uri: uri,
+					range: this._textSpanToRange(refModel, entry.textSpan)
+				});
 			}
-			return result;
-		});
+		}
+		return result;
 	}
 }
 
@@ -524,38 +536,38 @@ export class ReferenceAdapter extends Adapter implements monaco.languages.Refere
 
 export class OutlineAdapter extends Adapter implements monaco.languages.DocumentSymbolProvider {
 
-	public provideDocumentSymbols(model: monaco.editor.IReadOnlyModel, token: CancellationToken): Thenable<monaco.languages.DocumentSymbol[]> {
+	public async provideDocumentSymbols(model: monaco.editor.ITextModel, token: CancellationToken): Promise<monaco.languages.DocumentSymbol[] | undefined> {
 		const resource = model.uri;
+		const worker = await this._worker(resource);
+		const items = await worker.getNavigationBarItems(resource.toString());
 
-		return this._worker(resource).then(worker => worker.getNavigationBarItems(resource.toString())).then(items => {
-			if (!items) {
-				return;
-			}
+		if (!items || model.isDisposed()) {
+			return;
+		}
 
-			const convert = (bucket: monaco.languages.DocumentSymbol[], item: ts.NavigationBarItem, containerLabel?: string): void => {
-				let result: monaco.languages.DocumentSymbol = {
-					name: item.text,
-					detail: '',
-					kind: <monaco.languages.SymbolKind>(outlineTypeTable[item.kind] || monaco.languages.SymbolKind.Variable),
-					range: this._textSpanToRange(resource, item.spans[0]),
-					selectionRange: this._textSpanToRange(resource, item.spans[0]),
-					tags: [],
-					containerName: containerLabel
-				};
+		const convert = (bucket: monaco.languages.DocumentSymbol[], item: ts.NavigationBarItem, containerLabel?: string): void => {
+			let result: monaco.languages.DocumentSymbol = {
+				name: item.text,
+				detail: '',
+				kind: <monaco.languages.SymbolKind>(outlineTypeTable[item.kind] || monaco.languages.SymbolKind.Variable),
+				range: this._textSpanToRange(model, item.spans[0]),
+				selectionRange: this._textSpanToRange(model, item.spans[0]),
+				tags: [],
+				containerName: containerLabel
+			};
 
-				if (item.childItems && item.childItems.length > 0) {
-					for (let child of item.childItems) {
-						convert(bucket, child, result.name);
-					}
+			if (item.childItems && item.childItems.length > 0) {
+				for (let child of item.childItems) {
+					convert(bucket, child, result.name);
 				}
-
-				bucket.push(result);
 			}
 
-			let result: monaco.languages.DocumentSymbol[] = [];
-			items.forEach(item => convert(result, item));
-			return result;
-		});
+			bucket.push(result);
+		}
+
+		let result: monaco.languages.DocumentSymbol[] = [];
+		items.forEach(item => convert(result, item));
+		return result;
 	}
 }
 
@@ -629,29 +641,28 @@ export abstract class FormatHelper extends Adapter {
 		};
 	}
 
-	protected _convertTextChanges(uri: Uri, change: ts.TextChange): monaco.editor.ISingleEditOperation {
-		return <monaco.editor.ISingleEditOperation>{
+	protected _convertTextChanges(model: monaco.editor.ITextModel, change: ts.TextChange): monaco.languages.TextEdit {
+		return {
 			text: change.newText,
-			range: this._textSpanToRange(uri, change.span)
+			range: this._textSpanToRange(model, change.span)
 		};
 	}
 }
 
 export class FormatAdapter extends FormatHelper implements monaco.languages.DocumentRangeFormattingEditProvider {
 
-	provideDocumentRangeFormattingEdits(model: monaco.editor.IReadOnlyModel, range: Range, options: monaco.languages.FormattingOptions, token: CancellationToken): Thenable<monaco.editor.ISingleEditOperation[]> {
+	public async provideDocumentRangeFormattingEdits(model: monaco.editor.ITextModel, range: Range, options: monaco.languages.FormattingOptions, token: CancellationToken): Promise<monaco.languages.TextEdit[] | undefined> {
 		const resource = model.uri;
+		const startOffset = model.getOffsetAt({ lineNumber: range.startLineNumber, column: range.startColumn });
+		const endOffset = model.getOffsetAt({ lineNumber: range.endLineNumber, column: range.endColumn });
+		const worker = await this._worker(resource);
+		const edits = await worker.getFormattingEditsForRange(resource.toString(), startOffset, endOffset, FormatHelper._convertOptions(options));
 
-		return this._worker(resource).then(worker => {
-			return worker.getFormattingEditsForRange(resource.toString(),
-				this._positionToOffset(resource, { lineNumber: range.startLineNumber, column: range.startColumn }),
-				this._positionToOffset(resource, { lineNumber: range.endLineNumber, column: range.endColumn }),
-				FormatHelper._convertOptions(options));
-		}).then(edits => {
-			if (edits) {
-				return edits.map(edit => this._convertTextChanges(resource, edit));
-			}
-		});
+		if (!edits || model.isDisposed()) {
+			return;
+		}
+
+		return edits.map(edit => this._convertTextChanges(model, edit));
 	}
 }
 
@@ -661,18 +672,17 @@ export class FormatOnTypeAdapter extends FormatHelper implements monaco.language
 		return [';', '}', '\n'];
 	}
 
-	provideOnTypeFormattingEdits(model: monaco.editor.IReadOnlyModel, position: Position, ch: string, options: monaco.languages.FormattingOptions, token: CancellationToken): Thenable<monaco.editor.ISingleEditOperation[]> {
+	public async provideOnTypeFormattingEdits(model: monaco.editor.ITextModel, position: Position, ch: string, options: monaco.languages.FormattingOptions, token: CancellationToken): Promise<monaco.languages.TextEdit[] | undefined> {
 		const resource = model.uri;
+		const offset = model.getOffsetAt(position);
+		const worker = await this._worker(resource);
+		const edits = await worker.getFormattingEditsAfterKeystroke(resource.toString(), offset, ch, FormatHelper._convertOptions(options));
 
-		return this._worker(resource).then(worker => {
-			return worker.getFormattingEditsAfterKeystroke(resource.toString(),
-				this._positionToOffset(resource, position),
-				ch, FormatHelper._convertOptions(options));
-		}).then(edits => {
-			if (edits) {
-				return edits.map(edit => this._convertTextChanges(resource, edit));
-			}
-		});
+		if (!edits || model.isDisposed()) {
+			return;
+		}
+
+		return edits.map(edit => this._convertTextChanges(model, edit));
 	}
 }
 
@@ -680,32 +690,30 @@ export class FormatOnTypeAdapter extends FormatHelper implements monaco.language
 
 export class CodeActionAdaptor extends FormatHelper implements monaco.languages.CodeActionProvider {
 
-	public provideCodeActions(model: monaco.editor.ITextModel, range: Range, context: monaco.languages.CodeActionContext, token: CancellationToken): Promise<monaco.languages.CodeActionList> {
+	public async provideCodeActions(model: monaco.editor.ITextModel, range: Range, context: monaco.languages.CodeActionContext, token: CancellationToken): Promise<monaco.languages.CodeActionList | undefined> {
 		const resource = model.uri;
+		const start = model.getOffsetAt({ lineNumber: range.startLineNumber, column: range.startColumn });
+		const end = model.getOffsetAt({ lineNumber: range.endLineNumber, column: range.endColumn });
+		const formatOptions = FormatHelper._convertOptions(model.getOptions());
+		const errorCodes = context.markers.filter(m => m.code).map(m => m.code).map(Number);
+		const worker = await this._worker(resource);
+		const codeFixes = await worker.getCodeFixesAtPosition(resource.toString(), start, end, errorCodes, formatOptions);
 
-		return this._worker(resource).then(worker => {
-			const start = this._positionToOffset(resource, { lineNumber: range.startLineNumber, column: range.startColumn });
-			const end = this._positionToOffset(resource, { lineNumber: range.endLineNumber, column: range.endColumn });
-
-			const formatOptions = FormatHelper._convertOptions(model.getOptions());
-			const errorCodes = context.markers.filter(m => m.code).map(m => m.code).map(Number);
-
-			return worker.getCodeFixesAtPosition(resource.toString(), start, end, errorCodes, formatOptions);
-
-		}).then(codeFixes => {
+		if (!codeFixes || model.isDisposed()) {
+			return;
+		}
 
-			return codeFixes.filter(fix => {
-				// Removes any 'make a new file'-type code fix
-				return fix.changes.filter(change => change.isNewFile).length === 0;
-			}).map(fix => {
-				return this._tsCodeFixActionToMonacoCodeAction(model, context, fix);
-			})
-		}).then(result => {
-			return {
-				actions: result,
-				dispose: () => { }
-			};
+		const actions = codeFixes.filter(fix => {
+			// Removes any 'make a new file'-type code fix
+			return fix.changes.filter(change => change.isNewFile).length === 0;
+		}).map(fix => {
+			return this._tsCodeFixActionToMonacoCodeAction(model, context, fix);
 		});
+
+		return {
+			actions: actions,
+			dispose: () => { }
+		};
 	}
 
 
@@ -713,7 +721,7 @@ export class CodeActionAdaptor extends FormatHelper implements monaco.languages.
 		const edits: monaco.languages.ResourceTextEdit[] = codeFix.changes.map(edit => ({
 			resource: model.uri,
 			edits: edit.textChanges.map(tc => ({
-				range: this._textSpanToRange(model.uri, tc.span),
+				range: this._textSpanToRange(model, tc.span),
 				text: tc.newText
 			}))
 		}));
@@ -732,10 +740,10 @@ export class CodeActionAdaptor extends FormatHelper implements monaco.languages.
 
 export class RenameAdapter extends Adapter implements monaco.languages.RenameProvider {
 
-	async provideRenameEdits(model: monaco.editor.ITextModel, position: Position, newName: string, token: CancellationToken): Promise<monaco.languages.WorkspaceEdit & monaco.languages.Rejection> {
+	public async provideRenameEdits(model: monaco.editor.ITextModel, position: Position, newName: string, token: CancellationToken): Promise<monaco.languages.WorkspaceEdit & monaco.languages.Rejection | undefined> {
 		const resource = model.uri;
 		const fileName = resource.toString();
-		const offset = this._positionToOffset(resource, position);
+		const offset = model.getOffsetAt(position);
 		const worker = await this._worker(resource);
 
 		const renameInfo = await worker.getRenameInfo(fileName, offset, { allowRenameOfImportPath: false });
@@ -750,6 +758,11 @@ export class RenameAdapter extends Adapter implements monaco.languages.RenamePro
 		}
 
 		const renameLocations = await worker.findRenameLocations(fileName, offset, /*strings*/ false, /*comments*/ false, /*prefixAndSuffix*/ false);
+
+		if (!renameLocations || model.isDisposed()) {
+			return;
+		}
+
 		const fileNameToResourceTextEditMap: { [fileName: string]: monaco.languages.ResourceTextEdit } = {};
 
 		const edits: monaco.languages.ResourceTextEdit[] = [];
@@ -764,7 +777,7 @@ export class RenameAdapter extends Adapter implements monaco.languages.RenamePro
 			}
 
 			fileNameToResourceTextEditMap[renameLocation.fileName].edits.push({
-				range: this._textSpanToRange(resource, renameLocation.textSpan),
+				range: this._textSpanToRange(model, renameLocation.textSpan),
 				text: newName
 			});
 		}

+ 8 - 0
src/lib/editor.worker.d.ts

@@ -0,0 +1,8 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+declare module 'monaco-editor-core/esm/vs/editor/editor.worker' {
+	export function initialize(callback: (ctx: monaco.worker.IWorkerContext, createData: any) => any): void;
+}

+ 8 - 4
src/monaco.contribution.ts

@@ -30,13 +30,14 @@ export class LanguageServiceDefaultsImpl implements monaco.languages.typescript.
 	private _extraLibs: IExtraLibs;
 	private _workerMaxIdleTime: number;
 	private _eagerModelSync: boolean;
-	private _compilerOptions: monaco.languages.typescript.CompilerOptions;
-	private _diagnosticsOptions: monaco.languages.typescript.DiagnosticsOptions;
+	private _compilerOptions!: monaco.languages.typescript.CompilerOptions;
+	private _diagnosticsOptions!: monaco.languages.typescript.DiagnosticsOptions;
 	private _onDidExtraLibsChangeTimeout: number;
 
 	constructor(compilerOptions: monaco.languages.typescript.CompilerOptions, diagnosticsOptions: monaco.languages.typescript.DiagnosticsOptions) {
 		this._extraLibs = Object.create(null);
 		this._workerMaxIdleTime = 2 * 60 * 1000;
+		this._eagerModelSync = false;
 		this.setCompilerOptions(compilerOptions);
 		this.setDiagnosticsOptions(diagnosticsOptions);
 		this._onDidExtraLibsChangeTimeout = -1;
@@ -54,9 +55,12 @@ export class LanguageServiceDefaultsImpl implements monaco.languages.typescript.
 		return this._extraLibs;
 	}
 
-	addExtraLib(content: string, filePath?: string): IDisposable {
-		if (typeof filePath === 'undefined') {
+	addExtraLib(content: string, _filePath?: string): IDisposable {
+		let filePath: string;
+		if (typeof _filePath === 'undefined') {
 			filePath = `ts:extralib-${Math.random().toString(36).substring(2, 15)}`;
+		} else {
+			filePath = _filePath;
 		}
 
 		if (this._extraLibs[filePath] && this._extraLibs[filePath].content === content) {

+ 2 - 2
src/ts.worker.ts

@@ -5,11 +5,11 @@
 'use strict';
 
 import * as worker from 'monaco-editor-core/esm/vs/editor/editor.worker';
-import { TypeScriptWorker } from './tsWorker';
+import { TypeScriptWorker, ICreateData } from './tsWorker';
 
 self.onmessage = () => {
 	// ignore the first message
-	worker.initialize((ctx, createData) => {
+	worker.initialize((ctx: monaco.worker.IWorkerContext, createData: ICreateData) => {
 		return new TypeScriptWorker(ctx, createData)
 	});
 };

+ 12 - 11
src/tsWorker.ts

@@ -46,7 +46,7 @@ export class TypeScriptWorker implements ts.LanguageServiceHost {
 		return models.concat(Object.keys(this._extraLibs));
 	}
 
-	private _getModel(fileName: string): monaco.worker.IMirrorModel {
+	private _getModel(fileName: string): monaco.worker.IMirrorModel | null {
 		let models = this._ctx.getMirrorModels();
 		for (let i = 0; i < models.length; i++) {
 			if (models[i].uri.toString() === fileName) {
@@ -66,9 +66,10 @@ export class TypeScriptWorker implements ts.LanguageServiceHost {
 		} else if (fileName in this._extraLibs) {
 			return String(this._extraLibs[fileName].version);
 		}
+		return '';
 	}
 
-	getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
+	getScriptSnapshot(fileName: string): ts.IScriptSnapshot | undefined {
 		let text: string;
 		let model = this._getModel(fileName);
 		if (model) {
@@ -113,7 +114,7 @@ export class TypeScriptWorker implements ts.LanguageServiceHost {
 
 	getDefaultLibFileName(options: ts.CompilerOptions): string {
 		// TODO@joh support lib.es7.d.ts
-		return options.target <= ts.ScriptTarget.ES5 ? DEFAULT_LIB.NAME : ES6_LIB.NAME;
+		return (options.target || ts.ScriptTarget.ES5) <= ts.ScriptTarget.ES5 ? DEFAULT_LIB.NAME : ES6_LIB.NAME;
 	}
 
 	isDefaultLibFileName(fileName: string): boolean {
@@ -158,31 +159,31 @@ export class TypeScriptWorker implements ts.LanguageServiceHost {
 		return Promise.resolve(diagnostics);
 	}
 
-	getCompletionsAtPosition(fileName: string, position: number): Promise<ts.CompletionInfo> {
+	getCompletionsAtPosition(fileName: string, position: number): Promise<ts.CompletionInfo | undefined> {
 		return Promise.resolve(this._languageService.getCompletionsAtPosition(fileName, position, undefined));
 	}
 
-	getCompletionEntryDetails(fileName: string, position: number, entry: string): Promise<ts.CompletionEntryDetails> {
+	getCompletionEntryDetails(fileName: string, position: number, entry: string): Promise<ts.CompletionEntryDetails | undefined> {
 		return Promise.resolve(this._languageService.getCompletionEntryDetails(fileName, position, entry, undefined, undefined, undefined));
 	}
 
-	getSignatureHelpItems(fileName: string, position: number): Promise<ts.SignatureHelpItems> {
+	getSignatureHelpItems(fileName: string, position: number): Promise<ts.SignatureHelpItems | undefined> {
 		return Promise.resolve(this._languageService.getSignatureHelpItems(fileName, position, undefined));
 	}
 
-	getQuickInfoAtPosition(fileName: string, position: number): Promise<ts.QuickInfo> {
+	getQuickInfoAtPosition(fileName: string, position: number): Promise<ts.QuickInfo | undefined> {
 		return Promise.resolve(this._languageService.getQuickInfoAtPosition(fileName, position));
 	}
 
-	getOccurrencesAtPosition(fileName: string, position: number): Promise<ReadonlyArray<ts.ReferenceEntry>> {
+	getOccurrencesAtPosition(fileName: string, position: number): Promise<ReadonlyArray<ts.ReferenceEntry> | undefined> {
 		return Promise.resolve(this._languageService.getOccurrencesAtPosition(fileName, position));
 	}
 
-	getDefinitionAtPosition(fileName: string, position: number): Promise<ReadonlyArray<ts.DefinitionInfo>> {
+	getDefinitionAtPosition(fileName: string, position: number): Promise<ReadonlyArray<ts.DefinitionInfo> | undefined> {
 		return Promise.resolve(this._languageService.getDefinitionAtPosition(fileName, position));
 	}
 
-	getReferencesAtPosition(fileName: string, position: number): Promise<ts.ReferenceEntry[]> {
+	getReferencesAtPosition(fileName: string, position: number): Promise<ts.ReferenceEntry[] | undefined> {
 		return Promise.resolve(this._languageService.getReferencesAtPosition(fileName, position));
 	}
 
@@ -202,7 +203,7 @@ export class TypeScriptWorker implements ts.LanguageServiceHost {
 		return Promise.resolve(this._languageService.getFormattingEditsAfterKeystroke(fileName, postion, ch, options));
 	}
 
-	findRenameLocations(fileName: string, positon: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename: boolean): Promise<readonly ts.RenameLocation[]> {
+	findRenameLocations(fileName: string, positon: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename: boolean): Promise<readonly ts.RenameLocation[] | undefined> {
 		return Promise.resolve(this._languageService.findRenameLocations(fileName, positon, findInStrings, findInComments, providePrefixAndSuffixTextForRename));
 	}
 

+ 3 - 2
src/tsconfig.esm.json

@@ -11,7 +11,8 @@
       "es2015.collection",
       "es2015.iterable",
       "es2015.promise"
-    ]
+    ],
+    "strict": true
   },
   "include": [
     "**/*.ts"
@@ -19,4 +20,4 @@
   "files": [
     "../node_modules/monaco-editor-core/monaco.d.ts"
   ]
-}
+}

+ 3 - 2
src/tsconfig.json

@@ -11,7 +11,8 @@
       "es2015.collection",
       "es2015.iterable",
       "es2015.promise"
-    ]
+    ],
+    "strict": true,
   },
   "include": [
     "**/*.ts"
@@ -19,4 +20,4 @@
   "files": [
     "../node_modules/monaco-editor-core/monaco.d.ts"
   ]
-}
+}

+ 14 - 8
src/workerManager.ts

@@ -20,13 +20,14 @@ export class WorkerManager {
 	private _updateExtraLibsToken: number;
 	private _extraLibsChangeListener: IDisposable;
 
-	private _worker: monaco.editor.MonacoWebWorker<TypeScriptWorker>;
-	private _client: Promise<TypeScriptWorker>;
+	private _worker: monaco.editor.MonacoWebWorker<TypeScriptWorker> | null;
+	private _client: Promise<TypeScriptWorker> | null;
 
 	constructor(modeId: string, defaults: LanguageServiceDefaultsImpl) {
 		this._modeId = modeId;
 		this._defaults = defaults;
 		this._worker = null;
+		this._client = null;
 		this._idleCheckInterval = setInterval(() => this._checkIfIdle(), 30 * 1000);
 		this._lastUsedTime = 0;
 		this._configChangeListener = this._defaults.onDidChange(() => this._stopWorker());
@@ -95,11 +96,14 @@ export class WorkerManager {
 
 			if (this._defaults.getEagerModelSync()) {
 				p = p.then(worker => {
-					return this._worker.withSyncedResources(monaco.editor.getModels()
-						.filter(model => model.getModeId() === this._modeId)
-						.map(model => model.uri)
-					);
-				})
+					if (this._worker) {
+						return this._worker.withSyncedResources(monaco.editor.getModels()
+							.filter(model => model.getModeId() === this._modeId)
+							.map(model => model.uri)
+						);
+					}
+					return worker;
+				});
 			}
 
 			this._client = p;
@@ -113,7 +117,9 @@ export class WorkerManager {
 		return this._getClient().then((client) => {
 			_client = client
 		}).then(_ => {
-			return this._worker.withSyncedResources(resources)
+			if (this._worker) {
+				return this._worker.withSyncedResources(resources)
+			}
 		}).then(_ => _client);
 	}
 }