浏览代码

Merge remote-tracking branch 'origin/master' into pr/DenysVuika/24

Alex Dima 5 年之前
父节点
当前提交
1f15497486

+ 14 - 0
.github/workflows/ci.yml

@@ -0,0 +1,14 @@
+name: "CI"
+on: [pull_request]
+jobs:
+  build:
+    name: "Builds and Compiles"
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@master
+      - uses: actions/setup-node@v1
+        with:
+          node-version: '10.x'
+      - run: npm install
+      - run: npm run compile

+ 2 - 1
README.md

@@ -8,7 +8,7 @@ Simple TypeScript and JavaScript language support for the Monaco Editor.
 
 ## Issues
 
-Please file issues concering `monaco-typescript` in the [`monaco-editor` repository](https://github.com/Microsoft/monaco-editor/issues).
+Please file issues concerning `monaco-typescript` in the [`monaco-editor` repository](https://github.com/Microsoft/monaco-editor/issues).
 
 ## Installing
 
@@ -20,6 +20,7 @@ This npm module is bundled and distributed in the [monaco-editor](https://www.np
 * `git clone https://github.com/Microsoft/monaco-typescript`
 * `cd monaco-typescript`
 * `npm install .`
+* `npm run compile`
 * `npm run watch`
 * open `$/monaco-typescript/test/index.html` in your favorite browser.
 

+ 45 - 26
package-lock.json

@@ -1,25 +1,25 @@
 {
   "name": "monaco-typescript",
-  "version": "3.2.0",
+  "version": "3.5.1",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
-    "commander": {
-      "version": "2.16.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.16.0.tgz",
-      "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==",
+    "buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
       "dev": true
     },
     "monaco-editor-core": {
-      "version": "0.14.1",
-      "resolved": "https://registry.npmjs.org/monaco-editor-core/-/monaco-editor-core-0.14.1.tgz",
-      "integrity": "sha512-bWJuPbDEftxaN2bG+JZ29+aJhg0rmq+y6VmCUqPZpmw8bSevoYjuTcdLkt9BbNaGnwosAVy+vSktXgs/JW7OvQ==",
+      "version": "0.18.1",
+      "resolved": "https://registry.npmjs.org/monaco-editor-core/-/monaco-editor-core-0.18.1.tgz",
+      "integrity": "sha512-euzXzmwjZFG0oAPGjICMwINcZBzQDyfGDYlAR5YNMBJZO9Bmkqq1xpTTze/qQ0KKbVmawFXiwgUbg7WVgebP9Q==",
       "dev": true
     },
     "monaco-languages": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/monaco-languages/-/monaco-languages-1.4.0.tgz",
-      "integrity": "sha512-39MZMAEKToktfSpOS0Soj05IvkT5vbebL9AIBGPn8fGi8WgJOcfS3YLiMu07gZKViR7CFZ4RyQJKJHglx0fPdA==",
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/monaco-languages/-/monaco-languages-1.8.0.tgz",
+      "integrity": "sha512-vC/lqNgSslQT3vSlNOpyT34ELK0eoNbA/rHUvTUjQemIiR1GpRMKhuwB21BqzWk+0MjZuJydGSCQMCebBge7jg==",
       "dev": true
     },
     "monaco-plugin-helpers": {
@@ -28,7 +28,7 @@
       "integrity": "sha512-7kUx8dtd5qVNVgUARBRhnM8oftPglYwlINfigC4yGUiuzqtIN22u1tly8umiOCIPR0eFiBLjt6aN23oZh2QJgg==",
       "dev": true,
       "requires": {
-        "typescript": "2.9.2"
+        "typescript": "^2.7.2"
       },
       "dependencies": {
         "typescript": {
@@ -40,9 +40,9 @@
       }
     },
     "requirejs": {
-      "version": "2.3.5",
-      "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.5.tgz",
-      "integrity": "sha512-svnO+aNcR/an9Dpi44C7KSAy5fFGLtmPbaaCeQaklUz8BQhS64tWWIIlvEA5jrWICzlO/X9KSzSeXFnZdBu8nw==",
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
+      "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==",
       "dev": true
     },
     "source-map": {
@@ -51,21 +51,40 @@
       "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
       "dev": true
     },
-    "typescript": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.0.1.tgz",
-      "integrity": "sha512-zQIMOmC+372pC/CCVLqnQ0zSBiY7HHodU7mpQdjiZddek4GMj31I3dUJ7gAs9o65X7mnRma6OokOkc6f9jjfBg==",
-      "dev": true
+    "source-map-support": {
+      "version": "0.5.16",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
+      "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
     },
-    "uglify-js": {
-      "version": "3.4.7",
-      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.7.tgz",
-      "integrity": "sha512-J0M2i1mQA+ze3EdN9SBi751DNdAXmeFLfJrd/MDIkRc3G3Gbb9OPVSx7GIQvVwfWxQARcYV2DTxIkMyDAk3o9Q==",
+    "terser": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.3.tgz",
+      "integrity": "sha512-0ikKraVtRDKGzHrzkCv5rUNDzqlhmhowOBqC0XqUHFpW+vJ45+20/IFBcebwKfiS2Z9fJin6Eo+F1zLZsxi8RA==",
       "dev": true,
       "requires": {
-        "commander": "2.16.0",
-        "source-map": "0.6.1"
+        "commander": "^2.20.0",
+        "source-map": "~0.6.1",
+        "source-map-support": "~0.5.12"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.20.3",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+          "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+          "dev": true
+        }
       }
+    },
+    "typescript": {
+      "version": "3.7.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz",
+      "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==",
+      "dev": true
     }
   }
 }

+ 7 - 7
package.json

@@ -1,13 +1,13 @@
 {
   "name": "monaco-typescript",
-  "version": "3.2.0",
+  "version": "3.5.1",
   "description": "TypeScript and JavaScript language support for Monaco Editor",
   "scripts": {
     "compile-amd": "mcopy ./src/lib/typescriptServices-amd.js ./release/dev/lib/typescriptServices.js && tsc -p ./src/tsconfig.json",
     "compile-esm": "mcopy ./src/lib/typescriptServices.js ./release/esm/lib/typescriptServices.js && tsc -p ./src/tsconfig.esm.json",
     "compile": "mrmdir ./release && npm run compile-amd && npm run compile-esm",
     "watch": "tsc -p ./src --watch",
-    "prepublish": "npm run compile && node ./scripts/bundle && mcopy ./src/monaco.d.ts ./release/monaco.d.ts",
+    "prepublishOnly": "npm run compile && node ./scripts/bundle && mcopy ./src/monaco.d.ts ./release/monaco.d.ts",
     "import-typescript": "node ./scripts/importTypescript"
   },
   "author": "Microsoft Corporation",
@@ -20,11 +20,11 @@
     "url": "https://github.com/Microsoft/monaco-typescript/issues"
   },
   "devDependencies": {
-    "monaco-editor-core": "0.14.1",
-    "monaco-languages": "^1.4.0",
+    "monaco-editor-core": "^0.18.1",
+    "monaco-languages": "^1.8.0",
     "monaco-plugin-helpers": "^1.0.2",
-    "requirejs": "^2.3.5",
-    "typescript": "3.0.1",
-    "uglify-js": "^3.4.7"
+    "requirejs": "^2.3.6",
+    "terser": "^4.4.3",
+    "typescript": "^3.7.3"
   }
 }

+ 2 - 6
scripts/bundle.js

@@ -6,7 +6,7 @@
 const requirejs = require('requirejs');
 const path = require('path');
 const fs = require('fs');
-const UglifyJS = require('uglify-js');
+const terser = require('terser');
 const helpers = require('monaco-plugin-helpers');
 
 const REPO_ROOT = path.resolve(__dirname, '..');
@@ -44,11 +44,7 @@ function bundleOne(moduleId, exclude) {
 		const fileContents = fs.readFileSync(filePath).toString();
 		console.log();
 		console.log(`Minifying ${filePath}...`);
-		const result = UglifyJS.minify(fileContents, {
-			output: {
-				comments: 'some'
-			}
-		});
+		const result = terser.minify(fileContents);
 		console.log(`Done.`);
 		fs.writeFileSync(filePath, BUNDLED_FILE_HEADER + result.code);
 	})

+ 42 - 9
scripts/importTypescript.js

@@ -5,6 +5,7 @@
 
 const path = require('path');
 const fs = require('fs');
+const child_process = require('child_process');
 
 const TYPESCRIPT_LIB_SOURCE = path.join(__dirname, '../node_modules/typescript/lib');
 const TYPESCRIPT_LIB_DESTINATION = path.join(__dirname, '../src/lib');
@@ -17,6 +18,14 @@ const TYPESCRIPT_LIB_DESTINATION = path.join(__dirname, '../src/lib');
 	}
 	importLibs();
 
+	const npmLsOutput = JSON.parse(child_process.execSync("npm ls typescript --depth=0 --json=true").toString());
+	const typeScriptDependencyVersion = npmLsOutput.dependencies.typescript.version;
+
+	fs.writeFileSync(
+		path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServicesMetadata.ts'),
+		`export const typescriptVersion = "${typeScriptDependencyVersion}";\n`
+	);
+
 	var tsServices = fs.readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescriptServices.js')).toString();
 
 	// Ensure we never run into the node system...
@@ -25,6 +34,29 @@ const TYPESCRIPT_LIB_DESTINATION = path.join(__dirname, '../src/lib');
 		tsServices.replace(/\n    ts\.sys =([^]*)\n    \}\)\(\);/m, `\n    // MONACOCHANGE\n    ts.sys = undefined;\n    // END MONACOCHANGE`)
 	);
 
+	// Eliminate more require() calls...
+	tsServices = tsServices.replace(/^( +)etwModule = require\(.*$/m, '$1// MONACOCHANGE\n$1etwModule = undefined;\n$1// END MONACOCHANGE');
+	tsServices = tsServices.replace(/^( +)var result = ts\.sys\.require\(.*$/m, '$1// MONACOCHANGE\n$1var result = undefined;\n$1// END MONACOCHANGE');
+
+	// Flag any new require calls (outside comments) so they can be corrected preemptively.
+	// To avoid missing cases (or using an even more complex regex), temporarily remove comments
+	// about require() and then check for lines actually calling require().
+	// \/[*/] matches the start of a comment (single or multi-line).
+	// ^\s+\*[^/] matches (presumably) a later line of a multi-line comment.
+	const tsServicesNoCommentedRequire = tsServices.replace(/(\/[*/]|^\s+\*[^/]).*\brequire\(.*/gm, '');
+	const linesWithRequire = tsServicesNoCommentedRequire.match(/^.*?\brequire\(.*$/gm);
+	if (linesWithRequire && linesWithRequire.length) {
+		console.error('Found new require() calls on the following lines. These should be removed to avoid breaking webpack builds.\n');
+		console.error(linesWithRequire.join('\n'));
+		process.exit(1);
+	}
+
+	// Make sure process.args don't get called in the browser, this
+	// should only happen in TS 2.6.2
+	const beforeProcess = `ts.perfLogger.logInfoEvent("Starting TypeScript v" + ts.versionMajorMinor + " with command line: " + JSON.stringify(process.argv));`
+	const afterProcess = `// MONACOCHANGE\n    ts.perfLogger.logInfoEvent("Starting TypeScript v" + ts.versionMajorMinor + " with command line: " + JSON.stringify([]));\n// END MONACOCHANGE`
+	tsServices = tsServices.replace(beforeProcess, afterProcess);
+
 	var tsServices_amd = tsServices +
 		`
 // MONACOCHANGE
@@ -38,15 +70,15 @@ define("vs/language/typescript/lib/typescriptServices", [], function() { return
 	var tsServices_esm = tsServices +
 		`
 // MONACOCHANGE
-export const createClassifier = ts.createClassifier;
-export const createLanguageService = ts.createLanguageService;
-export const displayPartsToString = ts.displayPartsToString;
-export const EndOfLineState = ts.EndOfLineState;
-export const flattenDiagnosticMessageText = ts.flattenDiagnosticMessageText;
-export const IndentStyle = ts.IndentStyle;
-export const ScriptKind = ts.ScriptKind;
-export const ScriptTarget = ts.ScriptTarget;
-export const TokenClass = ts.TokenClass;
+export var createClassifier = ts.createClassifier;
+export var createLanguageService = ts.createLanguageService;
+export var displayPartsToString = ts.displayPartsToString;
+export var EndOfLineState = ts.EndOfLineState;
+export var flattenDiagnosticMessageText = ts.flattenDiagnosticMessageText;
+export var IndentStyle = ts.IndentStyle;
+export var ScriptKind = ts.ScriptKind;
+export var ScriptTarget = ts.ScriptTarget;
+export var TokenClass = ts.TokenClass;
 // END MONACOCHANGE
 `;
 	fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices.js'), tsServices_esm);
@@ -59,6 +91,7 @@ export = ts;
 // END MONACOCHANGE
 `;
 	fs.writeFileSync(path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices.d.ts'), dtsServices);
+
 })();
 
 function importLibs() {

+ 213 - 60
src/languageFeatures.ts

@@ -12,7 +12,6 @@ import Uri = monaco.Uri;
 import Position = monaco.Position;
 import Range = monaco.Range;
 import Thenable = monaco.Thenable;
-import Promise = monaco.Promise;
 import CancellationToken = monaco.CancellationToken;
 import IDisposable = monaco.IDisposable;
 
@@ -24,26 +23,29 @@ enum IndentStyle {
 	Smart = 2
 }
 
-function flattenDiagnosticMessageText(messageText: string | ts.DiagnosticMessageChain, newLine: '\n'): string {
-	if (typeof messageText === "string") {
-		return messageText;
-	} else {
-		let diagnosticChain = messageText;
-		let result = "";
-		let indent = 0;
-		while (diagnosticChain) {
-			if (indent) {
-				result += newLine;
-				for (let i = 0; i < indent; i++) {
-					result += "  ";
-				}
-			}
-			result += diagnosticChain.messageText;
-			indent++;
-			diagnosticChain = diagnosticChain.next;
+export function flattenDiagnosticMessageText(diag: string | ts.DiagnosticMessageChain | undefined, newLine: string, indent = 0): string {
+	if (typeof diag === "string") {
+		return diag;
+	}
+	else if (diag === undefined) {
+		return "";
+	}
+	let result = "";
+	if (indent) {
+		result += newLine;
+
+		for (let i = 0; i < indent; i++) {
+			result += "  ";
 		}
-		return result;
 	}
+	result += diag.messageText;
+	indent++;
+	if (diag.next) {
+		for (const kid of diag.next) {
+			result += flattenDiagnosticMessageText(kid, newLine, indent);
+		}
+	}
+	return result;
 }
 
 function displayPartsToString(displayParts: ts.SymbolDisplayPart[]): string {
@@ -81,7 +83,14 @@ export abstract class Adapter {
 
 // --- diagnostics --- ---
 
-export class DiagnostcsAdapter extends Adapter {
+enum DiagnosticCategory {
+	Warning = 0,
+	Error = 1,
+	Suggestion = 2,
+	Message = 3
+}
+
+export class DiagnosticsAdapter extends Adapter {
 
 	private _disposables: IDisposable[] = [];
 	private _listener: { [uri: string]: IDisposable } = Object.create(null);
@@ -136,13 +145,15 @@ export class DiagnostcsAdapter extends Adapter {
 			}
 		});
 
-		this._disposables.push(this._defaults.onDidChange(() => {
+		const recomputeDiagostics = () => {
 			// redo diagnostics when options change
 			for (const model of monaco.editor.getModels()) {
 				onModelRemoved(model);
 				onModelAdd(model);
 			}
-		}));
+		};
+		this._disposables.push(this._defaults.onDidChange(recomputeDiagostics));
+		this._disposables.push(this._defaults.onDidExtraLibsChange(recomputeDiagostics));
 
 		monaco.editor.getModels().forEach(onModelAdd);
 	}
@@ -159,14 +170,17 @@ export class DiagnostcsAdapter extends Adapter {
 				return null;
 			}
 			const promises: Promise<ts.Diagnostic[]>[] = [];
-			const { noSyntaxValidation, noSemanticValidation } = this._defaults.getDiagnosticsOptions();
+			const { noSyntaxValidation, noSemanticValidation, noSuggestionDiagnostics } = this._defaults.getDiagnosticsOptions();
 			if (!noSyntaxValidation) {
 				promises.push(worker.getSyntacticDiagnostics(resource.toString()));
 			}
 			if (!noSemanticValidation) {
 				promises.push(worker.getSemanticDiagnostics(resource.toString()));
 			}
-			return Promise.join(promises);
+			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
@@ -174,10 +188,11 @@ export class DiagnostcsAdapter extends Adapter {
 			}
 			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);
-		}).done(undefined, err => {
+		}).then(undefined, err => {
 			console.error(err);
 		});
 	}
@@ -187,14 +202,46 @@ export class DiagnostcsAdapter extends Adapter {
 		const { lineNumber: endLineNumber, column: endColumn } = this._offsetToPosition(resource, diag.start + diag.length);
 
 		return {
-			severity: monaco.MarkerSeverity.Error,
+			severity: this._tsDiagnosticCategoryToMarkerSeverity(diag.category),
 			startLineNumber,
 			startColumn,
 			endLineNumber,
 			endColumn,
-			message: flattenDiagnosticMessageText(diag.messageText, '\n')
+			message: flattenDiagnosticMessageText(diag.messageText, '\n'),
+			code: diag.code.toString(),
+			tags: diag.reportsUnnecessary ? [monaco.MarkerTag.Unnecessary] : [],
+			relatedInformation: this._convertRelatedInformation(resource, diag.relatedInformation),
 		};
 	}
+
+	private _convertRelatedInformation(resource: Uri, relatedInformation?: ts.DiagnosticRelatedInformation[]): monaco.editor.IRelatedInformation[] {
+		if (relatedInformation === undefined)
+			return undefined;
+
+		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);
+
+			return {
+				resource: relatedResource,
+				startLineNumber,
+				startColumn,
+				endLineNumber,
+				endColumn,
+				message: flattenDiagnosticMessageText(info.messageText, '\n')
+			};
+		});
+	}
+
+	private _tsDiagnosticCategoryToMarkerSeverity(category: ts.DiagnosticCategory): monaco.MarkerSeverity {
+		switch (category) {
+			case DiagnosticCategory.Error: return monaco.MarkerSeverity.Error
+			case DiagnosticCategory.Message: return monaco.MarkerSeverity.Info
+			case DiagnosticCategory.Warning: return monaco.MarkerSeverity.Warning
+			case DiagnosticCategory.Suggestion: return monaco.MarkerSeverity.Hint
+		}
+	}
 }
 
 // --- suggest ------
@@ -210,37 +257,49 @@ export class SuggestAdapter extends Adapter implements monaco.languages.Completi
 		return ['.'];
 	}
 
-	provideCompletionItems(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.CompletionItem[]> {
+	provideCompletionItems(model: monaco.editor.IReadOnlyModel, position: Position, _context: monaco.languages.CompletionContext, token: CancellationToken): Thenable<monaco.languages.CompletionList> {
 		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);
 
-		return wireCancellationToken(token, this._worker(resource).then(worker => {
+		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);
+				}
+
 				return {
 					uri: resource,
 					position: position,
+					range: range,
 					label: entry.name,
+					insertText: entry.name,
 					sortText: entry.sortText,
 					kind: SuggestAdapter.convertKind(entry.kind)
 				};
 			});
 
-			return suggestions;
-		}));
+			return {
+				suggestions
+			};
+		});
 	}
 
-	resolveCompletionItem(item: monaco.languages.CompletionItem, token: CancellationToken): Thenable<monaco.languages.CompletionItem> {
+	resolveCompletionItem(_model: monaco.editor.IReadOnlyModel, _position: Position, item: monaco.languages.CompletionItem, token: CancellationToken): Thenable<monaco.languages.CompletionItem> {
 		let myItem = <MyCompletionItem>item;
 		const resource = myItem.uri;
 		const position = myItem.position;
 
-		return wireCancellationToken(token, this._worker(resource).then(worker => {
+		return this._worker(resource).then(worker => {
 			return worker.getCompletionEntryDetails(resource.toString(),
 				this._positionToOffset(resource, position),
 				myItem.label);
@@ -255,9 +314,11 @@ export class SuggestAdapter extends Adapter implements monaco.languages.Completi
 				label: details.name,
 				kind: SuggestAdapter.convertKind(details.kind),
 				detail: displayPartsToString(details.displayParts),
-				documentation: displayPartsToString(details.documentation)
+				documentation: {
+					value: displayPartsToString(details.documentation)
+				}
 			};
-		}));
+		});
 	}
 
 	private static convertKind(kind: string): monaco.languages.CompletionItemKind {
@@ -298,9 +359,9 @@ export class SignatureHelpAdapter extends Adapter implements monaco.languages.Si
 
 	public signatureHelpTriggerCharacters = ['(', ','];
 
-	provideSignatureHelp(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.SignatureHelp> {
+	provideSignatureHelp(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.SignatureHelpResult> {
 		let resource = model.uri;
-		return wireCancellationToken(token, this._worker(resource).then(worker => worker.getSignatureHelpItems(resource.toString(), this._positionToOffset(resource, position))).then(info => {
+		return this._worker(resource).then(worker => worker.getSignatureHelpItems(resource.toString(), this._positionToOffset(resource, position))).then(info => {
 
 			if (!info) {
 				return;
@@ -316,10 +377,10 @@ export class SignatureHelpAdapter extends Adapter implements monaco.languages.Si
 
 				let signature: monaco.languages.SignatureInformation = {
 					label: '',
-					documentation: null,
 					parameters: []
 				};
 
+				signature.documentation = displayPartsToString(item.documentation);
 				signature.label += displayPartsToString(item.prefixDisplayParts);
 				item.parameters.forEach((p, i, a) => {
 					let label = displayPartsToString(p.displayParts);
@@ -337,9 +398,11 @@ export class SignatureHelpAdapter extends Adapter implements monaco.languages.Si
 				ret.signatures.push(signature);
 			});
 
-			return ret;
-
-		}));
+			return {
+				value: ret,
+				dispose() { }
+			};
+		});
 	}
 }
 
@@ -350,7 +413,7 @@ export class QuickInfoAdapter extends Adapter implements monaco.languages.HoverP
 	provideHover(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.Hover> {
 		let resource = model.uri;
 
-		return wireCancellationToken(token, this._worker(resource).then(worker => {
+		return this._worker(resource).then(worker => {
 			return worker.getQuickInfoAtPosition(resource.toString(), this._positionToOffset(resource, position));
 		}).then(info => {
 			if (!info) {
@@ -374,7 +437,7 @@ export class QuickInfoAdapter extends Adapter implements monaco.languages.HoverP
 					value: documentation + (tags ? '\n\n' + tags : '')
 				}]
 			};
-		}));
+		});
 	}
 }
 
@@ -385,7 +448,7 @@ export class OccurrencesAdapter extends Adapter implements monaco.languages.Docu
 	public provideDocumentHighlights(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.DocumentHighlight[]> {
 		const resource = model.uri;
 
-		return wireCancellationToken(token, this._worker(resource).then(worker => {
+		return this._worker(resource).then(worker => {
 			return worker.getOccurrencesAtPosition(resource.toString(), this._positionToOffset(resource, position));
 		}).then(entries => {
 			if (!entries) {
@@ -397,7 +460,7 @@ export class OccurrencesAdapter extends Adapter implements monaco.languages.Docu
 					kind: entry.isWriteAccess ? monaco.languages.DocumentHighlightKind.Write : monaco.languages.DocumentHighlightKind.Text
 				};
 			});
-		}));
+		});
 	}
 }
 
@@ -408,7 +471,7 @@ export class DefinitionAdapter extends Adapter {
 	public provideDefinition(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable<monaco.languages.Definition> {
 		const resource = model.uri;
 
-		return wireCancellationToken(token, this._worker(resource).then(worker => {
+		return this._worker(resource).then(worker => {
 			return worker.getDefinitionAtPosition(resource.toString(), this._positionToOffset(resource, position));
 		}).then(entries => {
 			if (!entries) {
@@ -425,7 +488,7 @@ export class DefinitionAdapter extends Adapter {
 				}
 			}
 			return result;
-		}));
+		});
 	}
 }
 
@@ -436,7 +499,7 @@ export class ReferenceAdapter extends Adapter implements monaco.languages.Refere
 	provideReferences(model: monaco.editor.IReadOnlyModel, position: Position, context: monaco.languages.ReferenceContext, token: CancellationToken): Thenable<monaco.languages.Location[]> {
 		const resource = model.uri;
 
-		return wireCancellationToken(token, this._worker(resource).then(worker => {
+		return this._worker(resource).then(worker => {
 			return worker.getReferencesAtPosition(resource.toString(), this._positionToOffset(resource, position));
 		}).then(entries => {
 			if (!entries) {
@@ -453,7 +516,7 @@ export class ReferenceAdapter extends Adapter implements monaco.languages.Refere
 				}
 			}
 			return result;
-		}));
+		});
 	}
 }
 
@@ -464,7 +527,7 @@ export class OutlineAdapter extends Adapter implements monaco.languages.Document
 	public provideDocumentSymbols(model: monaco.editor.IReadOnlyModel, token: CancellationToken): Thenable<monaco.languages.DocumentSymbol[]> {
 		const resource = model.uri;
 
-		return wireCancellationToken(token, this._worker(resource).then(worker => worker.getNavigationBarItems(resource.toString())).then(items => {
+		return this._worker(resource).then(worker => worker.getNavigationBarItems(resource.toString())).then(items => {
 			if (!items) {
 				return;
 			}
@@ -476,6 +539,7 @@ export class OutlineAdapter extends Adapter implements monaco.languages.Document
 					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
 				};
 
@@ -491,7 +555,7 @@ export class OutlineAdapter extends Adapter implements monaco.languages.Document
 			let result: monaco.languages.DocumentSymbol[] = [];
 			items.forEach(item => convert(result, item));
 			return result;
-		}));
+		});
 	}
 }
 
@@ -578,7 +642,7 @@ export class FormatAdapter extends FormatHelper implements monaco.languages.Docu
 	provideDocumentRangeFormattingEdits(model: monaco.editor.IReadOnlyModel, range: Range, options: monaco.languages.FormattingOptions, token: CancellationToken): Thenable<monaco.editor.ISingleEditOperation[]> {
 		const resource = model.uri;
 
-		return wireCancellationToken(token, this._worker(resource).then(worker => {
+		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 }),
@@ -587,7 +651,7 @@ export class FormatAdapter extends FormatHelper implements monaco.languages.Docu
 			if (edits) {
 				return edits.map(edit => this._convertTextChanges(resource, edit));
 			}
-		}));
+		});
 	}
 }
 
@@ -600,7 +664,7 @@ export class FormatOnTypeAdapter extends FormatHelper implements monaco.language
 	provideOnTypeFormattingEdits(model: monaco.editor.IReadOnlyModel, position: Position, ch: string, options: monaco.languages.FormattingOptions, token: CancellationToken): Thenable<monaco.editor.ISingleEditOperation[]> {
 		const resource = model.uri;
 
-		return wireCancellationToken(token, this._worker(resource).then(worker => {
+		return this._worker(resource).then(worker => {
 			return worker.getFormattingEditsAfterKeystroke(resource.toString(),
 				this._positionToOffset(resource, position),
 				ch, FormatHelper._convertOptions(options));
@@ -608,14 +672,103 @@ export class FormatOnTypeAdapter extends FormatHelper implements monaco.language
 			if (edits) {
 				return edits.map(edit => this._convertTextChanges(resource, edit));
 			}
+		});
+	}
+}
+
+// --- code actions ------
+
+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> {
+		const resource = model.uri;
+
+		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 => {
+
+			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: () => { }
+			};
+		});
+	}
+
+
+	private _tsCodeFixActionToMonacoCodeAction(model: monaco.editor.ITextModel, context: monaco.languages.CodeActionContext, codeFix: ts.CodeFixAction): monaco.languages.CodeAction {
+		const edits: monaco.languages.ResourceTextEdit[] = codeFix.changes.map(edit => ({
+			resource: model.uri,
+			edits: edit.textChanges.map(tc => ({
+				range: this._textSpanToRange(model.uri, tc.span),
+				text: tc.newText
+			}))
 		}));
+
+		const action: monaco.languages.CodeAction = {
+			title: codeFix.description,
+			edit: { edits: edits },
+			diagnostics: context.markers,
+			kind: "quickfix"
+		};
+
+		return action;
 	}
 }
+// --- rename ----
+
+export class RenameAdapter extends Adapter implements monaco.languages.RenameProvider {
 
-/**
- * Hook a cancellation token to a WinJS Promise
- */
-function wireCancellationToken<T>(token: CancellationToken, promise: Promise<T>): Thenable<T> {
-	token.onCancellationRequested(() => promise.cancel());
-	return promise;
+	async provideRenameEdits(model: monaco.editor.ITextModel, position: Position, newName: string, token: CancellationToken): Promise<monaco.languages.WorkspaceEdit & monaco.languages.Rejection> {
+		const resource = model.uri;
+		const fileName = resource.toString();
+		const offset = this._positionToOffset(resource, position);
+		const worker = await this._worker(resource);
+
+		const renameInfo = await worker.getRenameInfo(fileName, offset, { allowRenameOfImportPath: false });
+		if (renameInfo.canRename === false) { // use explicit comparison so that the discriminated union gets resolved properly
+			return {
+				edits: [],
+				rejectReason: renameInfo.localizedErrorMessage
+			};
+		}
+		if (renameInfo.fileToRename !== undefined) {
+			throw new Error("Renaming files is not supported.");
+		}
+
+		const renameLocations = await worker.findRenameLocations(fileName, offset, /*strings*/ false, /*comments*/ false, /*prefixAndSuffix*/ false);
+		const fileNameToResourceTextEditMap: { [fileName: string]: monaco.languages.ResourceTextEdit } = {};
+
+		const edits: monaco.languages.ResourceTextEdit[] = [];
+		for (const renameLocation of renameLocations) {
+			if (!(renameLocation.fileName in fileNameToResourceTextEditMap)) {
+				const resourceTextEdit = {
+					edits: [],
+					resource: monaco.Uri.parse(renameLocation.fileName)
+				};
+				fileNameToResourceTextEditMap[renameLocation.fileName] = resourceTextEdit;
+				edits.push(resourceTextEdit);
+			}
+
+			fileNameToResourceTextEditMap[renameLocation.fileName].edits.push({
+				range: this._textSpanToRange(resource, renameLocation.textSpan),
+				text: newName
+			});
+		}
+
+		return { edits };
+	}
 }

文件差异内容过多而无法显示
+ 0 - 0
src/lib/lib.ts


文件差异内容过多而无法显示
+ 682 - 252
src/lib/typescriptServices-amd.js


文件差异内容过多而无法显示
+ 8 - 7
src/lib/typescriptServices.d.ts


文件差异内容过多而无法显示
+ 682 - 252
src/lib/typescriptServices.js


+ 1 - 0
src/lib/typescriptServicesMetadata.ts

@@ -0,0 +1 @@
+export const typescriptVersion = "3.7.3";

+ 84 - 50
src/monaco.contribution.ts

@@ -5,6 +5,7 @@
 'use strict';
 
 import * as mode from './tsMode';
+import { typescriptVersion } from './lib/typescriptServicesMetadata'; // do not import the whole typescriptServices here
 
 import Emitter = monaco.Emitter;
 import IEvent = monaco.IEvent;
@@ -12,87 +13,113 @@ import IDisposable = monaco.IDisposable;
 
 // --- TypeScript configuration and defaults ---------
 
+export interface IExtraLib {
+	content: string;
+	version: number;
+}
+
+export interface IExtraLibs {
+	[path: string]: IExtraLib;
+}
+
 export class LanguageServiceDefaultsImpl implements monaco.languages.typescript.LanguageServiceDefaults {
 
-	private _onDidChange = new Emitter<monaco.languages.typescript.LanguageServiceDefaults>();
-	private _extraLibs: { [path: string]: string };
+	private _onDidChange = new Emitter<void>();
+	private _onDidExtraLibsChange = new Emitter<void>();
+
+	private _extraLibs: IExtraLibs;
 	private _workerMaxIdleTime: number;
 	private _eagerModelSync: boolean;
 	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.setCompilerOptions(compilerOptions);
 		this.setDiagnosticsOptions(diagnosticsOptions);
+		this._onDidExtraLibsChangeTimeout = -1;
 	}
 
-	get onDidChange(): IEvent<monaco.languages.typescript.LanguageServiceDefaults> {
+	get onDidChange(): IEvent<void> {
 		return this._onDidChange.event;
 	}
 
-	getExtraLibs(): { [path: string]: string; } {
-		const result = Object.create(null);
-		for (var key in this._extraLibs) {
-			result[key] = this._extraLibs[key];
-		}
-		return Object.freeze(result);
+	get onDidExtraLibsChange(): IEvent<void> {
+		return this._onDidExtraLibsChange.event;
+	}
+
+	getExtraLibs(): IExtraLibs {
+		return this._extraLibs;
 	}
 
 	addExtraLib(content: string, filePath?: string): IDisposable {
 		if (typeof filePath === 'undefined') {
-			filePath = `ts:extralib-${Date.now()}`;
+			filePath = `ts:extralib-${Math.random().toString(36).substring(2, 15)}`;
+		}
+
+		if (this._extraLibs[filePath] && this._extraLibs[filePath].content === content) {
+			// no-op, there already exists an extra lib with this content
+			return {
+				dispose: () => { }
+			};
 		}
 
+		let myVersion = 1;
 		if (this._extraLibs[filePath]) {
-			throw new Error(`${filePath} already a extra lib`);
+			myVersion = this._extraLibs[filePath].version + 1;
 		}
 
-		this._extraLibs[filePath] = content;
-		this._onDidChange.fire(this);
+		this._extraLibs[filePath] = {
+			content: content,
+			version: myVersion,
+		};
+		this._fireOnDidExtraLibsChangeSoon();
 
 		return {
 			dispose: () => {
-				if (delete this._extraLibs[filePath]) {
-					this._onDidChange.fire(this);
+				let extraLib = this._extraLibs[filePath];
+				if (!extraLib) {
+					return;
 				}
+				if (extraLib.version !== myVersion) {
+					return;
+				}
+
+				delete this._extraLibs[filePath];
+				this._fireOnDidExtraLibsChangeSoon();
 			}
 		};
 	}
 
-	setExtraLibs(libs: Array<{ content: string; filePath?: string }>): IDisposable {
-		const paths = [];
+	setExtraLibs(libs: { content: string; filePath?: string }[]): void {
+		// clear out everything
+		this._extraLibs = Object.create(null);
 
 		if (libs && libs.length > 0) {
-			libs.forEach(lib => {
-				const filePath = lib.filePath || `ts:extralib-${Date.now()}`;
+			for (const lib of libs) {
+				const filePath = lib.filePath || `ts:extralib-${Math.random().toString(36).substring(2, 15)}`;
 				const content = lib.content;
-
-				this._extraLibs[filePath] = content;
-				paths.push(filePath);
-			});
-
-			this._onDidChange.fire(this);
+				this._extraLibs[filePath] = {
+					content: content,
+					version: 1
+				};
+			}
 		}
 
-		return {
-			dispose: () => {
-				if (paths.length > 0) {
-					let changed = false;
-
-					paths.forEach(filePath => {
-						if (delete this._extraLibs[filePath]) {
-							changed = true;
-						}
-					});
-
-					if (changed) {
-						this._onDidChange.fire(this);
-					}
-				}
-			}
+		this._fireOnDidExtraLibsChangeSoon();
+	}
+
+	private _fireOnDidExtraLibsChangeSoon(): void {
+		if (this._onDidExtraLibsChangeTimeout !== -1) {
+			// already scheduled
+			return;
 		}
+		this._onDidExtraLibsChangeTimeout = setTimeout(() => {
+			this._onDidExtraLibsChangeTimeout = -1;
+			this._onDidExtraLibsChange.fire(undefined);
+		}, 0);
 	}
 
 	getCompilerOptions(): monaco.languages.typescript.CompilerOptions {
@@ -101,7 +128,7 @@ export class LanguageServiceDefaultsImpl implements monaco.languages.typescript.
 
 	setCompilerOptions(options: monaco.languages.typescript.CompilerOptions): void {
 		this._compilerOptions = options || Object.create(null);
-		this._onDidChange.fire(this);
+		this._onDidChange.fire(undefined);
 	}
 
 	getDiagnosticsOptions(): monaco.languages.typescript.DiagnosticsOptions {
@@ -110,7 +137,7 @@ export class LanguageServiceDefaultsImpl implements monaco.languages.typescript.
 
 	setDiagnosticsOptions(options: monaco.languages.typescript.DiagnosticsOptions): void {
 		this._diagnosticsOptions = options || Object.create(null);
-		this._onDidChange.fire(this);
+		this._onDidChange.fire(undefined);
 	}
 
 	setMaximumWorkerIdleTime(value: number): void {
@@ -143,18 +170,21 @@ enum ModuleKind {
 	UMD = 3,
 	System = 4,
 	ES2015 = 5,
-	ESNext = 6
+	ESNext = 99
 }
+
 enum JsxEmit {
 	None = 0,
 	Preserve = 1,
 	React = 2,
 	ReactNative = 3
 }
+
 enum NewLineKind {
 	CarriageReturnLineFeed = 0,
 	LineFeed = 1
 }
+
 enum ScriptTarget {
 	ES3 = 0,
 	ES5 = 1,
@@ -162,10 +192,13 @@ enum ScriptTarget {
 	ES2016 = 3,
 	ES2017 = 4,
 	ES2018 = 5,
-	ESNext = 6,
+	ES2019 = 6,
+	ES2020 = 7,
+	ESNext = 99,
 	JSON = 100,
-	Latest = 6
+	Latest = ESNext,
 }
+
 enum ModuleResolutionKind {
 	Classic = 1,
 	NodeJs = 2
@@ -180,11 +213,11 @@ const javascriptDefaults = new LanguageServiceDefaultsImpl(
 	{ allowNonTsExtensions: true, allowJs: true, target: ScriptTarget.Latest },
 	{ noSemanticValidation: true, noSyntaxValidation: false });
 
-function getTypeScriptWorker(): monaco.Promise<any> {
+function getTypeScriptWorker(): Promise<any> {
 	return getMode().then(mode => mode.getTypeScriptWorker());
 }
 
-function getJavaScriptWorker(): monaco.Promise<any> {
+function getJavaScriptWorker(): Promise<any> {
 	return getMode().then(mode => mode.getJavaScriptWorker());
 }
 
@@ -196,6 +229,7 @@ function createAPI(): typeof monaco.languages.typescript {
 		NewLineKind: NewLineKind,
 		ScriptTarget: ScriptTarget,
 		ModuleResolutionKind: ModuleResolutionKind,
+		typescriptVersion,
 		typescriptDefaults: typescriptDefaults,
 		javascriptDefaults: javascriptDefaults,
 		getTypeScriptWorker: getTypeScriptWorker,
@@ -206,8 +240,8 @@ monaco.languages.typescript = createAPI();
 
 // --- Registration to monaco editor ---
 
-function getMode(): monaco.Promise<typeof mode> {
-	return monaco.Promise.wrap(import('./tsMode'))
+function getMode(): Promise<typeof mode> {
+	return import('./tsMode');
 }
 
 monaco.languages.onLanguage('typescript', () => {

+ 16 - 13
src/monaco.d.ts

@@ -8,8 +8,9 @@ declare module monaco.languages.typescript {
         UMD = 3,
         System = 4,
         ES2015 = 5,
-        ESNext = 6
+        ESNext = 99
     }
+
     enum JsxEmit {
         None = 0,
         Preserve = 1,
@@ -28,9 +29,11 @@ declare module monaco.languages.typescript {
         ES2016 = 3,
         ES2017 = 4,
         ES2018 = 5,
-        ESNext = 6,
+        ES2019 = 6,
+        ES2020 = 7,
+        ESNext = 99,
         JSON = 100,
-        Latest = 6
+        Latest = ESNext,
     }
 
     export enum ModuleResolutionKind {
@@ -125,32 +128,30 @@ declare module monaco.languages.typescript {
     export interface DiagnosticsOptions {
         noSemanticValidation?: boolean;
         noSyntaxValidation?: boolean;
+        noSuggestionDiagnostics?: boolean;
+        diagnosticCodesToIgnore?: number[];
     }
 
     export interface LanguageServiceDefaults {
         /**
          * Add an additional source file to the language service. Use this
          * for typescript (definition) files that won't be loaded as editor
-         * document, like `jquery.d.ts`.
+         * documents, like `jquery.d.ts`.
          *
          * @param content The file content
          * @param filePath An optional file path
          * @returns A disposable which will remove the file from the
-         * language service upon cleanup.
+         * language service upon disposal.
          */
         addExtraLib(content: string, filePath?: string): IDisposable;
 
         /**
-         * Add multiple source files to the language service.
-         * Use this for multiple typescript (definition) files that won't be loaded
-         * as editor document, like `jquery.d.ts`.
-         * This method is optimised for performance and raises change events only once
-         * for the whole list.
+         * Remove all existing extra libs and set the additional source
+         * files to the language service. Use this for typescript definition
+         * files that won't be loaded as editor documents, like `jquery.d.ts`.
          * @param libs An array of entries to register.
-         * @returns A disposable which will remove the file from the
-         * language service upon cleanup.
          */
-        setExtraLibs(libs: Array<{ content: string; filePath?: string }>): IDisposable;
+        setExtraLibs(libs: { content: string; filePath?: string }[]): void;
 
         /**
          * Set TypeScript compiler options.
@@ -178,6 +179,8 @@ declare module monaco.languages.typescript {
         setEagerModelSync(value: boolean): void;
     }
 
+    export var typescriptVersion: string;
+
     export var typescriptDefaults: LanguageServiceDefaults;
     export var javascriptDefaults: LanguageServiceDefaults;
 

+ 5 - 4
src/tsMode.ts

@@ -9,7 +9,6 @@ import { TypeScriptWorker } from './tsWorker';
 import { LanguageServiceDefaultsImpl } from './monaco.contribution';
 import * as languageFeatures from './languageFeatures';
 
-import Promise = monaco.Promise;
 import Uri = monaco.Uri;
 
 let javaScriptWorker: (first: Uri, ...more: Uri[]) => Promise<TypeScriptWorker>;
@@ -30,7 +29,7 @@ export function setupJavaScript(defaults: LanguageServiceDefaultsImpl): void {
 }
 
 export function getJavaScriptWorker(): Promise<(first: Uri, ...more: Uri[]) => Promise<TypeScriptWorker>> {
-	return new monaco.Promise((resolve, reject) => {
+	return new Promise((resolve, reject) => {
 		if (!javaScriptWorker) {
 			return reject("JavaScript not registered!");
 		}
@@ -40,7 +39,7 @@ export function getJavaScriptWorker(): Promise<(first: Uri, ...more: Uri[]) => P
 }
 
 export function getTypeScriptWorker(): Promise<(first: Uri, ...more: Uri[]) => Promise<TypeScriptWorker>> {
-	return new monaco.Promise((resolve, reject) => {
+	return new Promise((resolve, reject) => {
 		if (!typeScriptWorker) {
 			return reject("TypeScript not registered!");
 		}
@@ -65,7 +64,9 @@ function setupMode(defaults: LanguageServiceDefaultsImpl, modeId: string): (firs
 	monaco.languages.registerDocumentSymbolProvider(modeId, new languageFeatures.OutlineAdapter(worker));
 	monaco.languages.registerDocumentRangeFormattingEditProvider(modeId, new languageFeatures.FormatAdapter(worker));
 	monaco.languages.registerOnTypeFormattingEditProvider(modeId, new languageFeatures.FormatOnTypeAdapter(worker));
-	new languageFeatures.DiagnostcsAdapter(defaults, modeId, worker);
+	monaco.languages.registerCodeActionProvider(modeId, new languageFeatures.CodeActionAdaptor(worker));
+	monaco.languages.registerRenameProvider(modeId, new languageFeatures.RenameAdapter(worker));
+	new languageFeatures.DiagnosticsAdapter(defaults, modeId, worker);
 
 	return worker;
 }

+ 49 - 24
src/tsWorker.ts

@@ -6,8 +6,8 @@
 
 import * as ts from './lib/typescriptServices';
 import { lib_dts, lib_es6_dts } from './lib/lib';
+import { IExtraLibs } from './monaco.contribution';
 
-import Promise = monaco.Promise;
 import IWorkerContext = monaco.worker.IWorkerContext;
 
 const DEFAULT_LIB = {
@@ -25,7 +25,7 @@ export class TypeScriptWorker implements ts.LanguageServiceHost {
 	// --- model sync -----------------------
 
 	private _ctx: IWorkerContext;
-	private _extraLibs: { [fileName: string]: string } = Object.create(null);
+	private _extraLibs: IExtraLibs = Object.create(null);
 	private _languageService = ts.createLanguageService(this);
 	private _compilerOptions: ts.CompilerOptions;
 
@@ -60,9 +60,11 @@ export class TypeScriptWorker implements ts.LanguageServiceHost {
 		let model = this._getModel(fileName);
 		if (model) {
 			return model.version.toString();
-		} else if (this.isDefaultLibFileName(fileName) || fileName in this._extraLibs) {
-			// extra lib and default lib are static
+		} else if (this.isDefaultLibFileName(fileName)) {
+			// default lib is static
 			return '1';
+		} else if (fileName in this._extraLibs) {
+			return String(this._extraLibs[fileName].version);
 		}
 	}
 
@@ -74,8 +76,8 @@ export class TypeScriptWorker implements ts.LanguageServiceHost {
 			text = model.getValue();
 
 		} else if (fileName in this._extraLibs) {
-			// static extra lib
-			text = this._extraLibs[fileName];
+			// extra lib
+			text = this._extraLibs[fileName].content;
 
 		} else if (fileName === DEFAULT_LIB.NAME) {
 			text = DEFAULT_LIB.CONTENTS;
@@ -135,73 +137,96 @@ export class TypeScriptWorker implements ts.LanguageServiceHost {
 	getSyntacticDiagnostics(fileName: string): Promise<ts.Diagnostic[]> {
 		const diagnostics = this._languageService.getSyntacticDiagnostics(fileName);
 		TypeScriptWorker.clearFiles(diagnostics);
-		return Promise.as(diagnostics);
+		return Promise.resolve(diagnostics);
 	}
 
 	getSemanticDiagnostics(fileName: string): Promise<ts.Diagnostic[]> {
 		const diagnostics = this._languageService.getSemanticDiagnostics(fileName);
 		TypeScriptWorker.clearFiles(diagnostics);
-		return Promise.as(diagnostics);
+		return Promise.resolve(diagnostics);
+	}
+
+	getSuggestionDiagnostics(fileName: string): Promise<ts.DiagnosticWithLocation[]> {
+		const diagnostics = this._languageService.getSuggestionDiagnostics(fileName);
+		TypeScriptWorker.clearFiles(diagnostics);
+		return Promise.resolve(diagnostics);
 	}
 
 	getCompilerOptionsDiagnostics(fileName: string): Promise<ts.Diagnostic[]> {
 		const diagnostics = this._languageService.getCompilerOptionsDiagnostics();
 		TypeScriptWorker.clearFiles(diagnostics);
-		return Promise.as(diagnostics);
+		return Promise.resolve(diagnostics);
 	}
 
 	getCompletionsAtPosition(fileName: string, position: number): Promise<ts.CompletionInfo> {
-		return Promise.as(this._languageService.getCompletionsAtPosition(fileName, position, undefined));
+		return Promise.resolve(this._languageService.getCompletionsAtPosition(fileName, position, undefined));
 	}
 
 	getCompletionEntryDetails(fileName: string, position: number, entry: string): Promise<ts.CompletionEntryDetails> {
-		return Promise.as(this._languageService.getCompletionEntryDetails(fileName, position, entry, undefined, undefined, undefined));
+		return Promise.resolve(this._languageService.getCompletionEntryDetails(fileName, position, entry, undefined, undefined, undefined));
 	}
 
 	getSignatureHelpItems(fileName: string, position: number): Promise<ts.SignatureHelpItems> {
-		return Promise.as(this._languageService.getSignatureHelpItems(fileName, position, undefined));
+		return Promise.resolve(this._languageService.getSignatureHelpItems(fileName, position, undefined));
 	}
 
 	getQuickInfoAtPosition(fileName: string, position: number): Promise<ts.QuickInfo> {
-		return Promise.as(this._languageService.getQuickInfoAtPosition(fileName, position));
+		return Promise.resolve(this._languageService.getQuickInfoAtPosition(fileName, position));
 	}
 
-	getOccurrencesAtPosition(fileName: string, position: number): Promise<ts.ReferenceEntry[]> {
-		return Promise.as(this._languageService.getOccurrencesAtPosition(fileName, position));
+	getOccurrencesAtPosition(fileName: string, position: number): Promise<ReadonlyArray<ts.ReferenceEntry>> {
+		return Promise.resolve(this._languageService.getOccurrencesAtPosition(fileName, position));
 	}
 
-	getDefinitionAtPosition(fileName: string, position: number): Promise<ts.DefinitionInfo[]> {
-		return Promise.as(this._languageService.getDefinitionAtPosition(fileName, position));
+	getDefinitionAtPosition(fileName: string, position: number): Promise<ReadonlyArray<ts.DefinitionInfo>> {
+		return Promise.resolve(this._languageService.getDefinitionAtPosition(fileName, position));
 	}
 
 	getReferencesAtPosition(fileName: string, position: number): Promise<ts.ReferenceEntry[]> {
-		return Promise.as(this._languageService.getReferencesAtPosition(fileName, position));
+		return Promise.resolve(this._languageService.getReferencesAtPosition(fileName, position));
 	}
 
 	getNavigationBarItems(fileName: string): Promise<ts.NavigationBarItem[]> {
-		return Promise.as(this._languageService.getNavigationBarItems(fileName));
+		return Promise.resolve(this._languageService.getNavigationBarItems(fileName));
 	}
 
 	getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): Promise<ts.TextChange[]> {
-		return Promise.as(this._languageService.getFormattingEditsForDocument(fileName, options));
+		return Promise.resolve(this._languageService.getFormattingEditsForDocument(fileName, options));
 	}
 
 	getFormattingEditsForRange(fileName: string, start: number, end: number, options: ts.FormatCodeOptions): Promise<ts.TextChange[]> {
-		return Promise.as(this._languageService.getFormattingEditsForRange(fileName, start, end, options));
+		return Promise.resolve(this._languageService.getFormattingEditsForRange(fileName, start, end, options));
 	}
 
 	getFormattingEditsAfterKeystroke(fileName: string, postion: number, ch: string, options: ts.FormatCodeOptions): Promise<ts.TextChange[]> {
-		return Promise.as(this._languageService.getFormattingEditsAfterKeystroke(fileName, postion, ch, options));
+		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[]> {
+		return Promise.resolve(this._languageService.findRenameLocations(fileName, positon, findInStrings, findInComments, providePrefixAndSuffixTextForRename));
+	}
+
+	getRenameInfo(fileName: string, positon: number, options: ts.RenameInfoOptions): Promise<ts.RenameInfo> {
+		return Promise.resolve(this._languageService.getRenameInfo(fileName, positon, options));
 	}
 
 	getEmitOutput(fileName: string): Promise<ts.EmitOutput> {
-		return Promise.as(this._languageService.getEmitOutput(fileName));
+		return Promise.resolve(this._languageService.getEmitOutput(fileName));
+	}
+
+	getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[], formatOptions: ts.FormatCodeOptions): Promise<ReadonlyArray<ts.CodeFixAction>> {
+		const preferences = {}
+		return Promise.resolve(this._languageService.getCodeFixesAtPosition(fileName, start, end, errorCodes, formatOptions, preferences));
+	}
+
+	updateExtraLibs(extraLibs: IExtraLibs) {
+		this._extraLibs = extraLibs;
 	}
 }
 
 export interface ICreateData {
 	compilerOptions: ts.CompilerOptions;
-	extraLibs: { [path: string]: string };
+	extraLibs: IExtraLibs;
 }
 
 export function create(ctx: IWorkerContext, createData: ICreateData): TypeScriptWorker {

+ 2 - 0
src/tsconfig.esm.json

@@ -2,12 +2,14 @@
   "compilerOptions": {
     "module": "esnext",
     "moduleResolution": "node",
+    "declaration": true,
     "outDir": "../release/esm",
     "target": "es5",
     "lib": [
       "dom",
       "es5",
       "es2015.collection",
+      "es2015.iterable",
       "es2015.promise"
     ]
   },

+ 2 - 0
src/tsconfig.json

@@ -3,11 +3,13 @@
     "module": "amd",
     "moduleResolution": "node",
     "outDir": "../release/dev",
+    "declaration": true,
     "target": "es5",
     "lib": [
       "dom",
       "es5",
       "es2015.collection",
+      "es2015.iterable",
       "es2015.promise"
     ]
   },

+ 24 - 23
src/workerManager.ts

@@ -7,7 +7,6 @@
 import { LanguageServiceDefaultsImpl } from './monaco.contribution';
 import { TypeScriptWorker } from './tsWorker';
 
-import Promise = monaco.Promise;
 import IDisposable = monaco.IDisposable;
 import Uri = monaco.Uri;
 
@@ -18,6 +17,8 @@ export class WorkerManager {
 	private _idleCheckInterval: number;
 	private _lastUsedTime: number;
 	private _configChangeListener: IDisposable;
+	private _updateExtraLibsToken: number;
+	private _extraLibsChangeListener: IDisposable;
 
 	private _worker: monaco.editor.MonacoWebWorker<TypeScriptWorker>;
 	private _client: Promise<TypeScriptWorker>;
@@ -29,6 +30,8 @@ export class WorkerManager {
 		this._idleCheckInterval = setInterval(() => this._checkIfIdle(), 30 * 1000);
 		this._lastUsedTime = 0;
 		this._configChangeListener = this._defaults.onDidChange(() => this._stopWorker());
+		this._updateExtraLibsToken = 0;
+		this._extraLibsChangeListener = this._defaults.onDidExtraLibsChange(() => this._updateExtraLibs());
 	}
 
 	private _stopWorker(): void {
@@ -42,9 +45,23 @@ export class WorkerManager {
 	dispose(): void {
 		clearInterval(this._idleCheckInterval);
 		this._configChangeListener.dispose();
+		this._extraLibsChangeListener.dispose();
 		this._stopWorker();
 	}
 
+	private async _updateExtraLibs(): Promise<void> {
+		if (!this._worker) {
+			return;
+		}
+		const myToken = ++this._updateExtraLibsToken;
+		const proxy = await this._worker.getProxy();
+		if (this._updateExtraLibsToken !== myToken) {
+			// avoid multiple calls
+			return;
+		}
+		proxy.updateExtraLibs(this._defaults.getExtraLibs());
+	}
+
 	private _checkIfIdle(): void {
 		if (!this._worker) {
 			return;
@@ -74,7 +91,7 @@ export class WorkerManager {
 				}
 			});
 
-			let p = this._worker.getProxy();
+			let p = <Promise<TypeScriptWorker>>this._worker.getProxy();
 
 			if (this._defaults.getEagerModelSync()) {
 				p = p.then(worker => {
@@ -93,26 +110,10 @@ export class WorkerManager {
 
 	getLanguageServiceWorker(...resources: Uri[]): Promise<TypeScriptWorker> {
 		let _client: TypeScriptWorker;
-		return toShallowCancelPromise(
-			this._getClient().then((client) => {
-				_client = client
-			}).then(_ => {
-				return this._worker.withSyncedResources(resources)
-			}).then(_ => _client)
-		);
+		return this._getClient().then((client) => {
+			_client = client
+		}).then(_ => {
+			return this._worker.withSyncedResources(resources)
+		}).then(_ => _client);
 	}
 }
-
-function toShallowCancelPromise<T>(p: Promise<T>): Promise<T> {
-	let completeCallback: (value: T) => void;
-	let errorCallback: (err: any) => void;
-
-	let r = new Promise<T>((c, e) => {
-		completeCallback = c;
-		errorCallback = e;
-	}, () => { });
-
-	p.then(completeCallback, errorCallback);
-
-	return r;
-}

+ 19 - 9
test/index.html

@@ -33,12 +33,9 @@
 <script src="../node_modules/monaco-editor-core/dev/vs/editor/editor.main.js"></script>
 
 <script>
-	require([
-		'vs/basic-languages/monaco.contribution',
-		'vs/language/typescript/monaco.contribution'
-	], function() {
-		var editor = monaco.editor.create(document.getElementById('container'), {
-			value: [
+	let text = localStorage.getItem("code")
+	if (!text) {
+		text = [
 				'/* Game of Life',
 				' * Implemented in TypeScript',
 				' * To learn more about TypeScript, please visit http://www.typescriptlang.org/',
@@ -163,12 +160,25 @@
 				'}',
 				'',
 				'var game = new Conway.GameOfLife();',
+			].join('\n');
+	}
+	require([
+		'vs/basic-languages/monaco.contribution',
+		'vs/language/typescript/monaco.contribution'
+	], function() {
 
-			].join('\n'),
-			language: 'typescript'
+		var editor = monaco.editor.create(document.getElementById('container'), {
+			value: text,
+			language: 'typescript',
+			lightbulb: { enabled: true }
 		});
+
+		editor.onDidChangeModelContent(() => {
+			const code = editor.getModel().getValue()
+			localStorage.setItem("code", code)
+		})
 	});
 </script>
 
 </body>
-</html>
+</html>

部分文件因为文件数量过多而无法显示