From a79240fbc61e1621bb119e3f71e7cf83d05b0f5e Mon Sep 17 00:00:00 2001 From: ikatyang Date: Thu, 6 Jul 2017 19:27:27 +0800 Subject: [PATCH 01/50] Add missing docs for module: 'none' in tsc --init --- src/compiler/commandLineParser.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- .../tsConfig/Default initialized TSConfig/tsconfig.json | 2 +- .../tsconfig.json | 2 +- .../tsconfig.json | 2 +- .../Initialized TSConfig with files options/tsconfig.json | 2 +- .../tsconfig.json | 2 +- .../tsconfig.json | 2 +- .../tsconfig.json | 2 +- .../tsconfig.json | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 422789f420f..0f9eba8d4fe 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -105,7 +105,7 @@ namespace ts { paramType: Diagnostics.KIND, showInSimplifiedHelpView: true, category: Diagnostics.Basic_Options, - description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_umd_es2015_or_ESNext, + description: Diagnostics.Specify_module_code_generation_Colon_none_commonjs_amd_system_umd_es2015_or_ESNext, }, { name: "lib", diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 1fd434abf4c..cf198ec06ec 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2658,7 +2658,7 @@ "category": "Message", "code": 6015 }, - "Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.": { + "Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.": { "category": "Message", "code": 6016 }, diff --git a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json index 772c218eb4e..b79b4f0f181 100644 --- a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation: */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json index 7a9b895636c..ecbaaf9d961 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation: */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json index 70d5ed9d738..321547908d9 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation: */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json index 914b9d99d1b..ec0ac6e4c32 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation: */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json index 50bd28442bb..655ece4d6d0 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "lib": ["es5","es2015.promise"], /* Specify library files to be included in the compilation: */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json index 772c218eb4e..b79b4f0f181 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation: */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json index afae7193d58..81b1636b8cb 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "lib": ["es5","es2015.core"], /* Specify library files to be included in the compilation: */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json index a87fe9c5206..66acf6df045 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { /* Basic Options */ "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ // "lib": [], /* Specify library files to be included in the compilation: */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ From 7e395c2f88cf58aa4da0e02c2b9dd7c8fb1d32e0 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Fri, 7 Jul 2017 15:53:24 -0700 Subject: [PATCH 02/50] import keyword a left-hand-side expression --- src/compiler/utilities.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0ac392a865d..c68e225364d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -5108,6 +5108,7 @@ namespace ts { || kind === SyntaxKind.ThisKeyword || kind === SyntaxKind.TrueKeyword || kind === SyntaxKind.SuperKeyword + || kind === SyntaxKind.ImportKeyword || kind === SyntaxKind.NonNullExpression || kind === SyntaxKind.MetaProperty; } From 1ac95c29e4385b929b99dbc9641a5ebe951b6a7d Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Fri, 7 Jul 2017 15:53:55 -0700 Subject: [PATCH 03/50] Add tests and update baselines --- .../importCallExpressionShouldNotGetParen.js | 18 +++++++++ ...ortCallExpressionShouldNotGetParen.symbols | 27 +++++++++++++ ...mportCallExpressionShouldNotGetParen.types | 39 +++++++++++++++++++ .../importCallExpressionWithTypeArgument.js | 4 +- .../importCallExpressionShouldNotGetParen.ts | 11 ++++++ 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/importCallExpressionShouldNotGetParen.js create mode 100644 tests/baselines/reference/importCallExpressionShouldNotGetParen.symbols create mode 100644 tests/baselines/reference/importCallExpressionShouldNotGetParen.types create mode 100644 tests/cases/conformance/dynamicImport/importCallExpressionShouldNotGetParen.ts diff --git a/tests/baselines/reference/importCallExpressionShouldNotGetParen.js b/tests/baselines/reference/importCallExpressionShouldNotGetParen.js new file mode 100644 index 00000000000..07539664aa3 --- /dev/null +++ b/tests/baselines/reference/importCallExpressionShouldNotGetParen.js @@ -0,0 +1,18 @@ +//// [importCallExpressionShouldNotGetParen.ts] +const localeName = "zh-CN"; +import(`./locales/${localeName}.js`).then(bar => { + let x = bar; +}); + +import("./locales/" + localeName + ".js").then(bar => { + let x = bar; +}); + +//// [importCallExpressionShouldNotGetParen.js] +const localeName = "zh-CN"; +import(`./locales/${localeName}.js`).then(bar => { + let x = bar; +}); +import("./locales/" + localeName + ".js").then(bar => { + let x = bar; +}); diff --git a/tests/baselines/reference/importCallExpressionShouldNotGetParen.symbols b/tests/baselines/reference/importCallExpressionShouldNotGetParen.symbols new file mode 100644 index 00000000000..ace34efcf0d --- /dev/null +++ b/tests/baselines/reference/importCallExpressionShouldNotGetParen.symbols @@ -0,0 +1,27 @@ +=== tests/cases/conformance/dynamicImport/importCallExpressionShouldNotGetParen.ts === +const localeName = "zh-CN"; +>localeName : Symbol(localeName, Decl(importCallExpressionShouldNotGetParen.ts, 0, 5)) + +import(`./locales/${localeName}.js`).then(bar => { +>import(`./locales/${localeName}.js`).then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>localeName : Symbol(localeName, Decl(importCallExpressionShouldNotGetParen.ts, 0, 5)) +>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>bar : Symbol(bar, Decl(importCallExpressionShouldNotGetParen.ts, 1, 42)) + + let x = bar; +>x : Symbol(x, Decl(importCallExpressionShouldNotGetParen.ts, 2, 7)) +>bar : Symbol(bar, Decl(importCallExpressionShouldNotGetParen.ts, 1, 42)) + +}); + +import("./locales/" + localeName + ".js").then(bar => { +>import("./locales/" + localeName + ".js").then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>localeName : Symbol(localeName, Decl(importCallExpressionShouldNotGetParen.ts, 0, 5)) +>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>bar : Symbol(bar, Decl(importCallExpressionShouldNotGetParen.ts, 5, 47)) + + let x = bar; +>x : Symbol(x, Decl(importCallExpressionShouldNotGetParen.ts, 6, 7)) +>bar : Symbol(bar, Decl(importCallExpressionShouldNotGetParen.ts, 5, 47)) + +}); diff --git a/tests/baselines/reference/importCallExpressionShouldNotGetParen.types b/tests/baselines/reference/importCallExpressionShouldNotGetParen.types new file mode 100644 index 00000000000..3e4001dc5b7 --- /dev/null +++ b/tests/baselines/reference/importCallExpressionShouldNotGetParen.types @@ -0,0 +1,39 @@ +=== tests/cases/conformance/dynamicImport/importCallExpressionShouldNotGetParen.ts === +const localeName = "zh-CN"; +>localeName : "zh-CN" +>"zh-CN" : "zh-CN" + +import(`./locales/${localeName}.js`).then(bar => { +>import(`./locales/${localeName}.js`).then(bar => { let x = bar;}) : Promise +>import(`./locales/${localeName}.js`).then : (onfulfilled?: (value: any) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>import(`./locales/${localeName}.js`) : Promise +>`./locales/${localeName}.js` : string +>localeName : "zh-CN" +>then : (onfulfilled?: (value: any) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>bar => { let x = bar;} : (bar: any) => void +>bar : any + + let x = bar; +>x : any +>bar : any + +}); + +import("./locales/" + localeName + ".js").then(bar => { +>import("./locales/" + localeName + ".js").then(bar => { let x = bar;}) : Promise +>import("./locales/" + localeName + ".js").then : (onfulfilled?: (value: any) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>import("./locales/" + localeName + ".js") : Promise +>"./locales/" + localeName + ".js" : string +>"./locales/" + localeName : string +>"./locales/" : "./locales/" +>localeName : "zh-CN" +>".js" : ".js" +>then : (onfulfilled?: (value: any) => TResult1 | PromiseLike, onrejected?: (reason: any) => TResult2 | PromiseLike) => Promise +>bar => { let x = bar;} : (bar: any) => void +>bar : any + + let x = bar; +>x : any +>bar : any + +}); diff --git a/tests/baselines/reference/importCallExpressionWithTypeArgument.js b/tests/baselines/reference/importCallExpressionWithTypeArgument.js index d8690cf5d75..cdea13b2fac 100644 --- a/tests/baselines/reference/importCallExpressionWithTypeArgument.js +++ b/tests/baselines/reference/importCallExpressionWithTypeArgument.js @@ -18,5 +18,5 @@ function foo() { return "foo"; } exports.foo = foo; //// [1.js] "use strict"; -var p1 = (import)("./0"); // error -var p2 = (import)("./0"); // error +var p1 = Promise.resolve().then(function () { return require("./0"); }); // error +var p2 = Promise.resolve().then(function () { return require("./0"); }); // error diff --git a/tests/cases/conformance/dynamicImport/importCallExpressionShouldNotGetParen.ts b/tests/cases/conformance/dynamicImport/importCallExpressionShouldNotGetParen.ts new file mode 100644 index 00000000000..6734e888a5e --- /dev/null +++ b/tests/cases/conformance/dynamicImport/importCallExpressionShouldNotGetParen.ts @@ -0,0 +1,11 @@ +// @module: esnext +// @target: es6 +// @noImplicitAny: true +const localeName = "zh-CN"; +import(`./locales/${localeName}.js`).then(bar => { + let x = bar; +}); + +import("./locales/" + localeName + ".js").then(bar => { + let x = bar; +}); \ No newline at end of file From ae533551c29fe4fd9f8dfa214400ff44a85124b9 Mon Sep 17 00:00:00 2001 From: Filipe Silva Date: Sun, 9 Jul 2017 18:50:45 +0100 Subject: [PATCH 04/50] Allow visitors to return undefined While implementing `ts.Visitor`, it is possible to return `undefined` in order to drop a node. However, the typings do not reflect this and only allow to return `Node | Node []`. This PR extends the typings to allow `undefined` as well. --- src/compiler/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 63b50d58e4a..da69c407ac8 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4350,7 +4350,7 @@ namespace ts { */ export type Visitor = (node: Node) => VisitResult; - export type VisitResult = T | T[]; + export type VisitResult = T | T[] | undefined; export interface Printer { /** From 91d7b22e6a9ad6664254df3f5b8714513a25170b Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 10 Jul 2017 11:35:54 -0700 Subject: [PATCH 05/50] Remove ILineInfo type (#17017) --- src/harness/unittests/versionCache.ts | 12 +-- src/server/protocol.ts | 2 +- src/server/scriptInfo.ts | 37 ++------ src/server/scriptVersionCache.ts | 120 +++++++++++++------------- src/server/session.ts | 59 ++++++------- 5 files changed, 100 insertions(+), 130 deletions(-) diff --git a/src/harness/unittests/versionCache.ts b/src/harness/unittests/versionCache.ts index d6e0a7c2278..10790d41302 100644 --- a/src/harness/unittests/versionCache.ts +++ b/src/harness/unittests/versionCache.ts @@ -7,8 +7,7 @@ namespace ts { } function lineColToPosition(lineIndex: server.LineIndex, line: number, col: number) { - const lineInfo = lineIndex.lineNumberToInfo(line); - return (lineInfo.offset + col - 1); + return lineIndex.absolutePositionOfStartOfLine(line) + (col - 1); } function validateEdit(lineIndex: server.LineIndex, sourceText: string, position: number, deleteLength: number, insertString: string): void { @@ -298,20 +297,17 @@ and grew 1cm per day`; it("Line/offset from pos", () => { for (let i = 0; i < iterationCount; i++) { - const lp = lineIndex.charOffsetToLineNumberAndPos(rsa[i]); + const lp = lineIndex.positionToLineOffset(rsa[i]); const lac = ts.computeLineAndCharacterOfPosition(lineMap, rsa[i]); assert.equal(lac.line + 1, lp.line, "Line number mismatch " + (lac.line + 1) + " " + lp.line + " " + i); - assert.equal(lac.character, (lp.offset), "Charachter offset mismatch " + lac.character + " " + lp.offset + " " + i); + assert.equal(lac.character, lp.offset - 1, "Character offset mismatch " + lac.character + " " + (lp.offset - 1) + " " + i); } }); it("Start pos from line", () => { for (let i = 0; i < iterationCount; i++) { for (let j = 0; j < lines.length; j++) { - const lineInfo = lineIndex.lineNumberToInfo(j + 1); - const lineIndexOffset = lineInfo.offset; - const lineMapOffset = lineMap[j]; - assert.equal(lineIndexOffset, lineMapOffset); + assert.equal(lineIndex.absolutePositionOfStartOfLine(j + 1), lineMap[j]); } } }); diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 916d9c51a14..b6756a7d4ff 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -640,7 +640,7 @@ namespace ts.server.protocol { } /** - * Location in source code expressed as (one-based) line and character offset. + * Location in source code expressed as (one-based) line and (one-based) column offset. */ export interface Location { line: number; diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 534d73a6410..5250a8ea66c 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -61,7 +61,7 @@ namespace ts.server { : ScriptSnapshot.fromString(this.getOrLoadText()); } - public getLineInfo(line: number) { + public getLineInfo(line: number): AbsolutePositionAndLineText { return this.switchToScriptVersionCache().getSnapshot().index.lineNumberToInfo(line); } /** @@ -75,16 +75,9 @@ namespace ts.server { return ts.createTextSpanFromBounds(start, end); } const index = this.svc.getSnapshot().index; - const lineInfo = index.lineNumberToInfo(line + 1); - let len: number; - if (lineInfo.leaf) { - len = lineInfo.leaf.text.length; - } - else { - const nextLineInfo = index.lineNumberToInfo(line + 2); - len = nextLineInfo.offset - lineInfo.offset; - } - return ts.createTextSpan(lineInfo.offset, len); + const { lineText, absolutePosition } = index.lineNumberToInfo(line + 1); + const len = lineText !== undefined ? lineText.length : index.absolutePositionOfStartOfLine(line + 2) - absolutePosition; + return ts.createTextSpan(absolutePosition, len); } /** @@ -95,25 +88,17 @@ namespace ts.server { if (!this.svc) { return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1); } - const index = this.svc.getSnapshot().index; - const lineInfo = index.lineNumberToInfo(line); // TODO: assert this offset is actually on the line - return (lineInfo.offset + offset - 1); + return this.svc.getSnapshot().index.absolutePositionOfStartOfLine(line) + (offset - 1); } - /** - * @param line 1-based index - * @param offset 1-based index - */ - positionToLineOffset(position: number): ILineInfo { + positionToLineOffset(position: number): protocol.Location { if (!this.svc) { const { line, character } = computeLineAndCharacterOfPosition(this.getLineMap(), position); return { line: line + 1, offset: character + 1 }; } - const index = this.svc.getSnapshot().index; - const lineOffset = index.charOffsetToLineNumberAndPos(position); - return { line: lineOffset.line, offset: lineOffset.offset + 1 }; + return this.svc.getSnapshot().index.positionToLineOffset(position); } private getFileText(tempFileName?: string) { @@ -334,7 +319,7 @@ namespace ts.server { } } - getLineInfo(line: number) { + getLineInfo(line: number): AbsolutePositionAndLineText { return this.textStorage.getLineInfo(line); } @@ -364,11 +349,7 @@ namespace ts.server { return this.textStorage.lineOffsetToPosition(line, offset); } - /** - * @param line 1-based index - * @param offset 1-based index - */ - positionToLineOffset(position: number): ILineInfo { + positionToLineOffset(position: number): protocol.Location { return this.textStorage.positionToLineOffset(position); } diff --git a/src/server/scriptVersionCache.ts b/src/server/scriptVersionCache.ts index 464eea2efab..022447e488e 100644 --- a/src/server/scriptVersionCache.ts +++ b/src/server/scriptVersionCache.ts @@ -8,15 +8,13 @@ namespace ts.server { export interface LineCollection { charCount(): number; lineCount(): number; - isLeaf(): boolean; + isLeaf(): this is LineLeaf; walk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker): void; } - export interface ILineInfo { - line: number; - offset: number; - text?: string; - leaf?: LineLeaf; + export interface AbsolutePositionAndLineText { + absolutePosition: number; + lineText: string | undefined; } export enum CharRangeSection { @@ -397,22 +395,27 @@ namespace ts.server { // set this to true to check each edit for accuracy checkEdits = false; - charOffsetToLineNumberAndPos(charOffset: number) { - return this.root.charOffsetToLineNumberAndPos(1, charOffset); + absolutePositionOfStartOfLine(oneBasedLine: number): number { + return this.lineNumberToInfo(oneBasedLine).absolutePosition; } - lineNumberToInfo(lineNumber: number): ILineInfo { + positionToLineOffset(position: number): protocol.Location { + const { oneBasedLine, zeroBasedColumn } = this.root.charOffsetToLineInfo(1, position); + return { line: oneBasedLine, offset: zeroBasedColumn + 1 }; + } + + private positionToColumnAndLineText(position: number): { zeroBasedColumn: number, lineText: string } { + return this.root.charOffsetToLineInfo(1, position); + } + + lineNumberToInfo(oneBasedLine: number): AbsolutePositionAndLineText { const lineCount = this.root.lineCount(); - if (lineNumber <= lineCount) { - const lineInfo = this.root.lineNumberToInfo(lineNumber, 0); - lineInfo.line = lineNumber; - return lineInfo; + if (oneBasedLine <= lineCount) { + const { position, leaf } = this.root.lineNumberToInfo(oneBasedLine, 0); + return { absolutePosition: position, lineText: leaf && leaf.text }; } else { - return { - line: lineNumber, - offset: this.root.charCount() - }; + return { absolutePosition: this.root.charCount(), lineText: undefined }; } } @@ -502,17 +505,12 @@ namespace ts.server { else if (deleteLength > 0) { // check whether last characters deleted are line break const e = pos + deleteLength; - const lineInfo = this.charOffsetToLineNumberAndPos(e); - if ((lineInfo && (lineInfo.offset === 0))) { + const { zeroBasedColumn, lineText } = this.positionToColumnAndLineText(e); + if (zeroBasedColumn === 0) { // move range end just past line that will merge with previous line - deleteLength += lineInfo.text.length; + deleteLength += lineText.length; // store text by appending to end of insertedText - if (newText) { - newText = newText + lineInfo.text; - } - else { - newText = lineInfo.text; - } + newText = newText ? newText + lineText : lineText; } } if (pos < this.root.charCount()) { @@ -676,90 +674,88 @@ namespace ts.server { } } - charOffsetToLineNumberAndPos(lineNumber: number, charOffset: number): ILineInfo { - const childInfo = this.childFromCharOffset(lineNumber, charOffset); + // Input position is relative to the start of this node. + // Output line number is absolute. + charOffsetToLineInfo(lineNumberAccumulator: number, relativePosition: number): { oneBasedLine: number, zeroBasedColumn: number, lineText: string | undefined } { + const childInfo = this.childFromCharOffset(lineNumberAccumulator, relativePosition); if (!childInfo.child) { return { - line: lineNumber, - offset: charOffset, + oneBasedLine: lineNumberAccumulator, + zeroBasedColumn: relativePosition, + lineText: undefined, }; } else if (childInfo.childIndex < this.children.length) { if (childInfo.child.isLeaf()) { return { - line: childInfo.lineNumber, - offset: childInfo.charOffset, - text: ((childInfo.child)).text, - leaf: ((childInfo.child)) + oneBasedLine: childInfo.lineNumberAccumulator, + zeroBasedColumn: childInfo.relativePosition, + lineText: childInfo.child.text, }; } else { const lineNode = (childInfo.child); - return lineNode.charOffsetToLineNumberAndPos(childInfo.lineNumber, childInfo.charOffset); + return lineNode.charOffsetToLineInfo(childInfo.lineNumberAccumulator, childInfo.relativePosition); } } else { const lineInfo = this.lineNumberToInfo(this.lineCount(), 0); - return { line: this.lineCount(), offset: lineInfo.leaf.charCount() }; + return { oneBasedLine: this.lineCount(), zeroBasedColumn: lineInfo.leaf.charCount(), lineText: undefined }; } } - lineNumberToInfo(lineNumber: number, charOffset: number): ILineInfo { - const childInfo = this.childFromLineNumber(lineNumber, charOffset); + lineNumberToInfo(relativeOneBasedLine: number, positionAccumulator: number): { position: number, leaf: LineLeaf | undefined } { + const childInfo = this.childFromLineNumber(relativeOneBasedLine, positionAccumulator); if (!childInfo.child) { - return { - line: lineNumber, - offset: charOffset - }; + return { position: positionAccumulator, leaf: undefined }; } else if (childInfo.child.isLeaf()) { - return { - line: lineNumber, - offset: childInfo.charOffset, - text: ((childInfo.child)).text, - leaf: ((childInfo.child)) - }; + return { position: childInfo.positionAccumulator, leaf: childInfo.child }; } else { const lineNode = (childInfo.child); - return lineNode.lineNumberToInfo(childInfo.relativeLineNumber, childInfo.charOffset); + return lineNode.lineNumberToInfo(childInfo.relativeOneBasedLine, childInfo.positionAccumulator); } } - childFromLineNumber(lineNumber: number, charOffset: number) { + /** + * Input line number is relative to the start of this node. + * Output line number is relative to the child. + * positionAccumulator will be an absolute position once relativeLineNumber reaches 0. + */ + private childFromLineNumber(relativeOneBasedLine: number, positionAccumulator: number): { child: LineCollection, relativeOneBasedLine: number, positionAccumulator: number } { let child: LineCollection; - let relativeLineNumber = lineNumber; let i: number; - let len: number; - for (i = 0, len = this.children.length; i < len; i++) { + for (i = 0; i < this.children.length; i++) { child = this.children[i]; const childLineCount = child.lineCount(); - if (childLineCount >= relativeLineNumber) { + if (childLineCount >= relativeOneBasedLine) { break; } else { - relativeLineNumber -= childLineCount; - charOffset += child.charCount(); + relativeOneBasedLine -= childLineCount; + positionAccumulator += child.charCount(); } } - return { child, childIndex: i, relativeLineNumber, charOffset }; + return { child, relativeOneBasedLine, positionAccumulator }; } - childFromCharOffset(lineNumber: number, charOffset: number) { + private childFromCharOffset(lineNumberAccumulator: number, relativePosition: number + ): { child: LineCollection, childIndex: number, relativePosition: number, lineNumberAccumulator: number } { let child: LineCollection; let i: number; let len: number; for (i = 0, len = this.children.length; i < len; i++) { child = this.children[i]; - if (child.charCount() > charOffset) { + if (child.charCount() > relativePosition) { break; } else { - charOffset -= child.charCount(); - lineNumber += child.lineCount(); + relativePosition -= child.charCount(); + lineNumberAccumulator += child.lineCount(); } } - return { child, childIndex: i, charOffset, lineNumber }; + return { child, childIndex: i, relativePosition, lineNumberAccumulator }; } private splitAfter(childIndex: number) { diff --git a/src/server/session.ts b/src/server/session.ts index 4b4d0a02265..cdd4306c064 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -49,7 +49,7 @@ namespace ts.server { interface FileStart { file: string; - start: ILineInfo; + start: protocol.Location; } function compareNumber(a: number, b: number) { @@ -84,15 +84,15 @@ namespace ts.server { }; } - function convertToILineInfo(lineAndCharacter: LineAndCharacter): ILineInfo { + function convertToLocation(lineAndCharacter: LineAndCharacter): protocol.Location { return { line: lineAndCharacter.line + 1, offset: lineAndCharacter.character + 1 }; } function formatConfigFileDiag(diag: ts.Diagnostic, includeFileName: true): protocol.DiagnosticWithFileName; function formatConfigFileDiag(diag: ts.Diagnostic, includeFileName: false): protocol.Diagnostic; function formatConfigFileDiag(diag: ts.Diagnostic, includeFileName: boolean): protocol.Diagnostic | protocol.DiagnosticWithFileName { - const start = diag.file && convertToILineInfo(getLineAndCharacterOfPosition(diag.file, diag.start)); - const end = diag.file && convertToILineInfo(getLineAndCharacterOfPosition(diag.file, diag.start + diag.length)); + const start = diag.file && convertToLocation(getLineAndCharacterOfPosition(diag.file, diag.start)); + const end = diag.file && convertToLocation(getLineAndCharacterOfPosition(diag.file, diag.start + diag.length)); const text = ts.flattenDiagnosticMessageText(diag.messageText, "\n"); const { code, source } = diag; const category = DiagnosticCategory[diag.category].toLowerCase(); @@ -555,8 +555,8 @@ namespace ts.server { length: d.length, category: DiagnosticCategory[d.category].toLowerCase(), code: d.code, - startLocation: d.file && convertToILineInfo(getLineAndCharacterOfPosition(d.file, d.start)), - endLocation: d.file && convertToILineInfo(getLineAndCharacterOfPosition(d.file, d.start + d.length)) + startLocation: d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start)), + endLocation: d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start + d.length)) }); } @@ -1131,32 +1131,29 @@ namespace ts.server { // only to the previous line. If all this is true, then // add edits necessary to properly indent the current line. if ((args.key === "\n") && ((!edits) || (edits.length === 0) || allEditsBeforePos(edits, position))) { - const lineInfo = scriptInfo.getLineInfo(args.line); - if (lineInfo && (lineInfo.leaf) && (lineInfo.leaf.text)) { - const lineText = lineInfo.leaf.text; - if (lineText.search("\\S") < 0) { - const preferredIndent = project.getLanguageService(/*ensureSynchronized*/ false).getIndentationAtPosition(file, position, formatOptions); - let hasIndent = 0; - let i: number, len: number; - for (i = 0, len = lineText.length; i < len; i++) { - if (lineText.charAt(i) === " ") { - hasIndent++; - } - else if (lineText.charAt(i) === "\t") { - hasIndent += formatOptions.tabSize; - } - else { - break; - } + const { lineText, absolutePosition } = scriptInfo.getLineInfo(args.line); + if (lineText && lineText.search("\\S") < 0) { + const preferredIndent = project.getLanguageService(/*ensureSynchronized*/ false).getIndentationAtPosition(file, position, formatOptions); + let hasIndent = 0; + let i: number, len: number; + for (i = 0, len = lineText.length; i < len; i++) { + if (lineText.charAt(i) === " ") { + hasIndent++; } - // i points to the first non whitespace character - if (preferredIndent !== hasIndent) { - const firstNoWhiteSpacePosition = lineInfo.offset + i; - edits.push({ - span: ts.createTextSpanFromBounds(lineInfo.offset, firstNoWhiteSpacePosition), - newText: formatting.getIndentationString(preferredIndent, formatOptions) - }); + else if (lineText.charAt(i) === "\t") { + hasIndent += formatOptions.tabSize; } + else { + break; + } + } + // i points to the first non whitespace character + if (preferredIndent !== hasIndent) { + const firstNoWhiteSpacePosition = absolutePosition + i; + edits.push({ + span: ts.createTextSpanFromBounds(absolutePosition, firstNoWhiteSpacePosition), + newText: formatting.getIndentationString(preferredIndent, formatOptions) + }); } } } @@ -1514,7 +1511,7 @@ namespace ts.server { if (simplifiedResult) { const file = result.renameFilename; - let location: ILineInfo | undefined = undefined; + let location: protocol.Location | undefined; if (file !== undefined && result.renameLocation !== undefined) { const renameScriptInfo = project.getScriptInfoForNormalizedPath(toNormalizedPath(file)); location = renameScriptInfo.positionToLineOffset(result.renameLocation); From 48876731b8b099610208f949d3b8e3b2cdb380d6 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 10 Jul 2017 11:44:56 -0700 Subject: [PATCH 06/50] Type-check `sum` (#16823) --- src/compiler/checker.ts | 6 +++--- src/compiler/core.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5fac69156eb..ee0a3a90d7e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -83,9 +83,9 @@ namespace ts { // extra cost of calling `getParseTreeNode` when calling these functions from inside the // checker. const checker: TypeChecker = { - getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"), - getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"), - getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount") + symbolCount, + getNodeCount: () => sum<"nodeCount">(host.getSourceFiles(), "nodeCount"), + getIdentifierCount: () => sum<"identifierCount">(host.getSourceFiles(), "identifierCount"), + getSymbolCount: () => sum<"symbolCount">(host.getSourceFiles(), "symbolCount") + symbolCount, getTypeCount: () => typeCount, isUndefinedSymbol: symbol => symbol === undefinedSymbol, isArgumentsSymbol: symbol => symbol === argumentsSymbol, diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 05466c3cc4b..745cce8a6cd 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -700,7 +700,7 @@ namespace ts { return result; } - export function sum(array: any[], prop: string): number { + export function sum(array: { [x in K]: number }[], prop: K): number { let result = 0; for (const v of array) { result += v[prop]; From 0c40c18e98baca899b1b70c8c138a5320ccacda0 Mon Sep 17 00:00:00 2001 From: Mine Starks Date: Mon, 10 Jul 2017 12:49:29 -0700 Subject: [PATCH 07/50] Applying edits in Fourslash can cause caret to move off-range --- src/harness/fourslash.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 434a3981461..04fc21e60e0 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1797,6 +1797,10 @@ namespace FourSlash { // this.languageService.getScriptLexicalStructure(fileName); } + if (this.currentCaretPosition < 0) { + this.currentCaretPosition = 0; + } + if (isFormattingEdit) { const newContent = this.getFileContent(fileName); From b6b6d0516eb3971bf55f869bb99dc919da5b97b6 Mon Sep 17 00:00:00 2001 From: Mine Starks Date: Mon, 10 Jul 2017 12:49:47 -0700 Subject: [PATCH 08/50] More detailed error logging in Fourslash --- src/harness/fourslash.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 04fc21e60e0..fc8299f57e9 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2352,7 +2352,7 @@ namespace FourSlash { private applyCodeActions(actions: ts.CodeAction[], index?: number): void { if (index === undefined) { if (!(actions && actions.length === 1)) { - this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found.`); + this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `"${a.description}"`).join(", ") : "" }`); } index = 0; } From dab682767cc62e2c6f8dbd71a7f965d9913adb17 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 10 Jul 2017 13:25:48 -0700 Subject: [PATCH 09/50] Fix call to getCodeFixesAtPosition (#17063) --- src/harness/fourslash.ts | 2 +- src/services/services.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 434a3981461..587bed813f1 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2337,7 +2337,7 @@ namespace FourSlash { continue; } - const newActions = this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.length, [diagnostic.code], this.formatCodeSettings); + const newActions = this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.formatCodeSettings); if (newActions && newActions.length) { actions = actions ? actions.concat(newActions) : newActions; } diff --git a/src/services/services.ts b/src/services/services.ts index 759de900b84..be17a35d3ed 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1768,7 +1768,7 @@ namespace ts { function getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[], formatOptions: FormatCodeSettings): CodeAction[] { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); - const span = { start, length: end - start }; + const span = createTextSpanFromBounds(start, end); const newLineCharacter = getNewLineOrDefaultFromHost(host); let allFixes: CodeAction[] = []; From 39e4b1f9e304351f6e9e081a20fb7a7f637589b1 Mon Sep 17 00:00:00 2001 From: Mine Starks Date: Mon, 10 Jul 2017 14:16:31 -0700 Subject: [PATCH 10/50] Code fix to remove unused import should preserve default import --- src/services/codefixes/fixUnusedIdentifier.ts | 41 +++++++++++-------- tests/cases/fourslash/unusedImports13FS.ts | 12 ++++++ 2 files changed, 36 insertions(+), 17 deletions(-) create mode 100644 tests/cases/fourslash/unusedImports13FS.ts diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index cbe2ba5b1c5..51c8a59627d 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -87,9 +87,7 @@ namespace ts.codefix { case SyntaxKind.ImportSpecifier: const namedImports = parent.parent; if (namedImports.elements.length === 1) { - // Only 1 import and it is unused. So the entire declaration should be removed. - const importSpec = getAncestor(identifier, SyntaxKind.ImportDeclaration); - return [deleteNode(importSpec)]; + return deleteNamedImportBinding(namedImports); } else { // delete import specifier @@ -100,7 +98,7 @@ namespace ts.codefix { // or "'import {a, b as ns} from './file'" case SyntaxKind.ImportClause: // this covers both 'import |d|' and 'import |d,| *' const importClause = parent; - if (!importClause.namedBindings) { // |import d from './file'| or |import * as ns from './file'| + if (!importClause.namedBindings) { // |import d from './file'| const importDecl = getAncestor(importClause, SyntaxKind.ImportDeclaration); return [deleteNode(importDecl)]; } @@ -118,25 +116,34 @@ namespace ts.codefix { } case SyntaxKind.NamespaceImport: - const namespaceImport = parent; - if (namespaceImport.name === identifier && !(namespaceImport.parent).name) { - const importDecl = getAncestor(namespaceImport, SyntaxKind.ImportDeclaration); - return [deleteNode(importDecl)]; - } - else { - const previousToken = getTokenAtPosition(sourceFile, namespaceImport.pos - 1, /*includeJsDocComment*/ false); - if (previousToken && previousToken.kind === SyntaxKind.CommaToken) { - const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, textChanges.Position.FullStart); - return [deleteRange({ pos: startPosition, end: namespaceImport.end })]; - } - return [deleteRange(namespaceImport)]; - } + return deleteNamedImportBinding(parent); default: return [deleteDefault()]; } } + function deleteNamedImportBinding(namedBindings: NamedImportBindings): CodeAction[] | undefined { + if ((namedBindings.parent).name) { + // Delete named imports while preserving the default import + // import d|, * as ns| from './file' + // import d|, { a }| from './file' + const previousToken = getTokenAtPosition(sourceFile, namedBindings.pos - 1, /*includeJsDocComment*/ false); + if (previousToken && previousToken.kind === SyntaxKind.CommaToken) { + const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, textChanges.Position.FullStart); + return [deleteRange({ pos: startPosition, end: namedBindings.end })]; + } + return undefined; + } + else { + // Delete the entire import declaration + // |import * as ns from './file'| + // |import { a } from './file'| + const importDecl = getAncestor(namedBindings, SyntaxKind.ImportDeclaration); + return [deleteNode(importDecl)]; + } + } + // token.parent is a variableDeclaration function deleteVariableDeclarationOrPrefixWithUnderscore(identifier: Identifier, varDecl: ts.VariableDeclaration): CodeAction[] | undefined { switch (varDecl.parent.parent.kind) { diff --git a/tests/cases/fourslash/unusedImports13FS.ts b/tests/cases/fourslash/unusedImports13FS.ts new file mode 100644 index 00000000000..4e19f4d7f1a --- /dev/null +++ b/tests/cases/fourslash/unusedImports13FS.ts @@ -0,0 +1,12 @@ +/// + +// @noUnusedLocals: true +// @Filename: file2.ts +//// [| import A, { x } from './a'; |] +//// console.log(A); + +// @Filename: file1.ts +//// export default 10; +//// export var x = 10; + +verify.rangeAfterCodeFix("import A from './a';"); \ No newline at end of file From d25fd23e042be44026677466cadedbf36f160933 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 10 Jul 2017 14:35:09 -0700 Subject: [PATCH 11/50] Declare 'sum' so that it doesn't require type arguments. --- src/compiler/checker.ts | 6 +++--- src/compiler/core.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ee0a3a90d7e..5fac69156eb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -83,9 +83,9 @@ namespace ts { // extra cost of calling `getParseTreeNode` when calling these functions from inside the // checker. const checker: TypeChecker = { - getNodeCount: () => sum<"nodeCount">(host.getSourceFiles(), "nodeCount"), - getIdentifierCount: () => sum<"identifierCount">(host.getSourceFiles(), "identifierCount"), - getSymbolCount: () => sum<"symbolCount">(host.getSourceFiles(), "symbolCount") + symbolCount, + getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"), + getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"), + getSymbolCount: () => sum(host.getSourceFiles(), "symbolCount") + symbolCount, getTypeCount: () => typeCount, isUndefinedSymbol: symbol => symbol === undefinedSymbol, isArgumentsSymbol: symbol => symbol === argumentsSymbol, diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 745cce8a6cd..42faf0b062e 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -700,10 +700,10 @@ namespace ts { return result; } - export function sum(array: { [x in K]: number }[], prop: K): number { + export function sum, K extends string>(array: T[], prop: K): number { let result = 0; for (const v of array) { - result += v[prop]; + result += (v[prop] as number); } return result; } From e29b2106e9545fddb5f185e42810499d4caa8abc Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 10 Jul 2017 15:08:57 -0700 Subject: [PATCH 12/50] Improvements to LineIndex.edit (#17056) --- src/server/scriptVersionCache.ts | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/server/scriptVersionCache.ts b/src/server/scriptVersionCache.ts index 022447e488e..35e39ce64f9 100644 --- a/src/server/scriptVersionCache.ts +++ b/src/server/scriptVersionCache.ts @@ -279,9 +279,9 @@ namespace ts.server { // REVIEW: can optimize by coalescing simple edits edit(pos: number, deleteLen: number, insertedText?: string) { this.changes.push(new TextChange(pos, deleteLen, insertedText)); - if ((this.changes.length > ScriptVersionCache.changeNumberThreshold) || - (deleteLen > ScriptVersionCache.changeLengthThreshold) || - (insertedText && (insertedText.length > ScriptVersionCache.changeLengthThreshold))) { + if (this.changes.length > ScriptVersionCache.changeNumberThreshold || + deleteLen > ScriptVersionCache.changeLengthThreshold || + insertedText && insertedText.length > ScriptVersionCache.changeLengthThreshold) { this.getSnapshot(); } } @@ -471,12 +471,9 @@ namespace ts.server { return !walkFns.done; } - edit(pos: number, deleteLength: number, newText?: string) { - function editFlat(source: string, s: number, dl: number, nt = "") { - return source.substring(0, s) + nt + source.substring(s + dl, source.length); - } + edit(pos: number, deleteLength: number, newText?: string): LineIndex { if (this.root.charCount() === 0) { - // TODO: assert deleteLength === 0 + Debug.assert(deleteLength === 0); // Can't delete from empty document if (newText !== undefined) { this.load(LineIndex.linesFromText(newText).lines); return this; @@ -485,7 +482,8 @@ namespace ts.server { else { let checkText: string; if (this.checkEdits) { - checkText = editFlat(this.getText(0, this.root.charCount()), pos, deleteLength, newText); + const source = this.getText(0, this.root.charCount()); + checkText = source.slice(0, pos) + newText + source.slice(pos + deleteLength); } const walker = new EditWalker(); let suppressTrailingText = false; @@ -513,14 +511,15 @@ namespace ts.server { newText = newText ? newText + lineText : lineText; } } - if (pos < this.root.charCount()) { - this.root.walk(pos, deleteLength, walker); - walker.insertLines(newText, suppressTrailingText); - } + + this.root.walk(pos, deleteLength, walker); + walker.insertLines(newText, suppressTrailingText); + if (this.checkEdits) { - const updatedText = this.getText(0, this.root.charCount()); + const updatedText = walker.lineIndex.getText(0, walker.lineIndex.getLength()); Debug.assert(checkText === updatedText, "buffer edit mismatch"); } + return walker.lineIndex; } } From b8b1fb305acb22c0404be3f2b708227f1be72652 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 10 Jul 2017 15:10:45 -0700 Subject: [PATCH 13/50] Minor cleanups in scriptVersionCache (#17021) --- src/server/scriptVersionCache.ts | 54 +++++++++++++++----------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/src/server/scriptVersionCache.ts b/src/server/scriptVersionCache.ts index 35e39ce64f9..17386285921 100644 --- a/src/server/scriptVersionCache.ts +++ b/src/server/scriptVersionCache.ts @@ -95,22 +95,20 @@ namespace ts.server { } // path at least length two (root and leaf) - let insertionNode = this.startPath[this.startPath.length - 2]; const leafNode = this.startPath[this.startPath.length - 1]; - const len = lines.length; - if (len > 0) { + if (lines.length > 0) { leafNode.text = lines[0]; - if (len > 1) { - let insertedNodes = new Array(len - 1); + if (lines.length > 1) { + let insertedNodes = new Array(lines.length - 1); let startNode = leafNode; for (let i = 1; i < lines.length; i++) { insertedNodes[i - 1] = new LineLeaf(lines[i]); } let pathIndex = this.startPath.length - 2; while (pathIndex >= 0) { - insertionNode = this.startPath[pathIndex]; + const insertionNode = this.startPath[pathIndex]; insertedNodes = insertionNode.insertAt(startNode, insertedNodes); pathIndex--; startNode = insertionNode; @@ -132,6 +130,7 @@ namespace ts.server { } } else { + const insertionNode = this.startPath[this.startPath.length - 2]; // no content for leaf node, so delete it insertionNode.remove(leafNode); for (let j = this.startPath.length - 2; j >= 0; j--) { @@ -524,27 +523,27 @@ namespace ts.server { } } - static buildTreeFromBottom(nodes: LineCollection[]): LineNode { - const nodeCount = Math.ceil(nodes.length / lineCollectionCapacity); - const interiorNodes: LineNode[] = []; + private static buildTreeFromBottom(nodes: LineCollection[]): LineNode { + const interiorNodeCount = Math.ceil(nodes.length / lineCollectionCapacity); + const interiorNodes: LineNode[] = new Array(interiorNodeCount); let nodeIndex = 0; - for (let i = 0; i < nodeCount; i++) { - interiorNodes[i] = new LineNode(); + for (let i = 0; i < interiorNodeCount; i++) { + const interiorNode = interiorNodes[i] = new LineNode(); let charCount = 0; let lineCount = 0; for (let j = 0; j < lineCollectionCapacity; j++) { - if (nodeIndex < nodes.length) { - interiorNodes[i].add(nodes[nodeIndex]); - charCount += nodes[nodeIndex].charCount(); - lineCount += nodes[nodeIndex].lineCount(); - } - else { + if (nodeIndex >= nodes.length) { break; } + + const node = nodes[nodeIndex]; + interiorNode.add(node); + charCount += node.charCount(); + lineCount += node.lineCount(); nodeIndex++; } - interiorNodes[i].totalChars = charCount; - interiorNodes[i].totalLines = lineCount; + interiorNode.totalChars = charCount; + interiorNode.totalLines = lineCount; } if (interiorNodes.length === 1) { return interiorNodes[0]; @@ -580,7 +579,7 @@ namespace ts.server { export class LineNode implements LineCollection { totalChars = 0; totalLines = 0; - children: LineCollection[] = []; + private children: LineCollection[] = []; isLeaf() { return false; @@ -595,7 +594,7 @@ namespace ts.server { } } - execWalk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker, childIndex: number, nodeType: CharRangeSection) { + private execWalk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker, childIndex: number, nodeType: CharRangeSection) { if (walkFns.pre) { walkFns.pre(rangeStart, rangeLength, this.children[childIndex], this, nodeType); } @@ -611,7 +610,7 @@ namespace ts.server { return walkFns.done; } - skipChild(relativeStart: number, relativeLength: number, childIndex: number, walkFns: ILineIndexWalker, nodeType: CharRangeSection) { + private skipChild(relativeStart: number, relativeLength: number, childIndex: number, walkFns: ILineIndexWalker, nodeType: CharRangeSection) { if (walkFns.pre && (!walkFns.done)) { walkFns.pre(relativeStart, relativeLength, this.children[childIndex], this, nodeType); walkFns.goSubtree = true; @@ -621,16 +620,14 @@ namespace ts.server { walk(rangeStart: number, rangeLength: number, walkFns: ILineIndexWalker) { // assume (rangeStart < this.totalChars) && (rangeLength <= this.totalChars) let childIndex = 0; - let child = this.children[0]; - let childCharCount = child.charCount(); + let childCharCount = this.children[childIndex].charCount(); // find sub-tree containing start let adjustedStart = rangeStart; while (adjustedStart >= childCharCount) { this.skipChild(adjustedStart, rangeLength, childIndex, walkFns, CharRangeSection.PreStart); adjustedStart -= childCharCount; childIndex++; - child = this.children[childIndex]; - childCharCount = child.charCount(); + childCharCount = this.children[childIndex].charCount(); } // Case I: both start and end of range in same subtree if ((adjustedStart + rangeLength) <= childCharCount) { @@ -645,7 +642,7 @@ namespace ts.server { } let adjustedLength = rangeLength - (childCharCount - adjustedStart); childIndex++; - child = this.children[childIndex]; + const child = this.children[childIndex]; childCharCount = child.charCount(); while (adjustedLength > childCharCount) { if (this.execWalk(0, childCharCount, walkFns, childIndex, CharRangeSection.Mid)) { @@ -653,8 +650,7 @@ namespace ts.server { } adjustedLength -= childCharCount; childIndex++; - child = this.children[childIndex]; - childCharCount = child.charCount(); + childCharCount = this.children[childIndex].charCount(); } if (adjustedLength > 0) { if (this.execWalk(0, adjustedLength, walkFns, childIndex, CharRangeSection.End)) { From e5f482d33981c4887923ab81c02ab51b20448316 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Mon, 10 Jul 2017 15:16:34 -0700 Subject: [PATCH 14/50] Treat both object and Object the same --- src/compiler/checker.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ee0a3a90d7e..8eee81073f6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6892,6 +6892,7 @@ namespace ts { case "Null": return nullType; case "Object": + case "object": return anyType; case "Function": case "function": From 7ae4ff3b3d8764e26fbd7855fe254fca74c2828d Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Mon, 10 Jul 2017 15:24:03 -0700 Subject: [PATCH 15/50] Add tests and update baselines --- .../baselines/reference/checkJsdocTypeTag1.js | 20 ++++++++++++++++++- .../reference/checkJsdocTypeTag1.symbols | 12 +++++++++++ .../reference/checkJsdocTypeTag1.types | 14 +++++++++++++ tests/baselines/reference/jsdocTypeTag.js | 7 +++++++ .../baselines/reference/jsdocTypeTag.symbols | 11 ++++++++-- tests/baselines/reference/jsdocTypeTag.types | 7 +++++++ .../conformance/jsdoc/checkJsdocTypeTag1.ts | 12 ++++++++++- tests/cases/conformance/jsdoc/jsdocTypeTag.ts | 4 ++++ 8 files changed, 83 insertions(+), 4 deletions(-) diff --git a/tests/baselines/reference/checkJsdocTypeTag1.js b/tests/baselines/reference/checkJsdocTypeTag1.js index e4586643c22..664a02c6047 100644 --- a/tests/baselines/reference/checkJsdocTypeTag1.js +++ b/tests/baselines/reference/checkJsdocTypeTag1.js @@ -28,7 +28,17 @@ x1(0); /** @type {function (number): number} */ const x2 = (a) => a + 1; -x2(0); +x2(0); + +/** + * @type {object} + */ +var props = {}; + +/** + * @type {Object} + */ +var props = {}; //// [0.js] // @ts-check @@ -54,3 +64,11 @@ x1(0); /** @type {function (number): number} */ var x2 = function (a) { return a + 1; }; x2(0); +/** + * @type {object} + */ +var props = {}; +/** + * @type {Object} + */ +var props = {}; diff --git a/tests/baselines/reference/checkJsdocTypeTag1.symbols b/tests/baselines/reference/checkJsdocTypeTag1.symbols index fff51499bc4..cd337f8b556 100644 --- a/tests/baselines/reference/checkJsdocTypeTag1.symbols +++ b/tests/baselines/reference/checkJsdocTypeTag1.symbols @@ -58,3 +58,15 @@ const x2 = (a) => a + 1; x2(0); >x2 : Symbol(x2, Decl(0.js, 28, 5)) +/** + * @type {object} + */ +var props = {}; +>props : Symbol(props, Decl(0.js, 34, 3), Decl(0.js, 39, 3)) + +/** + * @type {Object} + */ +var props = {}; +>props : Symbol(props, Decl(0.js, 34, 3), Decl(0.js, 39, 3)) + diff --git a/tests/baselines/reference/checkJsdocTypeTag1.types b/tests/baselines/reference/checkJsdocTypeTag1.types index 898cf58806a..8e59a2cdb7c 100644 --- a/tests/baselines/reference/checkJsdocTypeTag1.types +++ b/tests/baselines/reference/checkJsdocTypeTag1.types @@ -86,3 +86,17 @@ x2(0); >x2 : (arg0: number) => number >0 : 0 +/** + * @type {object} + */ +var props = {}; +>props : any +>{} : {} + +/** + * @type {Object} + */ +var props = {}; +>props : any +>{} : {} + diff --git a/tests/baselines/reference/jsdocTypeTag.js b/tests/baselines/reference/jsdocTypeTag.js index ff92f0f0474..4789b1d26af 100644 --- a/tests/baselines/reference/jsdocTypeTag.js +++ b/tests/baselines/reference/jsdocTypeTag.js @@ -55,6 +55,9 @@ var nullable; /** @type {Object} */ var Obj; +/** @type {object} */ +var obj; + /** @type {Function} */ var Func; @@ -77,6 +80,7 @@ var P: Promise; var p: Promise; var nullable: number | null; var Obj: any; +var obj: any; var Func: Function; @@ -117,6 +121,8 @@ var p; var nullable; /** @type {Object} */ var Obj; +/** @type {object} */ +var obj; /** @type {Function} */ var Func; //// [b.js] @@ -138,4 +144,5 @@ var P; var p; var nullable; var Obj; +var obj; var Func; diff --git a/tests/baselines/reference/jsdocTypeTag.symbols b/tests/baselines/reference/jsdocTypeTag.symbols index 5931c1cfe53..77df5b119f4 100644 --- a/tests/baselines/reference/jsdocTypeTag.symbols +++ b/tests/baselines/reference/jsdocTypeTag.symbols @@ -71,9 +71,13 @@ var nullable; var Obj; >Obj : Symbol(Obj, Decl(a.js, 52, 3), Decl(b.ts, 17, 3)) +/** @type {object} */ +var obj; +>obj : Symbol(obj, Decl(a.js, 55, 3), Decl(b.ts, 18, 3)) + /** @type {Function} */ var Func; ->Func : Symbol(Func, Decl(a.js, 55, 3), Decl(b.ts, 18, 3)) +>Func : Symbol(Func, Decl(a.js, 58, 3), Decl(b.ts, 19, 3)) === tests/cases/conformance/jsdoc/b.ts === var S: string; @@ -132,7 +136,10 @@ var nullable: number | null; var Obj: any; >Obj : Symbol(Obj, Decl(a.js, 52, 3), Decl(b.ts, 17, 3)) +var obj: any; +>obj : Symbol(obj, Decl(a.js, 55, 3), Decl(b.ts, 18, 3)) + var Func: Function; ->Func : Symbol(Func, Decl(a.js, 55, 3), Decl(b.ts, 18, 3)) +>Func : Symbol(Func, Decl(a.js, 58, 3), Decl(b.ts, 19, 3)) >Function : Symbol(Function, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) diff --git a/tests/baselines/reference/jsdocTypeTag.types b/tests/baselines/reference/jsdocTypeTag.types index f3e7dfad634..9813a186a00 100644 --- a/tests/baselines/reference/jsdocTypeTag.types +++ b/tests/baselines/reference/jsdocTypeTag.types @@ -71,6 +71,10 @@ var nullable; var Obj; >Obj : any +/** @type {object} */ +var obj; +>obj : any + /** @type {Function} */ var Func; >Func : Function @@ -135,6 +139,9 @@ var nullable: number | null; var Obj: any; >Obj : any +var obj: any; +>obj : any + var Func: Function; >Func : Function >Function : Function diff --git a/tests/cases/conformance/jsdoc/checkJsdocTypeTag1.ts b/tests/cases/conformance/jsdoc/checkJsdocTypeTag1.ts index fbed9d83b22..4dc7b934571 100644 --- a/tests/cases/conformance/jsdoc/checkJsdocTypeTag1.ts +++ b/tests/cases/conformance/jsdoc/checkJsdocTypeTag1.ts @@ -31,4 +31,14 @@ x1(0); /** @type {function (number): number} */ const x2 = (a) => a + 1; -x2(0); \ No newline at end of file +x2(0); + +/** + * @type {object} + */ +var props = {}; + +/** + * @type {Object} + */ +var props = {}; \ No newline at end of file diff --git a/tests/cases/conformance/jsdoc/jsdocTypeTag.ts b/tests/cases/conformance/jsdoc/jsdocTypeTag.ts index d566c61e185..1c19771a3cd 100644 --- a/tests/cases/conformance/jsdoc/jsdocTypeTag.ts +++ b/tests/cases/conformance/jsdoc/jsdocTypeTag.ts @@ -57,6 +57,9 @@ var nullable; /** @type {Object} */ var Obj; +/** @type {object} */ +var obj; + /** @type {Function} */ var Func; @@ -79,4 +82,5 @@ var P: Promise; var p: Promise; var nullable: number | null; var Obj: any; +var obj: any; var Func: Function; From 911f1f88ee969a3f131ba237fcdde2d74fe2bee9 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Mon, 10 Jul 2017 19:27:25 -0700 Subject: [PATCH 16/50] Correct FileWatcherEventKind in server polling method Was sending Changed on Creation. Caveat: the tests will probably still fail intermittently with a race - this just fixes the deterministic failure. --- src/server/server.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/server/server.ts b/src/server/server.ts index 873f7fc613c..75e46d28f7a 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -526,12 +526,18 @@ namespace ts.server { if (err) { watchedFile.callback(watchedFile.fileName, FileWatcherEventKind.Changed); } - else if (watchedFile.mtime.getTime() !== stats.mtime.getTime()) { - watchedFile.mtime = stats.mtime; - const eventKind = watchedFile.mtime.getTime() === 0 - ? FileWatcherEventKind.Deleted - : FileWatcherEventKind.Changed; - watchedFile.callback(watchedFile.fileName, eventKind); + else { + const oldTime = watchedFile.mtime.getTime(); + const newTime = stats.mtime.getTime(); + if (oldTime !== newTime) { + watchedFile.mtime = stats.mtime; + const eventKind = oldTime === 0 + ? FileWatcherEventKind.Created + : newTime === 0 + ? FileWatcherEventKind.Deleted + : FileWatcherEventKind.Changed; + watchedFile.callback(watchedFile.fileName, eventKind); + } } }); } From aa2d1008bf573fccdcc02335b11f9fb782888fcb Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 11 Jul 2017 07:23:32 -0700 Subject: [PATCH 17/50] Completion for default export should be '.default' (#16742) * Completion for default export should be '.default' * Don't include empty string in name table * getSymbolsInScope() should return local symbols, not exported symbols * Fix bug: getSymbolAtLocation should work for local symbol too --- src/compiler/checker.ts | 9 +- src/compiler/utilities.ts | 5 + src/harness/fourslash.ts | 24 +++++ src/services/completions.ts | 93 ++++++++----------- src/services/services.ts | 59 +++++------- src/services/symbolDisplay.ts | 12 +-- .../fourslash/commentsExternalModules.ts | 2 +- tests/cases/fourslash/commentsModules.ts | 2 +- .../completionListForUnicodeEscapeName.ts | 17 ++-- ...ListInScope_doesNotIncludeAugmentations.ts | 13 +++ .../completionListInvalidMemberNames.ts | 3 +- .../fourslash/completionListOnAliases2.ts | 18 ++-- ...pletionListWithModulesInsideModuleScope.ts | 57 ++++++------ .../fourslash/completionsDefaultExport.ts | 11 +++ .../cases/fourslash/exportDefaultFunction.ts | 2 +- .../findAllRefsForDefaultExport02.ts | 5 +- .../findAllRefsForDefaultExport08.ts | 4 +- tests/cases/fourslash/fourslash.ts | 2 + .../quickInfoOnNarrowedTypeInModule.ts | 8 +- .../typeOfSymbol_localSymbolOfExport.ts | 12 +++ 20 files changed, 200 insertions(+), 158 deletions(-) create mode 100644 tests/cases/fourslash/completionListInScope_doesNotIncludeAugmentations.ts create mode 100644 tests/cases/fourslash/completionsDefaultExport.ts create mode 100644 tests/cases/fourslash/typeOfSymbol_localSymbolOfExport.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ee0a3a90d7e..bf03958e4ff 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11875,6 +11875,8 @@ namespace ts { } function getTypeOfSymbolAtLocation(symbol: Symbol, location: Node) { + symbol = symbol.exportSymbol || symbol; + // If we have an identifier or a property access at the given location, if the location is // an dotted name expression, and if the location is not an assignment target, obtain the type // of the expression (which will reflect control flow analysis). If the expression indeed @@ -22281,11 +22283,6 @@ namespace ts { } switch (location.kind) { - case SyntaxKind.SourceFile: - if (!isExternalOrCommonJsModule(location)) { - break; - } - // falls through case SyntaxKind.ModuleDeclaration: copySymbols(getSymbolOfNode(location).exports, meaning & SymbolFlags.ModuleMember); break; @@ -22337,7 +22334,7 @@ namespace ts { * @param meaning meaning of symbol to filter by before adding to symbol table */ function copySymbol(symbol: Symbol, meaning: SymbolFlags): void { - if (symbol.flags & meaning) { + if (getCombinedLocalAndExportSymbolFlags(symbol) & meaning) { const id = symbol.name; // We will copy all symbol regardless of its reserved name because // symbolsToArray will check whether the key is a reserved name and diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 04479e6f9cd..e9cff262988 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3598,6 +3598,11 @@ namespace ts { } return previous[previous.length - 1]; } + + /** See comment on `declareModuleMember` in `binder.ts`. */ + export function getCombinedLocalAndExportSymbolFlags(symbol: Symbol): SymbolFlags { + return symbol.exportSymbol ? symbol.exportSymbol.flags | symbol.flags : symbol.flags; + } } namespace ts { diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 587bed813f1..411f6762ba3 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -949,6 +949,22 @@ namespace FourSlash { this.verifySymbol(symbol, declarationRanges); } + public symbolsInScope(range: Range): ts.Symbol[] { + const node = this.goToAndGetNode(range); + return this.getChecker().getSymbolsInScope(node, ts.SymbolFlags.Value | ts.SymbolFlags.Type | ts.SymbolFlags.Namespace); + } + + public verifyTypeOfSymbolAtLocation(range: Range, symbol: ts.Symbol, expected: string): void { + const node = this.goToAndGetNode(range); + const checker = this.getChecker(); + const type = checker.getTypeOfSymbolAtLocation(symbol, node); + + const actual = checker.typeToString(type); + if (actual !== expected) { + this.raiseError(`Expected: '${expected}', actual: '${actual}'`); + } + } + private verifyReferencesAre(expectedReferences: Range[]) { const actualReferences = this.getReferencesAtCaret() || []; @@ -3426,6 +3442,10 @@ namespace FourSlashInterface { public markerByName(s: string): FourSlash.Marker { return this.state.getMarkerByName(s); } + + public symbolsInScope(range: FourSlash.Range): ts.Symbol[] { + return this.state.symbolsInScope(range); + } } export class GoTo { @@ -3694,6 +3714,10 @@ namespace FourSlashInterface { this.state.verifySymbolAtLocation(startRange, declarationRanges); } + public typeOfSymbolAtLocation(range: FourSlash.Range, symbol: ts.Symbol, expected: string) { + this.state.verifyTypeOfSymbolAtLocation(range, symbol, expected); + } + public referencesOf(start: FourSlash.Range, references: FourSlash.Range[]) { this.state.verifyReferencesOf(start, references); } diff --git a/src/services/completions.ts b/src/services/completions.ts index 94e8779d1d7..9a43e10ad97 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -61,7 +61,7 @@ namespace ts.Completions { } else { if ((!symbols || symbols.length === 0) && keywordFilters === KeywordCompletionFilters.None) { - return undefined; + return undefined; } getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log); @@ -112,7 +112,7 @@ namespace ts.Completions { // Try to get a valid display name for this symbol, if we could not find one, then ignore it. // We would like to only show things that can be added after a dot, so for instance numeric properties can // not be accessed with a dot (a.1 <- invalid) - const displayName = getCompletionEntryDisplayNameForSymbol(typeChecker, symbol, target, performCharacterChecks, location); + const displayName = getCompletionEntryDisplayNameForSymbol(symbol, target, performCharacterChecks); if (!displayName) { return undefined; } @@ -307,7 +307,7 @@ namespace ts.Completions { // We don't need to perform character checks here because we're only comparing the // name against 'entryName' (which is known to be good), not building a new // completion entry. - const symbol = forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(typeChecker, s, compilerOptions.target, /*performCharacterChecks*/ false, location) === entryName ? s : undefined); + const symbol = forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(s, compilerOptions.target, /*performCharacterChecks*/ false) === entryName ? s : undefined); if (symbol) { const { displayParts, documentation, symbolKind, tags } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, sourceFile, location, location, SemanticMeaning.All); @@ -341,20 +341,14 @@ namespace ts.Completions { return undefined; } - export function getCompletionEntrySymbol(typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number, entryName: string): Symbol { + export function getCompletionEntrySymbol(typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number, entryName: string): Symbol | undefined { // Compute all the completion symbols again. const completionData = getCompletionData(typeChecker, log, sourceFile, position); - if (completionData) { - const { symbols, location } = completionData; - - // Find the symbol with the matching entry name. - // We don't need to perform character checks here because we're only comparing the - // name against 'entryName' (which is known to be good), not building a new - // completion entry. - return forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(typeChecker, s, compilerOptions.target, /*performCharacterChecks*/ false, location) === entryName ? s : undefined); - } - - return undefined; + // Find the symbol with the matching entry name. + // We don't need to perform character checks here because we're only comparing the + // name against 'entryName' (which is known to be good), not building a new + // completion entry. + return completionData && forEach(completionData.symbols, s => getCompletionEntryDisplayNameForSymbol(s, compilerOptions.target, /*performCharacterChecks*/ false) === entryName ? s : undefined); } interface CompletionData { @@ -369,7 +363,7 @@ namespace ts.Completions { } type Request = { kind: "JsDocTagName" } | { kind: "JsDocTag" } | { kind: "JsDocParameterName", tag: JSDocParameterTag }; - function getCompletionData(typeChecker: TypeChecker, log: (message: string) => void, sourceFile: SourceFile, position: number): CompletionData { + function getCompletionData(typeChecker: TypeChecker, log: (message: string) => void, sourceFile: SourceFile, position: number): CompletionData | undefined { const isJavaScriptFile = isSourceFileJavaScript(sourceFile); let request: Request | undefined; @@ -615,7 +609,7 @@ namespace ts.Completions { // Extract module or enum members const exportedSymbols = typeChecker.getExportsOfModule(symbol); const isValidValueAccess = (symbol: Symbol) => typeChecker.isValidPropertyAccess((node.parent), symbol.getUnescapedName()); - const isValidTypeAccess = (symbol: Symbol) => symbolCanbeReferencedAtTypeLocation(symbol); + const isValidTypeAccess = (symbol: Symbol) => symbolCanBeReferencedAtTypeLocation(symbol); const isValidAccess = isRhsOfImportDeclaration ? // Any kind is allowed when dotting off namespace in internal import equals declaration (symbol: Symbol) => isValidTypeAccess(symbol) || isValidValueAccess(symbol) : @@ -630,7 +624,7 @@ namespace ts.Completions { if (!isTypeLocation) { const type = typeChecker.getTypeAtLocation(node); - addTypeProperties(type); + if (type) addTypeProperties(type); } } @@ -642,17 +636,17 @@ namespace ts.Completions { symbols.push(symbol); } } + } - if (isJavaScriptFile && type.flags & TypeFlags.Union) { - // In javascript files, for union types, we don't just get the members that - // the individual types have in common, we also include all the members that - // each individual type has. This is because we're going to add all identifiers - // anyways. So we might as well elevate the members that were at least part - // of the individual types to a higher status since we know what they are. - const unionType = type; - for (const elementType of unionType.types) { - addTypeProperties(elementType); - } + if (isJavaScriptFile && type.flags & TypeFlags.Union) { + // In javascript files, for union types, we don't just get the members that + // the individual types have in common, we also include all the members that + // each individual type has. This is because we're going to add all identifiers + // anyways. So we might as well elevate the members that were at least part + // of the individual types to a higher status since we know what they are. + const unionType = type; + for (const elementType of unionType.types) { + addTypeProperties(elementType); } } } @@ -777,12 +771,12 @@ namespace ts.Completions { (!isContextTokenValueLocation(contextToken) && (isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken)))) { // Its a type, but you can reach it by namespace.type as well - return symbolCanbeReferencedAtTypeLocation(symbol); + return symbolCanBeReferencedAtTypeLocation(symbol); } } // expressions are value space (which includes the value namespaces) - return !!(symbol.flags & SymbolFlags.Value); + return !!(getCombinedLocalAndExportSymbolFlags(symbol) & SymbolFlags.Value); }); } @@ -812,7 +806,9 @@ namespace ts.Completions { } } - function symbolCanbeReferencedAtTypeLocation(symbol: Symbol): boolean { + function symbolCanBeReferencedAtTypeLocation(symbol: Symbol): boolean { + symbol = symbol.exportSymbol || symbol; + // This is an alias, follow what it aliases if (symbol && symbol.flags & SymbolFlags.Alias) { symbol = typeChecker.getAliasedSymbol(symbol); @@ -826,7 +822,7 @@ namespace ts.Completions { const exportedSymbols = typeChecker.getExportsOfModule(symbol); // If the exported symbols contains type, // symbol can be referenced at locations where type is allowed - return forEach(exportedSymbols, symbolCanbeReferencedAtTypeLocation); + return forEach(exportedSymbols, symbolCanBeReferencedAtTypeLocation); } } @@ -1598,22 +1594,23 @@ namespace ts.Completions { /** * Get the name to be display in completion from a given symbol. * - * @return undefined if the name is of external module otherwise a name with striped of any quote + * @return undefined if the name is of external module */ - function getCompletionEntryDisplayNameForSymbol(typeChecker: TypeChecker, symbol: Symbol, target: ScriptTarget, performCharacterChecks: boolean, location: Node): string { - const displayName: string = getDeclaredName(typeChecker, symbol, location); + function getCompletionEntryDisplayNameForSymbol(symbol: Symbol, target: ScriptTarget, performCharacterChecks: boolean): string | undefined { + const name = symbol.getUnescapedName(); + if (!name) return undefined; - if (displayName) { - const firstCharCode = displayName.charCodeAt(0); - // First check of the displayName is not external module; if it is an external module, it is not valid entry - if ((symbol.flags & SymbolFlags.Namespace) && (firstCharCode === CharacterCodes.singleQuote || firstCharCode === CharacterCodes.doubleQuote)) { + // First check of the displayName is not external module; if it is an external module, it is not valid entry + if (symbol.flags & SymbolFlags.Namespace) { + const firstCharCode = name.charCodeAt(0); + if (firstCharCode === CharacterCodes.singleQuote || firstCharCode === CharacterCodes.doubleQuote) { // If the symbol is external module, don't show it in the completion list // (i.e declare module "http" { const x; } | // <= request completion here, "http" should not be there) return undefined; } } - return getCompletionEntryDisplayName(displayName, target, performCharacterChecks); + return getCompletionEntryDisplayName(name, target, performCharacterChecks); } /** @@ -1621,24 +1618,12 @@ namespace ts.Completions { * and checking whether the name is valid identifier name. */ function getCompletionEntryDisplayName(name: string, target: ScriptTarget, performCharacterChecks: boolean): string { - if (!name) { - return undefined; - } - - name = stripQuotes(name); - - if (!name) { - return undefined; - } - // If the user entered name for the symbol was quoted, removing the quotes is not enough, as the name could be an // invalid identifier name. We need to check if whatever was inside the quotes is actually a valid identifier name. // e.g "b a" is valid quoted name but when we strip off the quotes, it is invalid. // We, thus, need to check if whatever was inside the quotes is actually a valid identifier name. - if (performCharacterChecks) { - if (!isIdentifierText(name, target)) { - return undefined; - } + if (performCharacterChecks && !isIdentifierText(name, target)) { + return undefined; } return name; diff --git a/src/services/services.ts b/src/services/services.ts index be17a35d3ed..d89051ee666 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2063,42 +2063,33 @@ namespace ts { } function initializeNameTable(sourceFile: SourceFile): void { - const nameTable = createUnderscoreEscapedMap(); - - walk(sourceFile); - sourceFile.nameTable = nameTable; - - function walk(node: Node) { - switch (node.kind) { - case SyntaxKind.Identifier: - setNameTable((node).text, node); - break; - case SyntaxKind.StringLiteral: - case SyntaxKind.NumericLiteral: - // We want to store any numbers/strings if they were a name that could be - // related to a declaration. So, if we have 'import x = require("something")' - // then we want 'something' to be in the name table. Similarly, if we have - // "a['propname']" then we want to store "propname" in the name table. - if (isDeclarationName(node) || - node.parent.kind === SyntaxKind.ExternalModuleReference || - isArgumentOfElementAccessExpression(node) || - isLiteralComputedPropertyDeclarationName(node)) { - setNameTable(getEscapedTextOfIdentifierOrLiteral((node)), node); - } - break; - default: - forEachChild(node, walk); - if (node.jsDoc) { - for (const jsDoc of node.jsDoc) { - forEachChild(jsDoc, walk); - } - } + const nameTable = sourceFile.nameTable = createUnderscoreEscapedMap(); + sourceFile.forEachChild(function walk(node) { + if ((isIdentifier(node) || isStringOrNumericLiteral(node) && literalIsName(node)) && node.text) { + const text = getEscapedTextOfIdentifierOrLiteral(node); + nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1); } - } - function setNameTable(text: __String, node: ts.Node): void { - nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1); - } + forEachChild(node, walk); + if (node.jsDoc) { + for (const jsDoc of node.jsDoc) { + forEachChild(jsDoc, walk); + } + } + }); + } + + /** + * We want to store any numbers/strings if they were a name that could be + * related to a declaration. So, if we have 'import x = require("something")' + * then we want 'something' to be in the name table. Similarly, if we have + * "a['propname']" then we want to store "propname" in the name table. + */ + function literalIsName(node: ts.StringLiteral | ts.NumericLiteral): boolean { + return isDeclarationName(node) || + node.parent.kind === SyntaxKind.ExternalModuleReference || + isArgumentOfElementAccessExpression(node) || + isLiteralComputedPropertyDeclarationName(node); } function isObjectLiteralElement(node: Node): node is ObjectLiteralElement { diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index b599839cfd3..0cb3916c9c5 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -2,7 +2,7 @@ namespace ts.SymbolDisplay { // TODO(drosen): use contextual SemanticMeaning. export function getSymbolKind(typeChecker: TypeChecker, symbol: Symbol, location: Node): ScriptElementKind { - const { flags } = symbol; + const flags = getCombinedLocalAndExportSymbolFlags(symbol); if (flags & SymbolFlags.Class) { return getDeclarationOfKind(symbol, SyntaxKind.ClassExpression) ? @@ -34,7 +34,7 @@ namespace ts.SymbolDisplay { if (location.kind === SyntaxKind.ThisKeyword && isExpression(location)) { return ScriptElementKind.parameterElement; } - const { flags } = symbol; + const flags = getCombinedLocalAndExportSymbolFlags(symbol); if (flags & SymbolFlags.Variable) { if (isFirstDeclarationOfSymbolParameter(symbol)) { return ScriptElementKind.parameterElement; @@ -96,7 +96,7 @@ namespace ts.SymbolDisplay { const displayParts: SymbolDisplayPart[] = []; let documentation: SymbolDisplayPart[]; let tags: JSDocTagInfo[]; - const symbolFlags = symbol.flags; + const symbolFlags = ts.getCombinedLocalAndExportSymbolFlags(symbol); let symbolKind = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location); let hasAddedSymbolInfo: boolean; const isThisExpression = location.kind === SyntaxKind.ThisKeyword && isExpression(location); @@ -110,7 +110,7 @@ namespace ts.SymbolDisplay { } let signature: Signature; - type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol, location); + type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol.exportSymbol || symbol, location); if (type) { if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) { const right = (location.parent).name; @@ -198,7 +198,7 @@ namespace ts.SymbolDisplay { hasAddedSymbolInfo = true; } } - else if ((isNameOfFunctionDeclaration(location) && !(symbol.flags & SymbolFlags.Accessor)) || // name of function declaration + else if ((isNameOfFunctionDeclaration(location) && !(symbolFlags & SymbolFlags.Accessor)) || // name of function declaration (location.kind === SyntaxKind.ConstructorKeyword && location.parent.kind === SyntaxKind.Constructor)) { // At constructor keyword of constructor declaration // get the signature from the declaration and write it const functionDeclaration = location.parent; @@ -429,7 +429,7 @@ namespace ts.SymbolDisplay { if (!documentation) { documentation = symbol.getDocumentationComment(); tags = symbol.getJsDocTags(); - if (documentation.length === 0 && symbol.flags & SymbolFlags.Property) { + if (documentation.length === 0 && symbolFlags & SymbolFlags.Property) { // For some special property access expressions like `exports.foo = foo` or `module.exports.foo = foo` // there documentation comments might be attached to the right hand side symbol of their declarations. // The pattern of such special property access is that the parent symbol is the symbol of the file. diff --git a/tests/cases/fourslash/commentsExternalModules.ts b/tests/cases/fourslash/commentsExternalModules.ts index 4f1a82bc0ce..582c6a31357 100644 --- a/tests/cases/fourslash/commentsExternalModules.ts +++ b/tests/cases/fourslash/commentsExternalModules.ts @@ -35,7 +35,7 @@ goTo.file("commentsExternalModules_file0.ts"); verify.quickInfoAt("1", "namespace m1", "Namespace comment"); goTo.marker('2'); -verify.completionListContains("b", "var m1.b: number", "b's comment"); +verify.completionListContains("b", "var b: number", "b's comment"); verify.completionListContains("foo", "function foo(): number", "foo's comment"); goTo.marker('3'); diff --git a/tests/cases/fourslash/commentsModules.ts b/tests/cases/fourslash/commentsModules.ts index c0b8ccc5731..0205b144f16 100644 --- a/tests/cases/fourslash/commentsModules.ts +++ b/tests/cases/fourslash/commentsModules.ts @@ -99,7 +99,7 @@ verify.quickInfoAt("1", "namespace m1", "Namespace comment"); goTo.marker('2'); -verify.completionListContains("b", "var m1.b: number", "b's comment"); +verify.completionListContains("b", "var b: number", "b's comment"); verify.completionListContains("foo", "function foo(): number", "foo's comment"); goTo.marker('3'); diff --git a/tests/cases/fourslash/completionListForUnicodeEscapeName.ts b/tests/cases/fourslash/completionListForUnicodeEscapeName.ts index afafd18a58b..9ada3e35961 100644 --- a/tests/cases/fourslash/completionListForUnicodeEscapeName.ts +++ b/tests/cases/fourslash/completionListForUnicodeEscapeName.ts @@ -6,8 +6,8 @@ /////*3*/ goTo.marker("0"); -verify.not.completionListContains("B"); -verify.not.completionListContains("\u0042"); +verify.completionListContains("B"); +verify.completionListContains("\u0042"); goTo.marker("2"); verify.not.completionListContains("C"); @@ -18,10 +18,9 @@ verify.not.completionListContains("A"); verify.not.completionListContains("\u0041"); goTo.marker("3"); -verify.not.completionListContains("B"); -verify.not.completionListContains("\u0042"); -verify.not.completionListContains("A"); -verify.not.completionListContains("\u0041"); -verify.not.completionListContains("C"); -verify.not.completionListContains("\u0043"); - +verify.completionListContains("B"); +verify.completionListContains("\u0042"); +verify.completionListContains("A"); +verify.completionListContains("\u0041"); +verify.completionListContains("C"); +verify.completionListContains("\u0043"); diff --git a/tests/cases/fourslash/completionListInScope_doesNotIncludeAugmentations.ts b/tests/cases/fourslash/completionListInScope_doesNotIncludeAugmentations.ts new file mode 100644 index 00000000000..de70890c4c6 --- /dev/null +++ b/tests/cases/fourslash/completionListInScope_doesNotIncludeAugmentations.ts @@ -0,0 +1,13 @@ +/// + +// @Filename: /a.ts +////import * as self from "./a"; +//// +////declare module "a" { +//// export const a: number; +////} +//// +/////**/ + +goTo.marker(); +verify.not.completionListContains("a"); diff --git a/tests/cases/fourslash/completionListInvalidMemberNames.ts b/tests/cases/fourslash/completionListInvalidMemberNames.ts index 95abe70415c..e0a65bfca4d 100644 --- a/tests/cases/fourslash/completionListInvalidMemberNames.ts +++ b/tests/cases/fourslash/completionListInvalidMemberNames.ts @@ -19,6 +19,7 @@ verify.completionListContains("bar"); verify.completionListContains("break"); verify.completionListContains("any"); verify.completionListContains("$"); +verify.completionListContains("b"); // Nothing else should show up -verify.completionListCount(4); +verify.completionListCount(5); diff --git a/tests/cases/fourslash/completionListOnAliases2.ts b/tests/cases/fourslash/completionListOnAliases2.ts index 63f895f276e..5933fbe89d4 100644 --- a/tests/cases/fourslash/completionListOnAliases2.ts +++ b/tests/cases/fourslash/completionListOnAliases2.ts @@ -41,19 +41,16 @@ function getVerify(isTypeLocation?: boolean) { verifyValueOrType: verify }; } -function typeLocationVerify(valueMarker: string, verify: (typeMarker: string) => void) { - verify(valueMarker + "Type"); - return valueMarker; -} function verifyModuleM(marker: string) { - const isTypeLocation = marker.indexOf("Type") !== -1; - const { verifyValue, verifyType, verifyValueOrType } = getVerify(isTypeLocation); - if (!isTypeLocation) { - marker = typeLocationVerify(marker, verifyModuleM); - } + verifyModuleMWorker(marker, /*isTypeLocation*/ false); + verifyModuleMWorker(`${marker}Type`, /*isTypeLocation*/ true); +} + +function verifyModuleMWorker(marker: string, isTypeLocation: boolean): void { goTo.marker(marker); + const { verifyValue, verifyType, verifyValueOrType } = getVerify(isTypeLocation); verifyType.completionListContains("I"); verifyValueOrType.completionListContains("C"); verifyValueOrType.completionListContains("E"); @@ -63,8 +60,9 @@ function verifyModuleM(marker: string) { verifyValueOrType.completionListContains("A"); } - // Module m +goTo.marker("1"); +verify.completionListContains("A"); verifyModuleM("1"); // Class C diff --git a/tests/cases/fourslash/completionListWithModulesInsideModuleScope.ts b/tests/cases/fourslash/completionListWithModulesInsideModuleScope.ts index 5d950a5d620..cb6bf283b69 100644 --- a/tests/cases/fourslash/completionListWithModulesInsideModuleScope.ts +++ b/tests/cases/fourslash/completionListWithModulesInsideModuleScope.ts @@ -232,6 +232,7 @@ interface GotoMarkVerifyOptions { isClassScope?: boolean; isTypeLocation?: boolean; + insideMod1?: boolean; } function getVerify(isTypeLocation: boolean) { @@ -243,30 +244,30 @@ function getVerify(isTypeLocation: boolean) { }; } -function goToMarkAndGeneralVerify(marker: string, { isClassScope, isTypeLocation }: GotoMarkVerifyOptions = {}) -{ +function goToMarkAndGeneralVerify(marker: string, { isClassScope, isTypeLocation, insideMod1 }: GotoMarkVerifyOptions = {}) { goTo.marker(marker); + const mod1Dot = insideMod1 ? "" : "mod1."; const verifyValueInModule = isClassScope || isTypeLocation ? verify.not : verify; const verifyValueOrTypeInModule = isClassScope ? verify.not : verify; const verifyTypeInModule = isTypeLocation ? verify : verify.not; verifyValueInModule.completionListContains('mod1var', 'var mod1var: number'); verifyValueInModule.completionListContains('mod1fn', 'function mod1fn(): void'); - verifyValueInModule.completionListContains('mod1evar', 'var mod1.mod1evar: number'); - verifyValueInModule.completionListContains('mod1efn', 'function mod1.mod1efn(): void'); - verifyValueInModule.completionListContains('mod1eexvar', 'var mod1.mod1eexvar: number'); + verifyValueInModule.completionListContains('mod1evar', `var ${mod1Dot}mod1evar: number`); + verifyValueInModule.completionListContains('mod1efn', `function ${mod1Dot}mod1efn(): void`); + verifyValueInModule.completionListContains('mod1eexvar', `var mod1.mod1eexvar: number`); verifyValueInModule.completionListContains('mod3', 'namespace mod3'); verifyValueInModule.completionListContains('shwvar', 'var shwvar: number'); verifyValueInModule.completionListContains('shwfn', 'function shwfn(): void'); verifyTypeInModule.completionListContains('mod1int', 'interface mod1int'); - verifyTypeInModule.completionListContains('mod1eint', 'interface mod1.mod1eint'); + verifyTypeInModule.completionListContains('mod1eint', `interface ${mod1Dot}mod1eint`); verifyTypeInModule.completionListContains('shwint', 'interface shwint'); verifyValueOrTypeInModule.completionListContains('mod1cls', 'class mod1cls'); verifyValueOrTypeInModule.completionListContains('mod1mod', 'namespace mod1mod'); - verifyValueOrTypeInModule.completionListContains('mod1ecls', 'class mod1.mod1ecls'); - verifyValueOrTypeInModule.completionListContains('mod1emod', 'namespace mod1.mod1emod'); + verifyValueOrTypeInModule.completionListContains('mod1ecls', `class ${mod1Dot}mod1ecls`); + verifyValueOrTypeInModule.completionListContains('mod1emod', `namespace ${mod1Dot}mod1emod`); verifyValueOrTypeInModule.completionListContains('mod2', 'namespace mod2'); verifyValueOrTypeInModule.completionListContains('shwcls', 'class shwcls'); @@ -295,12 +296,12 @@ function goToMarkAndGeneralVerify(marker: string, { isClassScope, isTypeLocation } // from mod1 -goToMarkAndGeneralVerify('mod1'); +goToMarkAndGeneralVerify('mod1', { insideMod1: true }); // from mod1 in type position -goToMarkAndGeneralVerify('mod1Type', { isTypeLocation: true }); +goToMarkAndGeneralVerify('mod1Type', { isTypeLocation: true, insideMod1: true }); // from function in mod1 -goToMarkAndGeneralVerify('function'); +goToMarkAndGeneralVerify('function', { insideMod1: true }); verify.completionListContains('bar', '(local var) bar: number'); verify.completionListContains('foob', '(local function) foob(): void'); @@ -310,34 +311,34 @@ goToMarkAndGeneralVerify('class', { isClassScope: true }); //verify.not.completionListContains('ceVar'); // from interface in mod1 -goToMarkAndGeneralVerify('interface'); +goToMarkAndGeneralVerify('interface', { insideMod1: true }); // from namespace in mod1 verifyNamespaceInMod1('namespace'); verifyNamespaceInMod1('namespaceType', /*isTypeLocation*/ true); function verifyNamespaceInMod1(marker: string, isTypeLocation?: boolean) { - goToMarkAndGeneralVerify(marker, { isTypeLocation }); + goToMarkAndGeneralVerify(marker, { isTypeLocation, insideMod1: true }); const { verifyValue, verifyType, verifyValueOrType, verifyNotValueOrType } = getVerify(isTypeLocation); verifyValue.completionListContains('m1X', 'var m1X: number'); verifyValue.completionListContains('m1Func', 'function m1Func(): void'); - verifyValue.completionListContains('m1eX', 'var mod1mod.m1eX: number'); - verifyValue.completionListContains('m1eFunc', 'function mod1mod.m1eFunc(): void'); + verifyValue.completionListContains('m1eX', 'var m1eX: number'); + verifyValue.completionListContains('m1eFunc', 'function m1eFunc(): void'); verifyType.completionListContains('m1Int', 'interface m1Int'); - verifyType.completionListContains('m1eInt', 'interface mod1mod.m1eInt'); + verifyType.completionListContains('m1eInt', 'interface m1eInt'); verifyValueOrType.completionListContains('m1Class', 'class m1Class'); - verifyValueOrType.completionListContains('m1eClass', 'class mod1mod.m1eClass'); + verifyValueOrType.completionListContains('m1eClass', 'class m1eClass'); verifyNotValueOrType.completionListContains('m1Mod', 'namespace m1Mod'); - verifyNotValueOrType.completionListContains('m1eMod', 'namespace mod1mod.m1eMod'); + verifyNotValueOrType.completionListContains('m1eMod', 'namespace m1eMod'); } // from exported function in mod1 -goToMarkAndGeneralVerify('exportedFunction'); +goToMarkAndGeneralVerify('exportedFunction', { insideMod1: true }); verify.completionListContains('bar', '(local var) bar: number'); verify.completionListContains('foob', '(local function) foob(): void'); @@ -347,27 +348,27 @@ verify.not.completionListContains('ceFunc'); verify.not.completionListContains('ceVar'); // from exported interface in mod1 -goToMarkAndGeneralVerify('exportedInterface'); +goToMarkAndGeneralVerify('exportedInterface', { insideMod1: true }); // from exported namespace in mod1 verifyExportedNamespace('exportedNamespace'); verifyExportedNamespace('exportedNamespaceType', /*isTypeLocation*/ true); function verifyExportedNamespace(marker: string, isTypeLocation?: boolean) { - goToMarkAndGeneralVerify(marker, { isTypeLocation }); + goToMarkAndGeneralVerify(marker, { isTypeLocation, insideMod1: true }); const { verifyValue, verifyType, verifyValueOrType, verifyNotValueOrType } = getVerify(isTypeLocation); verifyValue.completionListContains('mX', 'var mX: number'); verifyValue.completionListContains('mFunc', 'function mFunc(): void'); - verifyValue.completionListContains('meX', 'var mod1.mod1emod.meX: number'); - verifyValue.completionListContains('meFunc', 'function mod1.mod1emod.meFunc(): void'); + verifyValue.completionListContains('meX', 'var meX: number'); + verifyValue.completionListContains('meFunc', 'function meFunc(): void'); verifyType.completionListContains('mInt', 'interface mInt'); - verifyType.completionListContains('meInt', 'interface mod1.mod1emod.meInt'); + verifyType.completionListContains('meInt', 'interface meInt'); verifyValueOrType.completionListContains('mClass', 'class mClass'); - verifyValueOrType.completionListContains('meClass', 'class mod1.mod1emod.meClass'); + verifyValueOrType.completionListContains('meClass', 'class meClass'); verifyNotValueOrType.completionListContains('mMod', 'namespace mMod'); - verifyNotValueOrType.completionListContains('meMod', 'namespace mod1.mod1emod.meMod'); + verifyNotValueOrType.completionListContains('meMod', 'namespace meMod'); } // from extended namespace @@ -380,7 +381,7 @@ function verifyExtendedNamespace(marker: string, isTypeLocation?: boolean) { verifyValue.completionListContains('mod1evar', 'var mod1.mod1evar: number'); verifyValue.completionListContains('mod1efn', 'function mod1.mod1efn(): void'); - verifyValue.completionListContains('mod1eexvar', 'var mod1.mod1eexvar: number'); + verifyValue.completionListContains('mod1eexvar', 'var mod1eexvar: number'); verifyValue.completionListContains('mod3', 'namespace mod3'); verifyValue.completionListContains('shwvar', 'var shwvar: number'); verifyValue.completionListContains('shwfn', 'function shwfn(): void'); @@ -415,4 +416,4 @@ function verifyExtendedNamespace(marker: string, isTypeLocation?: boolean) { verify.not.completionListContains('sivar'); verify.not.completionListContains('sifn'); verify.not.completionListContains('mod2eexvar'); -} \ No newline at end of file +} diff --git a/tests/cases/fourslash/completionsDefaultExport.ts b/tests/cases/fourslash/completionsDefaultExport.ts new file mode 100644 index 00000000000..82bcefac403 --- /dev/null +++ b/tests/cases/fourslash/completionsDefaultExport.ts @@ -0,0 +1,11 @@ +/// + +// @Filename: /a.ts +////export default function f() {} + +// @Filename: /b.ts +////import * as a from "./a"; +////a./**/; + +goTo.marker(); +verify.completionListContains("default", "function f(): void"); diff --git a/tests/cases/fourslash/exportDefaultFunction.ts b/tests/cases/fourslash/exportDefaultFunction.ts index 859d9641acd..42ddced6994 100644 --- a/tests/cases/fourslash/exportDefaultFunction.ts +++ b/tests/cases/fourslash/exportDefaultFunction.ts @@ -9,4 +9,4 @@ goTo.marker('1'); verify.completionListContains("func", "function func(): void", /*documentation*/ undefined, "function"); goTo.marker('2'); -verify.completionListContains("func", "function func(): void", /*documentation*/ undefined, "function"); \ No newline at end of file +verify.completionListContains("func", "function func(): void", /*documentation*/ undefined, "function"); diff --git a/tests/cases/fourslash/findAllRefsForDefaultExport02.ts b/tests/cases/fourslash/findAllRefsForDefaultExport02.ts index 558bbd2de85..6ad8b29b5ce 100644 --- a/tests/cases/fourslash/findAllRefsForDefaultExport02.ts +++ b/tests/cases/fourslash/findAllRefsForDefaultExport02.ts @@ -15,8 +15,9 @@ const ranges = test.ranges(); const [r0, r1, r2, r3, r4] = ranges; const fnRanges = [r0, r1, r2, r3]; -verify.singleReferenceGroup("function DefaultExportedFunction(): () => typeof DefaultExportedFunction", fnRanges); +const fn = "function DefaultExportedFunction(): () => typeof DefaultExportedFunction"; +verify.singleReferenceGroup(fn, fnRanges); // The namespace and function do not merge, // so the namespace should be all alone. -verify.singleReferenceGroup("namespace DefaultExportedFunction", [r4]); +verify.singleReferenceGroup(`namespace DefaultExportedFunction\n${fn}`, [r4]); diff --git a/tests/cases/fourslash/findAllRefsForDefaultExport08.ts b/tests/cases/fourslash/findAllRefsForDefaultExport08.ts index b29060f6099..5583ba34c95 100644 --- a/tests/cases/fourslash/findAllRefsForDefaultExport08.ts +++ b/tests/cases/fourslash/findAllRefsForDefaultExport08.ts @@ -10,6 +10,8 @@ ////namespace [|{| "isWriteAccess": true, "isDefinition": true |}DefaultExportedClass|] { ////} +verify.noErrors(); + // The namespace and class do not merge, // so the namespace should be all alone. -verify.singleReferenceGroup("namespace DefaultExportedClass"); +verify.singleReferenceGroup("class DefaultExportedClass\nnamespace DefaultExportedClass"); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 767701a2af6..59cb881ab7b 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -116,6 +116,7 @@ declare namespace FourSlashInterface { ranges(): Range[]; rangesByText(): ts.Map; markerByName(s: string): Marker; + symbolsInScope(range: Range): any[]; } class goTo { marker(name?: string | Marker): void; @@ -192,6 +193,7 @@ declare namespace FourSlashInterface { verifyGetEmitOutputContentsForCurrentFile(expected: ts.OutputFile[]): void; noReferences(markerNameOrRange?: string | Range): void; symbolAtLocation(startRange: Range, ...declarationRanges: Range[]): void; + typeOfSymbolAtLocation(range: Range, symbol: any, expected: string): void; /** * @deprecated, prefer 'referenceGroups' * Like `referencesAre`, but goes to `start` first. diff --git a/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts b/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts index 25e6cd4c68e..54a80194f37 100644 --- a/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts +++ b/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts @@ -7,7 +7,7 @@ //// var num: number; //// var str: string; //// if (typeof /*1*/nonExportedStrOrNum === "number") { -//// num = /*2*/nonExportedStrOrNum; +//// num = /*2*/nonExportedStrOrNum; //// } //// else { //// str = /*3*/nonExportedStrOrNum.length; @@ -40,15 +40,15 @@ verify.completionListContains("nonExportedStrOrNum", "var nonExportedStrOrNum: s goTo.marker('4'); verify.quickInfoIs('var m.exportedStrOrNum: string | number'); -verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string | number"); +verify.completionListContains("exportedStrOrNum", "var exportedStrOrNum: string | number"); goTo.marker('5'); verify.quickInfoIs('var m.exportedStrOrNum: number'); -verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: number"); +verify.completionListContains("exportedStrOrNum", "var exportedStrOrNum: number"); goTo.marker('6'); verify.quickInfoIs('var m.exportedStrOrNum: string'); -verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string"); +verify.completionListContains("exportedStrOrNum", "var exportedStrOrNum: string"); goTo.marker('7'); verify.quickInfoIs('var m.exportedStrOrNum: string | number'); diff --git a/tests/cases/fourslash/typeOfSymbol_localSymbolOfExport.ts b/tests/cases/fourslash/typeOfSymbol_localSymbolOfExport.ts new file mode 100644 index 00000000000..4d23babbd21 --- /dev/null +++ b/tests/cases/fourslash/typeOfSymbol_localSymbolOfExport.ts @@ -0,0 +1,12 @@ +/// + +////export function f() {} +////[|1|]; + +const ranges = test.ranges(); +const symbolsInScope = test.symbolsInScope(ranges[0]); +const f = symbolsInScope.find(s => s.name === "f"); +if (f === undefined) throw new Error("'f' not in scope"); +if (f.exportSymbol === undefined) throw new Error("Expected to get the local symbol"); + +verify.typeOfSymbolAtLocation(ranges[0], f, "() => void"); From af147d15d6c1240684dbc4de6ccd205a420e7b25 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 11 Jul 2017 07:24:40 -0700 Subject: [PATCH 18/50] Fix typo (#17064) --- ...xutalObjectLiteral.ts => protoVarInContextualObjectLiteral.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/cases/fourslash/{protoVarInContexutalObjectLiteral.ts => protoVarInContextualObjectLiteral.ts} (100%) diff --git a/tests/cases/fourslash/protoVarInContexutalObjectLiteral.ts b/tests/cases/fourslash/protoVarInContextualObjectLiteral.ts similarity index 100% rename from tests/cases/fourslash/protoVarInContexutalObjectLiteral.ts rename to tests/cases/fourslash/protoVarInContextualObjectLiteral.ts From 2561ced1e3001fe8aa501ca95b0b1393864ae65f Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 11 Jul 2017 07:26:45 -0700 Subject: [PATCH 19/50] Consistently use `isInJavaScriptFile` helper (#17075) --- src/compiler/checker.ts | 8 ++++---- src/compiler/utilities.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bf03958e4ff..7a637e0e3ab 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4239,7 +4239,7 @@ namespace ts { return addOptionality(declaredType, /*optional*/ declaration.questionToken && includeOptionality); } - if ((noImplicitAny || declaration.flags & NodeFlags.JavaScriptFile) && + if ((noImplicitAny || isInJavaScriptFile(declaration)) && declaration.kind === SyntaxKind.VariableDeclaration && !isBindingPattern(declaration.name) && !(getCombinedModifierFlags(declaration) & ModifierFlags.Export) && !isInAmbientContext(declaration)) { // If --noImplicitAny is on or the declaration is in a Javascript file, @@ -4493,7 +4493,7 @@ namespace ts { if (declaration.kind === SyntaxKind.ExportAssignment) { return links.type = checkExpression((declaration).expression); } - if (declaration.flags & NodeFlags.JavaScriptFile && declaration.kind === SyntaxKind.JSDocPropertyTag && (declaration).typeExpression) { + if (isInJavaScriptFile(declaration) && declaration.kind === SyntaxKind.JSDocPropertyTag && (declaration).typeExpression) { return links.type = getTypeFromTypeNode((declaration).typeExpression.type); } // Handle variable, parameter or property @@ -4552,7 +4552,7 @@ namespace ts { const getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); const setter = getDeclarationOfKind(symbol, SyntaxKind.SetAccessor); - if (getter && getter.flags & NodeFlags.JavaScriptFile) { + if (getter && isInJavaScriptFile(getter)) { const jsDocType = getTypeForDeclarationFromJSDocComment(getter); if (jsDocType) { return links.type = jsDocType; @@ -6206,7 +6206,7 @@ namespace ts { } function isJSDocOptionalParameter(node: ParameterDeclaration) { - if (node.flags & NodeFlags.JavaScriptFile) { + if (isInJavaScriptFile(node)) { if (node.type && node.type.kind === SyntaxKind.JSDocOptionalType) { return true; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index e9cff262988..280a2f73a0e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1619,7 +1619,7 @@ namespace ts { } export function isRestParameter(node: ParameterDeclaration) { - if (node && (node.flags & NodeFlags.JavaScriptFile)) { + if (isInJavaScriptFile(node)) { if (node.type && node.type.kind === SyntaxKind.JSDocVariadicType || forEach(getJSDocParameterTags(node), t => t.typeExpression && t.typeExpression.type.kind === SyntaxKind.JSDocVariadicType)) { @@ -2743,7 +2743,7 @@ namespace ts { if (node.typeParameters) { return node.typeParameters; } - if (node.flags & NodeFlags.JavaScriptFile) { + if (isInJavaScriptFile(node)) { const templateTag = getJSDocTemplateTag(node); return templateTag && templateTag.typeParameters; } From 23da0ab501ddc036b5b89c38c8e0d62f8560c805 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 11 Jul 2017 09:00:34 -0700 Subject: [PATCH 20/50] Use array helpers in more places (#17055) --- src/harness/fourslash.ts | 34 ++++++++++++--------------------- src/services/pathCompletions.ts | 9 ++------- src/services/services.ts | 13 +++---------- 3 files changed, 17 insertions(+), 39 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 411f6762ba3..01caa3d70ef 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2336,29 +2336,19 @@ namespace FourSlash { * @param fileName Path to file where error should be retrieved from. */ private getCodeFixActions(fileName: string, errorCode?: number): ts.CodeAction[] { - const diagnosticsForCodeFix = this.getDiagnostics(fileName).map(diagnostic => { - return { - start: diagnostic.start, - length: diagnostic.length, - code: diagnostic.code - }; - }); - const dedupedDiagnositcs = ts.deduplicate(diagnosticsForCodeFix, ts.equalOwnProperties); - - let actions: ts.CodeAction[] = undefined; - - for (const diagnostic of dedupedDiagnositcs) { + const diagnosticsForCodeFix = this.getDiagnostics(fileName).map(diagnostic => ({ + start: diagnostic.start, + length: diagnostic.length, + code: diagnostic.code + })); + return ts.flatMap(ts.deduplicate(diagnosticsForCodeFix, ts.equalOwnProperties), diagnostic => { if (errorCode && errorCode !== diagnostic.code) { - continue; + return; } - const newActions = this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.formatCodeSettings); - if (newActions && newActions.length) { - actions = actions ? actions.concat(newActions) : newActions; - } - } - return actions; + return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code], this.formatCodeSettings); + }); } private applyCodeActions(actions: ts.CodeAction[], index?: number): void { @@ -2389,7 +2379,7 @@ namespace FourSlash { const codeFixes = this.getCodeFixActions(this.activeFile.fileName, errorCode); - if (!codeFixes || codeFixes.length === 0) { + if (codeFixes.length === 0) { if (expectedTextArray.length !== 0) { this.raiseError("No codefixes returned."); } @@ -2718,11 +2708,11 @@ namespace FourSlash { public verifyCodeFixAvailable(negative: boolean) { const codeFix = this.getCodeFixActions(this.activeFile.fileName); - if (negative && codeFix) { + if (negative && codeFix.length) { this.raiseError(`verifyCodeFixAvailable failed - expected no fixes but found one.`); } - if (!(negative || codeFix)) { + if (!(negative || codeFix.length)) { this.raiseError(`verifyCodeFixAvailable failed - expected code fixes but none found.`); } } diff --git a/src/services/pathCompletions.ts b/src/services/pathCompletions.ts index bc94fb7dbc5..8addcf494bf 100644 --- a/src/services/pathCompletions.ts +++ b/src/services/pathCompletions.ts @@ -40,13 +40,8 @@ namespace ts.Completions.PathCompletions { rootDirs = map(rootDirs, rootDirectory => normalizePath(isRootedDiskPath(rootDirectory) ? rootDirectory : combinePaths(basePath, rootDirectory))); // Determine the path to the directory containing the script relative to the root directory it is contained within - let relativeDirectory: string; - for (const rootDirectory of rootDirs) { - if (containsPath(rootDirectory, scriptPath, basePath, ignoreCase)) { - relativeDirectory = scriptPath.substr(rootDirectory.length); - break; - } - } + const relativeDirectory = forEach(rootDirs, rootDirectory => + containsPath(rootDirectory, scriptPath, basePath, ignoreCase) ? scriptPath.substr(rootDirectory.length) : undefined); // Now find a path for each potential directory that is to be merged with the one containing the script return deduplicate(map(rootDirs, rootDirectory => combinePaths(rootDirectory, relativeDirectory))); diff --git a/src/services/services.ts b/src/services/services.ts index d89051ee666..5b2cf949d2a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1770,19 +1770,12 @@ namespace ts { const sourceFile = getValidSourceFile(fileName); const span = createTextSpanFromBounds(start, end); const newLineCharacter = getNewLineOrDefaultFromHost(host); + const rulesProvider = getRuleProvider(formatOptions); - let allFixes: CodeAction[] = []; - - forEach(deduplicate(errorCodes), errorCode => { + return flatMap(deduplicate(errorCodes), errorCode => { cancellationToken.throwIfCancellationRequested(); - const rulesProvider = getRuleProvider(formatOptions); - const fixes = codefix.getFixes({ errorCode, sourceFile, span, program, newLineCharacter, host, cancellationToken, rulesProvider }); - if (fixes) { - allFixes = allFixes.concat(fixes); - } + return codefix.getFixes({ errorCode, sourceFile, span, program, newLineCharacter, host, cancellationToken, rulesProvider }); }); - - return allFixes; } function getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion { From 1408109487c6f4b5679f8f3ef01ac93d82630bdd Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 11 Jul 2017 09:40:02 -0700 Subject: [PATCH 21/50] buildTreeFromBottom: simplify loop (#17091) --- src/server/scriptVersionCache.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/server/scriptVersionCache.ts b/src/server/scriptVersionCache.ts index 17386285921..0862c13fec6 100644 --- a/src/server/scriptVersionCache.ts +++ b/src/server/scriptVersionCache.ts @@ -531,16 +531,12 @@ namespace ts.server { const interiorNode = interiorNodes[i] = new LineNode(); let charCount = 0; let lineCount = 0; - for (let j = 0; j < lineCollectionCapacity; j++) { - if (nodeIndex >= nodes.length) { - break; - } - + const end = Math.min(nodeIndex + lineCollectionCapacity, nodes.length); + for (; nodeIndex < end; nodeIndex++) { const node = nodes[nodeIndex]; interiorNode.add(node); charCount += node.charCount(); lineCount += node.lineCount(); - nodeIndex++; } interiorNode.totalChars = charCount; interiorNode.totalLines = lineCount; From fcc9823ac75bd73a2c8318aeafcd6009b156990a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 11 Jul 2017 09:51:18 -0700 Subject: [PATCH 22/50] Switch fillSignature boolean params to single enum --- src/compiler/parser.ts | 57 ++++++++++++++++++++---------------------- src/compiler/types.ts | 7 ++++++ 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 4a979fb3e6f..e7f8cb3ef85 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2218,15 +2218,12 @@ namespace ts { function fillSignature( returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, - yieldContext: boolean, - awaitContext: boolean, - requireCompleteParameterList: boolean, + context: SignatureContext, signature: SignatureDeclaration): void { + signature.typeParameters = parseTypeParameters(); + signature.parameters = parseParameterList(context); const returnTokenRequired = returnToken === SyntaxKind.EqualsGreaterThanToken; - signature.typeParameters = parseTypeParameters(); - signature.parameters = parseParameterList(yieldContext, awaitContext, requireCompleteParameterList); - if (returnTokenRequired) { parseExpected(returnToken); signature.type = parseTypeOrTypePredicate(); @@ -2236,7 +2233,7 @@ namespace ts { } } - function parseParameterList(yieldContext: boolean, awaitContext: boolean, requireCompleteParameterList: boolean) { + function parseParameterList(context: SignatureContext) { // FormalParameters [Yield,Await]: (modified) // [empty] // FormalParameterList[?Yield,Await] @@ -2254,15 +2251,15 @@ namespace ts { const savedYieldContext = inYieldContext(); const savedAwaitContext = inAwaitContext(); - setYieldContext(yieldContext); - setAwaitContext(awaitContext); + setYieldContext(!!(context & SignatureContext.Yield)); + setAwaitContext(!!(context & SignatureContext.Await)); const result = parseDelimitedList(ParsingContext.Parameters, parseParameter); setYieldContext(savedYieldContext); setAwaitContext(savedAwaitContext); - if (!parseExpected(SyntaxKind.CloseParenToken) && requireCompleteParameterList) { + if (!parseExpected(SyntaxKind.CloseParenToken) && (context & SignatureContext.RequireCompleteParameterList)) { // Caller insisted that we had to end with a ) We didn't. So just return // undefined here. return undefined; @@ -2274,7 +2271,7 @@ namespace ts { // We didn't even have an open paren. If the caller requires a complete parameter list, // we definitely can't provide that. However, if they're ok with an incomplete one, // then just return an empty set of parameters. - return requireCompleteParameterList ? undefined : createMissingList(); + return (context & SignatureContext.RequireCompleteParameterList) ? undefined : createMissingList(); } function parseTypeMemberSemicolon() { @@ -2293,7 +2290,7 @@ namespace ts { if (kind === SyntaxKind.ConstructSignature) { parseExpected(SyntaxKind.NewKeyword); } - fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node); + fillSignature(SyntaxKind.ColonToken, SignatureContext.Type, node); parseTypeMemberSemicolon(); return addJSDocComment(finishNode(node)); } @@ -2383,7 +2380,7 @@ namespace ts { // Method signatures don't exist in expression contexts. So they have neither // [Yield] nor [Await] - fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, method); + fillSignature(SyntaxKind.ColonToken, SignatureContext.Type, method); parseTypeMemberSemicolon(); return addJSDocComment(finishNode(method)); } @@ -2527,7 +2524,7 @@ namespace ts { if (kind === SyntaxKind.ConstructorType) { parseExpected(SyntaxKind.NewKeyword); } - fillSignature(SyntaxKind.EqualsGreaterThanToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node); + fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureContext.Type, node); return finishNode(node); } @@ -3254,7 +3251,7 @@ namespace ts { function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction { const node = createNode(SyntaxKind.ArrowFunction); node.modifiers = parseModifiersForArrowFunction(); - const isAsync = !!(getModifierFlags(node) & ModifierFlags.Async); + const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureContext.Await : 0; // Arrow functions are never generators. // @@ -3263,7 +3260,7 @@ namespace ts { // a => (b => c) // And think that "(b =>" was actually a parenthesized arrow function with a missing // close paren. - fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ !allowAmbiguity, node); + fillSignature(SyntaxKind.ColonToken, isAsync | (allowAmbiguity ? 0 : SignatureContext.RequireCompleteParameterList), node); // If we couldn't get parameters, we definitely could not parse out an arrow function. if (!node.parameters) { @@ -4386,16 +4383,16 @@ namespace ts { parseExpected(SyntaxKind.FunctionKeyword); node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); - const isGenerator = !!node.asteriskToken; - const isAsync = !!(getModifierFlags(node) & ModifierFlags.Async); + const isGenerator = node.asteriskToken ? SignatureContext.Yield : 0; + const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureContext.Await : 0; node.name = isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) : isGenerator ? doInYieldContext(parseOptionalIdentifier) : isAsync ? doInAwaitContext(parseOptionalIdentifier) : parseOptionalIdentifier(); - fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node); - node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false); + fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node); + node.body = parseFunctionBlock(/*allowYield*/ !!isGenerator, /*allowAwait*/ !!isAsync, /*ignoreMissingOpenBrace*/ false); if (saveDecoratorContext) { setDecoratorContext(/*val*/ true); @@ -5146,10 +5143,10 @@ namespace ts { parseExpected(SyntaxKind.FunctionKeyword); node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); node.name = hasModifier(node, ModifierFlags.Default) ? parseOptionalIdentifier() : parseIdentifier(); - const isGenerator = !!node.asteriskToken; - const isAsync = hasModifier(node, ModifierFlags.Async); - fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node); - node.body = parseFunctionBlockOrSemicolon(isGenerator, isAsync, Diagnostics.or_expected); + const isGenerator = node.asteriskToken ? SignatureContext.Yield : 0; + const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureContext.Await : 0; + fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node); + node.body = parseFunctionBlockOrSemicolon(!!isGenerator, !!isAsync, Diagnostics.or_expected); return addJSDocComment(finishNode(node)); } @@ -5158,7 +5155,7 @@ namespace ts { node.decorators = decorators; node.modifiers = modifiers; parseExpected(SyntaxKind.ConstructorKeyword); - fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node); + fillSignature(SyntaxKind.ColonToken, /*context*/ 0, node); node.body = parseFunctionBlockOrSemicolon(/*isGenerator*/ false, /*isAsync*/ false, Diagnostics.or_expected); return addJSDocComment(finishNode(node)); } @@ -5170,10 +5167,10 @@ namespace ts { method.asteriskToken = asteriskToken; method.name = name; method.questionToken = questionToken; - const isGenerator = !!asteriskToken; - const isAsync = hasModifier(method, ModifierFlags.Async); - fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, method); - method.body = parseFunctionBlockOrSemicolon(isGenerator, isAsync, diagnosticMessage); + const isGenerator = asteriskToken ? SignatureContext.Yield : 0; + const isAsync = hasModifier(method, ModifierFlags.Async) ? SignatureContext.Await : 0; + fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, method); + method.body = parseFunctionBlockOrSemicolon(!!isGenerator, !!isAsync, diagnosticMessage); return addJSDocComment(finishNode(method)); } @@ -5226,7 +5223,7 @@ namespace ts { node.decorators = decorators; node.modifiers = modifiers; node.name = parsePropertyName(); - fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ false, /*awaitContext*/ false, /*requireCompleteParameterList*/ false, node); + fillSignature(SyntaxKind.ColonToken, /*context*/ 0, node); node.body = parseFunctionBlockOrSemicolon(/*isGenerator*/ false, /*isAsync*/ false); return addJSDocComment(finishNode(node)); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index aefcce6d0bb..eb4fac52b7f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -36,6 +36,13 @@ namespace ts { end: number; } + export const enum SignatureContext { + Yield = 1 << 1, + Await = 1 << 2, + Type = 1 << 3, + RequireCompleteParameterList = 1 << 4, + } + // token > SyntaxKind.Identifer => token is a keyword // Also, If you add a new SyntaxKind be sure to keep the `Markers` section at the bottom in sync export const enum SyntaxKind { From f45ccf541d41a0a92dc7f01fa63926401dbecc23 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 11 Jul 2017 09:54:42 -0700 Subject: [PATCH 23/50] In getDeclarationSpaces, treat a type alias as a SymbolFlags.Type, not a SymbolFlags.Value (#16624) --- src/compiler/checker.ts | 10 ++++- .../mergedDeclarationExports.errors.txt | 44 +++++++++++++++++++ .../reference/mergedDeclarationExports.js | 36 +++++++++++++++ .../compiler/mergedDeclarationExports.ts | 22 ++++++++++ 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/mergedDeclarationExports.errors.txt create mode 100644 tests/baselines/reference/mergedDeclarationExports.js create mode 100644 tests/cases/compiler/mergedDeclarationExports.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7a637e0e3ab..8c274cf176e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18860,6 +18860,7 @@ namespace ts { function getDeclarationSpaces(d: Declaration): SymbolFlags { switch (d.kind) { case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.TypeAliasDeclaration: return SymbolFlags.ExportType; case SyntaxKind.ModuleDeclaration: return isAmbientModule(d) || getModuleInstanceState(d) !== ModuleInstanceState.NonInstantiated @@ -18869,12 +18870,17 @@ namespace ts { case SyntaxKind.EnumDeclaration: return SymbolFlags.ExportType | SymbolFlags.ExportValue; case SyntaxKind.ImportEqualsDeclaration: - let result: SymbolFlags = 0; + let result = SymbolFlags.None; const target = resolveAlias(getSymbolOfNode(d)); forEach(target.declarations, d => { result |= getDeclarationSpaces(d); }); return result; - default: + case SyntaxKind.VariableDeclaration: + case SyntaxKind.BindingElement: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ImportSpecifier: // https://github.com/Microsoft/TypeScript/pull/7591 return SymbolFlags.ExportValue; + default: + Debug.fail((ts as any).SyntaxKind[d.kind]); } } } diff --git a/tests/baselines/reference/mergedDeclarationExports.errors.txt b/tests/baselines/reference/mergedDeclarationExports.errors.txt new file mode 100644 index 00000000000..f7a09ad4bc6 --- /dev/null +++ b/tests/baselines/reference/mergedDeclarationExports.errors.txt @@ -0,0 +1,44 @@ +tests/cases/compiler/mergedDeclarationExports.ts(13,11): error TS2395: Individual declarations in merged declaration 'c' must be all exported or all local. +tests/cases/compiler/mergedDeclarationExports.ts(14,18): error TS2395: Individual declarations in merged declaration 'c' must be all exported or all local. +tests/cases/compiler/mergedDeclarationExports.ts(17,11): error TS2395: Individual declarations in merged declaration 'd' must be all exported or all local. +tests/cases/compiler/mergedDeclarationExports.ts(18,14): error TS2395: Individual declarations in merged declaration 'd' must be all exported or all local. +tests/cases/compiler/mergedDeclarationExports.ts(21,11): error TS2395: Individual declarations in merged declaration 'N' must be all exported or all local. +tests/cases/compiler/mergedDeclarationExports.ts(22,18): error TS2395: Individual declarations in merged declaration 'N' must be all exported or all local. + + +==== tests/cases/compiler/mergedDeclarationExports.ts (6 errors) ==== + // OK -- one is type, one is value + interface b {} + export const b = 1; + + // OK -- one is a type, one is a namespace, one is a value. + type t = 0; + namespace t { interface I {} } + export const t = 0; + + // Should get errors if they have some meaning in common. + + // both types + interface c {} + ~ +!!! error TS2395: Individual declarations in merged declaration 'c' must be all exported or all local. + export interface c {} + ~ +!!! error TS2395: Individual declarations in merged declaration 'c' must be all exported or all local. + + // both types (class is also value, but that doesn't matter) + interface d {} + ~ +!!! error TS2395: Individual declarations in merged declaration 'd' must be all exported or all local. + export class d {} + ~ +!!! error TS2395: Individual declarations in merged declaration 'd' must be all exported or all local. + + // both namespaces + namespace N { } + ~ +!!! error TS2395: Individual declarations in merged declaration 'N' must be all exported or all local. + export namespace N {} + ~ +!!! error TS2395: Individual declarations in merged declaration 'N' must be all exported or all local. + \ No newline at end of file diff --git a/tests/baselines/reference/mergedDeclarationExports.js b/tests/baselines/reference/mergedDeclarationExports.js new file mode 100644 index 00000000000..068f7b683c3 --- /dev/null +++ b/tests/baselines/reference/mergedDeclarationExports.js @@ -0,0 +1,36 @@ +//// [mergedDeclarationExports.ts] +// OK -- one is type, one is value +interface b {} +export const b = 1; + +// OK -- one is a type, one is a namespace, one is a value. +type t = 0; +namespace t { interface I {} } +export const t = 0; + +// Should get errors if they have some meaning in common. + +// both types +interface c {} +export interface c {} + +// both types (class is also value, but that doesn't matter) +interface d {} +export class d {} + +// both namespaces +namespace N { } +export namespace N {} + + +//// [mergedDeclarationExports.js] +"use strict"; +exports.__esModule = true; +exports.b = 1; +exports.t = 0; +var d = (function () { + function d() { + } + return d; +}()); +exports.d = d; diff --git a/tests/cases/compiler/mergedDeclarationExports.ts b/tests/cases/compiler/mergedDeclarationExports.ts new file mode 100644 index 00000000000..a27f1186e9f --- /dev/null +++ b/tests/cases/compiler/mergedDeclarationExports.ts @@ -0,0 +1,22 @@ +// OK -- one is type, one is value +interface b {} +export const b = 1; + +// OK -- one is a type, one is a namespace, one is a value. +type t = 0; +namespace t { interface I {} } +export const t = 0; + +// Should get errors if they have some meaning in common. + +// both types +interface c {} +export interface c {} + +// both types (class is also value, but that doesn't matter) +interface d {} +export class d {} + +// both namespaces +namespace N { } +export namespace N {} From b6ad43d4a5ad193e4dede7a0fed6537e0ac81af9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 11 Jul 2017 10:08:42 -0700 Subject: [PATCH 24/50] Better error for wrong return (: vs =>) in types It's very ambiguous in expression position, so impossible to give a better message from the parser. For example: let f = (x: number) => number => x + 1; ~~ Should be ':' But the parser doesn't know that 'number' isn't an expression now. --- src/compiler/parser.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index e7f8cb3ef85..74adccae089 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2231,6 +2231,16 @@ namespace ts { else if (parseOptional(returnToken)) { signature.type = parseTypeOrTypePredicate(); } + else if (context === SignatureContext.Type) { + const start = scanner.getTokenPos(); + const length = scanner.getTextPos() - start; + const backwardToken = parseOptional(returnToken === SyntaxKind.ColonToken ? SyntaxKind.EqualsGreaterThanToken : SyntaxKind.ColonToken); + if (backwardToken) { + // This is easy to get backward, especially in type contexts, so parse the type anyway + signature.type = parseTypeOrTypePredicate(); + parseErrorAtPosition(start, length, Diagnostics._0_expected, tokenToString(returnToken)); + } + } } function parseParameterList(context: SignatureContext) { From 3638ff19b3a9ed9b205fc764713f57fd490260bd Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 11 Jul 2017 10:16:35 -0700 Subject: [PATCH 25/50] Test:better error for wrong return token (: vs =>) --- .../parseErrorIncorrectReturnToken.errors.txt | 37 +++++++++++++++++++ .../parseErrorIncorrectReturnToken.js | 27 ++++++++++++++ .../parseErrorIncorrectReturnToken.ts | 13 +++++++ 3 files changed, 77 insertions(+) create mode 100644 tests/baselines/reference/parseErrorIncorrectReturnToken.errors.txt create mode 100644 tests/baselines/reference/parseErrorIncorrectReturnToken.js create mode 100644 tests/cases/compiler/parseErrorIncorrectReturnToken.ts diff --git a/tests/baselines/reference/parseErrorIncorrectReturnToken.errors.txt b/tests/baselines/reference/parseErrorIncorrectReturnToken.errors.txt new file mode 100644 index 00000000000..2cf728848e4 --- /dev/null +++ b/tests/baselines/reference/parseErrorIncorrectReturnToken.errors.txt @@ -0,0 +1,37 @@ +tests/cases/compiler/parseErrorIncorrectReturnToken.ts(2,17): error TS1005: ':' expected. +tests/cases/compiler/parseErrorIncorrectReturnToken.ts(4,22): error TS1005: '=>' expected. +tests/cases/compiler/parseErrorIncorrectReturnToken.ts(4,24): error TS2693: 'string' only refers to a type, but is being used as a value here. +tests/cases/compiler/parseErrorIncorrectReturnToken.ts(9,18): error TS1005: '{' expected. +tests/cases/compiler/parseErrorIncorrectReturnToken.ts(9,21): error TS2693: 'string' only refers to a type, but is being used as a value here. +tests/cases/compiler/parseErrorIncorrectReturnToken.ts(9,28): error TS1005: ';' expected. +tests/cases/compiler/parseErrorIncorrectReturnToken.ts(12,1): error TS1128: Declaration or statement expected. + + +==== tests/cases/compiler/parseErrorIncorrectReturnToken.ts (7 errors) ==== + type F1 = { + (n: number) => string; // should be : not => + ~~ +!!! error TS1005: ':' expected. + } + type F2 = (n: number): string; // should be => not : + ~ +!!! error TS1005: '=>' expected. + ~~~~~~ +!!! error TS2693: 'string' only refers to a type, but is being used as a value here. + + // doesn't work in non-type contexts, where the return type is optional + let f = (n: number) => string => n.toString(); + let o = { + m(n: number) => string { + ~~ +!!! error TS1005: '{' expected. + ~~~~~~ +!!! error TS2693: 'string' only refers to a type, but is being used as a value here. + ~ +!!! error TS1005: ';' expected. + return n.toString(); + } + }; + ~ +!!! error TS1128: Declaration or statement expected. + \ No newline at end of file diff --git a/tests/baselines/reference/parseErrorIncorrectReturnToken.js b/tests/baselines/reference/parseErrorIncorrectReturnToken.js new file mode 100644 index 00000000000..3d2a1d17a4a --- /dev/null +++ b/tests/baselines/reference/parseErrorIncorrectReturnToken.js @@ -0,0 +1,27 @@ +//// [parseErrorIncorrectReturnToken.ts] +type F1 = { + (n: number) => string; // should be : not => +} +type F2 = (n: number): string; // should be => not : + +// doesn't work in non-type contexts, where the return type is optional +let f = (n: number) => string => n.toString(); +let o = { + m(n: number) => string { + return n.toString(); + } +}; + + +//// [parseErrorIncorrectReturnToken.js] +string; // should be => not : +// doesn't work in non-type contexts, where the return type is optional +var f = function (n) { return function (string) { return n.toString(); }; }; +var o = { + m: function (n) { } +}; +string; +{ + return n.toString(); +} +; diff --git a/tests/cases/compiler/parseErrorIncorrectReturnToken.ts b/tests/cases/compiler/parseErrorIncorrectReturnToken.ts new file mode 100644 index 00000000000..0f45c38a3b1 --- /dev/null +++ b/tests/cases/compiler/parseErrorIncorrectReturnToken.ts @@ -0,0 +1,13 @@ + +type F1 = { + (n: number) => string; // should be : not => +} +type F2 = (n: number): string; // should be => not : + +// doesn't work in non-type contexts, where the return type is optional +let f = (n: number) => string => n.toString(); +let o = { + m(n: number) => string { + return n.toString(); + } +}; From 8856ddfd15e51ffe9901dd17255127ee0b6b9ca6 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 11 Jul 2017 10:45:25 -0700 Subject: [PATCH 26/50] Make enum private and fix fillSignature predicate --- src/compiler/parser.ts | 9 ++++++++- src/compiler/types.ts | 7 ------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 74adccae089..8608245cfcf 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2,6 +2,13 @@ /// namespace ts { + const enum SignatureContext { + Yield = 1 << 0, + Await = 1 << 1, + Type = 1 << 2, + RequireCompleteParameterList = 1 << 3, + } + let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; let TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; let IdentifierConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; @@ -2231,7 +2238,7 @@ namespace ts { else if (parseOptional(returnToken)) { signature.type = parseTypeOrTypePredicate(); } - else if (context === SignatureContext.Type) { + else if (context & SignatureContext.Type) { const start = scanner.getTokenPos(); const length = scanner.getTextPos() - start; const backwardToken = parseOptional(returnToken === SyntaxKind.ColonToken ? SyntaxKind.EqualsGreaterThanToken : SyntaxKind.ColonToken); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index eb4fac52b7f..aefcce6d0bb 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -36,13 +36,6 @@ namespace ts { end: number; } - export const enum SignatureContext { - Yield = 1 << 1, - Await = 1 << 2, - Type = 1 << 3, - RequireCompleteParameterList = 1 << 4, - } - // token > SyntaxKind.Identifer => token is a keyword // Also, If you add a new SyntaxKind be sure to keep the `Markers` section at the bottom in sync export const enum SyntaxKind { From 1b1f257dbf593b7b5e524a88954fc668b976e5ad Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 11 Jul 2017 14:49:47 -0700 Subject: [PATCH 27/50] Rename SignatureFlags enum and improve its usage As requested in the PR comments --- src/compiler/parser.ts | 74 ++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 8608245cfcf..089bff53a05 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2,11 +2,13 @@ /// namespace ts { - const enum SignatureContext { + const enum SignatureFlags { + None = 0, Yield = 1 << 0, Await = 1 << 1, Type = 1 << 2, RequireCompleteParameterList = 1 << 3, + IgnoreMissingOpenBrace = 1 << 4, } let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; @@ -2225,10 +2227,10 @@ namespace ts { function fillSignature( returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, - context: SignatureContext, + flags: SignatureFlags, signature: SignatureDeclaration): void { signature.typeParameters = parseTypeParameters(); - signature.parameters = parseParameterList(context); + signature.parameters = parseParameterList(flags); const returnTokenRequired = returnToken === SyntaxKind.EqualsGreaterThanToken; if (returnTokenRequired) { @@ -2238,7 +2240,7 @@ namespace ts { else if (parseOptional(returnToken)) { signature.type = parseTypeOrTypePredicate(); } - else if (context & SignatureContext.Type) { + else if (flags & SignatureFlags.Type) { const start = scanner.getTokenPos(); const length = scanner.getTextPos() - start; const backwardToken = parseOptional(returnToken === SyntaxKind.ColonToken ? SyntaxKind.EqualsGreaterThanToken : SyntaxKind.ColonToken); @@ -2250,7 +2252,7 @@ namespace ts { } } - function parseParameterList(context: SignatureContext) { + function parseParameterList(flags: SignatureFlags) { // FormalParameters [Yield,Await]: (modified) // [empty] // FormalParameterList[?Yield,Await] @@ -2268,15 +2270,15 @@ namespace ts { const savedYieldContext = inYieldContext(); const savedAwaitContext = inAwaitContext(); - setYieldContext(!!(context & SignatureContext.Yield)); - setAwaitContext(!!(context & SignatureContext.Await)); + setYieldContext(!!(flags & SignatureFlags.Yield)); + setAwaitContext(!!(flags & SignatureFlags.Await)); const result = parseDelimitedList(ParsingContext.Parameters, parseParameter); setYieldContext(savedYieldContext); setAwaitContext(savedAwaitContext); - if (!parseExpected(SyntaxKind.CloseParenToken) && (context & SignatureContext.RequireCompleteParameterList)) { + if (!parseExpected(SyntaxKind.CloseParenToken) && (flags & SignatureFlags.RequireCompleteParameterList)) { // Caller insisted that we had to end with a ) We didn't. So just return // undefined here. return undefined; @@ -2288,7 +2290,7 @@ namespace ts { // We didn't even have an open paren. If the caller requires a complete parameter list, // we definitely can't provide that. However, if they're ok with an incomplete one, // then just return an empty set of parameters. - return (context & SignatureContext.RequireCompleteParameterList) ? undefined : createMissingList(); + return (flags & SignatureFlags.RequireCompleteParameterList) ? undefined : createMissingList(); } function parseTypeMemberSemicolon() { @@ -2307,7 +2309,7 @@ namespace ts { if (kind === SyntaxKind.ConstructSignature) { parseExpected(SyntaxKind.NewKeyword); } - fillSignature(SyntaxKind.ColonToken, SignatureContext.Type, node); + fillSignature(SyntaxKind.ColonToken, SignatureFlags.Type, node); parseTypeMemberSemicolon(); return addJSDocComment(finishNode(node)); } @@ -2397,7 +2399,7 @@ namespace ts { // Method signatures don't exist in expression contexts. So they have neither // [Yield] nor [Await] - fillSignature(SyntaxKind.ColonToken, SignatureContext.Type, method); + fillSignature(SyntaxKind.ColonToken, SignatureFlags.Type, method); parseTypeMemberSemicolon(); return addJSDocComment(finishNode(method)); } @@ -2541,7 +2543,7 @@ namespace ts { if (kind === SyntaxKind.ConstructorType) { parseExpected(SyntaxKind.NewKeyword); } - fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureContext.Type, node); + fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureFlags.Type, node); return finishNode(node); } @@ -3268,7 +3270,7 @@ namespace ts { function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction { const node = createNode(SyntaxKind.ArrowFunction); node.modifiers = parseModifiersForArrowFunction(); - const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureContext.Await : 0; + const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None; // Arrow functions are never generators. // @@ -3277,7 +3279,7 @@ namespace ts { // a => (b => c) // And think that "(b =>" was actually a parenthesized arrow function with a missing // close paren. - fillSignature(SyntaxKind.ColonToken, isAsync | (allowAmbiguity ? 0 : SignatureContext.RequireCompleteParameterList), node); + fillSignature(SyntaxKind.ColonToken, isAsync | (allowAmbiguity ? SignatureFlags.None : SignatureFlags.RequireCompleteParameterList), node); // If we couldn't get parameters, we definitely could not parse out an arrow function. if (!node.parameters) { @@ -3302,7 +3304,7 @@ namespace ts { function parseArrowFunctionExpressionBody(isAsync: boolean): Block | Expression { if (token() === SyntaxKind.OpenBraceToken) { - return parseFunctionBlock(/*allowYield*/ false, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false); + return parseFunctionBlock(isAsync ? SignatureFlags.Await : SignatureFlags.None); } if (token() !== SyntaxKind.SemicolonToken && @@ -3323,8 +3325,8 @@ namespace ts { // try to recover better. If we don't do this, then the next close curly we see may end // up preemptively closing the containing construct. // - // Note: even when 'ignoreMissingOpenBrace' is passed as true, parseBody will still error. - return parseFunctionBlock(/*allowYield*/ false, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ true); + // Note: even when 'IgnoreMissingOpenBrace' is passed, parseBody will still error. + return parseFunctionBlock(SignatureFlags.IgnoreMissingOpenBrace | (isAsync ? SignatureFlags.Await : SignatureFlags.None)); } return isAsync @@ -4400,8 +4402,8 @@ namespace ts { parseExpected(SyntaxKind.FunctionKeyword); node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); - const isGenerator = node.asteriskToken ? SignatureContext.Yield : 0; - const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureContext.Await : 0; + const isGenerator = node.asteriskToken ? SignatureFlags.Yield : SignatureFlags.None; + const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None; node.name = isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) : isGenerator ? doInYieldContext(parseOptionalIdentifier) : @@ -4409,7 +4411,7 @@ namespace ts { parseOptionalIdentifier(); fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node); - node.body = parseFunctionBlock(/*allowYield*/ !!isGenerator, /*allowAwait*/ !!isAsync, /*ignoreMissingOpenBrace*/ false); + node.body = parseFunctionBlock(isGenerator | isAsync); if (saveDecoratorContext) { setDecoratorContext(/*val*/ true); @@ -4458,12 +4460,12 @@ namespace ts { return finishNode(node); } - function parseFunctionBlock(allowYield: boolean, allowAwait: boolean, ignoreMissingOpenBrace: boolean, diagnosticMessage?: DiagnosticMessage): Block { + function parseFunctionBlock(flags: SignatureFlags, diagnosticMessage?: DiagnosticMessage): Block { const savedYieldContext = inYieldContext(); - setYieldContext(allowYield); + setYieldContext(!!(flags & SignatureFlags.Yield)); const savedAwaitContext = inAwaitContext(); - setAwaitContext(allowAwait); + setAwaitContext(!!(flags & SignatureFlags.Await)); // We may be in a [Decorator] context when parsing a function expression or // arrow function. The body of the function is not in [Decorator] context. @@ -4472,7 +4474,7 @@ namespace ts { setDecoratorContext(/*val*/ false); } - const block = parseBlock(ignoreMissingOpenBrace, diagnosticMessage); + const block = parseBlock(!!(flags & SignatureFlags.IgnoreMissingOpenBrace), diagnosticMessage); if (saveDecoratorContext) { setDecoratorContext(/*val*/ true); @@ -5019,13 +5021,13 @@ namespace ts { return !scanner.hasPrecedingLineBreak() && (isIdentifier() || token() === SyntaxKind.StringLiteral); } - function parseFunctionBlockOrSemicolon(isGenerator: boolean, isAsync: boolean, diagnosticMessage?: DiagnosticMessage): Block { + function parseFunctionBlockOrSemicolon(flags: SignatureFlags, diagnosticMessage?: DiagnosticMessage): Block { if (token() !== SyntaxKind.OpenBraceToken && canParseSemicolon()) { parseSemicolon(); return; } - return parseFunctionBlock(isGenerator, isAsync, /*ignoreMissingOpenBrace*/ false, diagnosticMessage); + return parseFunctionBlock(flags, diagnosticMessage); } // DECLARATIONS @@ -5160,10 +5162,10 @@ namespace ts { parseExpected(SyntaxKind.FunctionKeyword); node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); node.name = hasModifier(node, ModifierFlags.Default) ? parseOptionalIdentifier() : parseIdentifier(); - const isGenerator = node.asteriskToken ? SignatureContext.Yield : 0; - const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureContext.Await : 0; + const isGenerator = node.asteriskToken ? SignatureFlags.Yield : SignatureFlags.None; + const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None; fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node); - node.body = parseFunctionBlockOrSemicolon(!!isGenerator, !!isAsync, Diagnostics.or_expected); + node.body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, Diagnostics.or_expected); return addJSDocComment(finishNode(node)); } @@ -5172,8 +5174,8 @@ namespace ts { node.decorators = decorators; node.modifiers = modifiers; parseExpected(SyntaxKind.ConstructorKeyword); - fillSignature(SyntaxKind.ColonToken, /*context*/ 0, node); - node.body = parseFunctionBlockOrSemicolon(/*isGenerator*/ false, /*isAsync*/ false, Diagnostics.or_expected); + fillSignature(SyntaxKind.ColonToken, SignatureFlags.None, node); + node.body = parseFunctionBlockOrSemicolon(SignatureFlags.None, Diagnostics.or_expected); return addJSDocComment(finishNode(node)); } @@ -5184,10 +5186,10 @@ namespace ts { method.asteriskToken = asteriskToken; method.name = name; method.questionToken = questionToken; - const isGenerator = asteriskToken ? SignatureContext.Yield : 0; - const isAsync = hasModifier(method, ModifierFlags.Async) ? SignatureContext.Await : 0; + const isGenerator = asteriskToken ? SignatureFlags.Yield : SignatureFlags.None; + const isAsync = hasModifier(method, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None; fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, method); - method.body = parseFunctionBlockOrSemicolon(!!isGenerator, !!isAsync, diagnosticMessage); + method.body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, diagnosticMessage); return addJSDocComment(finishNode(method)); } @@ -5240,8 +5242,8 @@ namespace ts { node.decorators = decorators; node.modifiers = modifiers; node.name = parsePropertyName(); - fillSignature(SyntaxKind.ColonToken, /*context*/ 0, node); - node.body = parseFunctionBlockOrSemicolon(/*isGenerator*/ false, /*isAsync*/ false); + fillSignature(SyntaxKind.ColonToken, SignatureFlags.None, node); + node.body = parseFunctionBlockOrSemicolon(SignatureFlags.None); return addJSDocComment(finishNode(node)); } From 003c28f1ef8ef093d2927901af8fafd3b388dc88 Mon Sep 17 00:00:00 2001 From: Mine Starks Date: Tue, 11 Jul 2017 13:32:56 -0700 Subject: [PATCH 28/50] Fix caret update logic in fourslash tests --- src/harness/fourslash.ts | 13 ++++++++----- tests/cases/fourslash/formattingOnEnter.ts | 6 +++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index fc8299f57e9..9688c565f57 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1790,17 +1790,20 @@ namespace FourSlash { this.editScriptAndUpdateMarkers(fileName, offsetStart, offsetEnd, edit.newText); const editDelta = edit.newText.length - edit.span.length; if (offsetStart <= this.currentCaretPosition) { - this.currentCaretPosition += editDelta; + if (offsetEnd <= this.currentCaretPosition) { + // The entirety of the edit span falls before the caret position, shift the caret accordingly + this.currentCaretPosition += editDelta; + } + else { + // The span being replaced includes the caret position, place the caret at the beginning of the span + this.currentCaretPosition = offsetStart; + } } runningOffset += editDelta; // TODO: Consider doing this at least some of the time for higher fidelity. Currently causes a failure (bug 707150) // this.languageService.getScriptLexicalStructure(fileName); } - if (this.currentCaretPosition < 0) { - this.currentCaretPosition = 0; - } - if (isFormattingEdit) { const newContent = this.getFileContent(fileName); diff --git a/tests/cases/fourslash/formattingOnEnter.ts b/tests/cases/fourslash/formattingOnEnter.ts index 1c0915e839e..0e7d5af1a6d 100644 --- a/tests/cases/fourslash/formattingOnEnter.ts +++ b/tests/cases/fourslash/formattingOnEnter.ts @@ -6,4 +6,8 @@ goTo.marker(); edit.insertLine(""); -verify.currentLineContentIs('class bar {'); +verify.currentFileContentIs( +`class foo { } +class bar { +} +// new line here`); From 5fd16cae18a653d3f4e6a82d0bb5d1f216d00b4f Mon Sep 17 00:00:00 2001 From: Mine Starks Date: Tue, 11 Jul 2017 13:55:34 -0700 Subject: [PATCH 29/50] format error message with newlines --- src/harness/fourslash.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 9688c565f57..7b5f53298ca 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2355,7 +2355,7 @@ namespace FourSlash { private applyCodeActions(actions: ts.CodeAction[], index?: number): void { if (index === undefined) { if (!(actions && actions.length === 1)) { - this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `"${a.description}"`).join(", ") : "" }`); + this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `\r\n "${a.description}"`) : "" }`); } index = 0; } From bb063f1b5c13e830ee8fbcd5c3899b769804cad1 Mon Sep 17 00:00:00 2001 From: Mine Starks Date: Tue, 11 Jul 2017 14:15:43 -0700 Subject: [PATCH 30/50] Remove incorrect comment --- src/services/codefixes/fixUnusedIdentifier.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index 51c8a59627d..6335ebb226b 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -94,8 +94,6 @@ namespace ts.codefix { return [deleteNodeInList(parent)]; } - // handle case where "import d, * as ns from './file'" - // or "'import {a, b as ns} from './file'" case SyntaxKind.ImportClause: // this covers both 'import |d|' and 'import |d,| *' const importClause = parent; if (!importClause.namedBindings) { // |import d from './file'| From 80b64de1e45bc37df699b48d2a7ca452f711545b Mon Sep 17 00:00:00 2001 From: Mine Starks Date: Tue, 11 Jul 2017 14:50:55 -0700 Subject: [PATCH 31/50] Fix comment behavior in remove unused named bindings --- src/services/codefixes/fixUnusedIdentifier.ts | 3 +-- tests/cases/fourslash/unusedImports14FS.ts | 12 ++++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 tests/cases/fourslash/unusedImports14FS.ts diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index 6335ebb226b..dd4c030b69d 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -128,8 +128,7 @@ namespace ts.codefix { // import d|, { a }| from './file' const previousToken = getTokenAtPosition(sourceFile, namedBindings.pos - 1, /*includeJsDocComment*/ false); if (previousToken && previousToken.kind === SyntaxKind.CommaToken) { - const startPosition = textChanges.getAdjustedStartPosition(sourceFile, previousToken, {}, textChanges.Position.FullStart); - return [deleteRange({ pos: startPosition, end: namedBindings.end })]; + return [deleteRange({ pos: previousToken.getStart(), end: namedBindings.end })]; } return undefined; } diff --git a/tests/cases/fourslash/unusedImports14FS.ts b/tests/cases/fourslash/unusedImports14FS.ts new file mode 100644 index 00000000000..90bd2c50587 --- /dev/null +++ b/tests/cases/fourslash/unusedImports14FS.ts @@ -0,0 +1,12 @@ +/// + +// @noUnusedLocals: true +// @Filename: file2.ts +//// [| import /* 1 */ A /* 2 */, /* 3 */ { x } from './a'; |] +//// console.log(A); + +// @Filename: file1.ts +//// export default 10; +//// export var x = 10; + +verify.rangeAfterCodeFix("import /* 1 */ A /* 2 */ from './a';"); \ No newline at end of file From 3915d46913f753911ea1b86a996b25c7befcac53 Mon Sep 17 00:00:00 2001 From: Mine Starks Date: Tue, 11 Jul 2017 15:10:04 -0700 Subject: [PATCH 32/50] Fix case where we can return [undefined] --- src/services/codefixes/fixUnusedIdentifier.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index dd4c030b69d..18491aaba26 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -25,15 +25,15 @@ namespace ts.codefix { return [deleteNode(token.parent)]; default: - return [deleteDefault()]; + return deleteDefault(); } - function deleteDefault() { + function deleteDefault(): CodeAction[] | undefined { if (isDeclarationName(token)) { - return deleteNode(token.parent); + return [deleteNode(token.parent)]; } else if (isLiteralComputedPropertyDeclarationName(token)) { - return deleteNode(token.parent.parent); + return [deleteNode(token.parent.parent)]; } else { return undefined; @@ -117,7 +117,7 @@ namespace ts.codefix { return deleteNamedImportBinding(parent); default: - return [deleteDefault()]; + return deleteDefault(); } } From 325f4b84cf6cc6f88aa023db91ffb4f3e3fada4e Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 11 Jul 2017 15:32:31 -0700 Subject: [PATCH 33/50] Addressed feedback. --- src/compiler/core.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 42faf0b062e..97b642d8920 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -703,7 +703,8 @@ namespace ts { export function sum, K extends string>(array: T[], prop: K): number { let result = 0; for (const v of array) { - result += (v[prop] as number); + // Note: we need the following type assertion because of GH #17069 + result += v[prop] as number; } return result; } From 0694a38728c81d45c344c2e88538ada3f8a0c0a2 Mon Sep 17 00:00:00 2001 From: Mine Starks Date: Tue, 11 Jul 2017 16:05:10 -0700 Subject: [PATCH 34/50] Use platform agnostic newline --- src/harness/fourslash.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 7b5f53298ca..e5dc3b0023a 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2355,7 +2355,7 @@ namespace FourSlash { private applyCodeActions(actions: ts.CodeAction[], index?: number): void { if (index === undefined) { if (!(actions && actions.length === 1)) { - this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `\r\n "${a.description}"`) : "" }`); + this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `${Harness.IO.newLine()} "${a.description}"`) : "" }`); } index = 0; } From 08030c7d0279e4f7d0215e55cac000947d5fe8c9 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 11 Jul 2017 17:39:33 -0700 Subject: [PATCH 35/50] Convert most of core.ts to accept ReadonlyArray (#17092) * Convert most of core.ts to accept ReadonlyArray * Fix lint * Fix isArray --- src/compiler/checker.ts | 4 +- src/compiler/commandLineParser.ts | 4 +- src/compiler/core.ts | 147 ++++++++++-------- src/compiler/sys.ts | 6 +- src/compiler/transformers/es2015.ts | 2 +- src/compiler/transformers/jsx.ts | 2 +- src/compiler/transformers/ts.ts | 2 +- src/compiler/types.ts | 2 +- src/compiler/utilities.ts | 2 +- src/harness/fourslash.ts | 4 +- src/harness/harness.ts | 2 +- src/harness/harnessLanguageService.ts | 2 +- src/harness/loggedIO.ts | 8 +- .../unittests/tsserverProjectSystem.ts | 14 +- src/harness/virtualFileSystem.ts | 2 +- src/server/builder.ts | 2 +- src/server/lsHost.ts | 2 +- src/services/jsTyping.ts | 2 +- src/services/pathCompletions.ts | 8 +- src/services/shims.ts | 4 +- src/services/types.ts | 2 +- 21 files changed, 117 insertions(+), 106 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5c0ca810a70..cc1504f8b9d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2010,7 +2010,7 @@ namespace ts { } function getAccessibleSymbolChainFromSymbolTableWorker(symbols: SymbolTable, visitedSymbolTables: SymbolTable[]): Symbol[] { - if (contains(visitedSymbolTables, symbols)) { + if (contains(visitedSymbolTables, symbols)) { return undefined; } visitedSymbolTables.push(symbols); @@ -20918,7 +20918,7 @@ namespace ts { } } - function areTypeParametersIdentical(declarations: (ClassDeclaration | InterfaceDeclaration)[], typeParameters: TypeParameter[]) { + function areTypeParametersIdentical(declarations: ReadonlyArray, typeParameters: TypeParameter[]) { const maxTypeArgumentCount = length(typeParameters); const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters); diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 0f9eba8d4fe..4f1210793bf 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -2136,7 +2136,7 @@ namespace ts { * @param extensionPriority The priority of the extension. * @param context The expansion context. */ - function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map, wildcardFiles: Map, extensions: string[], keyMapper: (value: string) => string) { + function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map, wildcardFiles: Map, extensions: ReadonlyArray, keyMapper: (value: string) => string) { const extensionPriority = getExtensionPriority(file, extensions); const adjustedExtensionPriority = adjustExtensionPriority(extensionPriority, extensions); for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) { @@ -2158,7 +2158,7 @@ namespace ts { * @param extensionPriority The priority of the extension. * @param context The expansion context. */ - function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map, extensions: string[], keyMapper: (value: string) => string) { + function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map, extensions: ReadonlyArray, keyMapper: (value: string) => string) { const extensionPriority = getExtensionPriority(file, extensions); const nextExtensionPriority = getNextLowestExtensionPriority(extensionPriority, extensions); for (let i = nextExtensionPriority; i < extensions.length; i++) { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 745cce8a6cd..efa354b4963 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -53,7 +53,7 @@ namespace ts { } /* @internal */ - export function createSymbolTable(symbols?: Symbol[]): SymbolTable { + export function createSymbolTable(symbols?: ReadonlyArray): SymbolTable { const result = createMap() as SymbolTable; if (symbols) { for (const symbol of symbols) { @@ -88,7 +88,7 @@ namespace ts { class MapIterator { private data: MapLike; - private keys: string[]; + private keys: ReadonlyArray; private index = 0; private selector: (data: MapLike, key: string) => U; constructor(data: MapLike, selector: (data: MapLike, key: string) => U) { @@ -175,7 +175,7 @@ namespace ts { GreaterThan = 1 } - export function length(array: any[]) { + export function length(array: ReadonlyArray) { return array ? array.length : 0; } @@ -184,7 +184,7 @@ namespace ts { * returns a truthy value, then returns that value. * If no such value is found, the callback is applied to each element of array and undefined is returned. */ - export function forEach(array: T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { + export function forEach(array: ReadonlyArray | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { if (array) { for (let i = 0; i < array.length; i++) { const result = callback(array[i], i); @@ -217,14 +217,14 @@ namespace ts { return undefined; } - export function zipWith(arrayA: T[], arrayB: U[], callback: (a: T, b: U, index: number) => void): void { + export function zipWith(arrayA: ReadonlyArray, arrayB: ReadonlyArray, callback: (a: T, b: U, index: number) => void): void { Debug.assert(arrayA.length === arrayB.length); for (let i = 0; i < arrayA.length; i++) { callback(arrayA[i], arrayB[i], i); } } - export function zipToMap(keys: string[], values: T[]): Map { + export function zipToMap(keys: ReadonlyArray, values: ReadonlyArray): Map { Debug.assert(keys.length === values.length); const map = createMap(); for (let i = 0; i < keys.length; ++i) { @@ -238,7 +238,7 @@ namespace ts { * returns a falsey value, then returns false. * If no such value is found, the callback is applied to each element of array and `true` is returned. */ - export function every(array: T[], callback: (element: T, index: number) => boolean): boolean { + export function every(array: ReadonlyArray, callback: (element: T, index: number) => boolean): boolean { if (array) { for (let i = 0; i < array.length; i++) { if (!callback(array[i], i)) { @@ -251,7 +251,7 @@ namespace ts { } /** Works like Array.prototype.find, returning `undefined` if no element satisfying the predicate is found. */ - export function find(array: T[], predicate: (element: T, index: number) => boolean): T | undefined { + export function find(array: ReadonlyArray, predicate: (element: T, index: number) => boolean): T | undefined { for (let i = 0; i < array.length; i++) { const value = array[i]; if (predicate(value, i)) { @@ -262,7 +262,7 @@ namespace ts { } /** Works like Array.prototype.findIndex, returning `-1` if no element satisfying the predicate is found. */ - export function findIndex(array: T[], predicate: (element: T, index: number) => boolean): number { + export function findIndex(array: ReadonlyArray, predicate: (element: T, index: number) => boolean): number { for (let i = 0; i < array.length; i++) { if (predicate(array[i], i)) { return i; @@ -275,7 +275,7 @@ namespace ts { * Returns the first truthy result of `callback`, or else fails. * This is like `forEach`, but never returns undefined. */ - export function findMap(array: T[], callback: (element: T, index: number) => U | undefined): U { + export function findMap(array: ReadonlyArray, callback: (element: T, index: number) => U | undefined): U { for (let i = 0; i < array.length; i++) { const result = callback(array[i], i); if (result) { @@ -285,7 +285,7 @@ namespace ts { Debug.fail(); } - export function contains(array: T[], value: T): boolean { + export function contains(array: ReadonlyArray, value: T): boolean { if (array) { for (const v of array) { if (v === value) { @@ -296,7 +296,7 @@ namespace ts { return false; } - export function indexOf(array: T[], value: T): number { + export function indexOf(array: ReadonlyArray, value: T): number { if (array) { for (let i = 0; i < array.length; i++) { if (array[i] === value) { @@ -307,7 +307,7 @@ namespace ts { return -1; } - export function indexOfAnyCharCode(text: string, charCodes: number[], start?: number): number { + export function indexOfAnyCharCode(text: string, charCodes: ReadonlyArray, start?: number): number { for (let i = start || 0; i < text.length; i++) { if (contains(charCodes, text.charCodeAt(i))) { return i; @@ -316,7 +316,7 @@ namespace ts { return -1; } - export function countWhere(array: T[], predicate: (x: T, i: number) => boolean): number { + export function countWhere(array: ReadonlyArray, predicate: (x: T, i: number) => boolean): number { let count = 0; if (array) { for (let i = 0; i < array.length; i++) { @@ -335,6 +335,8 @@ namespace ts { */ export function filter(array: T[], f: (x: T) => x is U): U[]; export function filter(array: T[], f: (x: T) => boolean): T[]; + export function filter(array: ReadonlyArray, f: (x: T) => x is U): ReadonlyArray; + export function filter(array: ReadonlyArray, f: (x: T) => boolean): ReadonlyArray; export function filter(array: T[], f: (x: T) => boolean): T[] { if (array) { const len = array.length; @@ -382,7 +384,7 @@ namespace ts { array.length = outIndex; } - export function map(array: T[], f: (x: T, i: number) => U): U[] { + export function map(array: ReadonlyArray, f: (x: T, i: number) => U): U[] { let result: U[]; if (array) { result = []; @@ -394,6 +396,8 @@ namespace ts { } // Maps from T to T and avoids allocation if all elements map to themselves + export function sameMap(array: T[], f: (x: T, i: number) => T): T[]; + export function sameMap(array: ReadonlyArray, f: (x: T, i: number) => T): ReadonlyArray; export function sameMap(array: T[], f: (x: T, i: number) => T): T[] { let result: T[]; if (array) { @@ -419,7 +423,7 @@ namespace ts { * * @param array The array to flatten. */ - export function flatten(array: (T | T[])[]): T[] { + export function flatten(array: ReadonlyArray>): T[] { let result: T[]; if (array) { result = []; @@ -444,7 +448,7 @@ namespace ts { * @param array The array to map. * @param mapfn The callback used to map the result into one or more values. */ - export function flatMap(array: T[] | undefined, mapfn: (x: T, i: number) => U | U[] | undefined): U[] | undefined { + export function flatMap(array: ReadonlyArray | undefined, mapfn: (x: T, i: number) => U | ReadonlyArray | undefined): U[] | undefined { let result: U[]; if (array) { result = []; @@ -470,6 +474,8 @@ namespace ts { * @param array The array to map. * @param mapfn The callback used to map the result into one or more values. */ + export function sameFlatMap(array: T[], mapfn: (x: T, i: number) => T | ReadonlyArray): T[]; + export function sameFlatMap(array: ReadonlyArray, mapfn: (x: T, i: number) => T | ReadonlyArray): ReadonlyArray; export function sameFlatMap(array: T[], mapfn: (x: T, i: number) => T | T[]): T[] { let result: T[]; if (array) { @@ -508,7 +514,7 @@ namespace ts { * Computes the first matching span of elements and returns a tuple of the first span * and the remaining elements. */ - export function span(array: T[], f: (x: T, i: number) => boolean): [T[], T[]] { + export function span(array: ReadonlyArray, f: (x: T, i: number) => boolean): [T[], T[]] { if (array) { for (let i = 0; i < array.length; i++) { if (!f(array[i], i)) { @@ -528,7 +534,7 @@ namespace ts { * @param keyfn A callback used to select the key for an element. * @param mapfn A callback used to map a contiguous chunk of values to a single value. */ - export function spanMap(array: T[], keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] { + export function spanMap(array: ReadonlyArray, keyfn: (x: T, i: number) => K, mapfn: (chunk: T[], key: K, start: number, end: number) => U): U[] { let result: U[]; if (array) { result = []; @@ -581,7 +587,7 @@ namespace ts { return result; } - export function some(array: T[], predicate?: (value: T) => boolean): boolean { + export function some(array: ReadonlyArray, predicate?: (value: T) => boolean): boolean { if (array) { if (predicate) { for (const v of array) { @@ -597,6 +603,8 @@ namespace ts { return false; } + export function concatenate(array1: T[], array2: T[]): T[]; + export function concatenate(array1: ReadonlyArray, array2: ReadonlyArray): ReadonlyArray; export function concatenate(array1: T[], array2: T[]): T[] { if (!some(array2)) return array1; if (!some(array1)) return array2; @@ -604,7 +612,7 @@ namespace ts { } // TODO: fixme (N^2) - add optional comparer so collection can be sorted before deduplication. - export function deduplicate(array: T[], areEqual?: (a: T, b: T) => boolean): T[] { + export function deduplicate(array: ReadonlyArray, areEqual?: (a: T, b: T) => boolean): T[] { let result: T[]; if (array) { result = []; @@ -661,6 +669,8 @@ namespace ts { /** * Compacts an array, removing any falsey elements. */ + export function compact(array: T[]): T[]; + export function compact(array: ReadonlyArray): ReadonlyArray; export function compact(array: T[]): T[] { let result: T[]; if (array) { @@ -727,7 +737,7 @@ namespace ts { * Gets the actual offset into an array for a relative offset. Negative offsets indicate a * position offset from the end of the array. */ - function toOffset(array: any[], offset: number) { + function toOffset(array: ReadonlyArray, offset: number) { return offset < 0 ? array.length + offset : offset; } @@ -741,7 +751,7 @@ namespace ts { * @param start The offset in `from` at which to start copying values. * @param end The offset in `from` at which to stop copying values (non-inclusive). */ - export function addRange(to: T[] | undefined, from: T[] | undefined, start?: number, end?: number): T[] | undefined { + export function addRange(to: T[] | undefined, from: ReadonlyArray | undefined, start?: number, end?: number): T[] | undefined { if (from === undefined) return to; if (to === undefined) return from.slice(start, end); start = start === undefined ? 0 : toOffset(from, start); @@ -758,14 +768,14 @@ namespace ts { /** * Stable sort of an array. Elements equal to each other maintain their relative position in the array. */ - export function stableSort(array: T[], comparer: (x: T, y: T) => Comparison = compareValues) { + export function stableSort(array: ReadonlyArray, comparer: (x: T, y: T) => Comparison = compareValues) { return array .map((_, i) => i) // create array of indices .sort((x, y) => comparer(array[x], array[y]) || compareValues(x, y)) // sort indices by value then position .map(i => array[i]); // get sorted array } - export function rangeEquals(array1: T[], array2: T[], pos: number, end: number) { + export function rangeEquals(array1: ReadonlyArray, array2: ReadonlyArray, pos: number, end: number) { while (pos < end) { if (array1[pos] !== array2[pos]) { return false; @@ -779,7 +789,7 @@ namespace ts { * Returns the element at a specific offset in an array if non-empty, `undefined` otherwise. * A negative offset indicates the element should be retrieved from the end of the array. */ - export function elementAt(array: T[] | undefined, offset: number): T | undefined { + export function elementAt(array: ReadonlyArray | undefined, offset: number): T | undefined { if (array) { offset = toOffset(array, offset); if (offset < array.length) { @@ -792,21 +802,21 @@ namespace ts { /** * Returns the first element of an array if non-empty, `undefined` otherwise. */ - export function firstOrUndefined(array: T[]): T | undefined { + export function firstOrUndefined(array: ReadonlyArray): T | undefined { return elementAt(array, 0); } /** * Returns the last element of an array if non-empty, `undefined` otherwise. */ - export function lastOrUndefined(array: T[]): T | undefined { + export function lastOrUndefined(array: ReadonlyArray): T | undefined { return elementAt(array, -1); } /** * Returns the only element of an array if it contains only one element, `undefined` otherwise. */ - export function singleOrUndefined(array: T[]): T | undefined { + export function singleOrUndefined(array: ReadonlyArray): T | undefined { return array && array.length === 1 ? array[0] : undefined; @@ -816,13 +826,15 @@ namespace ts { * Returns the only element of an array if it contains only one element; otheriwse, returns the * array. */ + export function singleOrMany(array: T[]): T | T[]; + export function singleOrMany(array: ReadonlyArray): T | ReadonlyArray; export function singleOrMany(array: T[]): T | T[] { return array && array.length === 1 ? array[0] : array; } - export function replaceElement(array: T[], index: number, value: T): T[] { + export function replaceElement(array: ReadonlyArray, index: number, value: T): T[] { const result = array.slice(0); result[index] = value; return result; @@ -835,7 +847,7 @@ namespace ts { * @param array A sorted array whose first element must be no larger than number * @param number The value to be searched for in the array. */ - export function binarySearch(array: T[], value: T, comparer?: (v1: T, v2: T) => number, offset?: number): number { + export function binarySearch(array: ReadonlyArray, value: T, comparer?: (v1: T, v2: T) => number, offset?: number): number { if (!array || array.length === 0) { return -1; } @@ -864,8 +876,8 @@ namespace ts { return ~low; } - export function reduceLeft(array: T[], f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U; - export function reduceLeft(array: T[], f: (memo: T, value: T, i: number) => T): T; + export function reduceLeft(array: ReadonlyArray, f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U; + export function reduceLeft(array: ReadonlyArray, f: (memo: T, value: T, i: number) => T): T; export function reduceLeft(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T, start?: number, count?: number): T { if (array && array.length > 0) { const size = array.length; @@ -890,8 +902,8 @@ namespace ts { return initial; } - export function reduceRight(array: T[], f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U; - export function reduceRight(array: T[], f: (memo: T, value: T, i: number) => T): T; + export function reduceRight(array: ReadonlyArray, f: (memo: U, value: T, i: number) => U, initial: U, start?: number, count?: number): U; + export function reduceRight(array: ReadonlyArray, f: (memo: T, value: T, i: number) => T): T; export function reduceRight(array: T[], f: (memo: T, value: T, i: number) => T, initial?: T, start?: number, count?: number): T { if (array) { const size = array.length; @@ -1058,9 +1070,9 @@ namespace ts { * the same key with the given 'makeKey' function, then the element with the higher * index in the array will be the one associated with the produced key. */ - export function arrayToMap(array: T[], makeKey: (value: T) => string): Map; - export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue: (value: T) => U): Map; - export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue?: (value: T) => U): Map { + export function arrayToMap(array: ReadonlyArray, makeKey: (value: T) => string): Map; + export function arrayToMap(array: ReadonlyArray, makeKey: (value: T) => string, makeValue: (value: T) => U): Map; + export function arrayToMap(array: ReadonlyArray, makeKey: (value: T) => string, makeValue?: (value: T) => U): Map { const result = createMap(); for (const value of array) { result.set(makeKey(value), makeValue ? makeValue(value) : value); @@ -1073,9 +1085,9 @@ namespace ts { * * @param array the array of input elements. */ - export function arrayToSet(array: string[]): Map; - export function arrayToSet(array: T[], makeKey: (value: T) => string): Map; - export function arrayToSet(array: any[], makeKey?: (value: any) => string): Map { + export function arrayToSet(array: ReadonlyArray): Map; + export function arrayToSet(array: ReadonlyArray, makeKey: (value: T) => string): Map; + export function arrayToSet(array: ReadonlyArray, makeKey?: (value: any) => string): Map { return arrayToMap(array, makeKey || (s => s), () => true); } @@ -1158,7 +1170,7 @@ namespace ts { /** * Tests whether a value is an array. */ - export function isArray(value: any): value is any[] { + export function isArray(value: any): value is ReadonlyArray { return Array.isArray ? Array.isArray(value) : value instanceof Array; } @@ -1628,7 +1640,7 @@ namespace ts { return getNormalizedPathFromPathComponents(getNormalizedPathComponents(fileName, currentDirectory)); } - export function getNormalizedPathFromPathComponents(pathComponents: string[]) { + export function getNormalizedPathFromPathComponents(pathComponents: ReadonlyArray) { if (pathComponents && pathComponents.length) { return pathComponents[0] + pathComponents.slice(1).join(directorySeparator); } @@ -1830,7 +1842,7 @@ namespace ts { } /* @internal */ - export function fileExtensionIsOneOf(path: string, extensions: string[]): boolean { + export function fileExtensionIsOneOf(path: string, extensions: ReadonlyArray): boolean { for (const extension of extensions) { if (fileExtensionIs(path, extension)) { return true; @@ -1855,7 +1867,7 @@ namespace ts { const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*"; const singleAsteriskRegexFragmentOther = "[^/]*"; - export function getRegularExpressionForWildcard(specs: string[], basePath: string, usage: "files" | "directories" | "exclude"): string | undefined { + export function getRegularExpressionForWildcard(specs: ReadonlyArray, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined { const patterns = getRegularExpressionsForWildcards(specs, basePath, usage); if (!patterns || !patterns.length) { return undefined; @@ -1867,7 +1879,7 @@ namespace ts { return `^(${pattern})${terminator}`; } - function getRegularExpressionsForWildcards(specs: string[], basePath: string, usage: "files" | "directories" | "exclude"): string[] | undefined { + function getRegularExpressionsForWildcards(specs: ReadonlyArray, basePath: string, usage: "files" | "directories" | "exclude"): string[] | undefined { if (specs === undefined || specs.length === 0) { return undefined; } @@ -1972,21 +1984,21 @@ namespace ts { } export interface FileSystemEntries { - files: string[]; - directories: string[]; + files: ReadonlyArray; + directories: ReadonlyArray; } export interface FileMatcherPatterns { /** One pattern for each "include" spec. */ - includeFilePatterns: string[]; + includeFilePatterns: ReadonlyArray; /** One pattern matching one of any of the "include" specs. */ includeFilePattern: string; includeDirectoryPattern: string; excludePattern: string; - basePaths: string[]; + basePaths: ReadonlyArray; } - export function getFileMatcherPatterns(path: string, excludes: string[], includes: string[], useCaseSensitiveFileNames: boolean, currentDirectory: string): FileMatcherPatterns { + export function getFileMatcherPatterns(path: string, excludes: ReadonlyArray, includes: ReadonlyArray, useCaseSensitiveFileNames: boolean, currentDirectory: string): FileMatcherPatterns { path = normalizePath(path); currentDirectory = normalizePath(currentDirectory); const absolutePath = combinePaths(currentDirectory, path); @@ -2000,7 +2012,7 @@ namespace ts { }; } - export function matchFiles(path: string, extensions: string[], excludes: string[], includes: string[], useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries): string[] { + export function matchFiles(path: string, extensions: ReadonlyArray, excludes: ReadonlyArray, includes: ReadonlyArray, useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined, getFileSystemEntries: (path: string) => FileSystemEntries): string[] { path = normalizePath(path); currentDirectory = normalizePath(currentDirectory); @@ -2020,7 +2032,7 @@ namespace ts { visitDirectory(basePath, combinePaths(currentDirectory, basePath), depth); } - return flatten(results); + return flatten(results); function visitDirectory(path: string, absolutePath: string, depth: number | undefined) { let { files, directories } = getFileSystemEntries(path); @@ -2064,7 +2076,7 @@ namespace ts { /** * Computes the unique non-wildcard base paths amongst the provided include patterns. */ - function getBasePaths(path: string, includes: string[], useCaseSensitiveFileNames: boolean) { + function getBasePaths(path: string, includes: ReadonlyArray, useCaseSensitiveFileNames: boolean) { // Storage for our results in the form of literal paths (e.g. the paths as written by the user). const basePaths: string[] = [path]; @@ -2136,13 +2148,13 @@ namespace ts { /** * List of supported extensions in order of file resolution precedence. */ - export const supportedTypeScriptExtensions = [Extension.Ts, Extension.Tsx, Extension.Dts]; + export const supportedTypeScriptExtensions: ReadonlyArray = [Extension.Ts, Extension.Tsx, Extension.Dts]; /** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */ - export const supportedTypescriptExtensionsForExtractExtension = [Extension.Dts, Extension.Ts, Extension.Tsx]; - export const supportedJavascriptExtensions = [Extension.Js, Extension.Jsx]; - const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions); + export const supportedTypescriptExtensionsForExtractExtension: ReadonlyArray = [Extension.Dts, Extension.Ts, Extension.Tsx]; + export const supportedJavascriptExtensions: ReadonlyArray = [Extension.Js, Extension.Jsx]; + const allSupportedExtensions = [...supportedTypeScriptExtensions, ...supportedJavascriptExtensions]; - export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: JsFileExtensionInfo[]): string[] { + export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: ReadonlyArray): ReadonlyArray { const needAllExtensions = options && options.allowJs; if (!extraFileExtensions || extraFileExtensions.length === 0 || !needAllExtensions) { return needAllExtensions ? allSupportedExtensions : supportedTypeScriptExtensions; @@ -2164,7 +2176,7 @@ namespace ts { return forEach(supportedTypeScriptExtensions, extension => fileExtensionIs(fileName, extension)); } - export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: JsFileExtensionInfo[]) { + export function isSupportedSourceFileName(fileName: string, compilerOptions?: CompilerOptions, extraFileExtensions?: ReadonlyArray) { if (!fileName) { return false; } for (const extension of getSupportedExtensions(compilerOptions, extraFileExtensions)) { @@ -2188,7 +2200,7 @@ namespace ts { Lowest = DeclarationAndJavaScriptFiles, } - export function getExtensionPriority(path: string, supportedExtensions: string[]): ExtensionPriority { + export function getExtensionPriority(path: string, supportedExtensions: ReadonlyArray): ExtensionPriority { for (let i = supportedExtensions.length - 1; i >= 0; i--) { if (fileExtensionIs(path, supportedExtensions[i])) { return adjustExtensionPriority(i, supportedExtensions); @@ -2203,7 +2215,7 @@ namespace ts { /** * Adjusts an extension priority to be the highest priority within the same range. */ - export function adjustExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: string[]): ExtensionPriority { + export function adjustExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: ReadonlyArray): ExtensionPriority { if (extensionPriority < ExtensionPriority.DeclarationAndJavaScriptFiles) { return ExtensionPriority.TypeScriptFiles; } @@ -2212,12 +2224,13 @@ namespace ts { } else { return supportedExtensions.length; - } } + } + } /** * Gets the next lowest extension priority for a given priority. */ - export function getNextLowestExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: string[]): ExtensionPriority { + export function getNextLowestExtensionPriority(extensionPriority: ExtensionPriority, supportedExtensions: ReadonlyArray): ExtensionPriority { if (extensionPriority < ExtensionPriority.DeclarationAndJavaScriptFiles) { return ExtensionPriority.DeclarationAndJavaScriptFiles; } @@ -2406,7 +2419,7 @@ namespace ts { * (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.) */ /* @internal */ - export function matchPatternOrExact(patternStrings: string[], candidate: string): string | Pattern | undefined { + export function matchPatternOrExact(patternStrings: ReadonlyArray, candidate: string): string | Pattern | undefined { const patterns: Pattern[] = []; for (const patternString of patternStrings) { const pattern = tryParsePattern(patternString); @@ -2439,7 +2452,7 @@ namespace ts { /** Return the object corresponding to the best pattern to match `candidate`. */ /* @internal */ - export function findBestPatternMatch(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined { + export function findBestPatternMatch(values: ReadonlyArray, getPattern: (value: T) => Pattern, candidate: string): T | undefined { let matchedValue: T | undefined = undefined; // use length of prefix as betterness criteria let longestMatchPrefixLength = -1; @@ -2495,7 +2508,7 @@ namespace ts { Debug.fail(`File ${path} has unknown extension.`); } export function tryGetExtensionFromPath(path: string): Extension | undefined { - return find(supportedTypescriptExtensionsForExtractExtension, e => fileExtensionIs(path, e)) || find(supportedJavascriptExtensions, e => fileExtensionIs(path, e)); + return find(supportedTypescriptExtensionsForExtractExtension, e => fileExtensionIs(path, e)) || find(supportedJavascriptExtensions, e => fileExtensionIs(path, e)); } export function isCheckJsEnabledForFile(sourceFile: SourceFile, compilerOptions: CompilerOptions) { diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index d5102da0b3f..3e3ff90c00c 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -39,7 +39,7 @@ namespace ts { getExecutingFilePath(): string; getCurrentDirectory(): string; getDirectories(path: string): string[]; - readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[]; + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; getModifiedTime?(path: string): Date; /** * This should be cryptographically secure. @@ -100,7 +100,7 @@ namespace ts { readFile(path: string): string; writeFile(path: string, contents: string): void; getDirectories(path: string): string[]; - readDirectory(path: string, extensions?: string[], basePaths?: string[], excludeEx?: string, includeFileEx?: string, includeDirEx?: string): string[]; + readDirectory(path: string, extensions?: ReadonlyArray, basePaths?: ReadonlyArray, excludeEx?: string, includeFileEx?: string, includeDirEx?: string): string[]; watchFile?(path: string, callback: FileWatcherCallback): FileWatcher; watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; realpath(path: string): string; @@ -287,7 +287,7 @@ namespace ts { } } - function readDirectory(path: string, extensions?: string[], excludes?: string[], includes?: string[], depth?: number): string[] { + function readDirectory(path: string, extensions?: ReadonlyArray, excludes?: ReadonlyArray, includes?: ReadonlyArray, depth?: number): string[] { return matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, process.cwd(), depth, getAccessibleFileSystemEntries); } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 3de73440193..19f9d00c656 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -3579,7 +3579,7 @@ namespace ts { // Map spans of spread expressions into their expressions and spans of other // expressions into an array literal. const numElements = elements.length; - const segments = flatten( + const segments = flatten( spanMap(elements, partitionSpread, (partition, visitPartition, _start, end) => visitPartition(partition, multiLine, hasTrailingComma && end === numElements) ) diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index eab5f69bd4f..ca22cb701d0 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -88,7 +88,7 @@ namespace ts { else { // Map spans of JsxAttribute nodes into object literals and spans // of JsxSpreadAttribute nodes into expressions. - const segments = flatten( + const segments = flatten( spanMap(attrs, isJsxSpreadAttribute, (attrs, isSpread) => isSpread ? map(attrs, transformJsxSpreadAttributeToExpression) : createObjectLiteral(map(attrs, transformJsxAttributeToObjectLiteralElement)) diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 5d6efa489b5..5fd1b7b62da 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1377,7 +1377,7 @@ namespace ts { const decoratorExpressions: Expression[] = []; addRange(decoratorExpressions, map(allDecorators.decorators, transformDecorator)); - addRange(decoratorExpressions, flatMap(allDecorators.parameters, transformDecoratorsOfParameter)); + addRange(decoratorExpressions, flatMap(allDecorators.parameters, transformDecoratorsOfParameter)); addTypeMetadata(node, container, decoratorExpressions); return decoratorExpressions; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index aefcce6d0bb..35320318ef9 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2406,7 +2406,7 @@ namespace ts { export interface ParseConfigHost { useCaseSensitiveFileNames: boolean; - readDirectory(rootDir: string, extensions: string[], excludes: string[], includes: string[], depth: number): string[]; + readDirectory(rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray, includes: ReadonlyArray, depth: number): string[]; /** * Gets a value indicating whether the specified path exists and is a file. diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 280a2f73a0e..77e3b776abd 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3334,7 +3334,7 @@ namespace ts { } } - return stableSort(result, (x, y) => compareValues(x[0], y[0])); + return stableSort<[number, string]>(result, (x, y) => compareValues(x[0], y[0])); } export function formatSyntaxKind(kind: SyntaxKind): string { diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 01caa3d70ef..60208fe8ae3 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -219,7 +219,7 @@ namespace FourSlash { // Add input file which has matched file name with the given reference-file path. // This is necessary when resolveReference flag is specified - private addMatchedInputFile(referenceFilePath: string, extensions: string[]) { + private addMatchedInputFile(referenceFilePath: string, extensions: ReadonlyArray) { const inputFiles = this.inputFiles; const languageServiceAdapterHost = this.languageServiceAdapterHost; if (!extensions) { @@ -605,7 +605,7 @@ namespace FourSlash { this.verifyGoToXPlain(arg0, endMarkerNames, getDefs); } else if (ts.isArray(arg0)) { - const pairs: [string | string[], string | string[]][] = arg0; + const pairs: ReadonlyArray<[string | string[], string | string[]]> = arg0; for (const [start, end] of pairs) { this.verifyGoToXPlain(start, end, getDefs); } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 026ff2de85f..d5218695f09 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -493,7 +493,7 @@ namespace Harness { args(): string[]; getExecutingFilePath(): string; exit(exitCode?: number): void; - readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[], depth?: number): string[]; + readDirectory(path: string, extension?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; tryEnableSourceMapsForHost?(): void; getEnvironmentVariable?(name: string): string; } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 82f0b87be94..339138ba30f 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -208,7 +208,7 @@ namespace Harness.LanguageService { const script = this.getScriptSnapshot(fileName); return script !== undefined; } - readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] { + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[] { return ts.matchFiles(path, extensions, exclude, include, /*useCaseSensitiveFileNames*/ false, this.getCurrentDirectory(), diff --git a/src/harness/loggedIO.ts b/src/harness/loggedIO.ts index d4a1e2e2bd0..f113b9ddb9d 100644 --- a/src/harness/loggedIO.ts +++ b/src/harness/loggedIO.ts @@ -64,11 +64,11 @@ interface IOLog { }[]; directoriesRead: { path: string, - extensions: string[], - exclude: string[], - include: string[], + extensions: ReadonlyArray, + exclude: ReadonlyArray, + include: ReadonlyArray, depth: number, - result: string[] + result: ReadonlyArray, }[]; } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index b34669c2690..3dde03a092b 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -438,24 +438,22 @@ namespace ts.projectSystem { } } - readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] { + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[] { return ts.matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, (dir) => { - const result: FileSystemEntries = { - directories: [], - files: [] - }; + const directories: string[] = []; + const files: string[] = []; const dirEntry = this.fs.get(this.toPath(dir)); if (isFolder(dirEntry)) { dirEntry.entries.forEach((entry) => { if (isFolder(entry)) { - result.directories.push(entry.fullPath); + directories.push(entry.fullPath); } else if (isFile(entry)) { - result.files.push(entry.fullPath); + files.push(entry.fullPath); } }); } - return result; + return { directories, files }; }); } diff --git a/src/harness/virtualFileSystem.ts b/src/harness/virtualFileSystem.ts index 3904a27bb1c..da7784770df 100644 --- a/src/harness/virtualFileSystem.ts +++ b/src/harness/virtualFileSystem.ts @@ -216,7 +216,7 @@ namespace Utils { } } - readDirectory(path: string, extensions: string[], excludes: string[], includes: string[], depth: number) { + readDirectory(path: string, extensions: ReadonlyArray, excludes: ReadonlyArray, includes: ReadonlyArray, depth: number) { return ts.matchFiles(path, extensions, excludes, includes, this.useCaseSensitiveFileNames, this.currentDirectory, depth, (path: string) => this.getAccessibleFileSystemEntries(path)); } } diff --git a/src/server/builder.ts b/src/server/builder.ts index 7d35247e974..8fd8fbf1e65 100644 --- a/src/server/builder.ts +++ b/src/server/builder.ts @@ -277,7 +277,7 @@ namespace ts.server { const referencedFilePaths = this.project.getReferencedFiles(fileInfo.scriptInfo.path); if (referencedFilePaths.length > 0) { - return map(referencedFilePaths, f => this.getOrCreateFileInfo(f)).sort(ModuleBuilderFileInfo.compareFileInfos); + return map(referencedFilePaths, f => this.getOrCreateFileInfo(f)).sort(ModuleBuilderFileInfo.compareFileInfos); } return []; } diff --git a/src/server/lsHost.ts b/src/server/lsHost.ts index 55513f1a285..620697e1742 100644 --- a/src/server/lsHost.ts +++ b/src/server/lsHost.ts @@ -225,7 +225,7 @@ namespace ts.server { return this.host.directoryExists(path); } - readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] { + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[] { return this.host.readDirectory(path, extensions, exclude, include, depth); } diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index ff90d5bdb34..60b29b81883 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -12,7 +12,7 @@ namespace ts.JsTyping { directoryExists: (path: string) => boolean; fileExists: (fileName: string) => boolean; readFile: (path: string, encoding?: string) => string; - readDirectory: (rootDir: string, extensions: string[], excludes: string[], includes: string[], depth?: number) => string[]; + readDirectory: (rootDir: string, extensions: ReadonlyArray, excludes: ReadonlyArray, includes: ReadonlyArray, depth?: number) => string[]; } interface PackageJson { diff --git a/src/services/pathCompletions.ts b/src/services/pathCompletions.ts index 8addcf494bf..9b645280a1e 100644 --- a/src/services/pathCompletions.ts +++ b/src/services/pathCompletions.ts @@ -47,7 +47,7 @@ namespace ts.Completions.PathCompletions { return deduplicate(map(rootDirs, rootDirectory => combinePaths(rootDirectory, relativeDirectory))); } - function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): CompletionEntry[] { + function getCompletionEntriesForDirectoryFragmentWithRootDirs(rootDirs: string[], fragment: string, scriptPath: string, extensions: ReadonlyArray, includeExtensions: boolean, span: TextSpan, compilerOptions: CompilerOptions, host: LanguageServiceHost, exclude?: string): CompletionEntry[] { const basePath = compilerOptions.project || host.getCurrentDirectory(); const ignoreCase = !(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames()); const baseDirectories = getBaseDirectoriesFromRootDirs(rootDirs, basePath, scriptPath, ignoreCase); @@ -64,7 +64,7 @@ namespace ts.Completions.PathCompletions { /** * Given a path ending at a directory, gets the completions for the path, and filters for those entries containing the basename. */ - function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: string[], includeExtensions: boolean, span: TextSpan, host: LanguageServiceHost, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] { + function getCompletionEntriesForDirectoryFragment(fragment: string, scriptPath: string, extensions: ReadonlyArray, includeExtensions: boolean, span: TextSpan, host: LanguageServiceHost, exclude?: string, result: CompletionEntry[] = []): CompletionEntry[] { if (fragment === undefined) { fragment = ""; } @@ -185,7 +185,7 @@ namespace ts.Completions.PathCompletions { return result; } - function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: string[], host: LanguageServiceHost): string[] { + function getModulesForPathsPattern(fragment: string, baseUrl: string, pattern: string, fileExtensions: ReadonlyArray, host: LanguageServiceHost): string[] { if (host.readDirectory) { const parsed = hasZeroOrOneAsteriskCharacter(pattern) ? tryParsePattern(pattern) : undefined; if (parsed) { @@ -500,7 +500,7 @@ namespace ts.Completions.PathCompletions { return tryIOAndConsumeErrors(host, host.getDirectories, directoryName); } - function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: string[], exclude?: string[], include?: string[]): string[] { + function tryReadDirectory(host: LanguageServiceHost, path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray): string[] { return tryIOAndConsumeErrors(host, host.readDirectory, path, extensions, exclude, include); } diff --git a/src/services/shims.ts b/src/services/shims.ts index 0a0b2c82c5b..4ecd2e4e9b8 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -444,7 +444,7 @@ namespace ts { return this.shimHost.getDefaultLibFileName(JSON.stringify(options)); } - public readDirectory(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[] { + public readDirectory(path: string, extensions?: ReadonlyArray, exclude?: string[], include?: string[], depth?: number): string[] { const pattern = getFileMatcherPatterns(path, exclude, include, this.shimHost.useCaseSensitiveFileNames(), this.shimHost.getCurrentDirectory()); return JSON.parse(this.shimHost.readDirectory( @@ -483,7 +483,7 @@ namespace ts { } } - public readDirectory(rootDir: string, extensions: string[], exclude: string[], include: string[], depth?: number): string[] { + public readDirectory(rootDir: string, extensions: ReadonlyArray, exclude: ReadonlyArray, include: ReadonlyArray, depth?: number): string[] { const pattern = getFileMatcherPatterns(rootDir, exclude, include, this.shimHost.useCaseSensitiveFileNames(), this.shimHost.getCurrentDirectory()); return JSON.parse(this.shimHost.readDirectory( diff --git a/src/services/types.ts b/src/services/types.ts index d440ecab28e..bfb8a80b769 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -164,7 +164,7 @@ namespace ts { * LS host can optionally implement these methods to support completions for module specifiers. * Without these methods, only completions for ambient modules will be provided. */ - readDirectory?(path: string, extensions?: string[], exclude?: string[], include?: string[], depth?: number): string[]; + readDirectory?(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; readFile?(path: string, encoding?: string): string; fileExists?(path: string): boolean; From 815af7da175429166fff3ddfe54ccad74c6550b7 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 12 Jul 2017 07:45:02 -0700 Subject: [PATCH 36/50] getSwitchClauseTypes: exit early if getTypeOfSwitchClause is undefined (#16865) --- src/compiler/checker.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8bab67f7a38..e454b43d4a1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11066,8 +11066,14 @@ namespace ts { if (!links.switchTypes) { // If all case clauses specify expressions that have unit types, we return an array // of those unit types. Otherwise we return an empty array. - const types = map(switchStatement.caseBlock.clauses, getTypeOfSwitchClause); - links.switchTypes = !contains(types, undefined) ? types : emptyArray; + links.switchTypes = []; + for (const clause of switchStatement.caseBlock.clauses) { + const type = getTypeOfSwitchClause(clause); + if (type === undefined) { + return links.switchTypes = emptyArray; + } + links.switchTypes.push(type); + } } return links.switchTypes; } From 38db79d6662b0960922b13f7ebf00a94715b91aa Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 12 Jul 2017 09:59:29 -0700 Subject: [PATCH 37/50] buildTreeFromBottom: Really simplify loop (#17105) --- src/server/scriptVersionCache.ts | 34 ++++++++++++-------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/server/scriptVersionCache.ts b/src/server/scriptVersionCache.ts index 0862c13fec6..a710347161d 100644 --- a/src/server/scriptVersionCache.ts +++ b/src/server/scriptVersionCache.ts @@ -524,29 +524,18 @@ namespace ts.server { } private static buildTreeFromBottom(nodes: LineCollection[]): LineNode { - const interiorNodeCount = Math.ceil(nodes.length / lineCollectionCapacity); - const interiorNodes: LineNode[] = new Array(interiorNodeCount); + if (nodes.length < lineCollectionCapacity) { + return new LineNode(nodes); + } + + const interiorNodes = new Array(Math.ceil(nodes.length / lineCollectionCapacity)); let nodeIndex = 0; - for (let i = 0; i < interiorNodeCount; i++) { - const interiorNode = interiorNodes[i] = new LineNode(); - let charCount = 0; - let lineCount = 0; + for (let i = 0; i < interiorNodes.length; i++) { const end = Math.min(nodeIndex + lineCollectionCapacity, nodes.length); - for (; nodeIndex < end; nodeIndex++) { - const node = nodes[nodeIndex]; - interiorNode.add(node); - charCount += node.charCount(); - lineCount += node.lineCount(); - } - interiorNode.totalChars = charCount; - interiorNode.totalLines = lineCount; - } - if (interiorNodes.length === 1) { - return interiorNodes[0]; - } - else { - return this.buildTreeFromBottom(interiorNodes); + interiorNodes[i] = new LineNode(nodes.slice(nodeIndex, end)); + nodeIndex = end; } + return this.buildTreeFromBottom(interiorNodes); } static linesFromText(text: string) { @@ -575,7 +564,10 @@ namespace ts.server { export class LineNode implements LineCollection { totalChars = 0; totalLines = 0; - private children: LineCollection[] = []; + + constructor(private readonly children: LineCollection[] = []) { + if (children.length) this.updateCounts(); + } isLeaf() { return false; From a0c672ac026553bfec3290976fe356e2daa7ee34 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 12 Jul 2017 10:13:33 -0700 Subject: [PATCH 38/50] Add assertion that module / type reference names are defined (#17124) --- src/compiler/program.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 97f6f36f3a1..153e4506ea9 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -447,7 +447,7 @@ namespace ts { let moduleResolutionCache: ModuleResolutionCache; let resolveModuleNamesWorker: (moduleNames: string[], containingFile: string) => ResolvedModuleFull[]; if (host.resolveModuleNames) { - resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile).map(resolved => { + resolveModuleNamesWorker = (moduleNames, containingFile) => host.resolveModuleNames(checkAllDefined(moduleNames), containingFile).map(resolved => { // An older host may have omitted extension, in which case we should infer it from the file extension of resolvedFileName. if (!resolved || (resolved as ResolvedModuleFull).extension !== undefined) { return resolved as ResolvedModuleFull; @@ -460,16 +460,16 @@ namespace ts { else { moduleResolutionCache = createModuleResolutionCache(currentDirectory, x => host.getCanonicalFileName(x)); const loader = (moduleName: string, containingFile: string) => resolveModuleName(moduleName, containingFile, options, host, moduleResolutionCache).resolvedModule; - resolveModuleNamesWorker = (moduleNames, containingFile) => loadWithLocalCache(moduleNames, containingFile, loader); + resolveModuleNamesWorker = (moduleNames, containingFile) => loadWithLocalCache(checkAllDefined(moduleNames), containingFile, loader); } let resolveTypeReferenceDirectiveNamesWorker: (typeDirectiveNames: string[], containingFile: string) => ResolvedTypeReferenceDirective[]; if (host.resolveTypeReferenceDirectives) { - resolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile); + resolveTypeReferenceDirectiveNamesWorker = (typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives(checkAllDefined(typeDirectiveNames), containingFile); } else { const loader = (typesRef: string, containingFile: string) => resolveTypeReferenceDirective(typesRef, containingFile, options, host).resolvedTypeReferenceDirective; - resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(typeReferenceDirectiveNames, containingFile, loader); + resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(checkAllDefined(typeReferenceDirectiveNames), containingFile, loader); } const filesByName = createMap(); @@ -2127,4 +2127,9 @@ namespace ts { return options.allowJs ? undefined : Diagnostics.Module_0_was_resolved_to_1_but_allowJs_is_not_set; } } + + function checkAllDefined(names: string[]): string[] { + Debug.assert(names.every(name => name !== undefined), "A name is undefined.", () => JSON.stringify(names)); + return names; + } } From 2368847f6bdd6df22bb73df385e0a8034d6d7224 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 12 Jul 2017 10:42:05 -0700 Subject: [PATCH 39/50] Indent filesToString (#17130) --- src/server/project.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/project.ts b/src/server/project.ts index 778f62947ff..d4964a4725f 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -690,7 +690,7 @@ namespace ts.server { } let strBuilder = ""; for (const file of this.program.getSourceFiles()) { - strBuilder += `${file.fileName}\n`; + strBuilder += `\t${file.fileName}\n`; } return strBuilder; } From abb229e91b83c06799d2f51200ff364afdc989b3 Mon Sep 17 00:00:00 2001 From: Mine Starks Date: Wed, 12 Jul 2017 11:14:48 -0700 Subject: [PATCH 40/50] Add a bit more validation around comments --- tests/cases/fourslash/unusedImports14FS.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/cases/fourslash/unusedImports14FS.ts b/tests/cases/fourslash/unusedImports14FS.ts index 90bd2c50587..75124802d35 100644 --- a/tests/cases/fourslash/unusedImports14FS.ts +++ b/tests/cases/fourslash/unusedImports14FS.ts @@ -2,11 +2,14 @@ // @noUnusedLocals: true // @Filename: file2.ts -//// [| import /* 1 */ A /* 2 */, /* 3 */ { x } from './a'; |] +//// [| import /* 1 */ A /* 2 */, /* 3 */ { /* 4 */ x /* 5 */ } /* 6 */ from './a'; |] //// console.log(A); // @Filename: file1.ts //// export default 10; //// export var x = 10; -verify.rangeAfterCodeFix("import /* 1 */ A /* 2 */ from './a';"); \ No newline at end of file + +// It's ambiguous which token comment /* 6 */ applies to or whether it should be removed. +// In the current implementation the comment is left behind, but this behavior isn't a requirement. +verify.rangeAfterCodeFix("import /* 1 */ A /* 2 */ /* 6 */ from './a';"); \ No newline at end of file From dbbf05188691cf7fa63f7729c226eeaf6bd2213d Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 13 Jul 2017 06:49:28 -0700 Subject: [PATCH 41/50] Add assertion to computePositionOfLineAndCharacter (#17121) --- src/compiler/scanner.ts | 13 ++++++++++--- src/server/scriptInfo.ts | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index dd43b3c8263..16555ff7937 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -326,13 +326,20 @@ namespace ts { } export function getPositionOfLineAndCharacter(sourceFile: SourceFile, line: number, character: number): number { - return computePositionOfLineAndCharacter(getLineStarts(sourceFile), line, character); + return computePositionOfLineAndCharacter(getLineStarts(sourceFile), line, character, sourceFile.text); } /* @internal */ - export function computePositionOfLineAndCharacter(lineStarts: number[], line: number, character: number): number { + export function computePositionOfLineAndCharacter(lineStarts: number[], line: number, character: number, debugText?: string): number { Debug.assert(line >= 0 && line < lineStarts.length); - return lineStarts[line] + character; + const res = lineStarts[line] + character; + if (line < lineStarts.length - 1) { + Debug.assert(res < lineStarts[line + 1]); + } + else if (debugText !== undefined) { + Debug.assert(res < debugText.length); + } + return res; } /* @internal */ diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 5250a8ea66c..1f2c1068eda 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -86,7 +86,7 @@ namespace ts.server { */ lineOffsetToPosition(line: number, offset: number): number { if (!this.svc) { - return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1); + return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1, this.text); } // TODO: assert this offset is actually on the line From 33836f891c3c30fdc8f8e102b6b6a9ba6a3a56f2 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 13 Jul 2017 06:54:04 -0700 Subject: [PATCH 42/50] Clean up getJavaScriptCompletionEntries (#16750) * Clean up getJavaScriptCompletionEntries * Move each parameter to its own line --- src/services/completions.ts | 43 +++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 9a43e10ad97..2e594c6cbcc 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -57,7 +57,7 @@ namespace ts.Completions { if (isSourceFileJavaScript(sourceFile)) { const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log); - addRange(entries, getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames, compilerOptions.target)); + getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames, compilerOptions.target, entries); } else { if ((!symbols || symbols.length === 0) && keywordFilters === KeywordCompletionFilters.None) { @@ -79,33 +79,34 @@ namespace ts.Completions { return { isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, entries }; } - function getJavaScriptCompletionEntries(sourceFile: SourceFile, position: number, uniqueNames: Map, target: ScriptTarget): CompletionEntry[] { - const entries: CompletionEntry[] = []; - - const nameTable = getNameTable(sourceFile); - nameTable.forEach((pos, name) => { + function getJavaScriptCompletionEntries( + sourceFile: SourceFile, + position: number, + uniqueNames: Map, + target: ScriptTarget, + entries: Push): void { + getNameTable(sourceFile).forEach((pos, name) => { // Skip identifiers produced only from the current location if (pos === position) { return; } const realName = unescapeLeadingUnderscores(name); - if (!uniqueNames.get(realName)) { - uniqueNames.set(realName, true); - const displayName = getCompletionEntryDisplayName(realName, target, /*performCharacterChecks*/ true); - if (displayName) { - const entry = { - name: displayName, - kind: ScriptElementKind.warning, - kindModifiers: "", - sortText: "1" - }; - entries.push(entry); - } + if (uniqueNames.has(realName)) { + return; + } + + uniqueNames.set(realName, true); + const displayName = getCompletionEntryDisplayName(realName, target, /*performCharacterChecks*/ true); + if (displayName) { + entries.push({ + name: displayName, + kind: ScriptElementKind.warning, + kindModifiers: "", + sortText: "1" + }); } }); - - return entries; } function createCompletionEntry(symbol: Symbol, location: Node, performCharacterChecks: boolean, typeChecker: TypeChecker, target: ScriptTarget): CompletionEntry { @@ -141,7 +142,7 @@ namespace ts.Completions { const entry = createCompletionEntry(symbol, location, performCharacterChecks, typeChecker, target); if (entry) { const id = entry.name; - if (!uniqueNames.get(id)) { + if (!uniqueNames.has(id)) { entries.push(entry); uniqueNames.set(id, true); } From efc861c76dd6bcb1f2026f30d06ad2d0152d6a92 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 13 Jul 2017 07:10:35 -0700 Subject: [PATCH 43/50] Add logging to discoverTypings (#16652) --- src/harness/unittests/typingsInstaller.ts | 40 +++++++++++++++++-- .../typingsInstaller/typingsInstaller.ts | 1 + src/services/jsTyping.ts | 24 +++++++---- src/services/shims.ts | 1 + 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index c23e1529984..5f6b1350578 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -44,6 +44,18 @@ namespace ts.projectSystem { }); } + function trackingLogger(): { log(message: string): void, finish(): string[] } { + const logs: string[] = []; + return { + log(message) { + logs.push(message); + }, + finish() { + return logs; + } + }; + } + import typingsName = TI.typingsName; describe("local module", () => { @@ -1031,7 +1043,12 @@ namespace ts.projectSystem { const cache = createMap(); const host = createServerHost([app, jquery, chroma]); - const result = JsTyping.discoverTypings(host, [app.path, jquery.path, chroma.path], getDirectoryPath(app.path), /*safeListPath*/ undefined, cache, { enable: true }, []); + const logger = trackingLogger(); + const result = JsTyping.discoverTypings(host, logger.log, [app.path, jquery.path, chroma.path], getDirectoryPath(app.path), /*safeListPath*/ undefined, cache, { enable: true }, []); + assert.deepEqual(logger.finish(), [ + 'Inferred typings from file names: ["jquery","chroma-js"]', + 'Result: {"cachedTypingPaths":[],"newTypingNames":["jquery","chroma-js"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}', + ]); assert.deepEqual(result.newTypingNames, ["jquery", "chroma-js"]); }); @@ -1044,7 +1061,12 @@ namespace ts.projectSystem { const cache = createMap(); for (const name of JsTyping.nodeCoreModuleList) { - const result = JsTyping.discoverTypings(host, [f.path], getDirectoryPath(f.path), /*safeListPath*/ undefined, cache, { enable: true }, [name, "somename"]); + const logger = trackingLogger(); + const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(f.path), /*safeListPath*/ undefined, cache, { enable: true }, [name, "somename"]); + assert.deepEqual(logger.finish(), [ + 'Inferred typings from unresolved imports: ["node","somename"]', + 'Result: {"cachedTypingPaths":[],"newTypingNames":["node","somename"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}', + ]); assert.deepEqual(result.newTypingNames.sort(), ["node", "somename"]); } }); @@ -1060,7 +1082,12 @@ namespace ts.projectSystem { }; const host = createServerHost([f, node]); const cache = createMapFromTemplate({ "node": node.path }); - const result = JsTyping.discoverTypings(host, [f.path], getDirectoryPath(f.path), /*safeListPath*/ undefined, cache, { enable: true }, ["fs", "bar"]); + const logger = trackingLogger(); + const result = JsTyping.discoverTypings(host, logger.log, [f.path], getDirectoryPath(f.path), /*safeListPath*/ undefined, cache, { enable: true }, ["fs", "bar"]); + assert.deepEqual(logger.finish(), [ + 'Inferred typings from unresolved imports: ["node","bar"]', + 'Result: {"cachedTypingPaths":["/a/b/node.d.ts"],"newTypingNames":["bar"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}', + ]); assert.deepEqual(result.cachedTypingPaths, [node.path]); assert.deepEqual(result.newTypingNames, ["bar"]); }); @@ -1080,7 +1107,12 @@ namespace ts.projectSystem { }; const host = createServerHost([app, a, b]); const cache = createMap(); - const result = JsTyping.discoverTypings(host, [app.path], getDirectoryPath(app.path), /*safeListPath*/ undefined, cache, { enable: true }, /*unresolvedImports*/ []); + const logger = trackingLogger(); + const result = JsTyping.discoverTypings(host, logger.log, [app.path], getDirectoryPath(app.path), /*safeListPath*/ undefined, cache, { enable: true }, /*unresolvedImports*/ []); + assert.deepEqual(logger.finish(), [ + 'Searching for typing names in /node_modules; all files: ["/node_modules/a/package.json"]', + 'Result: {"cachedTypingPaths":[],"newTypingNames":["a"],"filesToWatch":["/bower_components","/node_modules"]}', + ]); assert.deepEqual(result, { cachedTypingPaths: [], newTypingNames: ["a"], // But not "b" diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index 71ad0171bf3..730257d2ccc 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -145,6 +145,7 @@ namespace ts.server.typingsInstaller { const discoverTypingsResult = JsTyping.discoverTypings( this.installTypingHost, + this.log.isEnabled() ? this.log.writeLine : undefined, req.fileNames, req.projectRootPath, this.safeListPath, diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 60b29b81883..f31276b6498 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -51,6 +51,7 @@ namespace ts.JsTyping { */ export function discoverTypings( host: TypingResolutionHost, + log: ((message: string) => void) | undefined, fileNames: string[], projectRootPath: Path, safeListPath: Path, @@ -107,8 +108,9 @@ namespace ts.JsTyping { // add typings for unresolved imports if (unresolvedImports) { - for (const moduleId of unresolvedImports) { - const typingName = nodeCoreModules.has(moduleId) ? "node" : moduleId; + const x = unresolvedImports.map(moduleId => nodeCoreModules.has(moduleId) ? "node" : moduleId); + if (x.length && log) log(`Inferred typings from unresolved imports: ${JSON.stringify(x)}`); + for (const typingName of x) { if (!inferredTypings.has(typingName)) { inferredTypings.set(typingName, undefined); } @@ -136,7 +138,9 @@ namespace ts.JsTyping { newTypingNames.push(typing); } }); - return { cachedTypingPaths, newTypingNames, filesToWatch }; + const result = { cachedTypingPaths, newTypingNames, filesToWatch }; + if (log) log(`Result: ${JSON.stringify(result)}`); + return result; function addInferredTyping(typingName: string) { if (!inferredTypings.has(typingName)) { @@ -153,6 +157,7 @@ namespace ts.JsTyping { } filesToWatch.push(jsonPath); + if (log) log(`Searching for typing names in '${jsonPath}' dependencies`); const jsonConfig: PackageJson = readConfigFile(jsonPath, (path: string) => host.readFile(path)).config; addInferredTypingsFromKeys(jsonConfig.dependencies); addInferredTypingsFromKeys(jsonConfig.devDependencies); @@ -175,19 +180,23 @@ namespace ts.JsTyping { * @param fileNames are the names for source files in the project */ function getTypingNamesFromSourceFileNames(fileNames: string[]) { - for (const j of fileNames) { - if (!hasJavaScriptFileExtension(j)) continue; + const fromFileNames = mapDefined(fileNames, j => { + if (!hasJavaScriptFileExtension(j)) return undefined; const inferredTypingName = removeFileExtension(getBaseFileName(j.toLowerCase())); const cleanedTypingName = inferredTypingName.replace(/((?:\.|-)min(?=\.|$))|((?:-|\.)\d+)/g, ""); - const safe = safeList.get(cleanedTypingName); - if (safe !== undefined) { + return safeList.get(cleanedTypingName); + }); + if (fromFileNames.length) { + if (log) log(`Inferred typings from file names: ${JSON.stringify(fromFileNames)}`); + for (const safe of fromFileNames) { addInferredTyping(safe); } } const hasJsxFile = some(fileNames, f => fileExtensionIs(f, Extension.Jsx)); if (hasJsxFile) { + if (log) log(`Inferred 'react' typings due to presence of '.jsx' extension`); addInferredTyping("react"); } } @@ -206,6 +215,7 @@ namespace ts.JsTyping { // depth of 2, so we access `node_modules/foo` but not `node_modules/foo/bar` const fileNames = host.readDirectory(packagesFolderPath, [".json"], /*excludes*/ undefined, /*includes*/ undefined, /*depth*/ 2); + if (log) log(`Searching for typing names in ${packagesFolderPath}; all files: ${JSON.stringify(fileNames)}`); for (const fileName of fileNames) { const normalizedFileName = normalizePath(fileName); const baseFileName = getBaseFileName(normalizedFileName); diff --git a/src/services/shims.ts b/src/services/shims.ts index 4ecd2e4e9b8..2ee1853f18f 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -1116,6 +1116,7 @@ namespace ts { const info = JSON.parse(discoverTypingsJson); return ts.JsTyping.discoverTypings( this.host, + msg => this.logger.log(msg), info.fileNames, toPath(info.projectRootPath, info.projectRootPath, getCanonicalFileName), toPath(info.safeListPath, info.safeListPath, getCanonicalFileName), From 79b10081a9fdc2f0259c43676c8bda0a3fa3fe21 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 13 Jul 2017 07:32:41 -0700 Subject: [PATCH 44/50] getApplicableRefactors: Don't return undefined response (#16773) --- src/compiler/core.ts | 11 +++++++++++ src/services/refactorProvider.ts | 19 +++---------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 97a65c10933..a1880b46471 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -467,6 +467,17 @@ namespace ts { return result; } + export function flatMapIter(iter: Iterator, mapfn: (x: T) => U[] | undefined): U[] { + const result: U[] = []; + while (true) { + const { value, done } = iter.next(); + if (done) break; + const res = mapfn(value); + if (res) result.push(...res); + } + return result; + } + /** * Maps an array. If the mapped value is an array, it is spread into the result. * Avoids allocation if all elements map to themselves. diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 3c02ddf671c..432df8c53d0 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -33,22 +33,9 @@ namespace ts { refactors.set(refactor.name, refactor); } - export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] | undefined { - let results: ApplicableRefactorInfo[]; - const refactorList: Refactor[] = []; - refactors.forEach(refactor => { - refactorList.push(refactor); - }); - for (const refactor of refactorList) { - if (context.cancellationToken && context.cancellationToken.isCancellationRequested()) { - return results; - } - const infos = refactor.getAvailableActions(context); - if (infos && infos.length) { - (results || (results = [])).push(...infos); - } - } - return results; + export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] { + return flatMapIter(refactors.values(), refactor => + context.cancellationToken && context.cancellationToken.isCancellationRequested() ? [] : refactor.getAvailableActions(context)); } export function getEditsForRefactor(context: RefactorContext, refactorName: string, actionName: string): RefactorEditInfo | undefined { From 9a3847feac5b9c1b86611b35aeaffab891b194ac Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 13 Jul 2017 08:13:49 -0700 Subject: [PATCH 45/50] getSingleLineStringWriter: Use try-finally, and only one stringWriter (#16751) * getSingleLineStringWriter: Use try-finally, and only one stringWriter * Use a `usingSingleLineStringWriter` helper function * Add assert --- src/compiler/checker.ts | 27 ++++++---------- src/compiler/utilities.ts | 67 +++++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 49 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e454b43d4a1..45ec7f9f9b9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2285,21 +2285,15 @@ namespace ts { } function symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string { - const writer = getSingleLineStringWriter(); - getSymbolDisplayBuilder().buildSymbolDisplay(symbol, writer, enclosingDeclaration, meaning); - const result = writer.string(); - releaseStringWriter(writer); - - return result; + return usingSingleLineStringWriter(writer => { + getSymbolDisplayBuilder().buildSymbolDisplay(symbol, writer, enclosingDeclaration, meaning); + }); } function signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string { - const writer = getSingleLineStringWriter(); - getSymbolDisplayBuilder().buildSignatureDisplay(signature, writer, enclosingDeclaration, flags, kind); - const result = writer.string(); - releaseStringWriter(writer); - - return result; + return usingSingleLineStringWriter(writer => { + getSymbolDisplayBuilder().buildSignatureDisplay(signature, writer, enclosingDeclaration, flags, kind); + }); } function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { @@ -2996,12 +2990,9 @@ namespace ts { } function typePredicateToString(typePredicate: TypePredicate, enclosingDeclaration?: Declaration, flags?: TypeFormatFlags): string { - const writer = getSingleLineStringWriter(); - getSymbolDisplayBuilder().buildTypePredicateDisplay(typePredicate, writer, enclosingDeclaration, flags); - const result = writer.string(); - releaseStringWriter(writer); - - return result; + return usingSingleLineStringWriter(writer => { + getSymbolDisplayBuilder().buildTypePredicateDisplay(typePredicate, writer, enclosingDeclaration, flags); + }); } function formatUnionTypes(types: Type[]): Type[] { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b6f5616b7e3..79347d51f52 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -44,42 +44,47 @@ namespace ts { string(): string; } - // Pool writers to avoid needing to allocate them for every symbol we write. - const stringWriters: StringSymbolWriter[] = []; - export function getSingleLineStringWriter(): StringSymbolWriter { - if (stringWriters.length === 0) { - let str = ""; + const stringWriter = createSingleLineStringWriter(); + let stringWriterAcquired = false; - const writeText: (text: string) => void = text => str += text; - return { - string: () => str, - writeKeyword: writeText, - writeOperator: writeText, - writePunctuation: writeText, - writeSpace: writeText, - writeStringLiteral: writeText, - writeParameter: writeText, - writeProperty: writeText, - writeSymbol: writeText, + function createSingleLineStringWriter(): StringSymbolWriter { + let str = ""; - // Completely ignore indentation for string writers. And map newlines to - // a single space. - writeLine: () => str += " ", - increaseIndent: noop, - decreaseIndent: noop, - clear: () => str = "", - trackSymbol: noop, - reportInaccessibleThisError: noop, - reportPrivateInBaseOfClassExpression: noop, - }; - } + const writeText: (text: string) => void = text => str += text; + return { + string: () => str, + writeKeyword: writeText, + writeOperator: writeText, + writePunctuation: writeText, + writeSpace: writeText, + writeStringLiteral: writeText, + writeParameter: writeText, + writeProperty: writeText, + writeSymbol: writeText, - return stringWriters.pop(); + // Completely ignore indentation for string writers. And map newlines to + // a single space. + writeLine: () => str += " ", + increaseIndent: noop, + decreaseIndent: noop, + clear: () => str = "", + trackSymbol: noop, + reportInaccessibleThisError: noop, + reportPrivateInBaseOfClassExpression: noop, + }; } - export function releaseStringWriter(writer: StringSymbolWriter) { - writer.clear(); - stringWriters.push(writer); + export function usingSingleLineStringWriter(action: (writer: StringSymbolWriter) => void): string { + try { + Debug.assert(!stringWriterAcquired); + stringWriterAcquired = true; + action(stringWriter); + return stringWriter.string(); + } + finally { + stringWriter.clear(); + stringWriterAcquired = false; + } } export function getFullWidth(node: Node) { From 6880ee33a338ea74d7c89397daea608c494435b4 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 13 Jul 2017 08:14:02 -0700 Subject: [PATCH 46/50] displayPartWriter: Use try-finally to clear (#16807) --- src/services/utilities.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 5819b5a60ac..7fcfffc3ee2 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1240,10 +1240,13 @@ namespace ts { } export function mapToDisplayParts(writeDisplayParts: (writer: DisplayPartsSymbolWriter) => void): SymbolDisplayPart[] { - writeDisplayParts(displayPartWriter); - const result = displayPartWriter.displayParts(); - displayPartWriter.clear(); - return result; + try { + writeDisplayParts(displayPartWriter); + return displayPartWriter.displayParts(); + } + finally { + displayPartWriter.clear(); + } } export function typeToDisplayParts(typechecker: TypeChecker, type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): SymbolDisplayPart[] { From 69d3ca774a0f3c7546b1d450441c13c9fda120f4 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 13 Jul 2017 09:20:40 -0700 Subject: [PATCH 47/50] When adding completions for a module, don't get the type of the module if not necessary. (#16768) * When adding completions for a module, don't get the type of the module if not necessary. * Use SymbolFlags.Module alias --- src/compiler/utilities.ts | 4 ++ src/services/codefixes/importFixes.ts | 5 +- src/services/completions.ts | 64 +++++++++---------- .../completionsNamespaceMergedWithClass.ts | 21 ++++++ .../completionsNamespaceMergedWithObject.ts | 16 +++++ 5 files changed, 74 insertions(+), 36 deletions(-) create mode 100644 tests/cases/fourslash/completionsNamespaceMergedWithClass.ts create mode 100644 tests/cases/fourslash/completionsNamespaceMergedWithObject.ts diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 79347d51f52..c8751f497a6 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3604,6 +3604,10 @@ namespace ts { return previous[previous.length - 1]; } + export function skipAlias(symbol: Symbol, checker: TypeChecker) { + return symbol.flags & SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol; + } + /** See comment on `declareModuleMember` in `binder.ts`. */ export function getCombinedLocalAndExportSymbolFlags(symbol: Symbol): SymbolFlags { return symbol.exportSymbol ? symbol.exportSymbol.flags | symbol.flags : symbol.flags; diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 0fa0b72162b..d30997aa076 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -222,10 +222,7 @@ namespace ts.codefix { } function getUniqueSymbolId(symbol: Symbol) { - if (symbol.flags & SymbolFlags.Alias) { - return getSymbolId(checker.getAliasedSymbol(symbol)); - } - return getSymbolId(symbol); + return getSymbolId(skipAlias(symbol, checker)); } function checkSymbolHasMeaning(symbol: Symbol, meaning: SemanticMeaning) { diff --git a/src/services/completions.ts b/src/services/completions.ts index 2e594c6cbcc..12796a9b791 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -596,46 +596,48 @@ namespace ts.Completions { isNewIdentifierLocation = false; // Since this is qualified name check its a type node location - const isTypeLocation = isPartOfTypeNode(node.parent) || insideJsDocTagTypeExpression; + const isTypeLocation = insideJsDocTagTypeExpression || isPartOfTypeNode(node.parent); const isRhsOfImportDeclaration = isInRightSideOfInternalImportEqualsDeclaration(node); - if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.PropertyAccessExpression) { + if (isEntityName(node)) { let symbol = typeChecker.getSymbolAtLocation(node); + if (symbol) { + symbol = skipAlias(symbol, typeChecker); - // This is an alias, follow what it aliases - if (symbol && symbol.flags & SymbolFlags.Alias) { - symbol = typeChecker.getAliasedSymbol(symbol); - } - - if (symbol && symbol.flags & SymbolFlags.HasExports) { - // Extract module or enum members - const exportedSymbols = typeChecker.getExportsOfModule(symbol); - const isValidValueAccess = (symbol: Symbol) => typeChecker.isValidPropertyAccess((node.parent), symbol.getUnescapedName()); - const isValidTypeAccess = (symbol: Symbol) => symbolCanBeReferencedAtTypeLocation(symbol); - const isValidAccess = isRhsOfImportDeclaration ? - // Any kind is allowed when dotting off namespace in internal import equals declaration - (symbol: Symbol) => isValidTypeAccess(symbol) || isValidValueAccess(symbol) : - isTypeLocation ? isValidTypeAccess : isValidValueAccess; - forEach(exportedSymbols, symbol => { - if (isValidAccess(symbol)) { - symbols.push(symbol); + if (symbol.flags & (SymbolFlags.Module | SymbolFlags.Enum)) { + // Extract module or enum members + const exportedSymbols = typeChecker.getExportsOfModule(symbol); + const isValidValueAccess = (symbol: Symbol) => typeChecker.isValidPropertyAccess((node.parent), symbol.getUnescapedName()); + const isValidTypeAccess = (symbol: Symbol) => symbolCanBeReferencedAtTypeLocation(symbol); + const isValidAccess = isRhsOfImportDeclaration ? + // Any kind is allowed when dotting off namespace in internal import equals declaration + (symbol: Symbol) => isValidTypeAccess(symbol) || isValidValueAccess(symbol) : + isTypeLocation ? isValidTypeAccess : isValidValueAccess; + for (const symbol of exportedSymbols) { + if (isValidAccess(symbol)) { + symbols.push(symbol); + } } - }); + + // If the module is merged with a value, we must get the type of the class and add its propertes (for inherited static methods). + if (!isTypeLocation && symbol.declarations.some(d => d.kind !== SyntaxKind.SourceFile && d.kind !== SyntaxKind.ModuleDeclaration && d.kind !== SyntaxKind.EnumDeclaration)) { + addTypeProperties(typeChecker.getTypeOfSymbolAtLocation(symbol, node)); + } + + return; + } } } if (!isTypeLocation) { - const type = typeChecker.getTypeAtLocation(node); - if (type) addTypeProperties(type); + addTypeProperties(typeChecker.getTypeAtLocation(node)); } } function addTypeProperties(type: Type) { - if (type) { - // Filter private properties - for (const symbol of type.getApparentProperties()) { - if (typeChecker.isValidPropertyAccess((node.parent), symbol.getUnescapedName())) { - symbols.push(symbol); - } + // Filter private properties + for (const symbol of type.getApparentProperties()) { + if (typeChecker.isValidPropertyAccess((node.parent), symbol.getUnescapedName())) { + symbols.push(symbol); } } @@ -811,15 +813,13 @@ namespace ts.Completions { symbol = symbol.exportSymbol || symbol; // This is an alias, follow what it aliases - if (symbol && symbol.flags & SymbolFlags.Alias) { - symbol = typeChecker.getAliasedSymbol(symbol); - } + symbol = skipAlias(symbol, typeChecker); if (symbol.flags & SymbolFlags.Type) { return true; } - if (symbol.flags & (SymbolFlags.ValueModule | SymbolFlags.NamespaceModule)) { + if (symbol.flags & SymbolFlags.Module) { const exportedSymbols = typeChecker.getExportsOfModule(symbol); // If the exported symbols contains type, // symbol can be referenced at locations where type is allowed diff --git a/tests/cases/fourslash/completionsNamespaceMergedWithClass.ts b/tests/cases/fourslash/completionsNamespaceMergedWithClass.ts new file mode 100644 index 00000000000..414a233bdb1 --- /dev/null +++ b/tests/cases/fourslash/completionsNamespaceMergedWithClass.ts @@ -0,0 +1,21 @@ +/// + +////class C { +//// static m() { } +////} +//// +////class D extends C {} +////namespace D { +//// export type T = number; +////} +//// +////let x: D./*type*/; +////D./*value*/ + +goTo.marker("type"); +verify.completionListContains("T"); +verify.not.completionListContains("m"); + +goTo.marker("value"); +verify.not.completionListContains("T"); +verify.completionListContains("m"); diff --git a/tests/cases/fourslash/completionsNamespaceMergedWithObject.ts b/tests/cases/fourslash/completionsNamespaceMergedWithObject.ts new file mode 100644 index 00000000000..cd799981ee7 --- /dev/null +++ b/tests/cases/fourslash/completionsNamespaceMergedWithObject.ts @@ -0,0 +1,16 @@ +/// + +////namespace N { +//// export type T = number; +////} +////const N = { m() {} }; +////let x: N./*type*/; +////N./*value*/; + +goTo.marker("type"); +verify.completionListContains("T"); +verify.not.completionListContains("m"); + +goTo.marker("value"); +verify.not.completionListContains("T"); +verify.completionListContains("m"); From c6a6467073387dc657471e8eb2349f2bb2d0f4f7 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 13 Jul 2017 09:21:13 -0700 Subject: [PATCH 48/50] Remove unneeded ExportType and ExportNamespace flags (#16766) --- src/compiler/binder.ts | 14 +++++--------- src/compiler/checker.ts | 30 +++++++++++++++++------------- src/compiler/types.ts | 13 +++++-------- src/services/importTracker.ts | 6 ++---- 4 files changed, 29 insertions(+), 34 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5f4cf8442d6..e7b759ff64f 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -416,9 +416,8 @@ namespace ts { } } else { - // Exported module members are given 2 symbols: A local symbol that is classified with an ExportValue, - // ExportType, or ExportContainer flag, and an associated export symbol with all the correct flags set - // on it. There are 2 main reasons: + // Exported module members are given 2 symbols: A local symbol that is classified with an ExportValue flag, + // and an associated export symbol with all the correct flags set on it. There are 2 main reasons: // // 1. We treat locals and exports of the same name as mutually exclusive within a container. // That means the binder will issue a Duplicate Identifier error if you mix locals and exports @@ -438,10 +437,7 @@ namespace ts { (node as JSDocTypedefTag).name.kind === SyntaxKind.Identifier && ((node as JSDocTypedefTag).name as Identifier).isInJSDocNamespace; if ((!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) || isJSDocTypedefInJSDocNamespace) { - const exportKind = - (symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0) | - (symbolFlags & SymbolFlags.Type ? SymbolFlags.ExportType : 0) | - (symbolFlags & SymbolFlags.Namespace ? SymbolFlags.ExportNamespace : 0); + const exportKind = symbolFlags & SymbolFlags.Value ? SymbolFlags.ExportValue : 0; const local = declareSymbol(container.locals, /*parent*/ undefined, node, exportKind, symbolExcludes); local.exportSymbol = declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); node.localSymbol = local; @@ -2288,7 +2284,7 @@ namespace ts { // When we create a property via 'exports.foo = bar', the 'exports.foo' property access // expression is the declaration setCommonJsModuleIndicator(node); - declareSymbol(file.symbol.exports, file.symbol, node.left, SymbolFlags.Property | SymbolFlags.Export, SymbolFlags.None); + declareSymbol(file.symbol.exports, file.symbol, node.left, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None); } function isExportsOrModuleExportsOrAlias(node: Node): boolean { @@ -2329,7 +2325,7 @@ namespace ts { // 'module.exports = expr' assignment setCommonJsModuleIndicator(node); - declareSymbol(file.symbol.exports, file.symbol, node, SymbolFlags.Property | SymbolFlags.Export | SymbolFlags.ValueModule, SymbolFlags.None); + declareSymbol(file.symbol.exports, file.symbol, node, SymbolFlags.Property | SymbolFlags.ExportValue | SymbolFlags.ValueModule, SymbolFlags.None); } function bindThisPropertyAssignment(node: BinaryExpression) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 45ec7f9f9b9..6c5fcc36e4d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18800,7 +18800,7 @@ namespace ts { // local symbol is undefined => this declaration is non-exported. // however symbol might contain other declarations that are exported symbol = getSymbolOfNode(node); - if (!(symbol.flags & SymbolFlags.Export)) { + if (!symbol.exportSymbol) { // this is a pure local symbol (all declarations are non-exported) - no need to check anything return; } @@ -18811,11 +18811,9 @@ namespace ts { return; } - // we use SymbolFlags.ExportValue, SymbolFlags.ExportType and SymbolFlags.ExportNamespace - // to denote disjoint declarationSpaces (without making new enum type). - let exportedDeclarationSpaces = SymbolFlags.None; - let nonExportedDeclarationSpaces = SymbolFlags.None; - let defaultExportedDeclarationSpaces = SymbolFlags.None; + let exportedDeclarationSpaces = DeclarationSpaces.None; + let nonExportedDeclarationSpaces = DeclarationSpaces.None; + let defaultExportedDeclarationSpaces = DeclarationSpaces.None; for (const d of symbol.declarations) { const declarationSpaces = getDeclarationSpaces(d); const effectiveDeclarationFlags = getEffectiveDeclarationFlags(d, ModifierFlags.Export | ModifierFlags.Default); @@ -18855,20 +18853,26 @@ namespace ts { } } - function getDeclarationSpaces(d: Declaration): SymbolFlags { + const enum DeclarationSpaces { + None = 0, + ExportValue = 1 << 0, + ExportType = 1 << 1, + ExportNamespace = 1 << 2, + } + function getDeclarationSpaces(d: Declaration): DeclarationSpaces { switch (d.kind) { case SyntaxKind.InterfaceDeclaration: case SyntaxKind.TypeAliasDeclaration: - return SymbolFlags.ExportType; + return DeclarationSpaces.ExportType; case SyntaxKind.ModuleDeclaration: return isAmbientModule(d) || getModuleInstanceState(d) !== ModuleInstanceState.NonInstantiated - ? SymbolFlags.ExportNamespace | SymbolFlags.ExportValue - : SymbolFlags.ExportNamespace; + ? DeclarationSpaces.ExportNamespace | DeclarationSpaces.ExportValue + : DeclarationSpaces.ExportNamespace; case SyntaxKind.ClassDeclaration: case SyntaxKind.EnumDeclaration: - return SymbolFlags.ExportType | SymbolFlags.ExportValue; + return DeclarationSpaces.ExportType | DeclarationSpaces.ExportValue; case SyntaxKind.ImportEqualsDeclaration: - let result = SymbolFlags.None; + let result = DeclarationSpaces.None; const target = resolveAlias(getSymbolOfNode(d)); forEach(target.declarations, d => { result |= getDeclarationSpaces(d); }); return result; @@ -18876,7 +18880,7 @@ namespace ts { case SyntaxKind.BindingElement: case SyntaxKind.FunctionDeclaration: case SyntaxKind.ImportSpecifier: // https://github.com/Microsoft/TypeScript/pull/7591 - return SymbolFlags.ExportValue; + return DeclarationSpaces.ExportValue; default: Debug.fail((ts as any).SyntaxKind[d.kind]); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 35320318ef9..c9309a7d844 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2890,13 +2890,11 @@ namespace ts { TypeParameter = 1 << 18, // Type parameter TypeAlias = 1 << 19, // Type alias ExportValue = 1 << 20, // Exported value marker (see comment in declareModuleMember in binder) - ExportType = 1 << 21, // Exported type marker (see comment in declareModuleMember in binder) - ExportNamespace = 1 << 22, // Exported namespace marker (see comment in declareModuleMember in binder) - Alias = 1 << 23, // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker) - Prototype = 1 << 24, // Prototype property (no source representation) - ExportStar = 1 << 25, // Export * declaration - Optional = 1 << 26, // Optional property - Transient = 1 << 27, // Transient symbol (created during type check) + Alias = 1 << 21, // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker) + Prototype = 1 << 22, // Prototype property (no source representation) + ExportStar = 1 << 23, // Export * declaration + Optional = 1 << 24, // Optional property + Transient = 1 << 25, // Transient symbol (created during type check) Enum = RegularEnum | ConstEnum, Variable = FunctionScopedVariable | BlockScopedVariable, @@ -2941,7 +2939,6 @@ namespace ts { BlockScoped = BlockScopedVariable | Class | Enum, PropertyOrAccessor = Property | Accessor, - Export = ExportNamespace | ExportType | ExportValue, ClassMember = Method | Accessor | Property, diff --git a/src/services/importTracker.ts b/src/services/importTracker.ts index ecd52baa7dd..f17406b16f1 100644 --- a/src/services/importTracker.ts +++ b/src/services/importTracker.ts @@ -440,7 +440,7 @@ namespace ts.FindAllReferences { function getExport(): ExportedSymbol | ImportedSymbol | undefined { const parent = node.parent!; - if (symbol.flags & SymbolFlags.Export) { + if (symbol.exportSymbol) { if (parent.kind === SyntaxKind.PropertyAccessExpression) { // When accessing an export of a JS module, there's no alias. The symbol will still be flagged as an export even though we're at the use. // So check that we are at the declaration. @@ -449,9 +449,7 @@ namespace ts.FindAllReferences { : undefined; } else { - const { exportSymbol } = symbol; - Debug.assert(!!exportSymbol); - return exportInfo(exportSymbol, getExportKindForDeclaration(parent)); + return exportInfo(symbol.exportSymbol, getExportKindForDeclaration(parent)); } } else { From 7b5e1e9c49c9bb0d76ff78c560b0ed5f64db5702 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 13 Jul 2017 10:43:01 -0700 Subject: [PATCH 49/50] Use array helpers instead of 'reduce' (#17172) --- src/server/editorServices.ts | 2 +- src/server/session.ts | 41 ++++++++++++++---------------------- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 3edb880f620..868128a46ce 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -201,7 +201,7 @@ namespace ts.server { * This helper function processes a list of projects and return the concatenated, sortd and deduplicated output of processing each project. */ export function combineProjectOutput(projects: Project[], action: (project: Project) => T[], comparer?: (a: T, b: T) => number, areEqual?: (a: T, b: T) => boolean) { - const result = projects.reduce((previous, current) => concatenate(previous, action(current)), []).sort(comparer); + const result = ts.flatMap(projects, action).sort(comparer); return projects.length > 1 ? deduplicate(result, areEqual) : result; } diff --git a/src/server/session.ts b/src/server/session.ts index cdd4306c064..e17032c9404 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -846,21 +846,22 @@ namespace ts.server { compareRenameLocation, (a, b) => a.file === b.file && a.start.line === b.start.line && a.start.offset === b.start.offset ); - const locs = fileSpans.reduce((accum, cur) => { + + const locs: protocol.SpanGroup[] = []; + for (const cur of fileSpans) { let curFileAccum: protocol.SpanGroup; - if (accum.length > 0) { - curFileAccum = accum[accum.length - 1]; + if (locs.length > 0) { + curFileAccum = locs[locs.length - 1]; if (curFileAccum.file !== cur.file) { curFileAccum = undefined; } } if (!curFileAccum) { curFileAccum = { file: cur.file, locs: [] }; - accum.push(curFileAccum); + locs.push(curFileAccum); } curFileAccum.locs.push({ start: cur.start, end: cur.end }); - return accum; - }, []); + } return { info: renameInfo, locs }; } @@ -1183,15 +1184,13 @@ namespace ts.server { return undefined; } if (simplifiedResult) { - return completions.entries.reduce((result: protocol.CompletionEntry[], entry: ts.CompletionEntry) => { + return mapDefined(completions.entries, entry => { if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) { const { name, kind, kindModifiers, sortText, replacementSpan } = entry; - const convertedSpan: protocol.TextSpan = - replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined; - result.push({ name, kind, kindModifiers, sortText, replacementSpan: convertedSpan }); + const convertedSpan = replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined; + return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan }; } - return result; - }, []).sort((a, b) => ts.compareStrings(a.name, b.name)); + }).sort((a, b) => ts.compareStrings(a.name, b.name)); } else { return completions; @@ -1203,13 +1202,8 @@ namespace ts.server { const scriptInfo = project.getScriptInfoForNormalizedPath(file); const position = this.getPosition(args, scriptInfo); - return args.entryNames.reduce((accum: protocol.CompletionEntryDetails[], entryName: string) => { - const details = project.getLanguageService().getCompletionEntryDetails(file, position, entryName); - if (details) { - accum.push(details); - } - return accum; - }, []); + return mapDefined(args.entryNames, entryName => + project.getLanguageService().getCompletionEntryDetails(file, position, entryName)); } private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): protocol.CompileOnSaveAffectedFileListSingleProject[] { @@ -1274,14 +1268,11 @@ namespace ts.server { } private getDiagnostics(next: NextStep, delay: number, fileNames: string[]): void { - const checkList = fileNames.reduce((accum: PendingErrorCheck[], uncheckedFileName: string) => { + const checkList = mapDefined(fileNames, uncheckedFileName => { const fileName = toNormalizedPath(uncheckedFileName); const project = this.projectService.getDefaultProjectForFile(fileName, /*refreshInferredProjects*/ true); - if (project) { - accum.push({ fileName, project }); - } - return accum; - }, []); + return project && { fileName, project }; + }); if (checkList.length > 0) { this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay); From d2ec45f35467aaa08c7fa8f2b48de5d0ef5b457f Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 13 Jul 2017 13:08:59 -0700 Subject: [PATCH 50/50] Remove unnecessary 'ts.' qualifications (#17163) --- src/server/client.ts | 2 +- src/server/editorServices.ts | 12 +++--- src/server/lsHost.ts | 12 +++--- src/server/project.ts | 16 ++++---- src/server/scriptInfo.ts | 6 +-- src/server/scriptVersionCache.ts | 16 ++++---- src/server/server.ts | 2 +- src/server/session.ts | 70 ++++++++++++++++---------------- src/server/types.ts | 10 ++--- src/server/utilities.ts | 4 +- 10 files changed, 75 insertions(+), 75 deletions(-) diff --git a/src/server/client.ts b/src/server/client.ts index f0be887814d..a7e0615dce8 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -227,7 +227,7 @@ namespace ts.server { })); } - getFormattingEditsForRange(file: string, start: number, end: number, _options: ts.FormatCodeOptions): ts.TextChange[] { + getFormattingEditsForRange(file: string, start: number, end: number, _options: FormatCodeOptions): ts.TextChange[] { const args: protocol.FormatRequestArgs = this.createFileLocationRequestArgsWithEndLineAndOffset(file, start, end); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 868128a46ce..43cb429cfc5 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -45,7 +45,7 @@ namespace ts.server { * Any compiler options that might contain paths will be taken out. * Enum compiler options will be converted to strings. */ - readonly compilerOptions: ts.CompilerOptions; + readonly compilerOptions: CompilerOptions; // "extends", "files", "include", or "exclude" will be undefined if an external config is used. // Otherwise, we will use "true" if the property is present and "false" if it is missing. readonly extends: boolean | undefined; @@ -201,7 +201,7 @@ namespace ts.server { * This helper function processes a list of projects and return the concatenated, sortd and deduplicated output of processing each project. */ export function combineProjectOutput(projects: Project[], action: (project: Project) => T[], comparer?: (a: T, b: T) => number, areEqual?: (a: T, b: T) => boolean) { - const result = ts.flatMap(projects, action).sort(comparer); + const result = flatMap(projects, action).sort(comparer); return projects.length > 1 ? deduplicate(result, areEqual) : result; } @@ -240,7 +240,7 @@ namespace ts.server { getFileName: x => x, getScriptKind: _ => undefined, hasMixedContent: (fileName, extraFileExtensions) => { - const mixedContentExtensions = ts.map(ts.filter(extraFileExtensions, item => item.isMixedContent), item => item.extension); + const mixedContentExtensions = map(filter(extraFileExtensions, item => item.isMixedContent), item => item.extension); return forEach(mixedContentExtensions, extension => fileExtensionIs(fileName, extension)); } }; @@ -633,7 +633,7 @@ namespace ts.server { // If a change was made inside "folder/file", node will trigger the callback twice: // one with the fileName being "folder/file", and the other one with "folder". // We don't respond to the second one. - if (fileName && !ts.isSupportedSourceFileName(fileName, project.getCompilerOptions(), this.hostConfiguration.extraFileExtensions)) { + if (fileName && !isSupportedSourceFileName(fileName, project.getCompilerOptions(), this.hostConfiguration.extraFileExtensions)) { return; } @@ -1082,7 +1082,7 @@ namespace ts.server { configFileName: configFileName(), projectType: project instanceof server.ExternalProject ? "external" : "configured", languageServiceEnabled: project.languageServiceEnabled, - version: ts.version, + version, }; this.eventHandler({ eventName: ProjectInfoTelemetryEvent, data }); @@ -1092,7 +1092,7 @@ namespace ts.server { } const configFilePath = project instanceof server.ConfiguredProject && project.getConfigFilePath(); - const base = ts.getBaseFileName(configFilePath); + const base = getBaseFileName(configFilePath); return base === "tsconfig.json" || base === "jsconfig.json" ? base : "other"; } diff --git a/src/server/lsHost.ts b/src/server/lsHost.ts index 620697e1742..af4c39e4f8d 100644 --- a/src/server/lsHost.ts +++ b/src/server/lsHost.ts @@ -3,8 +3,8 @@ /// namespace ts.server { - export class LSHost implements ts.LanguageServiceHost, ModuleResolutionHost { - private compilationSettings: ts.CompilerOptions; + export class LSHost implements LanguageServiceHost, ModuleResolutionHost { + private compilationSettings: CompilerOptions; private readonly resolvedModuleNames = createMap>(); private readonly resolvedTypeReferenceDirectives = createMap>(); private readonly getCanonicalFileName: (fileName: string) => string; @@ -17,7 +17,7 @@ namespace ts.server { constructor(private readonly host: ServerHost, private project: Project, private readonly cancellationToken: HostCancellationToken) { this.cancellationToken = new ThrottledCancellationToken(cancellationToken, project.projectService.throttleWaitMilliseconds); - this.getCanonicalFileName = ts.createGetCanonicalFileName(this.host.useCaseSensitiveFileNames); + this.getCanonicalFileName = createGetCanonicalFileName(this.host.useCaseSensitiveFileNames); if (host.trace) { this.trace = s => host.trace(s); @@ -99,7 +99,7 @@ namespace ts.server { } } - ts.Debug.assert(resolution !== undefined); + Debug.assert(resolution !== undefined); resolvedModules.push(getResult(resolution)); } @@ -177,7 +177,7 @@ namespace ts.server { return combinePaths(nodeModuleBinDir, getDefaultLibFileName(this.compilationSettings)); } - getScriptSnapshot(filename: string): ts.IScriptSnapshot { + getScriptSnapshot(filename: string): IScriptSnapshot { const scriptInfo = this.project.getScriptInfoLSHost(filename); if (scriptInfo) { return scriptInfo.getSnapshot(); @@ -238,7 +238,7 @@ namespace ts.server { this.resolvedTypeReferenceDirectives.delete(info.path); } - setCompilationSettings(opt: ts.CompilerOptions) { + setCompilationSettings(opt: CompilerOptions) { if (changesAffectModuleResolution(this.compilationSettings, opt)) { this.resolvedModuleNames.clear(); this.resolvedTypeReferenceDirectives.clear(); diff --git a/src/server/project.ts b/src/server/project.ts index d4964a4725f..fab71474851 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -105,7 +105,7 @@ namespace ts.server { export abstract class Project { private rootFiles: ScriptInfo[] = []; private rootFilesMap: Map = createMap(); - private program: ts.Program; + private program: Program; private externalFiles: SortedReadonlyArray; private missingFilesMap: Map = createMap(); @@ -180,14 +180,14 @@ namespace ts.server { private readonly projectName: string, readonly projectKind: ProjectKind, readonly projectService: ProjectService, - private documentRegistry: ts.DocumentRegistry, + private documentRegistry: DocumentRegistry, hasExplicitListOfFiles: boolean, languageServiceEnabled: boolean, private compilerOptions: CompilerOptions, public compileOnSaveEnabled: boolean) { if (!this.compilerOptions) { - this.compilerOptions = ts.getDefaultCompilerOptions(); + this.compilerOptions = getDefaultCompilerOptions(); this.compilerOptions.allowNonTsExtensions = true; this.compilerOptions.allowJs = true; } @@ -201,7 +201,7 @@ namespace ts.server { this.lsHost = new LSHost(this.projectService.host, this, this.projectService.cancellationToken); this.lsHost.setCompilationSettings(this.compilerOptions); - this.languageService = ts.createLanguageService(this.lsHost, this.documentRegistry); + this.languageService = createLanguageService(this.lsHost, this.documentRegistry); if (!languageServiceEnabled) { this.disableLanguageService(); @@ -875,7 +875,7 @@ namespace ts.server { // Used to keep track of what directories are watched for this project directoriesWatchedForTsconfig: string[] = []; - constructor(projectService: ProjectService, documentRegistry: ts.DocumentRegistry, compilerOptions: CompilerOptions) { + constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions) { super(InferredProject.newName(), ProjectKind.Inferred, projectService, @@ -948,7 +948,7 @@ namespace ts.server { constructor(configFileName: NormalizedPath, projectService: ProjectService, - documentRegistry: ts.DocumentRegistry, + documentRegistry: DocumentRegistry, hasExplicitListOfFiles: boolean, compilerOptions: CompilerOptions, private wildcardDirectories: Map, @@ -1152,7 +1152,7 @@ namespace ts.server { } getEffectiveTypeRoots() { - return ts.getEffectiveTypeRoots(this.getCompilerOptions(), this.projectService.host) || []; + return getEffectiveTypeRoots(this.getCompilerOptions(), this.projectService.host) || []; } } @@ -1164,7 +1164,7 @@ namespace ts.server { private typeAcquisition: TypeAcquisition; constructor(public externalProjectName: string, projectService: ProjectService, - documentRegistry: ts.DocumentRegistry, + documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions, languageServiceEnabled: boolean, public compileOnSaveEnabled: boolean, diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 1f2c1068eda..52107359a32 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -72,12 +72,12 @@ namespace ts.server { const lineMap = this.getLineMap(); const start = lineMap[line]; // -1 since line is 1-based const end = line + 1 < lineMap.length ? lineMap[line + 1] : this.text.length; - return ts.createTextSpanFromBounds(start, end); + return createTextSpanFromBounds(start, end); } const index = this.svc.getSnapshot().index; const { lineText, absolutePosition } = index.lineNumberToInfo(line + 1); const len = lineText !== undefined ? lineText.length : index.absolutePositionOfStartOfLine(line + 2) - absolutePosition; - return ts.createTextSpan(absolutePosition, len); + return createTextSpan(absolutePosition, len); } /** @@ -147,7 +147,7 @@ namespace ts.server { * All projects that include this file */ readonly containingProjects: Project[] = []; - private formatCodeSettings: ts.FormatCodeSettings; + private formatCodeSettings: FormatCodeSettings; readonly path: Path; private fileWatcher: FileWatcher; diff --git a/src/server/scriptVersionCache.ts b/src/server/scriptVersionCache.ts index a710347161d..b6a189904db 100644 --- a/src/server/scriptVersionCache.ts +++ b/src/server/scriptVersionCache.ts @@ -248,7 +248,7 @@ namespace ts.server { } getTextChangeRange() { - return ts.createTextChangeRange(ts.createTextSpan(this.pos, this.deleteLen), + return createTextChangeRange(createTextSpan(this.pos, this.deleteLen), this.insertedText ? this.insertedText.length : 0); } } @@ -337,21 +337,21 @@ namespace ts.server { getTextChangesBetweenVersions(oldVersion: number, newVersion: number) { if (oldVersion < newVersion) { if (oldVersion >= this.minVersion) { - const textChangeRanges: ts.TextChangeRange[] = []; + const textChangeRanges: TextChangeRange[] = []; for (let i = oldVersion + 1; i <= newVersion; i++) { const snap = this.versions[this.versionToIndex(i)]; for (const textChange of snap.changesSincePreviousVersion) { textChangeRanges.push(textChange.getTextChangeRange()); } } - return ts.collapseTextChangeRangesAcrossMultipleVersions(textChangeRanges); + return collapseTextChangeRangesAcrossMultipleVersions(textChangeRanges); } else { return undefined; } } else { - return ts.unchangedTextChangeRange; + return unchangedTextChangeRange; } } @@ -365,7 +365,7 @@ namespace ts.server { } } - export class LineIndexSnapshot implements ts.IScriptSnapshot { + export class LineIndexSnapshot implements IScriptSnapshot { constructor(readonly version: number, readonly cache: ScriptVersionCache, readonly index: LineIndex, readonly changesSincePreviousVersion: ReadonlyArray = emptyArray) { } @@ -377,10 +377,10 @@ namespace ts.server { return this.index.root.charCount(); } - getChangeRange(oldSnapshot: ts.IScriptSnapshot): ts.TextChangeRange { + getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange { if (oldSnapshot instanceof LineIndexSnapshot && this.cache === oldSnapshot.cache) { if (this.version <= oldSnapshot.version) { - return ts.unchangedTextChangeRange; + return unchangedTextChangeRange; } else { return this.cache.getTextChangesBetweenVersions(oldSnapshot.version, this.version); @@ -539,7 +539,7 @@ namespace ts.server { } static linesFromText(text: string) { - const lineMap = ts.computeLineStarts(text); + const lineMap = computeLineStarts(text); if (lineMap.length === 0) { return { lines: [], lineMap }; diff --git a/src/server/server.ts b/src/server/server.ts index 75e46d28f7a..f672ec31e1e 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -138,7 +138,7 @@ namespace ts.server { terminal: false, }); - class Logger implements ts.server.Logger { + class Logger implements server.Logger { private fd = -1; private seq = 0; private inGroup = false; diff --git a/src/server/session.ts b/src/server/session.ts index e17032c9404..bd69640a4bb 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -72,12 +72,12 @@ namespace ts.server { } } - function formatDiag(fileName: NormalizedPath, project: Project, diag: ts.Diagnostic): protocol.Diagnostic { + function formatDiag(fileName: NormalizedPath, project: Project, diag: Diagnostic): protocol.Diagnostic { const scriptInfo = project.getScriptInfoForNormalizedPath(fileName); return { start: scriptInfo.positionToLineOffset(diag.start), end: scriptInfo.positionToLineOffset(diag.start + diag.length), - text: ts.flattenDiagnosticMessageText(diag.messageText, "\n"), + text: flattenDiagnosticMessageText(diag.messageText, "\n"), code: diag.code, category: DiagnosticCategory[diag.category].toLowerCase(), source: diag.source @@ -88,12 +88,12 @@ namespace ts.server { return { line: lineAndCharacter.line + 1, offset: lineAndCharacter.character + 1 }; } - function formatConfigFileDiag(diag: ts.Diagnostic, includeFileName: true): protocol.DiagnosticWithFileName; - function formatConfigFileDiag(diag: ts.Diagnostic, includeFileName: false): protocol.Diagnostic; - function formatConfigFileDiag(diag: ts.Diagnostic, includeFileName: boolean): protocol.Diagnostic | protocol.DiagnosticWithFileName { + function formatConfigFileDiag(diag: Diagnostic, includeFileName: true): protocol.DiagnosticWithFileName; + function formatConfigFileDiag(diag: Diagnostic, includeFileName: false): protocol.Diagnostic; + function formatConfigFileDiag(diag: Diagnostic, includeFileName: boolean): protocol.Diagnostic | protocol.DiagnosticWithFileName { const start = diag.file && convertToLocation(getLineAndCharacterOfPosition(diag.file, diag.start)); const end = diag.file && convertToLocation(getLineAndCharacterOfPosition(diag.file, diag.start + diag.length)); - const text = ts.flattenDiagnosticMessageText(diag.messageText, "\n"); + const text = flattenDiagnosticMessageText(diag.messageText, "\n"); const { code, source } = diag; const category = DiagnosticCategory[diag.category].toLowerCase(); return includeFileName ? { start, end, text, code, category, source, fileName: diag.file && diag.file.fileName } : @@ -389,8 +389,8 @@ namespace ts.server { this.host.write(formatMessage(msg, this.logger, this.byteLength, this.host.newLine)); } - public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: ts.Diagnostic[]) { - const bakedDiags = ts.map(diagnostics, diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ true)); + public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: Diagnostic[]) { + const bakedDiags = map(diagnostics, diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ true)); const ev: protocol.ConfigFileDiagnosticEvent = { seq: 0, type: "event", @@ -615,7 +615,7 @@ namespace ts.server { return { file: def.fileName, start: defScriptInfo.positionToLineOffset(def.textSpan.start), - end: defScriptInfo.positionToLineOffset(ts.textSpanEnd(def.textSpan)) + end: defScriptInfo.positionToLineOffset(textSpanEnd(def.textSpan)) }; }); } @@ -639,7 +639,7 @@ namespace ts.server { return { file: def.fileName, start: defScriptInfo.positionToLineOffset(def.textSpan.start), - end: defScriptInfo.positionToLineOffset(ts.textSpanEnd(def.textSpan)) + end: defScriptInfo.positionToLineOffset(textSpanEnd(def.textSpan)) }; }); } @@ -657,7 +657,7 @@ namespace ts.server { return { file: fileName, start: scriptInfo.positionToLineOffset(textSpan.start), - end: scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan)) + end: scriptInfo.positionToLineOffset(textSpanEnd(textSpan)) }; }); } @@ -681,7 +681,7 @@ namespace ts.server { const { fileName, isWriteAccess, textSpan, isInString } = occurrence; const scriptInfo = project.getScriptInfo(fileName); const start = scriptInfo.positionToLineOffset(textSpan.start); - const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan)); + const end = scriptInfo.positionToLineOffset(textSpanEnd(textSpan)); const result: protocol.OccurrencesResponseItem = { start, end, @@ -731,7 +731,7 @@ namespace ts.server { return documentHighlights; } - function convertToDocumentHighlightsItem(documentHighlights: ts.DocumentHighlights): ts.server.protocol.DocumentHighlightsItem { + function convertToDocumentHighlightsItem(documentHighlights: DocumentHighlights): protocol.DocumentHighlightsItem { const { fileName, highlightSpans } = documentHighlights; const scriptInfo = project.getScriptInfo(fileName); @@ -740,10 +740,10 @@ namespace ts.server { highlightSpans: highlightSpans.map(convertHighlightSpan) }; - function convertHighlightSpan(highlightSpan: ts.HighlightSpan): ts.server.protocol.HighlightSpan { + function convertHighlightSpan(highlightSpan: HighlightSpan): protocol.HighlightSpan { const { textSpan, kind } = highlightSpan; const start = scriptInfo.positionToLineOffset(textSpan.start); - const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(textSpan)); + const end = scriptInfo.positionToLineOffset(textSpanEnd(textSpan)); return { start, end, kind }; } } @@ -786,7 +786,7 @@ namespace ts.server { const scriptInfo = this.projectService.getScriptInfo(args.file); projects = scriptInfo.containingProjects; } - // ts.filter handles case when 'projects' is undefined + // filter handles case when 'projects' is undefined projects = filter(projects, p => p.languageServiceEnabled); if (!projects || !projects.length) { return Errors.ThrowNoProject(); @@ -839,7 +839,7 @@ namespace ts.server { return { file: location.fileName, start: locationScriptInfo.positionToLineOffset(location.textSpan.start), - end: locationScriptInfo.positionToLineOffset(ts.textSpanEnd(location.textSpan)), + end: locationScriptInfo.positionToLineOffset(textSpanEnd(location.textSpan)), }; }); }, @@ -921,10 +921,10 @@ namespace ts.server { return undefined; } - const displayString = ts.displayPartsToString(nameInfo.displayParts); + const displayString = displayPartsToString(nameInfo.displayParts); const nameSpan = nameInfo.textSpan; const nameColStart = scriptInfo.positionToLineOffset(nameSpan.start).offset; - const nameText = scriptInfo.getSnapshot().getText(nameSpan.start, ts.textSpanEnd(nameSpan)); + const nameText = scriptInfo.getSnapshot().getText(nameSpan.start, textSpanEnd(nameSpan)); const refs = combineProjectOutput( projects, (project: Project) => { @@ -937,12 +937,12 @@ namespace ts.server { const refScriptInfo = project.getScriptInfo(ref.fileName); const start = refScriptInfo.positionToLineOffset(ref.textSpan.start); const refLineSpan = refScriptInfo.lineToTextSpan(start.line - 1); - const lineText = refScriptInfo.getSnapshot().getText(refLineSpan.start, ts.textSpanEnd(refLineSpan)).replace(/\r|\n/g, ""); + const lineText = refScriptInfo.getSnapshot().getText(refLineSpan.start, textSpanEnd(refLineSpan)).replace(/\r|\n/g, ""); return { file: ref.fileName, start, lineText, - end: refScriptInfo.positionToLineOffset(ts.textSpanEnd(ref.textSpan)), + end: refScriptInfo.positionToLineOffset(textSpanEnd(ref.textSpan)), isWriteAccess: ref.isWriteAccess, isDefinition: ref.isDefinition }; @@ -1065,14 +1065,14 @@ namespace ts.server { } if (simplifiedResult) { - const displayString = ts.displayPartsToString(quickInfo.displayParts); - const docString = ts.displayPartsToString(quickInfo.documentation); + const displayString = displayPartsToString(quickInfo.displayParts); + const docString = displayPartsToString(quickInfo.documentation); return { kind: quickInfo.kind, kindModifiers: quickInfo.kindModifiers, start: scriptInfo.positionToLineOffset(quickInfo.textSpan.start), - end: scriptInfo.positionToLineOffset(ts.textSpanEnd(quickInfo.textSpan)), + end: scriptInfo.positionToLineOffset(textSpanEnd(quickInfo.textSpan)), displayString, documentation: docString, tags: quickInfo.tags || [] @@ -1152,7 +1152,7 @@ namespace ts.server { if (preferredIndent !== hasIndent) { const firstNoWhiteSpacePosition = absolutePosition + i; edits.push({ - span: ts.createTextSpanFromBounds(absolutePosition, firstNoWhiteSpacePosition), + span: createTextSpanFromBounds(absolutePosition, firstNoWhiteSpacePosition), newText: formatting.getIndentationString(preferredIndent, formatOptions) }); } @@ -1166,7 +1166,7 @@ namespace ts.server { return edits.map((edit) => { return { start: scriptInfo.positionToLineOffset(edit.span.start), - end: scriptInfo.positionToLineOffset(ts.textSpanEnd(edit.span)), + end: scriptInfo.positionToLineOffset(textSpanEnd(edit.span)), newText: edit.newText ? edit.newText : "" }; }); @@ -1190,7 +1190,7 @@ namespace ts.server { const convertedSpan = replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined; return { name, kind, kindModifiers, sortText, replacementSpan: convertedSpan }; } - }).sort((a, b) => ts.compareStrings(a.name, b.name)); + }).sort((a, b) => compareStrings(a.name, b.name)); } else { return completions; @@ -1317,11 +1317,11 @@ namespace ts.server { if (!fileName) { return; } - const file = ts.normalizePath(fileName); + const file = normalizePath(fileName); this.projectService.closeClientFile(file); } - private decorateNavigationBarItems(items: ts.NavigationBarItem[], scriptInfo: ScriptInfo): protocol.NavigationBarItem[] { + private decorateNavigationBarItems(items: NavigationBarItem[], scriptInfo: ScriptInfo): protocol.NavigationBarItem[] { return map(items, item => ({ text: item.text, kind: item.kind, @@ -1342,7 +1342,7 @@ namespace ts.server { : items; } - private decorateNavigationTree(tree: ts.NavigationTree, scriptInfo: ScriptInfo): protocol.NavigationTree { + private decorateNavigationTree(tree: NavigationTree, scriptInfo: ScriptInfo): protocol.NavigationTree { return { text: tree.text, kind: tree.kind, @@ -1355,7 +1355,7 @@ namespace ts.server { private decorateSpan(span: TextSpan, scriptInfo: ScriptInfo): protocol.TextSpan { return { start: scriptInfo.positionToLineOffset(span.start), - end: scriptInfo.positionToLineOffset(ts.textSpanEnd(span)) + end: scriptInfo.positionToLineOffset(textSpanEnd(span)) }; } @@ -1385,7 +1385,7 @@ namespace ts.server { return navItems.map((navItem) => { const scriptInfo = project.getScriptInfo(navItem.fileName); const start = scriptInfo.positionToLineOffset(navItem.textSpan.start); - const end = scriptInfo.positionToLineOffset(ts.textSpanEnd(navItem.textSpan)); + const end = scriptInfo.positionToLineOffset(textSpanEnd(navItem.textSpan)); const bakedItem: protocol.NavtoItem = { name: navItem.name, kind: navItem.kind, @@ -1450,7 +1450,7 @@ namespace ts.server { } private getSupportedCodeFixes(): string[] { - return ts.getSupportedCodeFixes(); + return getSupportedCodeFixes(); } private isLocation(locationOrSpan: protocol.FileLocationOrRangeRequestArgs): locationOrSpan is protocol.FileLocationRequestArgs { @@ -1481,7 +1481,7 @@ namespace ts.server { return project.getLanguageService().getApplicableRefactors(file, position || textRange); } - private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): ts.RefactorEditInfo | protocol.RefactorEditInfo { + private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): RefactorEditInfo | protocol.RefactorEditInfo { const { file, project } = this.getFileAndProjectWithoutRefreshingInferredProjects(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file); const { position, textRange } = this.extractPositionAndRange(args, scriptInfo); @@ -1650,7 +1650,7 @@ namespace ts.server { getCanonicalFileName(fileName: string) { const name = this.host.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase(); - return ts.normalizePath(name); + return normalizePath(name); } exit() { diff --git a/src/server/types.ts b/src/server/types.ts index 81e1f09639f..72d6a7c6cfc 100644 --- a/src/server/types.ts +++ b/src/server/types.ts @@ -31,9 +31,9 @@ declare namespace ts.server { export interface DiscoverTypings extends TypingInstallerRequest { readonly fileNames: string[]; - readonly projectRootPath: ts.Path; - readonly compilerOptions: ts.CompilerOptions; - readonly typeAcquisition: ts.TypeAcquisition; + readonly projectRootPath: Path; + readonly compilerOptions: CompilerOptions; + readonly typeAcquisition: TypeAcquisition; readonly unresolvedImports: SortedReadonlyArray; readonly cachePath?: string; readonly kind: "discover"; @@ -63,8 +63,8 @@ declare namespace ts.server { } export interface SetTypings extends ProjectResponse { - readonly typeAcquisition: ts.TypeAcquisition; - readonly compilerOptions: ts.CompilerOptions; + readonly typeAcquisition: TypeAcquisition; + readonly compilerOptions: CompilerOptions; readonly typings: string[]; readonly unresolvedImports: SortedReadonlyArray; readonly kind: ActionSet; diff --git a/src/server/utilities.ts b/src/server/utilities.ts index 10290fe864d..a3dcd916172 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -77,7 +77,7 @@ namespace ts.server { tabSize: 4, newLineCharacter: host.newLine || "\n", convertTabsToSpaces: true, - indentStyle: ts.IndentStyle.Smart, + indentStyle: IndentStyle.Smart, insertSpaceAfterConstructor: false, insertSpaceAfterCommaDelimiter: true, insertSpaceAfterSemicolonInForStatements: true, @@ -196,7 +196,7 @@ namespace ts.server { } export function enumerateInsertsAndDeletes(a: SortedReadonlyArray, b: SortedReadonlyArray, inserted: (item: T) => void, deleted: (item: T) => void, compare?: (a: T, b: T) => Comparison) { - compare = compare || ts.compareValues; + compare = compare || compareValues; let aIndex = 0; let bIndex = 0; const aLen = a.length;