From da3333e967a39aba6fffc7325f0a790f3ada1af1 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Tue, 14 Jun 2016 19:23:04 -0700 Subject: [PATCH 01/13] Clear out unused compiler options when transpiling --- src/services/services.ts | 10 +- tests/cases/unittests/transpile.ts | 225 +++++++++++++++++++++++++++-- 2 files changed, 220 insertions(+), 15 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index c20f95a56f2..19167a5f6e0 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2001,9 +2001,17 @@ namespace ts { // so pass --noLib to avoid reporting a file not found error. options.noLib = true; - // Clear out the lib and types option as well + // Clear out other settings that would not be participate in transpiling this module options.lib = undefined; options.types = undefined; + options.noEmit = undefined; + options.noEmitOnError = undefined; + options.paths = undefined; + options.rootDirs = undefined; + options.declaration = undefined; + options.declarationDir = undefined; + options.out = undefined; + options.outFile = undefined; // We are not doing a full typecheck, we are not resolving the whole context, // so pass --noResolve to avoid reporting missing file errors. diff --git a/tests/cases/unittests/transpile.ts b/tests/cases/unittests/transpile.ts index cac44994420..c8025bfc0a2 100644 --- a/tests/cases/unittests/transpile.ts +++ b/tests/cases/unittests/transpile.ts @@ -1,5 +1,9 @@ /// +interface ObjectConstructor { + assign(target: T, source: U): T & U; +} + namespace ts { describe("Transpile", () => { @@ -21,6 +25,9 @@ namespace ts { assert.equal(expectedDiagnosticTexts[i], diagnostics[i] && diagnostics[i].messageText); } }; + if (diagnostics.length !== n && diagnostics.length) { + console.log(JSON.stringify(diagnostics, undefined, 2)); + } assert.equal(diagnostics.length, n, "Resuting diagnostics count does not match expected"); } @@ -89,6 +96,19 @@ namespace ts { } + function testCompilerOption(options: CompilerOptions, input?: string, output?: string): void { + input = input || "x = 0;"; + output = output || `"use strict";\r\nx = 0;\r\n`; + test(input, { + expectedOutput: output, + options: { + compilerOptions: Object.assign({ module: ModuleKind.CommonJS, newLine: NewLineKind.CarriageReturnLineFeed }, options), + fileName: "input.js", + reportDiagnostics: true + } + }); + } + it("Generates no diagnostics with valid inputs", () => { // No errors test(`var x = 0;`, { options: { compilerOptions: { module: ModuleKind.CommonJS } } }); @@ -304,21 +324,198 @@ var x = 0;`, test("var x", { expectedOutput: `"use strict";\r\nvar x;\r\n`, options: { fileName: "http://somewhere/directory//directory2/file.ts" } }); }); - it("Support options with lib values", () => { - const input = "const a = 10;"; - const output = `"use strict";\r\nvar a = 10;\r\n`; - test(input, { - expectedOutput: output, - options: { compilerOptions: { lib: ["es6", "dom"], module: ModuleKind.CommonJS }, fileName: "input.js", reportDiagnostics: true } - }); - }); + describe("Works with all compiler options", () => { - it("Support options with types values", () => { - const input = "const a = 10;"; - const output = `"use strict";\r\nvar a = 10;\r\n`; - test(input, { - expectedOutput: output, - options: { compilerOptions: { types: ["jquery", "typescript"], module: ModuleKind.CommonJS }, fileName: "input.js", reportDiagnostics: true } + it("Supports setting 'allowJs'", () => { + testCompilerOption({ allowJs: true }); + }); + + it("Supports setting 'allowSyntheticDefaultImports'", () => { + testCompilerOption({ allowSyntheticDefaultImports: true }); + }); + + it("Supports setting 'allowUnreachableCode'", () => { + testCompilerOption({ allowUnreachableCode: true }); + }); + + it("Supports setting 'allowUnusedLabels'", () => { + testCompilerOption({ allowUnusedLabels: true }); + }); + + it("Supports setting 'baseUrl'", () => { + testCompilerOption({ baseUrl: "./folder/baseUrl" }); + }); + + it("Supports setting 'charset'", () => { + testCompilerOption({ charset: "en-us" }); + }); + + it("Supports setting 'declaration'", () => { + testCompilerOption({ declaration: true }); + }); + + it("Supports setting 'declarationDir'", () => { + testCompilerOption({ declarationDir: "out/declarations" }); + }); + + it("Supports setting 'emitBOM'", () => { + testCompilerOption({ emitBOM: true }); + }); + + it("Supports setting 'emitDecoratorMetadata'", () => { + testCompilerOption({ emitDecoratorMetadata: true, experimentalDecorators: true }); + }); + + it("Supports setting 'experimentalDecorators'", () => { + testCompilerOption({ experimentalDecorators: true }); + }); + + it("Supports setting 'forceConsistentCasingInFileNames'", () => { + testCompilerOption({ forceConsistentCasingInFileNames: true }); + }); + + it("Supports setting 'isolatedModules'", () => { + testCompilerOption({ isolatedModules: true }); + }); + + it("Supports setting 'jsx'", () => { + testCompilerOption({ jsx: 1 }); + }); + + it("Supports setting 'lib'", () => { + testCompilerOption({ lib: ["es2015", "dom"] }); + }); + + it("Supports setting 'locale'", () => { + testCompilerOption({ locale: "en-us" }); + }); + + it("Supports setting 'module'", () => { + testCompilerOption({ module: 1 }); + }); + + it("Supports setting 'moduleResolution'", () => { + testCompilerOption({ moduleResolution: 2 }); + }); + + it("Supports setting 'newLine'", () => { + testCompilerOption({ newLine: 0 }); + }); + + it("Supports setting 'noEmit'", () => { + testCompilerOption({ noEmit: true }); + }); + + it("Supports setting 'noEmitHelpers'", () => { + testCompilerOption({ noEmitHelpers: true }); + }); + + it("Supports setting 'noEmitOnError'", () => { + testCompilerOption({ noEmitOnError: true }); + }); + + it("Supports setting 'noErrorTruncation'", () => { + testCompilerOption({ noErrorTruncation: true }); + }); + + it("Supports setting 'noFallthroughCasesInSwitch'", () => { + testCompilerOption({ noFallthroughCasesInSwitch: true }); + }); + + it("Supports setting 'noImplicitAny'", () => { + testCompilerOption({ noImplicitAny: true }); + }); + + it("Supports setting 'noImplicitReturns'", () => { + testCompilerOption({ noImplicitReturns: true }); + }); + + it("Supports setting 'noImplicitThis'", () => { + testCompilerOption({ noImplicitThis: true }); + }); + + it("Supports setting 'noImplicitUseStrict'", () => { + testCompilerOption({ noImplicitUseStrict: true }, "x;", "x;\r\n"); + }); + + it("Supports setting 'noLib'", () => { + testCompilerOption({ noLib: true }); + }); + + it("Supports setting 'noResolve'", () => { + testCompilerOption({ noResolve: true }); + }); + + it("Supports setting 'out'", () => { + testCompilerOption({ out: "./out" }); + }); + + it("Supports setting 'outDir'", () => { + testCompilerOption({ outDir: "./outDir" }); + }); + + it("Supports setting 'outFile'", () => { + testCompilerOption({ outFile: "./outFile" }); + }); + + it("Supports setting 'paths'", () => { + testCompilerOption({ paths: { "*": ["./generated*"] } }); + }); + + it("Supports setting 'preserveConstEnums'", () => { + testCompilerOption({ preserveConstEnums: true }); + }); + + it("Supports setting 'reactNamespace'", () => { + testCompilerOption({ reactNamespace: "react" }); + }); + + it("Supports setting 'removeComments'", () => { + testCompilerOption({ removeComments: true }); + }); + + it("Supports setting 'rootDir'", () => { + testCompilerOption({ rootDir: "./rootDir" }); + }); + + it("Supports setting 'rootDirs'", () => { + testCompilerOption({ rootDirs: ["./a", "./b"] }); + }); + + it("Supports setting 'skipLibCheck'", () => { + testCompilerOption({ skipLibCheck: true }); + }); + + it("Supports setting 'skipDefaultLibCheck'", () => { + testCompilerOption({ skipDefaultLibCheck: true }); + }); + + it("Supports setting 'strictNullChecks'", () => { + testCompilerOption({ strictNullChecks: true }); + }); + + it("Supports setting 'stripInternal'", () => { + testCompilerOption({ stripInternal: true }); + }); + + it("Supports setting 'suppressExcessPropertyErrors'", () => { + testCompilerOption({ suppressExcessPropertyErrors: true }); + }); + + it("Supports setting 'suppressImplicitAnyIndexErrors'", () => { + testCompilerOption({ suppressImplicitAnyIndexErrors: true }); + }); + + it("Supports setting 'target'", () => { + testCompilerOption({ target: 2 }); + }); + + it("Supports setting 'types'", () => { + testCompilerOption({ types: ["jquery", "jasmine"] }); + }); + + it("Supports setting 'typeRoots'", () => { + testCompilerOption({ typeRoots: ["./folder"] }); }); }); From 95ddfc7efc788c14900dc20da03f2b805e495854 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Wed, 15 Jun 2016 11:38:28 -0700 Subject: [PATCH 02/13] Do not use Object.assing in test --- tests/cases/unittests/transpile.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/cases/unittests/transpile.ts b/tests/cases/unittests/transpile.ts index c8025bfc0a2..6c88c2558fc 100644 --- a/tests/cases/unittests/transpile.ts +++ b/tests/cases/unittests/transpile.ts @@ -1,9 +1,5 @@ /// -interface ObjectConstructor { - assign(target: T, source: U): T & U; -} - namespace ts { describe("Transpile", () => { @@ -102,7 +98,7 @@ namespace ts { test(input, { expectedOutput: output, options: { - compilerOptions: Object.assign({ module: ModuleKind.CommonJS, newLine: NewLineKind.CarriageReturnLineFeed }, options), + compilerOptions: extend(options, { module: ModuleKind.CommonJS, newLine: NewLineKind.CarriageReturnLineFeed }), fileName: "input.js", reportDiagnostics: true } From ce45ee797c919b914ce65f4e5ef1065696d04662 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Wed, 15 Jun 2016 12:23:00 -0700 Subject: [PATCH 03/13] Fix comment --- src/services/services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/services.ts b/src/services/services.ts index 19167a5f6e0..62a50db65ef 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2001,7 +2001,7 @@ namespace ts { // so pass --noLib to avoid reporting a file not found error. options.noLib = true; - // Clear out other settings that would not be participate in transpiling this module + // Clear out other settings that would not be used in transpiling this module options.lib = undefined; options.types = undefined; options.noEmit = undefined; From 478d76347b17c30104c766c0d73b229464d73eff Mon Sep 17 00:00:00 2001 From: zhengbli Date: Thu, 16 Jun 2016 02:16:47 -0700 Subject: [PATCH 04/13] ignore casing when converting a source file path to relative path --- src/compiler/utilities.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 31b464b81ce..f4b9592043e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2330,7 +2330,9 @@ namespace ts { export function getSourceFilePathInNewDir(sourceFile: SourceFile, host: EmitHost, newDirPath: string) { let sourceFilePath = getNormalizedAbsolutePath(sourceFile.fileName, host.getCurrentDirectory()); - sourceFilePath = sourceFilePath.replace(host.getCommonSourceDirectory(), ""); + const commonSourceDirectory = host.getCommonSourceDirectory(); + const isSourceFileInCommonSourceDirectory = sourceFilePath.toLowerCase().indexOf(commonSourceDirectory.toLowerCase()) === 0; + sourceFilePath = isSourceFileInCommonSourceDirectory ? sourceFilePath.substring(commonSourceDirectory.length) : sourceFilePath; return combinePaths(newDirPath, sourceFilePath); } From 166bc49f0c666f5a2a75bcfacd421e6b8f5135c7 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 16 Jun 2016 13:20:12 -0700 Subject: [PATCH 05/13] Refactor navigation bar --- src/compiler/core.ts | 11 + src/compiler/parser.ts | 8 +- src/server/client.ts | 17 +- src/server/editorServices.ts | 14 +- src/server/session.ts | 10 +- src/services/navigationBar.ts | 1374 +++++++---------- src/services/services.ts | 2 +- .../navbar_contains-no-duplicates.ts | 14 +- tests/cases/fourslash/navbar_exportDefault.ts | 16 +- ...BarAnonymousClassAndFunctionExpressions.ts | 147 ++ ...arAnonymousClassAndFunctionExpressions2.ts | 64 + .../fourslash/navigationBarGetterAndSetter.ts | 48 + tests/cases/fourslash/navigationBarImports.ts | 30 + .../fourslash/navigationBarItemsFunctions.ts | 14 + .../navigationBarItemsFunctionsBroken.ts | 6 + .../navigationBarItemsFunctionsBroken2.ts | 10 + ...ionBarItemsInsideMethodsAndConstructors.ts | 12 +- .../fourslash/navigationBarItemsItems2.ts | 7 +- ...rItemsItemsContainsNoAnonymousFunctions.ts | 80 - .../navigationBarItemsMissingName1.ts | 5 + .../navigationBarItemsMissingName2.ts | 10 +- .../fourslash/navigationBarItemsModules.ts | 196 +-- ...ationBarItemsMultilineStringIdentifiers.ts | 56 +- tests/cases/fourslash/navigationBarJsDoc.ts | 28 +- tests/cases/fourslash/navigationBarMerging.ts | 189 +++ .../cases/fourslash/navigationBarVariables.ts | 53 + .../server/jsdocTypedefTagNavigateTo.ts | 48 +- 27 files changed, 1416 insertions(+), 1053 deletions(-) create mode 100644 tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions.ts create mode 100644 tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions2.ts create mode 100644 tests/cases/fourslash/navigationBarGetterAndSetter.ts create mode 100644 tests/cases/fourslash/navigationBarImports.ts delete mode 100644 tests/cases/fourslash/navigationBarItemsItemsContainsNoAnonymousFunctions.ts create mode 100644 tests/cases/fourslash/navigationBarMerging.ts create mode 100644 tests/cases/fourslash/navigationBarVariables.ts diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 8a2040a83df..10a0526d6ae 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -138,6 +138,17 @@ namespace ts { return result; } + export function filterMutate(array: T[], f: (x: T) => boolean): void { + let outIndex = 0; + for (const item of array) { + if (f(item)) { + array[outIndex] = item; + outIndex++; + } + } + array.length = outIndex; + } + export function map(array: T[], f: (x: T) => U): U[] { let result: U[]; if (array) { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b2f3755ec46..6e0a37c3d9f 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -17,19 +17,19 @@ namespace ts { } function visitNode(cbNode: (node: Node) => T, node: Node): T { - if (node) { + if (node !== void 0) { return cbNode(node); } } function visitNodeArray(cbNodes: (nodes: Node[]) => T, nodes: Node[]) { - if (nodes) { + if (nodes !== void 0) { return cbNodes(nodes); } } function visitEachNode(cbNode: (node: Node) => T, nodes: Node[]) { - if (nodes) { + if (nodes !== void 0) { for (const node of nodes) { const result = cbNode(node); if (result) { @@ -44,7 +44,7 @@ namespace ts { // embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns // a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned. export function forEachChild(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T { - if (!node) { + if (node === void 0) { return; } // The visitXXX functions could be written as local functions that close over the cbNode and cbNodeArray diff --git a/src/server/client.ts b/src/server/client.ts index 864dac9fdaa..09cfa2ac739 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -45,8 +45,9 @@ namespace ts.server { return lineMap; } - private lineOffsetToPosition(fileName: string, lineOffset: protocol.Location): number { - return ts.computePositionOfLineAndCharacter(this.getLineMap(fileName), lineOffset.line - 1, lineOffset.offset - 1); + private lineOffsetToPosition(fileName: string, lineOffset: protocol.Location, lineMap?: number[]): number { + lineMap = lineMap || this.getLineMap(fileName); + return ts.computePositionOfLineAndCharacter(lineMap, lineOffset.line - 1, lineOffset.offset - 1); } private positionToOneBasedLineOffset(fileName: string, position: number): protocol.Location { @@ -449,7 +450,7 @@ namespace ts.server { return this.lastRenameEntry.locations; } - decodeNavigationBarItems(items: protocol.NavigationBarItem[], fileName: string): NavigationBarItem[] { + decodeNavigationBarItems(items: protocol.NavigationBarItem[], fileName: string, lineMap: number[]): NavigationBarItem[] { if (!items) { return []; } @@ -458,8 +459,11 @@ namespace ts.server { text: item.text, kind: item.kind, kindModifiers: item.kindModifiers || "", - spans: item.spans.map(span => createTextSpanFromBounds(this.lineOffsetToPosition(fileName, span.start), this.lineOffsetToPosition(fileName, span.end))), - childItems: this.decodeNavigationBarItems(item.childItems, fileName), + spans: item.spans.map(span => + createTextSpanFromBounds( + this.lineOffsetToPosition(fileName, span.start, lineMap), + this.lineOffsetToPosition(fileName, span.end, lineMap))), + childItems: this.decodeNavigationBarItems(item.childItems, fileName, lineMap), indent: item.indent, bolded: false, grayed: false @@ -474,7 +478,8 @@ namespace ts.server { const request = this.processRequest(CommandNames.NavBar, args); const response = this.processResponse(request); - return this.decodeNavigationBarItems(response.body, fileName); + const lineMap = this.getLineMap(fileName); + return this.decodeNavigationBarItems(response.body, fileName, lineMap); } getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 346b2e00a10..74dbb7a0785 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -359,12 +359,16 @@ namespace ts.server { * @param line 1-based index * @param offset 1-based index */ - positionToLineOffset(filename: string, position: number): ILineInfo { + positionToLineOffset(filename: string, position: number, lineIndex?: LineIndex): ILineInfo { + lineIndex = lineIndex || this.getLineIndex(filename); + const lineOffset = lineIndex.charOffsetToLineNumberAndPos(position); + return { line: lineOffset.line, offset: lineOffset.offset + 1 }; + } + + getLineIndex(filename: string): LineIndex { const path = toPath(filename, this.host.getCurrentDirectory(), this.getCanonicalFileName); const script: ScriptInfo = this.filenameToScript.get(path); - const index = script.snap().index; - const lineOffset = index.charOffsetToLineNumberAndPos(position); - return { line: lineOffset.line, offset: lineOffset.offset + 1 }; + return script.snap().index; } } @@ -1452,7 +1456,7 @@ namespace ts.server { } // if the project is too large, the root files might not have been all loaded if the total - // program size reached the upper limit. In that case project.projectOptions.files should + // program size reached the upper limit. In that case project.projectOptions.files should // be more precise. However this would only happen for configured project. const oldFileNames = project.projectOptions ? project.projectOptions.files : project.compilerService.host.roots.map(info => info.fileName); const newFileNames = ts.filter(projectOptions.files, f => this.host.fileExists(f)); diff --git a/src/server/session.ts b/src/server/session.ts index 65540082f60..aafc02a2b0b 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -863,7 +863,7 @@ namespace ts.server { this.projectService.closeClientFile(file); } - private decorateNavigationBarItem(project: Project, fileName: string, items: ts.NavigationBarItem[]): protocol.NavigationBarItem[] { + private decorateNavigationBarItem(project: Project, fileName: string, items: ts.NavigationBarItem[], lineIndex: LineIndex): protocol.NavigationBarItem[] { if (!items) { return undefined; } @@ -875,10 +875,10 @@ namespace ts.server { kind: item.kind, kindModifiers: item.kindModifiers, spans: item.spans.map(span => ({ - start: compilerService.host.positionToLineOffset(fileName, span.start), - end: compilerService.host.positionToLineOffset(fileName, ts.textSpanEnd(span)) + start: compilerService.host.positionToLineOffset(fileName, span.start, lineIndex), + end: compilerService.host.positionToLineOffset(fileName, ts.textSpanEnd(span), lineIndex) })), - childItems: this.decorateNavigationBarItem(project, fileName, item.childItems), + childItems: this.decorateNavigationBarItem(project, fileName, item.childItems, lineIndex), indent: item.indent })); } @@ -896,7 +896,7 @@ namespace ts.server { return undefined; } - return this.decorateNavigationBarItem(project, fileName, items); + return this.decorateNavigationBarItem(project, fileName, items, compilerService.host.getLineIndex(fileName)); } private getNavigateToItems(searchValue: string, fileName: string, maxResultCount?: number): protocol.NavtoItem[] { diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 59dc4a38283..e722f33bc70 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -2,860 +2,660 @@ /* @internal */ namespace ts.NavigationBar { - export function getNavigationBarItems(sourceFile: SourceFile, compilerOptions: CompilerOptions): ts.NavigationBarItem[] { - // TODO: Handle JS files differently in 'navbar' calls for now, but ideally we should unify - // the 'navbar' and 'navto' logic for TypeScript and JavaScript. - if (isSourceFileJavaScript(sourceFile)) { - return getJsNavigationBarItems(sourceFile, compilerOptions); + /** + * Represents a navigation bar item and its children. + * The returned NavigationBarItem is more complicated and doesn't include 'parent', so we use these to do work before converting. + */ + interface NavigationBarNode { + node: Node; + additionalNodes: Node[] | undefined; + parent: NavigationBarNode | undefined; // Present for all but root node + children: NavigationBarNode[] | undefined; + indent: number; // # of parents + } + + export function getNavigationBarItems(sourceFile_: SourceFile): NavigationBarItem[] { + sourceFile = sourceFile_; + const result = map(topLevelItems(rootNavigationBarNode(sourceFile)), convertToTopLevelItem); + sourceFile = void 0; + return result; + } + + // Keep sourceFile handy so we don't have to search for it every time we need to call `getText`. + let sourceFile: SourceFile; + function nodeText(node: Node): string { + return node.getText(sourceFile); + } + + function navigationBarNodeKind(n: NavigationBarNode): SyntaxKind { + return n.node.kind; + } + + function pushChild(parent: NavigationBarNode, child: NavigationBarNode): void { + if (parent.children !== void 0) { + parent.children.push(child); + } + else { + parent.children = [child]; + } + } + + /* + For performance, we keep navigation bar parents on a stack rather than passing them through each recursion. + `parent` is the current parent and is *not* stored in parentsStack. + `startNode` sets a new parent and `endNode` returns to the previous parent. + */ + const parentsStack: NavigationBarNode[] = []; + let parent: NavigationBarNode; + + function rootNavigationBarNode(sourceFile: SourceFile): NavigationBarNode { + Debug.assert(!parentsStack.length); + const root: NavigationBarNode = { node: sourceFile, additionalNodes: void 0, parent: void 0, children: void 0, indent: 0 }; + parent = root; + for (const statement of sourceFile.statements) { + addChildrenRecursively(statement); + } + endNode(); + Debug.assert(parent === void 0 && !parentsStack.length); + return root; + } + + function addLeafNode(node: Node): void { + pushChild(parent, emptyNavigationBarNode(node)); + } + + function emptyNavigationBarNode(node: Node): NavigationBarNode { + return { + node, + additionalNodes: void 0, + parent, + children: void 0, + indent: parent.indent + 1 + }; + } + + /** + * Add a new level of NavigationBarNodes. + * This pushes to the stack, so you must call `endNode` when you are done adding to this node. + */ + function startNode(node: Node): void { + const navNode: NavigationBarNode = emptyNavigationBarNode(node); + pushChild(parent, navNode); + + // Save the old parent + parentsStack.push(parent); + parent = navNode; + } + + /** Call after calling `startNode` and adding children to it. */ + function endNode(): void { + if (parent.children !== void 0) { + mergeChildren(parent.children); + sortChildren(parent.children); + } + parent = parentsStack.pop(); + } + + function addNodeWithRecursiveChild(node: Node, child: Node): void { + startNode(node); + addChildrenRecursively(child); + endNode(); + } + + /** Look for navigation bar items in node's subtree, adding them to the current `parent`. */ + function addChildrenRecursively(node: Node): void { + if (node === void 0 || isToken(node)) { + return; } - return getItemsWorker(getTopLevelNodes(sourceFile), createTopLevelItem); + switch (node.kind) { + case SyntaxKind.Constructor: + // Get parameter properties, and treat them as being on the *same* level as the constructor, not under it. + const ctr = node; + addNodeWithRecursiveChild(ctr, ctr.body); - function getIndent(node: Node): number { - let indent = 1; // Global node is the only one with indent 0. + // Parameter properties are children of the class, not the constructor. + for (const param of ctr.parameters) { + if (isParameterPropertyDeclaration(param)) { + addLeafNode(param); + } + } + break; - let current = node.parent; - while (current) { - switch (current.kind) { - case SyntaxKind.ModuleDeclaration: - // If we have a module declared as A.B.C, it is more "intuitive" - // to say it only has a single layer of depth - do { - current = current.parent; - } - while (current.kind === SyntaxKind.ModuleDeclaration); + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.MethodSignature: + if (!hasDynamicName((node))) { + addNodeWithRecursiveChild(node, (node).body); + } + break; - // fall through - case SyntaxKind.ClassDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.FunctionDeclaration: - indent++; + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertySignature: + if (!hasDynamicName((node))) { + addLeafNode(node); + } + break; + + case SyntaxKind.ImportClause: + let importClause = node; + // Handle default import case e.g.: + // import d from "mod"; + if (importClause.name !== void 0) { + addLeafNode(importClause); } - current = current.parent; - } - - return indent; - } - - function getChildNodes(nodes: Node[]): Node[] { - const childNodes: Node[] = []; - - function visit(node: Node) { - switch (node.kind) { - case SyntaxKind.VariableStatement: - forEach((node).declarationList.declarations, visit); - break; - case SyntaxKind.ObjectBindingPattern: - case SyntaxKind.ArrayBindingPattern: - forEach((node).elements, visit); - break; - - case SyntaxKind.ExportDeclaration: - // Handle named exports case e.g.: - // export {a, b as B} from "mod"; - if ((node).exportClause) { - forEach((node).exportClause.elements, visit); + // Handle named bindings in imports e.g.: + // import * as NS from "mod"; + // import {a, b as B} from "mod"; + const {namedBindings} = importClause; + if (namedBindings !== void 0) { + if (namedBindings.kind === SyntaxKind.NamespaceImport) { + addLeafNode(namedBindings); + } + else { + for (const element of (namedBindings).elements) { + addLeafNode(element); } - break; - - case SyntaxKind.ImportDeclaration: - let importClause = (node).importClause; - if (importClause) { - // Handle default import case e.g.: - // import d from "mod"; - if (importClause.name) { - childNodes.push(importClause); - } - - // Handle named bindings in imports e.g.: - // import * as NS from "mod"; - // import {a, b as B} from "mod"; - if (importClause.namedBindings) { - if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { - childNodes.push(importClause.namedBindings); - } - else { - forEach((importClause.namedBindings).elements, visit); - } - } - } - break; - - case SyntaxKind.BindingElement: - case SyntaxKind.VariableDeclaration: - if (isBindingPattern((node).name)) { - visit((node).name); - break; - } - // Fall through - case SyntaxKind.ClassDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ImportSpecifier: - case SyntaxKind.ExportSpecifier: - case SyntaxKind.TypeAliasDeclaration: - childNodes.push(node); - break; + } } - } + break; - // for (let i = 0, n = nodes.length; i < n; i++) { - // let node = nodes[i]; - - // if (node.kind === SyntaxKind.ClassDeclaration || - // node.kind === SyntaxKind.EnumDeclaration || - // node.kind === SyntaxKind.InterfaceDeclaration || - // node.kind === SyntaxKind.ModuleDeclaration || - // node.kind === SyntaxKind.FunctionDeclaration) { - - // childNodes.push(node); - // } - // else if (node.kind === SyntaxKind.VariableStatement) { - // childNodes.push.apply(childNodes, (node).declarations); - // } - // } - forEach(nodes, visit); - return sortNodes(childNodes); - } - - function getTopLevelNodes(node: SourceFile): Node[] { - const topLevelNodes: Node[] = []; - topLevelNodes.push(node); - - addTopLevelNodes(node.statements, topLevelNodes); - - return topLevelNodes; - } - - function sortNodes(nodes: Node[]): Node[] { - return nodes.slice(0).sort((n1: Declaration, n2: Declaration) => { - if (n1.name && n2.name) { - return localeCompareFix(getPropertyNameForPropertyNameNode(n1.name), getPropertyNameForPropertyNameNode(n2.name)); + case SyntaxKind.BindingElement: + case SyntaxKind.VariableDeclaration: + const decl = node; + const name = decl.name; + if (isBindingPattern(name)) { + addChildrenRecursively(name); } - else if (n1.name) { - return 1; - } - else if (n2.name) { - return -1; + else if (decl.initializer !== void 0 && isFunctionOrClassExpression(decl.initializer)) { + // For `const x = function() {}`, just use the function node, not the const. + addChildrenRecursively(decl.initializer); } else { - return n1.kind - n2.kind; + addNodeWithRecursiveChild(decl, decl.initializer); } - }); + break; - // node 0.10 treats "a" as greater than "B". - // For consistency, sort alphabetically, falling back to which is lower-case. - function localeCompareFix(a: string, b: string) { - const cmp = a.toLowerCase().localeCompare(b.toLowerCase()); - if (cmp !== 0) - return cmp; - // Return the *opposite* of the `<` operator, which works the same in node 0.10 and 6.0. - return a < b ? 1 : a > b ? -1 : 0; - } - } + case SyntaxKind.ArrowFunction: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + addNodeWithRecursiveChild(node, (node).body); + break; - function addTopLevelNodes(nodes: Node[], topLevelNodes: Node[]): void { - nodes = sortNodes(nodes); + case SyntaxKind.EnumDeclaration: + startNode(node); + for (const member of (node).members) { + if (!isComputedProperty(member)) { + addLeafNode(member); + } + } + endNode(); + break; - for (const node of nodes) { - switch (node.kind) { - case SyntaxKind.ClassDeclaration: - topLevelNodes.push(node); - for (const member of (node).members) { - if (member.kind === SyntaxKind.MethodDeclaration || member.kind === SyntaxKind.Constructor) { - type FunctionLikeMember = MethodDeclaration | ConstructorDeclaration; - if ((member).body) { - // We do not include methods that does not have child functions in it, because of duplications. - if (hasNamedFunctionDeclarations(((member).body).statements)) { - topLevelNodes.push(member); - } - addTopLevelNodes(((member).body).statements, topLevelNodes); - } + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: + case SyntaxKind.InterfaceDeclaration: + startNode(node); + for (const member of (node).members) { + addChildrenRecursively(member); + } + endNode(); + break; + + case SyntaxKind.ModuleDeclaration: + addNodeWithRecursiveChild(node, getInteriorModule(node).body); + break; + + case SyntaxKind.ExportSpecifier: + case SyntaxKind.ImportEqualsDeclaration: + case SyntaxKind.IndexSignature: + case SyntaxKind.CallSignature: + case SyntaxKind.ConstructSignature: + case SyntaxKind.TypeAliasDeclaration: + addLeafNode(node); + break; + + default: + if (node.jsDocComments !== void 0) { + for (const jsDocComment of node.jsDocComments) { + for (const tag of jsDocComment.tags) { + if (tag.kind === SyntaxKind.JSDocTypedefTag) { + addLeafNode(tag); } } - break; - case SyntaxKind.EnumDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - topLevelNodes.push(node); - break; - - case SyntaxKind.ModuleDeclaration: - let moduleDeclaration = node; - topLevelNodes.push(node); - const inner = getInnermostModule(moduleDeclaration); - if (inner.body) { - addTopLevelNodes((inner.body).statements, topLevelNodes); - } - break; - - case SyntaxKind.FunctionDeclaration: - let functionDeclaration = node; - if (isTopLevelFunctionDeclaration(functionDeclaration)) { - topLevelNodes.push(node); - addTopLevelNodes((functionDeclaration.body).statements, topLevelNodes); - } - break; + } } - } - } - function hasNamedFunctionDeclarations(nodes: NodeArray): boolean { - for (const s of nodes) { - if (s.kind === SyntaxKind.FunctionDeclaration && !isEmpty((s).name.text)) { + forEachChild(node, addChildrenRecursively); + } + } + + /** Merge declarations of the same kind. */ + function mergeChildren(children: NavigationBarNode[]): void { + const nameToItems: Map = {}; + filterMutate(children, child => { + const decl = child.node; + const name = decl.name && nodeText(decl.name); + if (name === void 0) { + // Anonymous items are never merged. + return true; + } + + const itemsWithSameName = getProperty(nameToItems, name); + if (itemsWithSameName === void 0) { + nameToItems[name] = child; + return true; + } + + if (itemsWithSameName instanceof Array) { + for (const itemWithSameName of itemsWithSameName) { + if (tryMerge(itemWithSameName, child)) { + return false; + } + } + itemsWithSameName.push(child); + return true; + } + else { + const itemWithSameName = itemsWithSameName; + if (tryMerge(itemWithSameName, child)) { + return false; + } + nameToItems[name] = [itemWithSameName, child]; + return true; + } + + function tryMerge(a: NavigationBarNode, b: NavigationBarNode): boolean { + if (shouldReallyMerge(a.node, b.node)) { + merge(a, b); return true; } + return false; } - return false; - } + }); - function isTopLevelFunctionDeclaration(functionDeclaration: FunctionLikeDeclaration): boolean { - if (functionDeclaration.kind === SyntaxKind.FunctionDeclaration) { - // A function declaration is 'top level' if it contains any function declarations - // within it. - if (functionDeclaration.body && functionDeclaration.body.kind === SyntaxKind.Block) { - // Proper function declarations can only have identifier names - if (hasNamedFunctionDeclarations((functionDeclaration.body).statements)) { - return true; - } + /** a and b have the same name, but they may not be mergeable. */ + function shouldReallyMerge(a: Node, b: Node): boolean { + return a.kind === b.kind && (a.kind !== SyntaxKind.ModuleDeclaration || areSameModule(a, b)); - // Or if it is not parented by another function. I.e all functions at module scope are 'top level'. - if (!isFunctionBlock(functionDeclaration.parent)) { - return true; - } - - // Or if it is nested inside class methods and constructors. - else { - // We have made sure that a grand parent node exists with 'isFunctionBlock()' above. - const grandParentKind = functionDeclaration.parent.parent.kind; - if (grandParentKind === SyntaxKind.MethodDeclaration || - grandParentKind === SyntaxKind.Constructor) { - - return true; - } - } + // We use 1 NavNode to represent 'A.B.C', but there are multiple source nodes. + // Only merge module nodes that have the same chain. Don't merge 'A.B.C' with 'A'! + function areSameModule(a: ModuleDeclaration, b: ModuleDeclaration): boolean { + if (a.body.kind !== b.body.kind) { + return false; } - } - - return false; - } - - function getItemsWorker(nodes: Node[], createItem: (n: Node) => ts.NavigationBarItem): ts.NavigationBarItem[] { - const items: ts.NavigationBarItem[] = []; - - const keyToItem: Map = {}; - - for (const child of nodes) { - const item = createItem(child); - if (item !== undefined) { - if (item.text.length > 0) { - const key = item.text + "-" + item.kind + "-" + item.indent; - - const itemWithSameName = keyToItem[key]; - if (itemWithSameName) { - // We had an item with the same name. Merge these items together. - merge(itemWithSameName, item); - } - else { - keyToItem[key] = item; - items.push(item); - } - } - } - } - - return items; - } - - function merge(target: ts.NavigationBarItem, source: ts.NavigationBarItem) { - // First, add any spans in the source to the target. - addRange(target.spans, source.spans); - - if (source.childItems) { - if (!target.childItems) { - target.childItems = []; - } - - // Next, recursively merge or add any children in the source as appropriate. - outer: - for (const sourceChild of source.childItems) { - for (const targetChild of target.childItems) { - if (targetChild.text === sourceChild.text && targetChild.kind === sourceChild.kind) { - // Found a match. merge them. - merge(targetChild, sourceChild); - continue outer; - } - } - - // Didn't find a match, just add this child to the list. - target.childItems.push(sourceChild); + if (a.body.kind !== SyntaxKind.ModuleDeclaration) { + return true; } + return areSameModule(a.body, b.body); } } - function createChildItem(node: Node): ts.NavigationBarItem { - switch (node.kind) { - case SyntaxKind.Parameter: - if (isBindingPattern((node).name)) { - break; + /** Merge source into target. Source should be thrown away after this is called. */ + function merge(target: NavigationBarNode, source: NavigationBarNode): void { + target.additionalNodes = target.additionalNodes || []; + target.additionalNodes.push(source.node); + if (source.additionalNodes !== void 0) { + target.additionalNodes.push(...source.additionalNodes); + } + + target.children = concatenate(target.children, source.children); + if (target.children !== void 0) { + mergeChildren(target.children); + sortChildren(target.children); + } + } + } + + /** Recursively ensure that each NavNode's children are in sorted order. */ + function sortChildren(children: NavigationBarNode[]): void { + children.sort(compareChildren); + } + + function compareChildren(child1: NavigationBarNode, child2: NavigationBarNode): number { + const name1 = tryGetName(child1.node), name2 = tryGetName(child2.node); + if (name1 !== void 0 && name2 !== void 0) { + const cmp = localeCompareFix(name1, name2); + return cmp !== 0 ? cmp : navigationBarNodeKind(child1) - navigationBarNodeKind(child2); + } + else { + return name1 !== void 0 ? 1 : name2 !== void 0 ? -1 : navigationBarNodeKind(child1) - navigationBarNodeKind(child2); + } + } + + // More efficient to create a collator once and use its `compare` than to call `a.localeCompare(b)` many times. + const collator: { compare(a: string, b: string): number } = typeof Intl === "undefined" ? void 0 : new Intl.Collator(); + // Intl is missing in Safari, and node 0.10 treats "a" as greater than "B". + const localeCompareIsCorrect = collator && collator.compare("a", "B") < 0; + const localeCompareFix: (a: string, b: string) => number = localeCompareIsCorrect ? collator.compare : function(a, b) { + // This isn't perfect, but it passes all of our tests. + for (let i = 0; i < Math.min(a.length, b.length); i++) { + const chA = a.charAt(i), chB = b.charAt(i); + if (chA === "\"" && chB === "'") { + return 1; + } + if (chA === "'" && chB === "\"") { + return -1; + } + const cmp = chA.toLocaleLowerCase().localeCompare(chB.toLocaleLowerCase()); + if (cmp !== 0) { + return cmp; + } + } + return a.length - b.length; + }; + + /** + * This differs from getItemName because this is just used for sorting. + * We only sort nodes by name that have a more-or-less "direct" name, as opposed to `new()` and the like. + * So `new()` can still come before an `aardvark` method. + */ + function tryGetName(node: Node): string | undefined { + if (node.kind === SyntaxKind.ModuleDeclaration) { + return getModuleName(node); + } + + const decl = node; + if (decl.name !== void 0) { + return getPropertyNameForPropertyNameNode(decl.name); + } + switch (node.kind) { + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + case SyntaxKind.ClassExpression: + return getFunctionOrClassName(node); + case SyntaxKind.JSDocTypedefTag: + return getJSDocTypedefTagName(node); + default: + return void 0; + } + } + + function getItemName(node: Node): string { + if (node.kind === SyntaxKind.ModuleDeclaration) { + return getModuleName(node); + } + + const name = (node).name; + if (name !== void 0) { + const text = nodeText(name); + if (text.length > 0) { + return text; + } + } + + switch (node.kind) { + case SyntaxKind.SourceFile: + const sourceFile = node; + return isExternalModule(sourceFile) + ? `"${escapeString(getBaseFileName(removeFileExtension(normalizePath(sourceFile.fileName))))}"` + : ""; + case SyntaxKind.ArrowFunction: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: + if (node.flags & NodeFlags.Default) { + return "default"; + } + return getFunctionOrClassName(node); + case SyntaxKind.Constructor: + return "constructor"; + case SyntaxKind.ConstructSignature: + return "new()"; + case SyntaxKind.CallSignature: + return "()"; + case SyntaxKind.IndexSignature: + return "[]"; + case SyntaxKind.JSDocTypedefTag: + return getJSDocTypedefTagName(node); + default: + Debug.fail(); + return ""; + } + } + + function getJSDocTypedefTagName(node: JSDocTypedefTag): string { + if (node.name !== void 0) { + return node.name.text; + } + else { + const parentNode = node.parent && node.parent.parent; + if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) { + if ((parentNode).declarationList.declarations.length > 0) { + const nameIdentifier = (parentNode).declarationList.declarations[0].name; + if (nameIdentifier.kind === SyntaxKind.Identifier) { + return (nameIdentifier).text; } - if ((node.flags & NodeFlags.Modifier) === 0) { - return undefined; + } + } + return ""; + } + } + + /** Flattens the NavNode tree to a list, keeping only the top-level items. */ + function topLevelItems(root: NavigationBarNode): NavigationBarNode[] { + const topLevel: NavigationBarNode[] = []; + function recur(item: NavigationBarNode) { + if (isTopLevel(item)) { + topLevel.push(item); + if (item.children !== void 0) { + for (const child of item.children) { + recur(child); } - return createItem(node, getTextOfNode((node).name), ts.ScriptElementKind.memberVariableElement); - - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - return createItem(node, getTextOfNode((node).name), ts.ScriptElementKind.memberFunctionElement); - - case SyntaxKind.GetAccessor: - return createItem(node, getTextOfNode((node).name), ts.ScriptElementKind.memberGetAccessorElement); - - case SyntaxKind.SetAccessor: - return createItem(node, getTextOfNode((node).name), ts.ScriptElementKind.memberSetAccessorElement); - - case SyntaxKind.IndexSignature: - return createItem(node, "[]", ts.ScriptElementKind.indexSignatureElement); - - case SyntaxKind.EnumDeclaration: - return createItem(node, getTextOfNode((node).name), ts.ScriptElementKind.enumElement); - - case SyntaxKind.EnumMember: - return createItem(node, getTextOfNode((node).name), ts.ScriptElementKind.memberVariableElement); - - case SyntaxKind.ModuleDeclaration: - return createItem(node, getModuleName(node), ts.ScriptElementKind.moduleElement); - - case SyntaxKind.InterfaceDeclaration: - return createItem(node, getTextOfNode((node).name), ts.ScriptElementKind.interfaceElement); - - case SyntaxKind.TypeAliasDeclaration: - return createItem(node, getTextOfNode((node).name), ts.ScriptElementKind.typeElement); - - case SyntaxKind.CallSignature: - return createItem(node, "()", ts.ScriptElementKind.callSignatureElement); - - case SyntaxKind.ConstructSignature: - return createItem(node, "new()", ts.ScriptElementKind.constructSignatureElement); - - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - return createItem(node, getTextOfNode((node).name), ts.ScriptElementKind.memberVariableElement); + } + } + } + recur(root); + return topLevel; + function isTopLevel(item: NavigationBarNode): boolean { + switch (navigationBarNodeKind(item)) { case SyntaxKind.ClassDeclaration: - return createItem(node, getTextOfNode((node).name), ts.ScriptElementKind.classElement); - - case SyntaxKind.FunctionDeclaration: - return createItem(node, getTextOfNode((node).name), ts.ScriptElementKind.functionElement); - - case SyntaxKind.VariableDeclaration: - case SyntaxKind.BindingElement: - let variableDeclarationNode: Node; - let name: Node; - - if (node.kind === SyntaxKind.BindingElement) { - name = (node).name; - variableDeclarationNode = node; - // binding elements are added only for variable declarations - // bubble up to the containing variable declaration - while (variableDeclarationNode && variableDeclarationNode.kind !== SyntaxKind.VariableDeclaration) { - variableDeclarationNode = variableDeclarationNode.parent; - } - Debug.assert(variableDeclarationNode !== undefined); - } - else { - Debug.assert(!isBindingPattern((node).name)); - variableDeclarationNode = node; - name = (node).name; - } - - if (isConst(variableDeclarationNode)) { - return createItem(node, getTextOfNode(name), ts.ScriptElementKind.constElement); - } - else if (isLet(variableDeclarationNode)) { - return createItem(node, getTextOfNode(name), ts.ScriptElementKind.letElement); - } - else { - return createItem(node, getTextOfNode(name), ts.ScriptElementKind.variableElement); - } + case SyntaxKind.ClassExpression: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.SourceFile: + case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.JSDocTypedefTag: + return true; case SyntaxKind.Constructor: - return createItem(node, "constructor", ts.ScriptElementKind.constructorImplementationElement); + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + return hasSomeImportantChild(item); - case SyntaxKind.ExportSpecifier: - case SyntaxKind.ImportSpecifier: - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ImportClause: - case SyntaxKind.NamespaceImport: - return createItem(node, getTextOfNode((node).name), ts.ScriptElementKind.alias); + case SyntaxKind.ArrowFunction: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + return isTopLevelFunctionDeclaration(item); + + default: + return false; } + function isTopLevelFunctionDeclaration(item: NavigationBarNode): boolean { + if ((item.node).body === void 0) { + return false; + } - return undefined; - - function createItem(node: Node, name: string, scriptElementKind: string): NavigationBarItem { - return getNavigationBarItem(name, scriptElementKind, getNodeModifiers(node), [getNodeSpan(node)]); + switch (navigationBarNodeKind(item.parent)) { + case SyntaxKind.ModuleBlock: + case SyntaxKind.SourceFile: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.Constructor: + return true; + default: + return hasSomeImportantChild(item); + } + } + function hasSomeImportantChild(item: NavigationBarNode) { + return forEach(item.children, child => { + const childKind = navigationBarNodeKind(child); + return childKind !== SyntaxKind.VariableDeclaration && childKind !== SyntaxKind.BindingElement; + }); } } + } - function isEmpty(text: string) { - return !text || text.trim() === ""; - } + // NavigationBarItem requires an array, but will not mutate it, so just give it this for performance. + const emptyChildItemArray: NavigationBarItem[] = []; - function getNavigationBarItem(text: string, kind: string, kindModifiers: string, spans: TextSpan[], childItems: NavigationBarItem[] = [], indent = 0): NavigationBarItem { - if (isEmpty(text)) { - return undefined; - } + function convertToTopLevelItem(n: NavigationBarNode): NavigationBarItem { + return { + text: getItemName(n.node), + kind: nodeKind(n.node), + kindModifiers: getNodeModifiers(n.node), + spans: getSpans(n), + childItems: map(n.children, convertToChildItem) || emptyChildItemArray, + indent: n.indent, + bolded: false, + grayed: false + }; + function convertToChildItem(n: NavigationBarNode): NavigationBarItem { return { - text, - kind, - kindModifiers, - spans, - childItems, - indent, + text: getItemName(n.node), + kind: nodeKind(n.node), + kindModifiers: getNodeModifiers(n.node), + spans: getSpans(n), + childItems: emptyChildItemArray, + indent: 0, bolded: false, grayed: false }; } - function createTopLevelItem(node: Node): ts.NavigationBarItem { - switch (node.kind) { - case SyntaxKind.SourceFile: - return createSourceFileItem(node); - - case SyntaxKind.ClassDeclaration: - return createClassItem(node); - - case SyntaxKind.MethodDeclaration: - case SyntaxKind.Constructor: - return createMemberFunctionLikeItem(node); - - case SyntaxKind.EnumDeclaration: - return createEnumItem(node); - - case SyntaxKind.InterfaceDeclaration: - return createInterfaceItem(node); - - case SyntaxKind.ModuleDeclaration: - return createModuleItem(node); - - case SyntaxKind.FunctionDeclaration: - return createFunctionItem(node); - - case SyntaxKind.TypeAliasDeclaration: - return createTypeAliasItem(node); - } - - return undefined; - - function createModuleItem(node: ModuleDeclaration): NavigationBarItem { - const moduleName = getModuleName(node); - - const body = getInnermostModule(node).body; - const childItems = body ? getItemsWorker(getChildNodes(body.statements), createChildItem) : []; - - return getNavigationBarItem(moduleName, - ts.ScriptElementKind.moduleElement, - getNodeModifiers(node), - [getNodeSpan(node)], - childItems, - getIndent(node)); - } - - function createFunctionItem(node: FunctionDeclaration): ts.NavigationBarItem { - if (node.body && node.body.kind === SyntaxKind.Block) { - const childItems = getItemsWorker(sortNodes((node.body).statements), createChildItem); - - return getNavigationBarItem(!node.name ? "default" : node.name.text, - ts.ScriptElementKind.functionElement, - getNodeModifiers(node), - [getNodeSpan(node)], - childItems, - getIndent(node)); + function getSpans(n: NavigationBarNode): TextSpan[] { + const spans = [getNodeSpan(n.node)]; + if (n.additionalNodes !== void 0) { + for (const node of n.additionalNodes) { + spans.push(getNodeSpan(node)); } - - return undefined; } - - function createTypeAliasItem(node: TypeAliasDeclaration): ts.NavigationBarItem { - return getNavigationBarItem(node.name.text, - ts.ScriptElementKind.typeElement, - getNodeModifiers(node), - [getNodeSpan(node)], - [], - getIndent(node)); - } - - function createMemberFunctionLikeItem(node: MethodDeclaration | ConstructorDeclaration): ts.NavigationBarItem { - if (node.body && node.body.kind === SyntaxKind.Block) { - const childItems = getItemsWorker(sortNodes((node.body).statements), createChildItem); - let scriptElementKind: string; - let memberFunctionName: string; - if (node.kind === SyntaxKind.MethodDeclaration) { - memberFunctionName = getPropertyNameForPropertyNameNode(node.name); - scriptElementKind = ts.ScriptElementKind.memberFunctionElement; - } - else { - memberFunctionName = "constructor"; - scriptElementKind = ts.ScriptElementKind.constructorImplementationElement; - } - - return getNavigationBarItem(memberFunctionName, - scriptElementKind, - getNodeModifiers(node), - [getNodeSpan(node)], - childItems, - getIndent(node)); - } - - return undefined; - } - - function createSourceFileItem(node: SourceFile): ts.NavigationBarItem { - const childItems = getItemsWorker(getChildNodes(node.statements), createChildItem); - - const rootName = isExternalModule(node) - ? "\"" + escapeString(getBaseFileName(removeFileExtension(normalizePath(node.fileName)))) + "\"" - : ""; - - return getNavigationBarItem(rootName, - ts.ScriptElementKind.moduleElement, - ts.ScriptElementKindModifier.none, - [getNodeSpan(node)], - childItems); - } - - function createClassItem(node: ClassDeclaration): ts.NavigationBarItem { - let childItems: NavigationBarItem[]; - - if (node.members) { - const constructor = forEach(node.members, member => { - return member.kind === SyntaxKind.Constructor && member; - }); - - // Add the constructor parameters in as children of the class (for property parameters). - // Note that *all non-binding pattern named* parameters will be added to the nodes array, but parameters that - // are not properties will be filtered out later by createChildItem. - const nodes: Node[] = removeDynamicallyNamedProperties(node); - if (constructor) { - addRange(nodes, filter(constructor.parameters, p => !isBindingPattern(p.name))); - } - - childItems = getItemsWorker(sortNodes(nodes), createChildItem); - } - - const nodeName = !node.name ? "default" : node.name.text; - - return getNavigationBarItem( - nodeName, - ts.ScriptElementKind.classElement, - getNodeModifiers(node), - [getNodeSpan(node)], - childItems, - getIndent(node)); - } - - function createEnumItem(node: EnumDeclaration): ts.NavigationBarItem { - const childItems = getItemsWorker(sortNodes(removeComputedProperties(node)), createChildItem); - return getNavigationBarItem( - node.name.text, - ts.ScriptElementKind.enumElement, - getNodeModifiers(node), - [getNodeSpan(node)], - childItems, - getIndent(node)); - } - - function createInterfaceItem(node: InterfaceDeclaration): ts.NavigationBarItem { - const childItems = getItemsWorker(sortNodes(removeDynamicallyNamedProperties(node)), createChildItem); - return getNavigationBarItem( - node.name.text, - ts.ScriptElementKind.interfaceElement, - getNodeModifiers(node), - [getNodeSpan(node)], - childItems, - getIndent(node)); - } - } - - function getModuleName(moduleDeclaration: ModuleDeclaration): string { - // We want to maintain quotation marks. - if (isAmbientModule(moduleDeclaration)) { - return getTextOfNode(moduleDeclaration.name); - } - - // Otherwise, we need to aggregate each identifier to build up the qualified name. - const result: string[] = []; - - result.push(moduleDeclaration.name.text); - - while (moduleDeclaration.body && moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) { - moduleDeclaration = moduleDeclaration.body; - - result.push(moduleDeclaration.name.text); - } - - return result.join("."); - } - - function removeComputedProperties(node: EnumDeclaration): Declaration[] { - return filter(node.members, member => member.name === undefined || member.name.kind !== SyntaxKind.ComputedPropertyName); - } - - /** - * Like removeComputedProperties, but retains the properties with well known symbol names - */ - function removeDynamicallyNamedProperties(node: ClassDeclaration | InterfaceDeclaration): Declaration[] { - return filter(node.members, member => !hasDynamicName(member)); - } - - function getInnermostModule(node: ModuleDeclaration): ModuleDeclaration { - while (node.body && node.body.kind === SyntaxKind.ModuleDeclaration) { - node = node.body; - } - - return node; - } - - function getNodeSpan(node: Node) { - return node.kind === SyntaxKind.SourceFile - ? createTextSpanFromBounds(node.getFullStart(), node.getEnd()) - : createTextSpanFromBounds(node.getStart(), node.getEnd()); - } - - function getTextOfNode(node: Node): string { - return getTextOfNodeFromSourceText(sourceFile.text, node); + return spans; } } - export function getJsNavigationBarItems(sourceFile: SourceFile, compilerOptions: CompilerOptions): NavigationBarItem[] { - const anonFnText = ""; - const anonClassText = ""; - let indent = 0; + // TODO: GH#9145: We should just use getNodeKind. No reason why navigationBar and navigateTo should have different behaviors. + function nodeKind(node: Node): string { + switch (node.kind) { + case SyntaxKind.SourceFile: + return ScriptElementKind.moduleElement; - const rootName = isExternalModule(sourceFile) ? - "\"" + escapeString(getBaseFileName(removeFileExtension(normalizePath(sourceFile.fileName)))) + "\"" - : ""; + case SyntaxKind.EnumMember: + return ScriptElementKind.memberVariableElement; - const sourceFileItem = getNavBarItem(rootName, ScriptElementKind.moduleElement, [getNodeSpan(sourceFile)]); - let topItem = sourceFileItem; + case SyntaxKind.VariableDeclaration: + case SyntaxKind.BindingElement: + let variableDeclarationNode: Node; + let name: Node; - // Walk the whole file, because we want to also find function expressions - which may be in variable initializer, - // call arguments, expressions, etc... - forEachChild(sourceFile, visitNode); - - function visitNode(node: Node) { - const newItem = createNavBarItem(node); - - if (newItem) { - topItem.childItems.push(newItem); - } - - if (node.jsDocComments && node.jsDocComments.length > 0) { - for (const jsDocComment of node.jsDocComments) { - visitNode(jsDocComment); - } - } - - // Add a level if traversing into a container - if (newItem && (isFunctionLike(node) || isClassLike(node))) { - const lastTop = topItem; - indent++; - topItem = newItem; - forEachChild(node, visitNode); - topItem = lastTop; - indent--; - - // If the last item added was an anonymous function expression, and it had no children, discard it. - if (newItem && newItem.text === anonFnText && newItem.childItems.length === 0) { - topItem.childItems.pop(); - } - } - else { - forEachChild(node, visitNode); - } - } - - function createNavBarItem(node: Node): NavigationBarItem { - switch (node.kind) { - case SyntaxKind.VariableDeclaration: - // Only add to the navbar if at the top-level of the file - // Note: "const" and "let" are also SyntaxKind.VariableDeclarations - if (node.parent/*VariableDeclarationList*/.parent/*VariableStatement*/ - .parent/*SourceFile*/.kind !== SyntaxKind.SourceFile) { - return undefined; + if (node.kind === SyntaxKind.BindingElement) { + name = (node).name; + variableDeclarationNode = node; + // binding elements are added only for variable declarations + // bubble up to the containing variable declaration + while (variableDeclarationNode && variableDeclarationNode.kind !== SyntaxKind.VariableDeclaration) { + variableDeclarationNode = variableDeclarationNode.parent; } - // If it is initialized with a function expression, handle it when we reach the function expression node - const varDecl = node as VariableDeclaration; - if (varDecl.initializer && (varDecl.initializer.kind === SyntaxKind.FunctionExpression || - varDecl.initializer.kind === SyntaxKind.ArrowFunction || - varDecl.initializer.kind === SyntaxKind.ClassExpression)) { - return undefined; - } - // Fall through - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.Constructor: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - // "export default function().." looks just like a regular function/class declaration, except with the 'default' flag - const name = node.flags && (node.flags & NodeFlags.Default) && !(node as (Declaration)).name ? "default" : - node.kind === SyntaxKind.Constructor ? "constructor" : - declarationNameToString((node as (Declaration)).name); - return getNavBarItem(name, getScriptKindForElementKind(node.kind), [getNodeSpan(node)]); - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.ClassExpression: - return getDefineModuleItem(node) || getFunctionOrClassExpressionItem(node); - case SyntaxKind.MethodDeclaration: - const methodDecl = node as MethodDeclaration; - return getNavBarItem(declarationNameToString(methodDecl.name), - ScriptElementKind.memberFunctionElement, - [getNodeSpan(node)]); - case SyntaxKind.ExportAssignment: - // e.g. "export default " - return getNavBarItem("default", ScriptElementKind.variableElement, [getNodeSpan(node)]); - case SyntaxKind.ImportClause: // e.g. 'def' in: import def from 'mod' (in ImportDeclaration) - if (!(node as ImportClause).name) { - // No default import (this node is still a parent of named & namespace imports, which are handled below) - return undefined; - } - // fall through - case SyntaxKind.ImportSpecifier: // e.g. 'id' in: import {id} from 'mod' (in NamedImports, in ImportClause) - case SyntaxKind.NamespaceImport: // e.g. '* as ns' in: import * as ns from 'mod' (in ImportClause) - case SyntaxKind.ExportSpecifier: // e.g. 'a' or 'b' in: export {a, foo as b} from 'mod' - // Export specifiers are only interesting if they are reexports from another module, or renamed, else they are already globals - if (node.kind === SyntaxKind.ExportSpecifier) { - if (!(node.parent.parent as ExportDeclaration).moduleSpecifier && !(node as ExportSpecifier).propertyName) { - return undefined; - } - } - const decl = node as (ImportSpecifier | ImportClause | NamespaceImport | ExportSpecifier); - if (!decl.name) { - return undefined; - } - const declName = declarationNameToString(decl.name); - return getNavBarItem(declName, ScriptElementKind.constElement, [getNodeSpan(node)]); - case SyntaxKind.JSDocTypedefTag: - if ((node).name) { - return getNavBarItem( - (node).name.text, - ScriptElementKind.typeElement, - [getNodeSpan(node)]); - } - else { - const parentNode = node.parent && node.parent.parent; - if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) { - if ((parentNode).declarationList.declarations.length > 0) { - const nameIdentifier = (parentNode).declarationList.declarations[0].name; - if (nameIdentifier.kind === SyntaxKind.Identifier) { - return getNavBarItem( - (nameIdentifier).text, - ScriptElementKind.typeElement, - [getNodeSpan(node)]); - } - } - } - } - default: - return undefined; - } - } - - function getNavBarItem(text: string, kind: string, spans: TextSpan[], kindModifiers = ScriptElementKindModifier.none): NavigationBarItem { - return { - text, kind, kindModifiers, spans, childItems: [], indent, bolded: false, grayed: false - }; - } - - function getDefineModuleItem(node: Node): NavigationBarItem { - if (node.kind !== SyntaxKind.FunctionExpression && node.kind !== SyntaxKind.ArrowFunction) { - return undefined; - } - - // No match if this is not a call expression to an identifier named 'define' - if (node.parent.kind !== SyntaxKind.CallExpression) { - return undefined; - } - const callExpr = node.parent as CallExpression; - if (callExpr.expression.kind !== SyntaxKind.Identifier || callExpr.expression.getText() !== "define") { - return undefined; - } - - // Return a module of either the given text in the first argument, or of the source file path - let defaultName = node.getSourceFile().fileName; - if (callExpr.arguments[0].kind === SyntaxKind.StringLiteral) { - defaultName = ((callExpr.arguments[0]) as StringLiteral).text; - } - return getNavBarItem(defaultName, ScriptElementKind.moduleElement, [getNodeSpan(node.parent)]); - } - - function getFunctionOrClassExpressionItem(node: Node): NavigationBarItem { - if (node.kind !== SyntaxKind.FunctionExpression && - node.kind !== SyntaxKind.ArrowFunction && - node.kind !== SyntaxKind.ClassExpression) { - return undefined; - } - - const fnExpr = node as FunctionExpression | ArrowFunction | ClassExpression; - let fnName: string; - if (fnExpr.name && getFullWidth(fnExpr.name) > 0) { - // The expression has an identifier, so use that as the name - fnName = declarationNameToString(fnExpr.name); - } - else { - // See if it is a var initializer. If so, use the var name. - if (fnExpr.parent.kind === SyntaxKind.VariableDeclaration) { - fnName = declarationNameToString((fnExpr.parent as VariableDeclaration).name); - } - // See if it is of the form " = function(){...}". If so, use the text from the left-hand side. - else if (fnExpr.parent.kind === SyntaxKind.BinaryExpression && - (fnExpr.parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken) { - fnName = (fnExpr.parent as BinaryExpression).left.getText(); - } - // See if it is a property assignment, and if so use the property name - else if (fnExpr.parent.kind === SyntaxKind.PropertyAssignment && - (fnExpr.parent as PropertyAssignment).name) { - fnName = (fnExpr.parent as PropertyAssignment).name.getText(); + Debug.assert(variableDeclarationNode !== void 0); } else { - fnName = node.kind === SyntaxKind.ClassExpression ? anonClassText : anonFnText; + Debug.assert(!isBindingPattern((node).name)); + variableDeclarationNode = node; + name = (node).name; } - } - const scriptKind = node.kind === SyntaxKind.ClassExpression ? ScriptElementKind.classElement : ScriptElementKind.functionElement; - return getNavBarItem(fnName, scriptKind, [getNodeSpan(node)]); + + if (isConst(variableDeclarationNode)) { + return ts.ScriptElementKind.constElement; + } + else if (isLet(variableDeclarationNode)) { + return ts.ScriptElementKind.letElement; + } + else { + return ts.ScriptElementKind.variableElement; + } + + case SyntaxKind.ArrowFunction: + return ts.ScriptElementKind.functionElement; + + case SyntaxKind.JSDocTypedefTag: + return ScriptElementKind.typeElement; + + default: + return getNodeKind(node); + } + } + + function getModuleName(moduleDeclaration: ModuleDeclaration): string { + // We want to maintain quotation marks. + if (isAmbientModule(moduleDeclaration)) { + return getTextOfNode(moduleDeclaration.name); } - function getNodeSpan(node: Node) { - return node.kind === SyntaxKind.SourceFile - ? createTextSpanFromBounds(node.getFullStart(), node.getEnd()) - : createTextSpanFromBounds(node.getStart(), node.getEnd()); + // Otherwise, we need to aggregate each identifier to build up the qualified name. + const result: string[] = []; + + result.push(moduleDeclaration.name.text); + + while (moduleDeclaration.body && moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) { + moduleDeclaration = moduleDeclaration.body; + + result.push(moduleDeclaration.name.text); } - function getScriptKindForElementKind(kind: SyntaxKind) { - switch (kind) { - case SyntaxKind.VariableDeclaration: - return ScriptElementKind.variableElement; - case SyntaxKind.FunctionDeclaration: - return ScriptElementKind.functionElement; - case SyntaxKind.ClassDeclaration: - return ScriptElementKind.classElement; - case SyntaxKind.Constructor: - return ScriptElementKind.constructorImplementationElement; - case SyntaxKind.GetAccessor: - return ScriptElementKind.memberGetAccessorElement; - case SyntaxKind.SetAccessor: - return ScriptElementKind.memberSetAccessorElement; - default: - return "unknown"; - } - } + return result.join("."); + } - return sourceFileItem.childItems; + /** + * For 'module A.B.C', we want to get the node for 'C'. + * We store 'A' as associated with a NavNode, and use getModuleName to traverse down again. + */ + function getInteriorModule(decl: ModuleDeclaration): ModuleDeclaration { + return decl.body.kind === SyntaxKind.ModuleDeclaration ? getInteriorModule(decl.body) : decl; + } + + function isComputedProperty(member: EnumMember): boolean { + return member.name === void 0 || member.name.kind === SyntaxKind.ComputedPropertyName; + } + + function getNodeSpan(node: Node): TextSpan { + return node.kind === SyntaxKind.SourceFile + ? createTextSpanFromBounds(node.getFullStart(), node.getEnd()) + : createTextSpanFromBounds(node.getStart(sourceFile), node.getEnd()); + } + + function getFunctionOrClassName(node: FunctionExpression | FunctionDeclaration | ArrowFunction | ClassLikeDeclaration): string { + if (node.name !== void 0 && getFullWidth(node.name) > 0) { + return declarationNameToString(node.name); + } + // See if it is a var initializer. If so, use the var name. + else if (node.parent.kind === SyntaxKind.VariableDeclaration) { + return declarationNameToString((node.parent as VariableDeclaration).name); + } + // See if it is of the form " = function(){...}". If so, use the text from the left-hand side. + else if (node.parent.kind === SyntaxKind.BinaryExpression && + (node.parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken) { + return nodeText((node.parent as BinaryExpression).left); + } + // See if it is a property assignment, and if so use the property name + else if (node.parent.kind === SyntaxKind.PropertyAssignment && (node.parent as PropertyAssignment).name) { + return nodeText((node.parent as PropertyAssignment).name); + } + // Default exports are named "default" + else if (node.flags & NodeFlags.Default) { + return "default"; + } + else { + return isClassLike(node) ? "" : ""; + } + } + + function isFunctionOrClassExpression(node: Node): boolean { + return node.kind === SyntaxKind.FunctionExpression || node.kind === SyntaxKind.ArrowFunction || node.kind === SyntaxKind.ClassExpression; } } diff --git a/src/services/services.ts b/src/services/services.ts index 983ddfbf250..163be48695d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -7039,7 +7039,7 @@ namespace ts { function getNavigationBarItems(fileName: string): NavigationBarItem[] { const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); - return NavigationBar.getNavigationBarItems(sourceFile, host.getCompilationSettings()); + return NavigationBar.getNavigationBarItems(sourceFile); } function getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[] { diff --git a/tests/cases/fourslash/navbar_contains-no-duplicates.ts b/tests/cases/fourslash/navbar_contains-no-duplicates.ts index e259a7bef90..10e4027ff84 100644 --- a/tests/cases/fourslash/navbar_contains-no-duplicates.ts +++ b/tests/cases/fourslash/navbar_contains-no-duplicates.ts @@ -7,7 +7,7 @@ //// } //// } //// } -//// +//// //// declare module Windows { //// export module Foundation { //// export var B; @@ -16,13 +16,13 @@ //// } //// } //// } -//// +//// //// class ABC { //// public foo() { //// return 3; //// } //// } -//// +//// //// module ABC { //// export var x = 3; //// } @@ -95,13 +95,13 @@ verify.navigationBar([ "kindModifiers": "export,declare" }, { - "text": "Test", - "kind": "class", + "text": "B", + "kind": "var", "kindModifiers": "export,declare" }, { - "text": "B", - "kind": "var", + "text": "Test", + "kind": "class", "kindModifiers": "export,declare" }, { diff --git a/tests/cases/fourslash/navbar_exportDefault.ts b/tests/cases/fourslash/navbar_exportDefault.ts index dc99cff7d64..e547c9129a6 100644 --- a/tests/cases/fourslash/navbar_exportDefault.ts +++ b/tests/cases/fourslash/navbar_exportDefault.ts @@ -16,7 +16,14 @@ goTo.file("a.ts"); verify.navigationBar([ { "text": "\"a\"", - "kind": "module" + "kind": "module", + "childItems": [ + { + "text": "default", + "kind": "class", + "kindModifiers": "export" + } + ] }, { "text": "default", @@ -52,6 +59,13 @@ verify.navigationBar([ { "text": "\"c\"", "kind": "module", + "childItems": [ + { + "text": "default", + "kind": "function", + "kindModifiers": "export" + } + ] }, { "text": "default", diff --git a/tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions.ts b/tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions.ts new file mode 100644 index 00000000000..f15959812fc --- /dev/null +++ b/tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions.ts @@ -0,0 +1,147 @@ +/// + +////global.cls = class { }; +////(function() { +//// const x = () => { +//// // Presence of inner function causes x to be a top-level function. +//// function xx() {} +//// }; +//// const y = { +//// // This is not a top-level function (contains nothing, but shows up in childItems of its parent.) +//// foo: function() {} +//// }; +//// (function nest() { +//// function moreNest() {} +//// })(); +////})(); +////(function() { // Different anonymous functions are not merged +//// // These will only show up as childItems. +//// function z() {} +//// console.log(function() {}) +////}) +////(function classes() { +//// // Classes show up in top-level regardless of whether they have names or inner declarations. +//// const cls2 = class { }; +//// console.log(class cls3 {}); +//// (class { }); +////}) + +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "", + "kind": "function" + }, + { + "text": "", + "kind": "function" + }, + { + "text": "classes", + "kind": "function" + }, + { + "text": "global.cls", + "kind": "class" + } + ] + }, + { + "text": "", + "kind": "function", + "childItems": [ + { + "text": "nest", + "kind": "function" + }, + { + "text": "x", + "kind": "function" + }, + { + "text": "y", + "kind": "const" + } + ], + "indent": 1 + }, + { + "text": "nest", + "kind": "function", + "childItems": [ + { + "text": "moreNest", + "kind": "function" + } + ], + "indent": 2 + }, + { + "text": "x", + "kind": "function", + "childItems": [ + { + "text": "xx", + "kind": "function" + } + ], + "indent": 2 + }, + { + "text": "", + "kind": "function", + "childItems": [ + { + "text": "", + "kind": "function" + }, + { + "text": "z", + "kind": "function" + } + ], + "indent": 1 + }, + { + "text": "classes", + "kind": "function", + "childItems": [ + { + "text": "", + "kind": "class" + }, + { + "text": "cls2", + "kind": "class" + }, + { + "text": "cls3", + "kind": "class" + } + ], + "indent": 1 + }, + { + "text": "", + "kind": "class", + "indent": 2 + }, + { + "text": "cls2", + "kind": "class", + "indent": 2 + }, + { + "text": "cls3", + "kind": "class", + "indent": 2 + }, + { + "text": "global.cls", + "kind": "class", + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions2.ts b/tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions2.ts new file mode 100644 index 00000000000..214a14949ac --- /dev/null +++ b/tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions2.ts @@ -0,0 +1,64 @@ +/// + +////console.log(console.log(class Y {}, class X {}), console.log(class B {}, class A {})); +////console.log(class Cls { meth() {} }); + +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "A", + "kind": "class" + }, + { + "text": "B", + "kind": "class" + }, + { + "text": "Cls", + "kind": "class" + }, + { + "text": "X", + "kind": "class" + }, + { + "text": "Y", + "kind": "class" + } + ] + }, + { + "text": "A", + "kind": "class", + "indent": 1 + }, + { + "text": "B", + "kind": "class", + "indent": 1 + }, + { + "text": "Cls", + "kind": "class", + "childItems": [ + { + "text": "meth", + "kind": "method" + } + ], + "indent": 1 + }, + { + "text": "X", + "kind": "class", + "indent": 1 + }, + { + "text": "Y", + "kind": "class", + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarGetterAndSetter.ts b/tests/cases/fourslash/navigationBarGetterAndSetter.ts new file mode 100644 index 00000000000..48105e4e9af --- /dev/null +++ b/tests/cases/fourslash/navigationBarGetterAndSetter.ts @@ -0,0 +1,48 @@ +/// + +////class X { +//// get x() {} +//// set x(value) { +//// // Inner declaration should make the setter top-level. +//// function f() {} +//// } +////} + +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "X", + "kind": "class" + } + ] + }, + { + "text": "X", + "kind": "class", + "childItems": [ + { + "text": "x", + "kind": "getter" + }, + { + "text": "x", + "kind": "setter" + } + ], + "indent": 1 + }, + { + "text": "x", + "kind": "setter", + "childItems": [ + { + "text": "f", + "kind": "function" + } + ], + "indent": 2 + } +]); diff --git a/tests/cases/fourslash/navigationBarImports.ts b/tests/cases/fourslash/navigationBarImports.ts new file mode 100644 index 00000000000..43cb99c556a --- /dev/null +++ b/tests/cases/fourslash/navigationBarImports.ts @@ -0,0 +1,30 @@ +/// + +////import a, {b} from "m"; +////import c = require("m"); +////import * as d from "m"; + +verify.navigationBar([ + { + "text": "\"navigationBarImports\"", + "kind": "module", + "childItems": [ + { + "text": "a", + "kind": "alias" + }, + { + "text": "b", + "kind": "alias" + }, + { + "text": "c", + "kind": "alias" + }, + { + "text": "d", + "kind": "alias" + } + ] + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsFunctions.ts b/tests/cases/fourslash/navigationBarItemsFunctions.ts index 91546462a25..f5aa3fb681d 100644 --- a/tests/cases/fourslash/navigationBarItemsFunctions.ts +++ b/tests/cases/fourslash/navigationBarItemsFunctions.ts @@ -32,6 +32,12 @@ verify.navigationBar([ { "text": "baz", "kind": "function", + "childItems": [ + { + "text": "v", + "kind": "var" + } + ], "indent": 1 }, { @@ -41,6 +47,10 @@ verify.navigationBar([ { "text": "bar", "kind": "function" + }, + { + "text": "x", + "kind": "var" } ], "indent": 1 @@ -52,6 +62,10 @@ verify.navigationBar([ { "text": "biz", "kind": "function" + }, + { + "text": "y", + "kind": "var" } ], "indent": 2 diff --git a/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts b/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts index 96dcbb944ae..2f17a5006ff 100644 --- a/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts +++ b/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts @@ -18,6 +18,12 @@ verify.navigationBar([ { "text": "f", "kind": "function", + "childItems": [ + { + "text": "", + "kind": "function" + } + ], "indent": 1 } ]); diff --git a/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts b/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts index 127bde8c9e7..f907bdad067 100644 --- a/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts +++ b/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts @@ -10,6 +10,10 @@ verify.navigationBar([ "text": "", "kind": "module", "childItems": [ + { + "text": "", + "kind": "function" + }, { "text": "f", "kind": "function" @@ -19,6 +23,12 @@ verify.navigationBar([ { "text": "f", "kind": "function", + "childItems": [ + { + "text": "", + "kind": "function" + } + ], "indent": 1 } ]); diff --git a/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts b/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts index 28ec25c6f1d..3af3b351114 100644 --- a/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts +++ b/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts @@ -76,17 +76,17 @@ verify.navigationBar([ "kind": "property" } ], - "indent": 2 + "indent": 3 }, { "text": "LocalFunctionInConstructor", "kind": "function", - "indent": 2 + "indent": 3 }, { "text": "LocalInterfaceInConstrcutor", "kind": "interface", - "indent": 2 + "indent": 3 }, { "text": "method", @@ -116,7 +116,7 @@ verify.navigationBar([ "kind": "property" } ], - "indent": 2 + "indent": 3 }, { "text": "LocalFunctionInMethod", @@ -127,11 +127,11 @@ verify.navigationBar([ "kind": "function" } ], - "indent": 2 + "indent": 3 }, { "text": "LocalInterfaceInMethod", "kind": "interface", - "indent": 2 + "indent": 3 } ]); diff --git a/tests/cases/fourslash/navigationBarItemsItems2.ts b/tests/cases/fourslash/navigationBarItemsItems2.ts index 762531d8778..6ab0827e8f9 100644 --- a/tests/cases/fourslash/navigationBarItemsItems2.ts +++ b/tests/cases/fourslash/navigationBarItemsItems2.ts @@ -12,6 +12,11 @@ verify.navigationBar([ "text": "\"navigationBarItemsItems2\"", "kind": "module", "childItems": [ + { + "text": "", + "kind": "class", + "kindModifiers": "export" + }, { "text": "A", "kind": "module" @@ -19,7 +24,7 @@ verify.navigationBar([ ] }, { - "text": "default", + "text": "", "kind": "class", "kindModifiers": "export", "indent": 1 diff --git a/tests/cases/fourslash/navigationBarItemsItemsContainsNoAnonymousFunctions.ts b/tests/cases/fourslash/navigationBarItemsItemsContainsNoAnonymousFunctions.ts deleted file mode 100644 index 276e19624c9..00000000000 --- a/tests/cases/fourslash/navigationBarItemsItemsContainsNoAnonymousFunctions.ts +++ /dev/null @@ -1,80 +0,0 @@ -/// -// @Filename: scriptLexicalStructureItemsContainsNoAnonymouseFunctions_0.ts -/////*file1*/ -////(function() { -//// // this should not be included -//// var x = 0; -//// -//// // this should not be included either -//// function foo() { -//// -//// } -////})(); -//// -// @Filename: scriptLexicalStructureItemsContainsNoAnonymouseFunctions_1.ts -/////*file2*/ -////var x = function() { -//// // this should not be included -//// var x = 0; -//// -//// // this should not be included either -//// function foo() { -////}; -//// -// @Filename: scriptLexicalStructureItemsContainsNoAnonymouseFunctions_2.ts -////// Named functions should still show up -/////*file3*/ -////function foo() { -////} -////function bar() { -////} - -goTo.marker("file1"); -verify.navigationBar([ - { - "text": "", - "kind": "module" - } -]); - -goTo.marker("file2"); -verify.navigationBar([ - { - "text": "", - "kind": "module", - "childItems": [ - { - "text": "x", - "kind": "var" - } - ] - } -]); - -goTo.marker("file3"); -verify.navigationBar([ - { - "text": "", - "kind": "module", - "childItems": [ - { - "text": "bar", - "kind": "function" - }, - { - "text": "foo", - "kind": "function" - } - ] - }, - { - "text": "bar", - "kind": "function", - "indent": 1 - }, - { - "text": "foo", - "kind": "function", - "indent": 1 - } -]); diff --git a/tests/cases/fourslash/navigationBarItemsMissingName1.ts b/tests/cases/fourslash/navigationBarItemsMissingName1.ts index af0e89683bc..2a445a5e1ed 100644 --- a/tests/cases/fourslash/navigationBarItemsMissingName1.ts +++ b/tests/cases/fourslash/navigationBarItemsMissingName1.ts @@ -8,6 +8,11 @@ verify.navigationBar([ "text": "\"navigationBarItemsMissingName1\"", "kind": "module", "childItems": [ + { + "text": "", + "kind": "function", + "kindModifiers": "export" + }, { "text": "C", "kind": "class" diff --git a/tests/cases/fourslash/navigationBarItemsMissingName2.ts b/tests/cases/fourslash/navigationBarItemsMissingName2.ts index 2eda3b07855..a878c5be845 100644 --- a/tests/cases/fourslash/navigationBarItemsMissingName2.ts +++ b/tests/cases/fourslash/navigationBarItemsMissingName2.ts @@ -9,10 +9,16 @@ verify.navigationBar([ { "text": "", - "kind": "module" + "kind": "module", + "childItems": [ + { + "text": "", + "kind": "class" + } + ] }, { - "text": "default", + "text": "", "kind": "class", "childItems": [ { diff --git a/tests/cases/fourslash/navigationBarItemsModules.ts b/tests/cases/fourslash/navigationBarItemsModules.ts index 979f22084e5..ec11cd52b94 100644 --- a/tests/cases/fourslash/navigationBarItemsModules.ts +++ b/tests/cases/fourslash/navigationBarItemsModules.ts @@ -28,107 +28,107 @@ //The declarations of A.B.C.x do not get merged, so the 4 vars are independent. //The two 'A' modules, however, do get merged, so in reality we have 7 modules. verify.navigationBar([ - { - "text": "", - "kind": "module", - "childItems": [ - { - "text": "A.B.C", - "kind": "module" - }, - { - "text": "A.B", - "kind": "module" - }, - { - "text": "A", - "kind": "module" - }, - { - "text": "\"X.Y.Z\"", + { + "text": "", "kind": "module", - "kindModifiers": "declare" - }, - { + "childItems": [ + { + "text": "'X2.Y2.Z2'", + "kind": "module", + "kindModifiers": "declare" + }, + { + "text": "\"X.Y.Z\"", + "kind": "module", + "kindModifiers": "declare" + }, + { + "text": "A", + "kind": "module" + }, + { + "text": "A.B", + "kind": "module" + }, + { + "text": "A.B.C", + "kind": "module" + } + ] + }, + { "text": "'X2.Y2.Z2'", "kind": "module", - "kindModifiers": "declare" - } - ] - }, - { - "text": "A.B.C", - "kind": "module", - "childItems": [ - { - "text": "x", - "kind": "var", - "kindModifiers": "export" - } - ], - "indent": 1 - }, - { - "text": "A.B", - "kind": "module", - "childItems": [ - { - "text": "y", - "kind": "var", - "kindModifiers": "export" - } - ], - "indent": 1 - }, - { - "text": "A", - "kind": "module", - "childItems": [ - { - "text": "z", - "kind": "var", - "kindModifiers": "export" - }, - { + "kindModifiers": "declare", + "indent": 1 + }, + { + "text": "\"X.Y.Z\"", + "kind": "module", + "kindModifiers": "declare", + "indent": 1 + }, + { + "text": "A", + "kind": "module", + "childItems": [ + { + "text": "B", + "kind": "module" + }, + { + "text": "z", + "kind": "var", + "kindModifiers": "export" + } + ], + "indent": 1 + }, + { "text": "B", - "kind": "module" - } - ], - "indent": 1 - }, - { - "text": "B", - "kind": "module", - "childItems": [ - { + "kind": "module", + "childItems": [ + { + "text": "C", + "kind": "module" + } + ], + "indent": 2 + }, + { "text": "C", - "kind": "module" - } - ], - "indent": 2 - }, - { - "text": "C", - "kind": "module", - "childItems": [ - { - "text": "x", - "kind": "var", - "kindModifiers": "declare" - } - ], - "indent": 3 - }, - { - "text": "\"X.Y.Z\"", - "kind": "module", - "kindModifiers": "declare", - "indent": 1 - }, - { - "text": "'X2.Y2.Z2'", - "kind": "module", - "kindModifiers": "declare", - "indent": 1 - } + "kind": "module", + "childItems": [ + { + "text": "x", + "kind": "var", + "kindModifiers": "declare" + } + ], + "indent": 3 + }, + { + "text": "A.B", + "kind": "module", + "childItems": [ + { + "text": "y", + "kind": "var", + "kindModifiers": "export" + } + ], + "indent": 1 + }, + { + "text": "A.B.C", + "kind": "module", + "childItems": [ + { + "text": "x", + "kind": "var", + "kindModifiers": "export" + } + ], + "indent": 1 + } ]); diff --git a/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts b/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts index 2ee4c93a2f1..4a29d718f2e 100644 --- a/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts +++ b/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts @@ -30,12 +30,9 @@ verify.navigationBar([ "kind": "module", "childItems": [ { - "text": "Bar", - "kind": "class" - }, - { - "text": "Foo", - "kind": "interface" + "text": "\"Multiline\\\nMadness\"", + "kind": "module", + "kindModifiers": "declare" }, { "text": "\"Multiline\\r\\nMadness\"", @@ -43,17 +40,38 @@ verify.navigationBar([ "kindModifiers": "declare" }, { - "text": "\"Multiline\\\nMadness\"", + "text": "\"MultilineMadness\"", "kind": "module", "kindModifiers": "declare" }, { - "text": "\"MultilineMadness\"", - "kind": "module", - "kindModifiers": "declare" + "text": "Bar", + "kind": "class" + }, + { + "text": "Foo", + "kind": "interface" } ] }, + { + "text": "\"Multiline\\\nMadness\"", + "kind": "module", + "kindModifiers": "declare", + "indent": 1 + }, + { + "text": "\"Multiline\\r\\nMadness\"", + "kind": "module", + "kindModifiers": "declare", + "indent": 1 + }, + { + "text": "\"MultilineMadness\"", + "kind": "module", + "kindModifiers": "declare", + "indent": 1 + }, { "text": "Bar", "kind": "class", @@ -83,23 +101,5 @@ verify.navigationBar([ } ], "indent": 1 - }, - { - "text": "\"Multiline\\r\\nMadness\"", - "kind": "module", - "kindModifiers": "declare", - "indent": 1 - }, - { - "text": "\"Multiline\\\nMadness\"", - "kind": "module", - "kindModifiers": "declare", - "indent": 1 - }, - { - "text": "\"MultilineMadness\"", - "kind": "module", - "kindModifiers": "declare", - "indent": 1 } ]); diff --git a/tests/cases/fourslash/navigationBarJsDoc.ts b/tests/cases/fourslash/navigationBarJsDoc.ts index 20a235bce95..a2d33e216bf 100644 --- a/tests/cases/fourslash/navigationBarJsDoc.ts +++ b/tests/cases/fourslash/navigationBarJsDoc.ts @@ -6,16 +6,32 @@ ////const x = 0; verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "NumberLike", + "kind": "type" + }, + { + "text": "x", + "kind": "const" + }, + { + "text": "x", + "kind": "type" + } + ] + }, { "text": "NumberLike", - "kind": "type" + "kind": "type", + "indent": 1, }, { "text": "x", - "kind": "type" - }, - { - "text": "x", - "kind": "var" + "kind": "type", + "indent": 1 } ]); diff --git a/tests/cases/fourslash/navigationBarMerging.ts b/tests/cases/fourslash/navigationBarMerging.ts new file mode 100644 index 00000000000..192efe5db44 --- /dev/null +++ b/tests/cases/fourslash/navigationBarMerging.ts @@ -0,0 +1,189 @@ +/// + +// @Filename: file1.ts +////module a { +//// function foo() {} +////} +////module b { +//// function foo() {} +////} +////module a { +//// function bar() {} +////} + +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "a", + "kind": "module" + }, + { + "text": "b", + "kind": "module" + } + ] + }, + { + "text": "a", + "kind": "module", + "childItems": [ + { + "text": "bar", + "kind": "function" + }, + { + "text": "foo", + "kind": "function" + } + ], + "indent": 1 + }, + { + "text": "b", + "kind": "module", + "childItems": [ + { + "text": "foo", + "kind": "function" + } + ], + "indent": 1 + } +]); + +// Does not merge unlike declarations. +// @Filename: file2.ts +////module a {} +////function a() {} + +goTo.file("file2.ts"); +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "a", + "kind": "function" + }, + { + "text": "a", + "kind": "module" + } + ] + }, + { + "text": "a", + "kind": "function", + "indent": 1 + }, + { + "text": "a", + "kind": "module", + "indent": 1 + } +]); + +// Merges recursively +// @Filename: file3.ts +////module a { +//// interface A { +//// foo: number; +//// } +////} +////module a { +//// interface A { +//// bar: number; +//// } +////} + +goTo.file("file3.ts"); +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "a", + "kind": "module" + } + ] + }, + { + "text": "a", + "kind": "module", + "childItems": [ + { + "text": "A", + "kind": "interface" + } + ], + "indent": 1 + }, + { + "text": "A", + "kind": "interface", + "childItems": [ + { + "text": "bar", + "kind": "property" + }, + { + "text": "foo", + "kind": "property" + } + ], + "indent": 2 + } +]); + +// Does not merge 'module A' with 'module A.B' + +// @Filename: file4.ts +////module A { export var x; } +////module A.B { export var y; } + +goTo.file("file4.ts"); +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "A", + "kind": "module" + }, + { + "text": "A.B", + "kind": "module" + } + ] + }, + { + "text": "A", + "kind": "module", + "childItems": [ + { + "text": "x", + "kind": "var", + "kindModifiers": "export" + } + ], + "indent": 1 + }, + { + "text": "A.B", + "kind": "module", + "childItems": [ + { + "text": "y", + "kind": "var", + "kindModifiers": "export" + } + ], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarVariables.ts b/tests/cases/fourslash/navigationBarVariables.ts new file mode 100644 index 00000000000..42344c96f73 --- /dev/null +++ b/tests/cases/fourslash/navigationBarVariables.ts @@ -0,0 +1,53 @@ +/// + +////var x = 0; +////let y = 1; +////const z = 2; + +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "x", + "kind": "var" + }, + { + "text": "y", + "kind": "let" + }, + { + "text": "z", + "kind": "const" + } + ] + } +]); + +// @Filename: file2.ts +////var {a} = 0; +////let {a: b} = 0; +////const [c] = 0; + +goTo.file("file2.ts"); +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "a", + "kind": "var" + }, + { + "text": "b", + "kind": "let" + }, + { + "text": "c", + "kind": "const" + } + ] + } +]); diff --git a/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts b/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts index 77cd75aa44c..1c617091f4c 100644 --- a/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts +++ b/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts @@ -11,20 +11,36 @@ //// var numberLike; verify.navigationBar([ - { - "text": "NumberLike", - "kind": "type" - }, - { - "text": "NumberLike2", - "kind": "type" - }, - { - "text": "NumberLike2", - "kind": "var" - }, - { - "text": "numberLike", - "kind": "var" - } + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "numberLike", + "kind": "var" + }, + { + "text": "NumberLike", + "kind": "type" + }, + { + "text": "NumberLike2", + "kind": "var" + }, + { + "text": "NumberLike2", + "kind": "type" + } + ] + }, + { + "text": "NumberLike", + "kind": "type", + "indent": 1, + }, + { + "text": "NumberLike2", + "kind": "type", + "indent": 1 + } ]); \ No newline at end of file From e9226af3f6495b9eff8f40554f5a51e0c2d0b46d Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Fri, 17 Jun 2016 17:18:16 -0700 Subject: [PATCH 06/13] Array#map -> ts.map. --- src/compiler/program.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 111343e08ff..a01f3a4c5b2 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -180,7 +180,7 @@ namespace ts { function getEffectiveTypeRoots(options: CompilerOptions, host: ModuleResolutionHost) { return options.typeRoots || - defaultTypeRoots.map(d => combinePaths(options.configFilePath ? getDirectoryPath(options.configFilePath) : host.getCurrentDirectory(), d)); + map(defaultTypeRoots, d => combinePaths(options.configFilePath ? getDirectoryPath(options.configFilePath) : host.getCurrentDirectory(), d)); } /** From f568ad0ce446557c2198f0892f76d9f7db63d709 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Sat, 18 Jun 2016 12:01:29 -0700 Subject: [PATCH 07/13] Add conditional index signature for Canvas2DContextAttributes (https://github.com/Microsoft/TypeScript/issues/9244) --- src/lib/dom.generated.d.ts | 140 ++++++++++++++++++------------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/src/lib/dom.generated.d.ts b/src/lib/dom.generated.d.ts index 40cd883af61..0911586dd5d 100644 --- a/src/lib/dom.generated.d.ts +++ b/src/lib/dom.generated.d.ts @@ -2290,7 +2290,7 @@ declare var DeviceRotationRate: { interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEvent { /** - * Sets or gets the URL for the current document. + * Sets or gets the URL for the current document. */ readonly URL: string; /** @@ -2318,7 +2318,7 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ applets: HTMLCollectionOf; /** - * Deprecated. Sets or retrieves a value that indicates the background color behind the object. + * Deprecated. Sets or retrieves a value that indicates the background color behind the object. */ bgColor: string; /** @@ -2346,19 +2346,19 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ designMode: string; /** - * Sets or retrieves a value that indicates the reading order of the object. + * Sets or retrieves a value that indicates the reading order of the object. */ dir: string; /** - * Gets an object representing the document type declaration associated with the current document. + * Gets an object representing the document type declaration associated with the current document. */ readonly doctype: DocumentType; /** - * Gets a reference to the root node of the document. + * Gets a reference to the root node of the document. */ documentElement: HTMLElement; /** - * Sets or gets the security domain of the document. + * Sets or gets the security domain of the document. */ domain: string; /** @@ -2382,7 +2382,7 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ images: HTMLCollectionOf; /** - * Gets the implementation object of the current document. + * Gets the implementation object of the current document. */ readonly implementation: DOMImplementation; /** @@ -2390,11 +2390,11 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ readonly inputEncoding: string | null; /** - * Gets the date that the page was last modified, if the page supplies one. + * Gets the date that the page was last modified, if the page supplies one. */ readonly lastModified: string; /** - * Sets or gets the color of the document links. + * Sets or gets the color of the document links. */ linkColor: string; /** @@ -2402,7 +2402,7 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ links: HTMLCollectionOf; /** - * Contains information about the current URL. + * Contains information about the current URL. */ readonly location: Location; msCSSOMElementFloatMetrics: boolean; @@ -2427,19 +2427,19 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven * @param ev The event. */ onbeforedeactivate: (ev: UIEvent) => any; - /** - * Fires when the object loses the input focus. + /** + * Fires when the object loses the input focus. * @param ev The focus event. */ onblur: (ev: FocusEvent) => any; /** - * Occurs when playback is possible, but would require further buffering. + * Occurs when playback is possible, but would require further buffering. * @param ev The event. */ oncanplay: (ev: Event) => any; oncanplaythrough: (ev: Event) => any; /** - * Fires when the contents of the object or selection have changed. + * Fires when the contents of the object or selection have changed. * @param ev The event. */ onchange: (ev: Event) => any; @@ -2449,7 +2449,7 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ onclick: (ev: MouseEvent) => any; /** - * Fires when the user clicks the right mouse button in the client area, opening the context menu. + * Fires when the user clicks the right mouse button in the client area, opening the context menu. * @param ev The mouse event. */ oncontextmenu: (ev: PointerEvent) => any; @@ -2473,12 +2473,12 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven * @param ev The event. */ ondragend: (ev: DragEvent) => any; - /** + /** * Fires on the target element when the user drags the object to a valid drop target. * @param ev The drag event. */ ondragenter: (ev: DragEvent) => any; - /** + /** * Fires on the target object when the user moves the mouse out of a valid drop target during a drag operation. * @param ev The drag event. */ @@ -2489,23 +2489,23 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ ondragover: (ev: DragEvent) => any; /** - * Fires on the source object when the user starts to drag a text selection or selected object. + * Fires on the source object when the user starts to drag a text selection or selected object. * @param ev The event. */ ondragstart: (ev: DragEvent) => any; ondrop: (ev: DragEvent) => any; /** - * Occurs when the duration attribute is updated. + * Occurs when the duration attribute is updated. * @param ev The event. */ ondurationchange: (ev: Event) => any; /** - * Occurs when the media element is reset to its initial state. + * Occurs when the media element is reset to its initial state. * @param ev The event. */ onemptied: (ev: Event) => any; /** - * Occurs when the end of playback is reached. + * Occurs when the end of playback is reached. * @param ev The event */ onended: (ev: MediaStreamErrorEvent) => any; @@ -2515,7 +2515,7 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ onerror: (ev: ErrorEvent) => any; /** - * Fires when the object receives focus. + * Fires when the object receives focus. * @param ev The event. */ onfocus: (ev: FocusEvent) => any; @@ -2539,12 +2539,12 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ onkeyup: (ev: KeyboardEvent) => any; /** - * Fires immediately after the browser loads the object. + * Fires immediately after the browser loads the object. * @param ev The event. */ onload: (ev: Event) => any; /** - * Occurs when media data is loaded at the current playback position. + * Occurs when media data is loaded at the current playback position. * @param ev The event. */ onloadeddata: (ev: Event) => any; @@ -2554,22 +2554,22 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ onloadedmetadata: (ev: Event) => any; /** - * Occurs when Internet Explorer begins looking for media data. + * Occurs when Internet Explorer begins looking for media data. * @param ev The event. */ onloadstart: (ev: Event) => any; /** - * Fires when the user clicks the object with either mouse button. + * Fires when the user clicks the object with either mouse button. * @param ev The mouse event. */ onmousedown: (ev: MouseEvent) => any; /** - * Fires when the user moves the mouse over the object. + * Fires when the user moves the mouse over the object. * @param ev The mouse event. */ onmousemove: (ev: MouseEvent) => any; /** - * Fires when the user moves the mouse pointer outside the boundaries of the object. + * Fires when the user moves the mouse pointer outside the boundaries of the object. * @param ev The mouse event. */ onmouseout: (ev: MouseEvent) => any; @@ -2579,12 +2579,12 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ onmouseover: (ev: MouseEvent) => any; /** - * Fires when the user releases a mouse button while the mouse is over the object. + * Fires when the user releases a mouse button while the mouse is over the object. * @param ev The mouse event. */ onmouseup: (ev: MouseEvent) => any; /** - * Fires when the wheel button is rotated. + * Fires when the wheel button is rotated. * @param ev The mouse event */ onmousewheel: (ev: WheelEvent) => any; @@ -2606,7 +2606,7 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven onmspointerover: (ev: MSPointerEvent) => any; onmspointerup: (ev: MSPointerEvent) => any; /** - * Occurs when an item is removed from a Jump List of a webpage running in Site Mode. + * Occurs when an item is removed from a Jump List of a webpage running in Site Mode. * @param ev The event. */ onmssitemodejumplistitemremoved: (ev: MSSiteModeEvent) => any; @@ -2621,24 +2621,24 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ onpause: (ev: Event) => any; /** - * Occurs when the play method is requested. + * Occurs when the play method is requested. * @param ev The event. */ onplay: (ev: Event) => any; /** - * Occurs when the audio or video has started playing. + * Occurs when the audio or video has started playing. * @param ev The event. */ onplaying: (ev: Event) => any; onpointerlockchange: (ev: Event) => any; onpointerlockerror: (ev: Event) => any; /** - * Occurs to indicate progress while downloading media data. + * Occurs to indicate progress while downloading media data. * @param ev The event. */ onprogress: (ev: ProgressEvent) => any; /** - * Occurs when the playback rate is increased or decreased. + * Occurs when the playback rate is increased or decreased. * @param ev The event. */ onratechange: (ev: Event) => any; @@ -2648,22 +2648,22 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ onreadystatechange: (ev: ProgressEvent) => any; /** - * Fires when the user resets a form. + * Fires when the user resets a form. * @param ev The event. */ onreset: (ev: Event) => any; /** - * Fires when the user repositions the scroll box in the scroll bar on the object. + * Fires when the user repositions the scroll box in the scroll bar on the object. * @param ev The event. */ onscroll: (ev: UIEvent) => any; /** - * Occurs when the seek operation ends. + * Occurs when the seek operation ends. * @param ev The event. */ onseeked: (ev: Event) => any; /** - * Occurs when the current playback position is moved. + * Occurs when the current playback position is moved. * @param ev The event. */ onseeking: (ev: Event) => any; @@ -2679,7 +2679,7 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven onselectionchange: (ev: Event) => any; onselectstart: (ev: Event) => any; /** - * Occurs when the download has stopped. + * Occurs when the download has stopped. * @param ev The event. */ onstalled: (ev: Event) => any; @@ -2690,7 +2690,7 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven onstop: (ev: Event) => any; onsubmit: (ev: Event) => any; /** - * Occurs if the load operation has been intentionally halted. + * Occurs if the load operation has been intentionally halted. * @param ev The event. */ onsuspend: (ev: Event) => any; @@ -2709,7 +2709,7 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ onvolumechange: (ev: Event) => any; /** - * Occurs when playback stops because the next frame of a video resource is not available. + * Occurs when playback stops because the next frame of a video resource is not available. * @param ev The event. */ onwaiting: (ev: Event) => any; @@ -2743,7 +2743,7 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ title: string; readonly visibilityState: string; - /** + /** * Sets or gets the color of the links that the user has visited. */ vlinkColor: string; @@ -2933,7 +2933,7 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven createExpression(expression: string, resolver: XPathNSResolver): XPathExpression; createNSResolver(nodeResolver: Node): XPathNSResolver; /** - * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document. + * Creates a NodeIterator object that you can use to traverse filtered lists of nodes or elements in a document. * @param root The root element or node to start traversing on. * @param whatToShow The type of nodes or elements to appear in the node list * @param filter A custom NodeFilter function to use. For more information, see filter. Use null for no filter. @@ -2942,11 +2942,11 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven createNodeIterator(root: Node, whatToShow?: number, filter?: NodeFilter, entityReferenceExpansion?: boolean): NodeIterator; createProcessingInstruction(target: string, data: string): ProcessingInstruction; /** - * Returns an empty range object that has both of its boundary points positioned at the beginning of the document. + * Returns an empty range object that has both of its boundary points positioned at the beginning of the document. */ createRange(): Range; /** - * Creates a text string from the specified value. + * Creates a text string from the specified value. * @param data String that specifies the nodeValue property of the text node. */ createTextNode(data: string): Text; @@ -2961,7 +2961,7 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven */ createTreeWalker(root: Node, whatToShow?: number, filter?: NodeFilter, entityReferenceExpansion?: boolean): TreeWalker; /** - * Returns the element for the specified x coordinate and the specified y coordinate. + * Returns the element for the specified x coordinate and the specified y coordinate. * @param x The x-offset * @param y The y-offset */ @@ -3199,7 +3199,7 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven * @param replace Specifies whether the existing entry for the document is replaced in the history list. */ open(url?: string, name?: string, features?: string, replace?: boolean): Document; - /** + /** * Returns a Boolean value that indicates whether a specified command can be successfully executed using execCommand, given the current state of the document. * @param commandId Specifies a command identifier. */ @@ -3221,7 +3221,7 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven queryCommandSupported(commandId: string): boolean; /** * Retrieves the string associated with a command. - * @param commandId String that contains the identifier of a command. This can be any command identifier given in the list of Command Identifiers. + * @param commandId String that contains the identifier of a command. This can be any command identifier given in the list of Command Identifiers. */ queryCommandText(commandId: string): string; /** @@ -3237,12 +3237,12 @@ interface Document extends Node, GlobalEventHandlers, NodeSelector, DocumentEven webkitCancelFullScreen(): void; webkitExitFullscreen(): void; /** - * Writes one or more HTML expressions to a document in the specified window. + * Writes one or more HTML expressions to a document in the specified window. * @param content Specifies the text and HTML tags to write. */ write(...content: string[]): void; /** - * Writes one or more HTML expressions, followed by a carriage return, to a document in the specified window. + * Writes one or more HTML expressions, followed by a carriage return, to a document in the specified window. * @param content The text and HTML tags to write. */ writeln(...content: string[]): void; @@ -3971,12 +3971,12 @@ interface HTMLAnchorElement extends HTMLElement { */ target: string; /** - * Retrieves or sets the text of the object as a string. + * Retrieves or sets the text of the object as a string. */ text: string; type: string; urn: string; - /** + /** * Returns a string representation of an object. */ toString(): string; @@ -4078,7 +4078,7 @@ interface HTMLAreaElement extends HTMLElement { */ host: string; /** - * Sets or retrieves the host name part of the location or URL. + * Sets or retrieves the host name part of the location or URL. */ hostname: string; /** @@ -4114,7 +4114,7 @@ interface HTMLAreaElement extends HTMLElement { * Sets or retrieves the window or frame at which to target content. */ target: string; - /** + /** * Returns a string representation of an object. */ toString(): string; @@ -4380,7 +4380,7 @@ interface HTMLButtonElement extends HTMLElement { * Overrides the target attribute on a form element. */ formTarget: string; - /** + /** * Sets or retrieves the name of the object. */ name: string; @@ -4397,7 +4397,7 @@ interface HTMLButtonElement extends HTMLElement { * Returns a ValidityState object that represents the validity states of an element. */ readonly validity: ValidityState; - /** + /** * Sets or retrieves the default or selected value of the control. */ value: string; @@ -4504,7 +4504,7 @@ declare var HTMLDirectoryElement: { interface HTMLDivElement extends HTMLElement { /** - * Sets or retrieves how the object is aligned with adjacent text. + * Sets or retrieves how the object is aligned with adjacent text. */ align: string; /** @@ -5647,7 +5647,7 @@ interface HTMLInputElement extends HTMLElement { */ readonly files: FileList | null; /** - * Retrieves a reference to the form that the object is embedded in. + * Retrieves a reference to the form that the object is embedded in. */ readonly form: HTMLFormElement; /** @@ -6367,7 +6367,7 @@ interface HTMLMetaElement extends HTMLElement { */ scheme: string; /** - * Sets or retrieves the URL property that will be loaded after the specified time has elapsed. + * Sets or retrieves the URL property that will be loaded after the specified time has elapsed. */ url: string; } @@ -6630,7 +6630,7 @@ declare var HTMLOptionsCollection: { interface HTMLParagraphElement extends HTMLElement { /** - * Sets or retrieves how the object is aligned with adjacent text. + * Sets or retrieves how the object is aligned with adjacent text. */ align: string; clear: string; @@ -6732,10 +6732,10 @@ interface HTMLScriptElement extends HTMLElement { */ defer: boolean; /** - * Sets or retrieves the event for which the script is written. + * Sets or retrieves the event for which the script is written. */ event: string; - /** + /** * Sets or retrieves the object that is bound to the event script. */ htmlFor: string; @@ -6744,7 +6744,7 @@ interface HTMLScriptElement extends HTMLElement { */ src: string; /** - * Retrieves or sets the text of the object as a string. + * Retrieves or sets the text of the object as a string. */ text: string; /** @@ -6765,7 +6765,7 @@ interface HTMLSelectElement extends HTMLElement { autofocus: boolean; disabled: boolean; /** - * Retrieves a reference to the form that the object is embedded in. + * Retrieves a reference to the form that the object is embedded in. */ readonly form: HTMLFormElement; /** @@ -6791,7 +6791,7 @@ interface HTMLSelectElement extends HTMLElement { selectedIndex: number; selectedOptions: HTMLCollectionOf; /** - * Sets or retrieves the number of rows in the list box. + * Sets or retrieves the number of rows in the list box. */ size: number; /** @@ -6817,7 +6817,7 @@ interface HTMLSelectElement extends HTMLElement { /** * Adds an element to the areas, controlRange, or options collection. * @param element Variant of type Number that specifies the index position in the collection where the element is placed. If no value is given, the method places the element at the end of the collection. - * @param before Variant of type Object that specifies an element to insert before, or null to append the object to the collection. + * @param before Variant of type Object that specifies an element to insert before, or null to append the object to the collection. */ add(element: HTMLElement, before?: HTMLElement | number): void; /** @@ -7012,7 +7012,7 @@ interface HTMLTableElement extends HTMLElement { */ border: string; /** - * Sets or retrieves the border color of the object. + * Sets or retrieves the border color of the object. */ borderColor: any; /** @@ -7307,7 +7307,7 @@ declare var HTMLTextAreaElement: { interface HTMLTitleElement extends HTMLElement { /** - * Retrieves or sets the text of the object as a string. + * Retrieves or sets the text of the object as a string. */ text: string; } @@ -13985,7 +13985,7 @@ interface Canvas2DContextAttributes { alpha?: boolean; willReadFrequently?: boolean; storage?: boolean; - [attribute: string]: boolean | string; + [attribute: string]: boolean | string | undefined; } interface NodeListOf extends NodeList { From dfed7625afc06738f249c3536195590abdd13457 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Sat, 18 Jun 2016 12:40:22 -0700 Subject: [PATCH 08/13] Add libcheck tests --- src/harness/harness.ts | 2 +- tests/baselines/reference/verifyDefaultLib_dom.js | 6 ++++++ tests/baselines/reference/verifyDefaultLib_dom.symbols | 6 ++++++ tests/baselines/reference/verifyDefaultLib_dom.types | 6 ++++++ tests/cases/compiler/verifyDefaultLib_dom.ts | 7 +++++++ tests/cases/compiler/verifyDefaultLib_webworker.ts | 7 +++++++ 6 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/verifyDefaultLib_dom.js create mode 100644 tests/baselines/reference/verifyDefaultLib_dom.symbols create mode 100644 tests/baselines/reference/verifyDefaultLib_dom.types create mode 100644 tests/cases/compiler/verifyDefaultLib_dom.ts create mode 100644 tests/cases/compiler/verifyDefaultLib_webworker.ts diff --git a/src/harness/harness.ts b/src/harness/harness.ts index f804dc4eb6a..0b4a58260ec 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1051,7 +1051,7 @@ namespace Harness { options.target = options.target || ts.ScriptTarget.ES3; options.newLine = options.newLine || ts.NewLineKind.CarriageReturnLineFeed; options.noErrorTruncation = true; - options.skipDefaultLibCheck = true; + options.skipDefaultLibCheck = typeof options.skipDefaultLibCheck === "undefined" ? true : options.skipDefaultLibCheck; if (typeof currentDirectory === "undefined") { currentDirectory = Harness.IO.getCurrentDirectory(); diff --git a/tests/baselines/reference/verifyDefaultLib_dom.js b/tests/baselines/reference/verifyDefaultLib_dom.js new file mode 100644 index 00000000000..b65e133572a --- /dev/null +++ b/tests/baselines/reference/verifyDefaultLib_dom.js @@ -0,0 +1,6 @@ +//// [verifyDefaultLib_dom.ts] + +var x: HTMLElement; + +//// [verifyDefaultLib_dom.js] +var x; diff --git a/tests/baselines/reference/verifyDefaultLib_dom.symbols b/tests/baselines/reference/verifyDefaultLib_dom.symbols new file mode 100644 index 00000000000..53502b040c3 --- /dev/null +++ b/tests/baselines/reference/verifyDefaultLib_dom.symbols @@ -0,0 +1,6 @@ +=== tests/cases/compiler/verifyDefaultLib_dom.ts === + +var x: HTMLElement; +>x : Symbol(x, Decl(verifyDefaultLib_dom.ts, 1, 3)) +>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + diff --git a/tests/baselines/reference/verifyDefaultLib_dom.types b/tests/baselines/reference/verifyDefaultLib_dom.types new file mode 100644 index 00000000000..aafd063846d --- /dev/null +++ b/tests/baselines/reference/verifyDefaultLib_dom.types @@ -0,0 +1,6 @@ +=== tests/cases/compiler/verifyDefaultLib_dom.ts === + +var x: HTMLElement; +>x : HTMLElement +>HTMLElement : HTMLElement + diff --git a/tests/cases/compiler/verifyDefaultLib_dom.ts b/tests/cases/compiler/verifyDefaultLib_dom.ts new file mode 100644 index 00000000000..519485320ae --- /dev/null +++ b/tests/cases/compiler/verifyDefaultLib_dom.ts @@ -0,0 +1,7 @@ +// @strictNullChecks: true +// @noImplicitAny: true +// @noImplicitThis: true +// @skipDefaultLibCheck: false +// @lib: es2015,es2016,es2017,dom,scripthost + +var x: HTMLElement; \ No newline at end of file diff --git a/tests/cases/compiler/verifyDefaultLib_webworker.ts b/tests/cases/compiler/verifyDefaultLib_webworker.ts new file mode 100644 index 00000000000..7da62387d2f --- /dev/null +++ b/tests/cases/compiler/verifyDefaultLib_webworker.ts @@ -0,0 +1,7 @@ +// @strictNullChecks: true +// @noImplicitAny: true +// @noImplicitThis: true +// @skipDefaultLibCheck: false +// @lib: es2015,es2016,es2017,webworker + +var x: Worker; \ No newline at end of file From 634818cbac8a064dd5efac3c90859fa0d7d6e7f0 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Sat, 18 Jun 2016 20:35:52 -0700 Subject: [PATCH 09/13] Add missing worker types --- src/lib/webworker.generated.d.ts | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/lib/webworker.generated.d.ts b/src/lib/webworker.generated.d.ts index 6db86d71b2a..56c8cc84367 100644 --- a/src/lib/webworker.generated.d.ts +++ b/src/lib/webworker.generated.d.ts @@ -3,6 +3,10 @@ /// IE Worker APIs ///////////////////////////// +interface Algorithm { + name: string; +} + interface EventInit { bubbles?: boolean; cancelable?: boolean; @@ -18,6 +22,10 @@ interface IDBObjectStoreParameters { keyPath?: IDBKeyPath; } +interface KeyAlgorithm { + name?: string; +} + interface EventListener { (evt: Event): void; } @@ -107,6 +115,18 @@ declare var Coordinates: { new(): Coordinates; } +interface CryptoKey { + readonly algorithm: KeyAlgorithm; + readonly extractable: boolean; + readonly type: string; + readonly usages: string[]; +} + +declare var CryptoKey: { + prototype: CryptoKey; + new(): CryptoKey; +} + interface DOMError { readonly name: string; toString(): string; @@ -1178,7 +1198,7 @@ declare var console: Console; declare function addEventListener(type: "error", listener: (ev: ErrorEvent) => any, useCapture?: boolean): void; declare function addEventListener(type: "message", listener: (ev: MessageEvent) => any, useCapture?: boolean): void; declare function addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void; +type AlgorithmIdentifier = string | Algorithm; type IDBKeyPath = string; type IDBValidKey = number | string | Date | IDBArrayKey; -type BufferSource = ArrayBuffer | ArrayBufferView; -type MouseWheelEvent = WheelEvent; \ No newline at end of file +type BufferSource = ArrayBuffer | ArrayBufferView; \ No newline at end of file From 5697d21a580b3b0048327e05c8bb08e39b8257b8 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Mon, 20 Jun 2016 00:19:42 -0700 Subject: [PATCH 10/13] Accept webworker baselines --- tests/baselines/reference/verifyDefaultLib_webworker.js | 6 ++++++ .../baselines/reference/verifyDefaultLib_webworker.symbols | 6 ++++++ tests/baselines/reference/verifyDefaultLib_webworker.types | 6 ++++++ 3 files changed, 18 insertions(+) create mode 100644 tests/baselines/reference/verifyDefaultLib_webworker.js create mode 100644 tests/baselines/reference/verifyDefaultLib_webworker.symbols create mode 100644 tests/baselines/reference/verifyDefaultLib_webworker.types diff --git a/tests/baselines/reference/verifyDefaultLib_webworker.js b/tests/baselines/reference/verifyDefaultLib_webworker.js new file mode 100644 index 00000000000..515b5d9632f --- /dev/null +++ b/tests/baselines/reference/verifyDefaultLib_webworker.js @@ -0,0 +1,6 @@ +//// [verifyDefaultLib_webworker.ts] + +var x: Worker; + +//// [verifyDefaultLib_webworker.js] +var x; diff --git a/tests/baselines/reference/verifyDefaultLib_webworker.symbols b/tests/baselines/reference/verifyDefaultLib_webworker.symbols new file mode 100644 index 00000000000..ac27e7c83dc --- /dev/null +++ b/tests/baselines/reference/verifyDefaultLib_webworker.symbols @@ -0,0 +1,6 @@ +=== tests/cases/compiler/verifyDefaultLib_webworker.ts === + +var x: Worker; +>x : Symbol(x, Decl(verifyDefaultLib_webworker.ts, 1, 3)) +>Worker : Symbol(Worker, Decl(lib.webworker.d.ts, --, --), Decl(lib.webworker.d.ts, --, --)) + diff --git a/tests/baselines/reference/verifyDefaultLib_webworker.types b/tests/baselines/reference/verifyDefaultLib_webworker.types new file mode 100644 index 00000000000..38accb39842 --- /dev/null +++ b/tests/baselines/reference/verifyDefaultLib_webworker.types @@ -0,0 +1,6 @@ +=== tests/cases/compiler/verifyDefaultLib_webworker.ts === + +var x: Worker; +>x : Worker +>Worker : Worker + From c0eb472b62e0552b602b64baf8124c39af1e2582 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 20 Jun 2016 08:27:41 -0700 Subject: [PATCH 11/13] Classify `this` in parameter position as a keyword --- src/services/services.ts | 3 ++- tests/cases/fourslash/classifyThisParameter.ts | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/classifyThisParameter.ts diff --git a/src/services/services.ts b/src/services/services.ts index 983ddfbf250..5dd73abe82f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -7533,7 +7533,8 @@ namespace ts { return; case SyntaxKind.Parameter: if ((token.parent).name === token) { - return ClassificationType.parameterName; + const isThis = token.kind === SyntaxKind.Identifier && (token).originalKeywordKind === SyntaxKind.ThisKeyword; + return isThis ? ClassificationType.keyword : ClassificationType.parameterName; } return; } diff --git a/tests/cases/fourslash/classifyThisParameter.ts b/tests/cases/fourslash/classifyThisParameter.ts new file mode 100644 index 00000000000..5e0c9a9d4b6 --- /dev/null +++ b/tests/cases/fourslash/classifyThisParameter.ts @@ -0,0 +1,13 @@ +/// + +////function f(this){} + +var c = classification; +verify.syntacticClassificationsAre( + c.keyword("function"), + c.identifier("f"), + c.punctuation("("), + c.keyword("this"), + c.punctuation(")"), + c.punctuation("{"), + c.punctuation("}")); From 95cfaafdee9127b66535abc22cbe6722ee7dbd3a Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 20 Jun 2016 11:30:48 -0700 Subject: [PATCH 12/13] Use implicit boolean casts; it doesn't hurt performance --- src/compiler/parser.ts | 8 ++--- src/services/navigationBar.ts | 68 +++++++++++++++++------------------ 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 6e0a37c3d9f..b2f3755ec46 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -17,19 +17,19 @@ namespace ts { } function visitNode(cbNode: (node: Node) => T, node: Node): T { - if (node !== void 0) { + if (node) { return cbNode(node); } } function visitNodeArray(cbNodes: (nodes: Node[]) => T, nodes: Node[]) { - if (nodes !== void 0) { + if (nodes) { return cbNodes(nodes); } } function visitEachNode(cbNode: (node: Node) => T, nodes: Node[]) { - if (nodes !== void 0) { + if (nodes) { for (const node of nodes) { const result = cbNode(node); if (result) { @@ -44,7 +44,7 @@ namespace ts { // embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns // a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned. export function forEachChild(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T { - if (node === void 0) { + if (!node) { return; } // The visitXXX functions could be written as local functions that close over the cbNode and cbNodeArray diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index e722f33bc70..bfbaf5a6287 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -14,17 +14,17 @@ namespace ts.NavigationBar { indent: number; // # of parents } - export function getNavigationBarItems(sourceFile_: SourceFile): NavigationBarItem[] { - sourceFile = sourceFile_; + export function getNavigationBarItems(sourceFile: SourceFile): NavigationBarItem[] { + curSourceFile = sourceFile; const result = map(topLevelItems(rootNavigationBarNode(sourceFile)), convertToTopLevelItem); - sourceFile = void 0; + curSourceFile = undefined; return result; } // Keep sourceFile handy so we don't have to search for it every time we need to call `getText`. - let sourceFile: SourceFile; + let curSourceFile: SourceFile; function nodeText(node: Node): string { - return node.getText(sourceFile); + return node.getText(curSourceFile); } function navigationBarNodeKind(n: NavigationBarNode): SyntaxKind { @@ -32,7 +32,7 @@ namespace ts.NavigationBar { } function pushChild(parent: NavigationBarNode, child: NavigationBarNode): void { - if (parent.children !== void 0) { + if (parent.children) { parent.children.push(child); } else { @@ -50,13 +50,13 @@ namespace ts.NavigationBar { function rootNavigationBarNode(sourceFile: SourceFile): NavigationBarNode { Debug.assert(!parentsStack.length); - const root: NavigationBarNode = { node: sourceFile, additionalNodes: void 0, parent: void 0, children: void 0, indent: 0 }; + const root: NavigationBarNode = { node: sourceFile, additionalNodes: undefined, parent: undefined, children: undefined, indent: 0 }; parent = root; for (const statement of sourceFile.statements) { addChildrenRecursively(statement); } endNode(); - Debug.assert(parent === void 0 && !parentsStack.length); + Debug.assert(!parent && !parentsStack.length); return root; } @@ -67,9 +67,9 @@ namespace ts.NavigationBar { function emptyNavigationBarNode(node: Node): NavigationBarNode { return { node, - additionalNodes: void 0, + additionalNodes: undefined, parent, - children: void 0, + children: undefined, indent: parent.indent + 1 }; } @@ -89,7 +89,7 @@ namespace ts.NavigationBar { /** Call after calling `startNode` and adding children to it. */ function endNode(): void { - if (parent.children !== void 0) { + if (parent.children) { mergeChildren(parent.children); sortChildren(parent.children); } @@ -104,7 +104,7 @@ namespace ts.NavigationBar { /** Look for navigation bar items in node's subtree, adding them to the current `parent`. */ function addChildrenRecursively(node: Node): void { - if (node === void 0 || isToken(node)) { + if (!node || isToken(node)) { return; } @@ -142,7 +142,7 @@ namespace ts.NavigationBar { let importClause = node; // Handle default import case e.g.: // import d from "mod"; - if (importClause.name !== void 0) { + if (importClause.name) { addLeafNode(importClause); } @@ -150,7 +150,7 @@ namespace ts.NavigationBar { // import * as NS from "mod"; // import {a, b as B} from "mod"; const {namedBindings} = importClause; - if (namedBindings !== void 0) { + if (namedBindings) { if (namedBindings.kind === SyntaxKind.NamespaceImport) { addLeafNode(namedBindings); } @@ -169,7 +169,7 @@ namespace ts.NavigationBar { if (isBindingPattern(name)) { addChildrenRecursively(name); } - else if (decl.initializer !== void 0 && isFunctionOrClassExpression(decl.initializer)) { + else if (decl.initializer && isFunctionOrClassExpression(decl.initializer)) { // For `const x = function() {}`, just use the function node, not the const. addChildrenRecursively(decl.initializer); } @@ -218,7 +218,7 @@ namespace ts.NavigationBar { break; default: - if (node.jsDocComments !== void 0) { + if (node.jsDocComments) { for (const jsDocComment of node.jsDocComments) { for (const tag of jsDocComment.tags) { if (tag.kind === SyntaxKind.JSDocTypedefTag) { @@ -238,13 +238,13 @@ namespace ts.NavigationBar { filterMutate(children, child => { const decl = child.node; const name = decl.name && nodeText(decl.name); - if (name === void 0) { + if (!name) { // Anonymous items are never merged. return true; } const itemsWithSameName = getProperty(nameToItems, name); - if (itemsWithSameName === void 0) { + if (!itemsWithSameName) { nameToItems[name] = child; return true; } @@ -297,12 +297,12 @@ namespace ts.NavigationBar { function merge(target: NavigationBarNode, source: NavigationBarNode): void { target.additionalNodes = target.additionalNodes || []; target.additionalNodes.push(source.node); - if (source.additionalNodes !== void 0) { + if (source.additionalNodes) { target.additionalNodes.push(...source.additionalNodes); } target.children = concatenate(target.children, source.children); - if (target.children !== void 0) { + if (target.children) { mergeChildren(target.children); sortChildren(target.children); } @@ -316,17 +316,17 @@ namespace ts.NavigationBar { function compareChildren(child1: NavigationBarNode, child2: NavigationBarNode): number { const name1 = tryGetName(child1.node), name2 = tryGetName(child2.node); - if (name1 !== void 0 && name2 !== void 0) { + if (name1 && name2) { const cmp = localeCompareFix(name1, name2); return cmp !== 0 ? cmp : navigationBarNodeKind(child1) - navigationBarNodeKind(child2); } else { - return name1 !== void 0 ? 1 : name2 !== void 0 ? -1 : navigationBarNodeKind(child1) - navigationBarNodeKind(child2); + return name1 ? 1 : name2 ? -1 : navigationBarNodeKind(child1) - navigationBarNodeKind(child2); } } // More efficient to create a collator once and use its `compare` than to call `a.localeCompare(b)` many times. - const collator: { compare(a: string, b: string): number } = typeof Intl === "undefined" ? void 0 : new Intl.Collator(); + const collator: { compare(a: string, b: string): number } = typeof Intl === "undefined" ? undefined : new Intl.Collator(); // Intl is missing in Safari, and node 0.10 treats "a" as greater than "B". const localeCompareIsCorrect = collator && collator.compare("a", "B") < 0; const localeCompareFix: (a: string, b: string) => number = localeCompareIsCorrect ? collator.compare : function(a, b) { @@ -358,7 +358,7 @@ namespace ts.NavigationBar { } const decl = node; - if (decl.name !== void 0) { + if (decl.name) { return getPropertyNameForPropertyNameNode(decl.name); } switch (node.kind) { @@ -369,7 +369,7 @@ namespace ts.NavigationBar { case SyntaxKind.JSDocTypedefTag: return getJSDocTypedefTagName(node); default: - return void 0; + return undefined; } } @@ -379,7 +379,7 @@ namespace ts.NavigationBar { } const name = (node).name; - if (name !== void 0) { + if (name) { const text = nodeText(name); if (text.length > 0) { return text; @@ -418,7 +418,7 @@ namespace ts.NavigationBar { } function getJSDocTypedefTagName(node: JSDocTypedefTag): string { - if (node.name !== void 0) { + if (node.name) { return node.name.text; } else { @@ -441,7 +441,7 @@ namespace ts.NavigationBar { function recur(item: NavigationBarNode) { if (isTopLevel(item)) { topLevel.push(item); - if (item.children !== void 0) { + if (item.children) { for (const child of item.children) { recur(child); } @@ -478,7 +478,7 @@ namespace ts.NavigationBar { return false; } function isTopLevelFunctionDeclaration(item: NavigationBarNode): boolean { - if ((item.node).body === void 0) { + if (!(item.node).body) { return false; } @@ -531,7 +531,7 @@ namespace ts.NavigationBar { function getSpans(n: NavigationBarNode): TextSpan[] { const spans = [getNodeSpan(n.node)]; - if (n.additionalNodes !== void 0) { + if (n.additionalNodes) { for (const node of n.additionalNodes) { spans.push(getNodeSpan(node)); } @@ -562,7 +562,7 @@ namespace ts.NavigationBar { while (variableDeclarationNode && variableDeclarationNode.kind !== SyntaxKind.VariableDeclaration) { variableDeclarationNode = variableDeclarationNode.parent; } - Debug.assert(variableDeclarationNode !== void 0); + Debug.assert(!!variableDeclarationNode); } else { Debug.assert(!isBindingPattern((node).name)); @@ -620,17 +620,17 @@ namespace ts.NavigationBar { } function isComputedProperty(member: EnumMember): boolean { - return member.name === void 0 || member.name.kind === SyntaxKind.ComputedPropertyName; + return !member.name || member.name.kind === SyntaxKind.ComputedPropertyName; } function getNodeSpan(node: Node): TextSpan { return node.kind === SyntaxKind.SourceFile ? createTextSpanFromBounds(node.getFullStart(), node.getEnd()) - : createTextSpanFromBounds(node.getStart(sourceFile), node.getEnd()); + : createTextSpanFromBounds(node.getStart(curSourceFile), node.getEnd()); } function getFunctionOrClassName(node: FunctionExpression | FunctionDeclaration | ArrowFunction | ClassLikeDeclaration): string { - if (node.name !== void 0 && getFullWidth(node.name) > 0) { + if (node.name && getFullWidth(node.name) > 0) { return declarationNameToString(node.name); } // See if it is a var initializer. If so, use the var name. From d9bd31fc5288577100718c2d2278705b12dd0342 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Mon, 20 Jun 2016 12:39:07 -0700 Subject: [PATCH 13/13] Use getCanonicalFileName --- src/compiler/utilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f4b9592043e..b4ef62c6c3e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2331,7 +2331,7 @@ namespace ts { export function getSourceFilePathInNewDir(sourceFile: SourceFile, host: EmitHost, newDirPath: string) { let sourceFilePath = getNormalizedAbsolutePath(sourceFile.fileName, host.getCurrentDirectory()); const commonSourceDirectory = host.getCommonSourceDirectory(); - const isSourceFileInCommonSourceDirectory = sourceFilePath.toLowerCase().indexOf(commonSourceDirectory.toLowerCase()) === 0; + const isSourceFileInCommonSourceDirectory = host.getCanonicalFileName(sourceFilePath).indexOf(host.getCanonicalFileName(commonSourceDirectory)) === 0; sourceFilePath = isSourceFileInCommonSourceDirectory ? sourceFilePath.substring(commonSourceDirectory.length) : sourceFilePath; return combinePaths(newDirPath, sourceFilePath); }