diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 9645c068595..31b348caaec 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2258,6 +2258,18 @@ namespace FourSlash { } } + public verifyNavigationTree(json: any) { + const tree = this.languageService.getNavigationTree(this.activeFile.fileName); + if (JSON.stringify(tree, replacer) !== JSON.stringify(json)) { + this.raiseError(`verifyNavigationTree failed - expected: ${stringify(json)}, got: ${stringify(tree, replacer)}`); + } + + function replacer(key: string, value: any) { + // Don't check "spans", and omit falsy values. + return key === "spans" ? undefined : (value || undefined); + } + } + public printNavigationItems(searchValue: string) { const items = this.languageService.getNavigateToItems(searchValue); const length = items && items.length; @@ -3346,6 +3358,10 @@ namespace FourSlashInterface { this.state.verifyNavigationBar(json); } + public navigationTree(json: any) { + this.state.verifyNavigationTree(json); + } + public navigationItemsListCount(count: number, searchValue: string, matchKind?: string, fileName?: string) { this.state.verifyNavigationItemsCount(count, searchValue, matchKind, fileName); } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 3139511d2c7..b01ada6fd64 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -459,6 +459,10 @@ namespace Harness.LanguageService { getNavigationBarItems(fileName: string): ts.NavigationBarItem[] { return unwrapJSONCallResult(this.shim.getNavigationBarItems(fileName)); } + getNavigationTree(fileName: string): ts.NavigationTree { + return unwrapJSONCallResult(this.shim.getNavigationTree(fileName)); + } + getOutliningSpans(fileName: string): ts.OutliningSpan[] { return unwrapJSONCallResult(this.shim.getOutliningSpans(fileName)); } diff --git a/src/server/client.ts b/src/server/client.ts index a9fbb585364..a97dc1c0e1c 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -512,7 +512,7 @@ namespace ts.server { return this.lastRenameEntry.locations; } - decodeNavigationBarItems(items: protocol.NavigationBarItem[], fileName: string, lineMap: number[]): NavigationBarItem[] { + private decodeNavigationBarItems(items: protocol.NavigationBarItem[], fileName: string, lineMap: number[]): NavigationBarItem[] { if (!items) { return []; } @@ -521,10 +521,7 @@ namespace ts.server { text: item.text, kind: item.kind, kindModifiers: item.kindModifiers || "", - spans: item.spans.map(span => - createTextSpanFromBounds( - this.lineOffsetToPosition(fileName, span.start, lineMap), - this.lineOffsetToPosition(fileName, span.end, lineMap))), + spans: item.spans.map(span => this.decodeSpan(span, fileName, lineMap)), childItems: this.decodeNavigationBarItems(item.childItems, fileName, lineMap), indent: item.indent, bolded: false, @@ -533,17 +530,37 @@ namespace ts.server { } getNavigationBarItems(fileName: string): NavigationBarItem[] { - const args: protocol.FileRequestArgs = { - file: fileName - }; - - const request = this.processRequest(CommandNames.NavBar, args); + const request = this.processRequest(CommandNames.NavBar, { file: fileName }); const response = this.processResponse(request); const lineMap = this.getLineMap(fileName); return this.decodeNavigationBarItems(response.body, fileName, lineMap); } + private decodeNavigationTree(tree: protocol.NavigationTree, fileName: string, lineMap: number[]): NavigationTree { + return { + text: tree.text, + kind: tree.kind, + kindModifiers: tree.kindModifiers, + spans: tree.spans.map(span => this.decodeSpan(span, fileName, lineMap)), + childItems: map(tree.childItems, item => this.decodeNavigationTree(item, fileName, lineMap)) + }; + } + + getNavigationTree(fileName: string): NavigationTree { + const request = this.processRequest(CommandNames.NavTree, { file: fileName }); + const response = this.processResponse(request); + + const lineMap = this.getLineMap(fileName); + return this.decodeNavigationTree(response.body, fileName, lineMap); + } + + private decodeSpan(span: protocol.TextSpan, fileName: string, lineMap: number[]) { + return createTextSpanFromBounds( + this.lineOffsetToPosition(fileName, span.start, lineMap), + this.lineOffsetToPosition(fileName, span.end, lineMap)); + } + getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): TextSpan { throw new Error("Not Implemented Yet."); } diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts index 0ae54b8e0cf..2f447934040 100644 --- a/src/server/protocol.d.ts +++ b/src/server/protocol.d.ts @@ -110,7 +110,7 @@ declare namespace ts.server.protocol { */ export interface TodoCommentRequestArgs extends FileRequestArgs { /** - * Array of target TodoCommentDescriptors that describes TODO comments to be found + * Array of target TodoCommentDescriptors that describes TODO comments to be found */ descriptors: TodoCommentDescriptor[]; } @@ -231,7 +231,7 @@ declare namespace ts.server.protocol { offset?: number; /** - * Position (can be specified instead of line/offset pair) + * Position (can be specified instead of line/offset pair) */ position?: number; } @@ -624,12 +624,12 @@ declare namespace ts.server.protocol { /** * Represents a file in external project. - * External project is project whose set of files, compilation options and open\close state + * External project is project whose set of files, compilation options and open\close state * is maintained by the client (i.e. if all this data come from .csproj file in Visual Studio). * External project will exist even if all files in it are closed and should be closed explicity. - * If external project includes one or more tsconfig.json/jsconfig.json files then tsserver will + * If external project includes one or more tsconfig.json/jsconfig.json files then tsserver will * create configured project for every config file but will maintain a link that these projects were created - * as a result of opening external project so they should be removed once external project is closed. + * as a result of opening external project so they should be removed once external project is closed. */ export interface ExternalFile { /** @@ -1045,7 +1045,7 @@ declare namespace ts.server.protocol { } /** - * Response for CompileOnSaveAffectedFileListRequest request; + * Response for CompileOnSaveAffectedFileListRequest request; */ export interface CompileOnSaveAffectedFileListResponse extends Response { body: CompileOnSaveAffectedFileListSingleProject[]; @@ -1812,6 +1812,13 @@ declare namespace ts.server.protocol { export interface NavBarRequest extends FileRequest { } + /** + * NavTree request; value of command field is "navtree". + * Return response giving the navigation tree of the requested file. + */ + export interface NavTreeRequest extends FileRequest { + } + export interface NavigationBarItem { /** * The item's display text. @@ -1844,7 +1851,20 @@ declare namespace ts.server.protocol { indent: number; } + /** protocol.NavigationTree is identical to ts.NavigationTree, except using protocol.TextSpan instead of ts.TextSpan */ + export interface NavigationTree { + text: string; + kind: string; + kindModifiers: string; + spans: TextSpan[]; + childItems?: NavigationTree[]; + } + export interface NavBarResponse extends Response { body?: NavigationBarItem[]; } + + export interface NavTreeResponse extends Response { + body?: NavigationTree; + } } diff --git a/src/server/session.ts b/src/server/session.ts index 0acdb1e57b9..bab8c2a1431 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -110,6 +110,8 @@ namespace ts.server { export const SyntacticDiagnosticsSync = "syntacticDiagnosticsSync"; export const NavBar = "navbar"; export const NavBarFull = "navbar-full"; + export const NavTree = "navtree"; + export const NavTreeFull = "navtree-full"; export const Navto = "navto"; export const NavtoFull = "navto-full"; export const Occurrences = "occurrences"; @@ -960,15 +962,8 @@ namespace ts.server { return completions.entries.reduce((result: protocol.CompletionEntry[], entry: ts.CompletionEntry) => { if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) { const { name, kind, kindModifiers, sortText, replacementSpan } = entry; - - let convertedSpan: protocol.TextSpan = undefined; - if (replacementSpan) { - convertedSpan = { - start: scriptInfo.positionToLineOffset(replacementSpan.start), - end: scriptInfo.positionToLineOffset(replacementSpan.start + replacementSpan.length) - }; - } - + const convertedSpan: protocol.TextSpan = + replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined; result.push({ name, kind, kindModifiers, sortText, replacementSpan: convertedSpan }); } return result; @@ -1106,22 +1101,13 @@ namespace ts.server { this.projectService.closeClientFile(file); } - private decorateNavigationBarItem(project: Project, fileName: NormalizedPath, items: ts.NavigationBarItem[]): protocol.NavigationBarItem[] { - if (!items) { - return undefined; - } - - const scriptInfo = project.getScriptInfoForNormalizedPath(fileName); - - return items.map(item => ({ + private decorateNavigationBarItems(items: ts.NavigationBarItem[], scriptInfo: ScriptInfo): protocol.NavigationBarItem[] { + return map(items, item => ({ text: item.text, kind: item.kind, kindModifiers: item.kindModifiers, - spans: item.spans.map(span => ({ - start: scriptInfo.positionToLineOffset(span.start), - end: scriptInfo.positionToLineOffset(ts.textSpanEnd(span)) - })), - childItems: this.decorateNavigationBarItem(project, fileName, item.childItems), + spans: item.spans.map(span => this.decorateSpan(span, scriptInfo)), + childItems: this.decorateNavigationBarItems(item.childItems, scriptInfo), indent: item.indent })); } @@ -1129,15 +1115,40 @@ namespace ts.server { private getNavigationBarItems(args: protocol.FileRequestArgs, simplifiedResult: boolean): protocol.NavigationBarItem[] | NavigationBarItem[] { const { file, project } = this.getFileAndProject(args); const items = project.getLanguageService(/*ensureSynchronized*/ false).getNavigationBarItems(file); - if (!items) { - return undefined; - } - - return simplifiedResult - ? this.decorateNavigationBarItem(project, file, items) + return !items + ? undefined + : simplifiedResult + ? this.decorateNavigationBarItems(items, project.getScriptInfoForNormalizedPath(file)) : items; } + private decorateNavigationTree(tree: ts.NavigationTree, scriptInfo: ScriptInfo): protocol.NavigationTree { + return { + text: tree.text, + kind: tree.kind, + kindModifiers: tree.kindModifiers, + spans: tree.spans.map(span => this.decorateSpan(span, scriptInfo)), + childItems: map(tree.childItems, item => this.decorateNavigationTree(item, scriptInfo)) + }; + } + + private decorateSpan(span: TextSpan, scriptInfo: ScriptInfo): protocol.TextSpan { + return { + start: scriptInfo.positionToLineOffset(span.start), + end: scriptInfo.positionToLineOffset(ts.textSpanEnd(span)) + }; + } + + private getNavigationTree(args: protocol.FileRequestArgs, simplifiedResult: boolean): protocol.NavigationTree | NavigationTree { + const { file, project } = this.getFileAndProject(args); + const tree = project.getLanguageService(/*ensureSynchronized*/ false).getNavigationTree(file); + return !tree + ? undefined + : simplifiedResult + ? this.decorateNavigationTree(tree, project.getScriptInfoForNormalizedPath(file)) + : tree; + } + private getNavigateToItems(args: protocol.NavtoRequestArgs, simplifiedResult: boolean): protocol.NavtoItem[] | NavigateToItem[] { const projects = this.getProjects(args); @@ -1274,19 +1285,11 @@ namespace ts.server { const position = this.getPosition(args, scriptInfo); const spans = project.getLanguageService(/*ensureSynchronized*/ false).getBraceMatchingAtPosition(file, position); - if (!spans) { - return undefined; - } - if (simplifiedResult) { - - return spans.map(span => ({ - start: scriptInfo.positionToLineOffset(span.start), - end: scriptInfo.positionToLineOffset(span.start + span.length) - })); - } - else { - return spans; - } + return !spans + ? undefined + : simplifiedResult + ? spans.map(span => this.decorateSpan(span, scriptInfo)) + : spans; } getDiagnosticsForProject(delay: number, fileName: string) { @@ -1571,6 +1574,12 @@ namespace ts.server { [CommandNames.NavBarFull]: (request: protocol.FileRequest) => { return this.requiredResponse(this.getNavigationBarItems(request.arguments, /*simplifiedResult*/ false)); }, + [CommandNames.NavTree]: (request: protocol.FileRequest) => { + return this.requiredResponse(this.getNavigationTree(request.arguments, /*simplifiedResult*/ true)); + }, + [CommandNames.NavTreeFull]: (request: protocol.FileRequest) => { + return this.requiredResponse(this.getNavigationTree(request.arguments, /*simplifiedResult*/ false)); + }, [CommandNames.Occurrences]: (request: protocol.FileLocationRequest) => { return this.requiredResponse(this.getOccurrences(request.arguments)); }, diff --git a/src/server/utilities.ts b/src/server/utilities.ts index 110727e5c4f..a9db3d69f48 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -131,52 +131,53 @@ namespace ts.server { return fileName; } - function throwLanguageServiceIsDisabledError() { + function throwLanguageServiceIsDisabledError(): never { throw new Error("LanguageService is disabled"); } export const nullLanguageService: LanguageService = { - cleanupSemanticCache: (): any => throwLanguageServiceIsDisabledError(), - getSyntacticDiagnostics: (): any => throwLanguageServiceIsDisabledError(), - getSemanticDiagnostics: (): any => throwLanguageServiceIsDisabledError(), - getCompilerOptionsDiagnostics: (): any => throwLanguageServiceIsDisabledError(), - getSyntacticClassifications: (): any => throwLanguageServiceIsDisabledError(), - getEncodedSyntacticClassifications: (): any => throwLanguageServiceIsDisabledError(), - getSemanticClassifications: (): any => throwLanguageServiceIsDisabledError(), - getEncodedSemanticClassifications: (): any => throwLanguageServiceIsDisabledError(), - getCompletionsAtPosition: (): any => throwLanguageServiceIsDisabledError(), - findReferences: (): any => throwLanguageServiceIsDisabledError(), - getCompletionEntryDetails: (): any => throwLanguageServiceIsDisabledError(), - getQuickInfoAtPosition: (): any => throwLanguageServiceIsDisabledError(), - findRenameLocations: (): any => throwLanguageServiceIsDisabledError(), - getNameOrDottedNameSpan: (): any => throwLanguageServiceIsDisabledError(), - getBreakpointStatementAtPosition: (): any => throwLanguageServiceIsDisabledError(), - getBraceMatchingAtPosition: (): any => throwLanguageServiceIsDisabledError(), - getSignatureHelpItems: (): any => throwLanguageServiceIsDisabledError(), - getDefinitionAtPosition: (): any => throwLanguageServiceIsDisabledError(), - getRenameInfo: (): any => throwLanguageServiceIsDisabledError(), - getTypeDefinitionAtPosition: (): any => throwLanguageServiceIsDisabledError(), - getReferencesAtPosition: (): any => throwLanguageServiceIsDisabledError(), - getDocumentHighlights: (): any => throwLanguageServiceIsDisabledError(), - getOccurrencesAtPosition: (): any => throwLanguageServiceIsDisabledError(), - getNavigateToItems: (): any => throwLanguageServiceIsDisabledError(), - getNavigationBarItems: (): any => throwLanguageServiceIsDisabledError(), - getOutliningSpans: (): any => throwLanguageServiceIsDisabledError(), - getTodoComments: (): any => throwLanguageServiceIsDisabledError(), - getIndentationAtPosition: (): any => throwLanguageServiceIsDisabledError(), - getFormattingEditsForRange: (): any => throwLanguageServiceIsDisabledError(), - getFormattingEditsForDocument: (): any => throwLanguageServiceIsDisabledError(), - getFormattingEditsAfterKeystroke: (): any => throwLanguageServiceIsDisabledError(), - getDocCommentTemplateAtPosition: (): any => throwLanguageServiceIsDisabledError(), - isValidBraceCompletionAtPosition: (): any => throwLanguageServiceIsDisabledError(), - getEmitOutput: (): any => throwLanguageServiceIsDisabledError(), - getProgram: (): any => throwLanguageServiceIsDisabledError(), - getNonBoundSourceFile: (): any => throwLanguageServiceIsDisabledError(), - dispose: (): any => throwLanguageServiceIsDisabledError(), - getCompletionEntrySymbol: (): any => throwLanguageServiceIsDisabledError(), - getImplementationAtPosition: (): any => throwLanguageServiceIsDisabledError(), - getSourceFile: (): any => throwLanguageServiceIsDisabledError(), - getCodeFixesAtPosition: (): any => throwLanguageServiceIsDisabledError() + cleanupSemanticCache: throwLanguageServiceIsDisabledError, + getSyntacticDiagnostics: throwLanguageServiceIsDisabledError, + getSemanticDiagnostics: throwLanguageServiceIsDisabledError, + getCompilerOptionsDiagnostics: throwLanguageServiceIsDisabledError, + getSyntacticClassifications: throwLanguageServiceIsDisabledError, + getEncodedSyntacticClassifications: throwLanguageServiceIsDisabledError, + getSemanticClassifications: throwLanguageServiceIsDisabledError, + getEncodedSemanticClassifications: throwLanguageServiceIsDisabledError, + getCompletionsAtPosition: throwLanguageServiceIsDisabledError, + findReferences: throwLanguageServiceIsDisabledError, + getCompletionEntryDetails: throwLanguageServiceIsDisabledError, + getQuickInfoAtPosition: throwLanguageServiceIsDisabledError, + findRenameLocations: throwLanguageServiceIsDisabledError, + getNameOrDottedNameSpan: throwLanguageServiceIsDisabledError, + getBreakpointStatementAtPosition: throwLanguageServiceIsDisabledError, + getBraceMatchingAtPosition: throwLanguageServiceIsDisabledError, + getSignatureHelpItems: throwLanguageServiceIsDisabledError, + getDefinitionAtPosition: throwLanguageServiceIsDisabledError, + getRenameInfo: throwLanguageServiceIsDisabledError, + getTypeDefinitionAtPosition: throwLanguageServiceIsDisabledError, + getReferencesAtPosition: throwLanguageServiceIsDisabledError, + getDocumentHighlights: throwLanguageServiceIsDisabledError, + getOccurrencesAtPosition: throwLanguageServiceIsDisabledError, + getNavigateToItems: throwLanguageServiceIsDisabledError, + getNavigationBarItems: throwLanguageServiceIsDisabledError, + getNavigationTree: throwLanguageServiceIsDisabledError, + getOutliningSpans: throwLanguageServiceIsDisabledError, + getTodoComments: throwLanguageServiceIsDisabledError, + getIndentationAtPosition: throwLanguageServiceIsDisabledError, + getFormattingEditsForRange: throwLanguageServiceIsDisabledError, + getFormattingEditsForDocument: throwLanguageServiceIsDisabledError, + getFormattingEditsAfterKeystroke: throwLanguageServiceIsDisabledError, + getDocCommentTemplateAtPosition: throwLanguageServiceIsDisabledError, + isValidBraceCompletionAtPosition: throwLanguageServiceIsDisabledError, + getEmitOutput: throwLanguageServiceIsDisabledError, + getProgram: throwLanguageServiceIsDisabledError, + getNonBoundSourceFile: throwLanguageServiceIsDisabledError, + dispose: throwLanguageServiceIsDisabledError, + getCompletionEntrySymbol: throwLanguageServiceIsDisabledError, + getImplementationAtPosition: throwLanguageServiceIsDisabledError, + getSourceFile: throwLanguageServiceIsDisabledError, + getCodeFixesAtPosition: throwLanguageServiceIsDisabledError }; export interface ServerLanguageServiceHost { diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 57cfc1f2c40..7f27c222414 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -21,6 +21,13 @@ namespace ts.NavigationBar { return result; } + export function getNavigationTree(sourceFile: SourceFile): NavigationTree { + curSourceFile = sourceFile; + const result = convertToTree(rootNavigationBarNode(sourceFile)); + curSourceFile = undefined; + return result; + } + // Keep sourceFile handy so we don't have to search for it every time we need to call `getText`. let curSourceFile: SourceFile; function nodeText(node: Node): string { @@ -502,6 +509,16 @@ namespace ts.NavigationBar { // NavigationBarItem requires an array, but will not mutate it, so just give it this for performance. const emptyChildItemArray: NavigationBarItem[] = []; + function convertToTree(n: NavigationBarNode): NavigationTree { + return { + text: getItemName(n.node), + kind: getNodeKind(n.node), + kindModifiers: getNodeModifiers(n.node), + spans: getSpans(n), + childItems: map(n.children, convertToTree) + }; + } + function convertToTopLevelItem(n: NavigationBarNode): NavigationBarItem { return { text: getItemName(n.node), @@ -526,16 +543,16 @@ namespace ts.NavigationBar { grayed: false }; } + } - function getSpans(n: NavigationBarNode): TextSpan[] { - const spans = [getNodeSpan(n.node)]; - if (n.additionalNodes) { - for (const node of n.additionalNodes) { - spans.push(getNodeSpan(node)); - } + function getSpans(n: NavigationBarNode): TextSpan[] { + const spans = [getNodeSpan(n.node)]; + if (n.additionalNodes) { + for (const node of n.additionalNodes) { + spans.push(getNodeSpan(node)); } - return spans; } + return spans; } function getModuleName(moduleDeclaration: ModuleDeclaration): string { diff --git a/src/services/services.ts b/src/services/services.ts index 74b6a5459db..86f51f2618a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1523,9 +1523,11 @@ namespace ts { } function getNavigationBarItems(fileName: string): NavigationBarItem[] { - const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); + return NavigationBar.getNavigationBarItems(syntaxTreeCache.getCurrentSourceFile(fileName)); + } - return NavigationBar.getNavigationBarItems(sourceFile); + function getNavigationTree(fileName: string): NavigationTree { + return NavigationBar.getNavigationTree(syntaxTreeCache.getCurrentSourceFile(fileName)); } function isTsOrTsxFile(fileName: string): boolean { @@ -1903,6 +1905,7 @@ namespace ts { getRenameInfo, findRenameLocations, getNavigationBarItems, + getNavigationTree, getOutliningSpans, getTodoComments, getBraceMatchingAtPosition, diff --git a/src/services/shims.ts b/src/services/shims.ts index b4fff2ee54c..e892b409c77 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -224,6 +224,9 @@ namespace ts { */ getNavigationBarItems(fileName: string): string; + /** Returns a JSON-encoded value of the type ts.NavigationTree. */ + getNavigationTree(fileName: string): string; + /** * Returns a JSON-encoded value of the type: * { textSpan: { start: number, length: number }; hintSpan: { start: number, length: number }; bannerText: string; autoCollapse: boolean } [] = []; @@ -971,6 +974,13 @@ namespace ts { ); } + public getNavigationTree(fileName: string): string { + return this.forwardJSONCall( + `getNavigationTree('${fileName}')`, + () => this.languageService.getNavigationTree(fileName) + ); + } + public getOutliningSpans(fileName: string): string { return this.forwardJSONCall( `getOutliningSpans('${fileName}')`, diff --git a/src/services/types.ts b/src/services/types.ts index 31ab65d2013..aecdfbe4171 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -225,6 +225,7 @@ namespace ts { getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string, excludeDtsFiles?: boolean): NavigateToItem[]; getNavigationBarItems(fileName: string): NavigationBarItem[]; + getNavigationTree(fileName: string): NavigationTree; getOutliningSpans(fileName: string): OutliningSpan[]; getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[]; @@ -266,6 +267,12 @@ namespace ts { classificationType: string; // ClassificationTypeNames } + /** + * Navigation bar interface designed for visual studio's dual-column layout. + * This does not form a proper tree. + * The navbar is returned as a list of top-level items, each of which has a list of child items. + * Child items always have an empty array for their `childItems`. + */ export interface NavigationBarItem { text: string; kind: string; @@ -277,6 +284,26 @@ namespace ts { grayed: boolean; } + /** + * Node in a tree of nested declarations in a file. + * The top node is always a script or module node. + */ + export interface NavigationTree { + /** Name of the declaration, or a short description, e.g. "". */ + text: string; + /** A ScriptElementKind */ + kind: string; + /** ScriptElementKindModifier separated by commas, e.g. "public,abstract" */ + kindModifiers: string; + /** + * Spans of the nodes that generated this declaration. + * There will be more than one if this is the result of merging. + */ + spans: TextSpan[]; + /** Present if non-empty */ + childItems?: NavigationTree[]; + } + export interface TodoCommentDescriptor { text: string; priority: number; diff --git a/tests/cases/fourslash/deleteClassWithEnumPresent.ts b/tests/cases/fourslash/deleteClassWithEnumPresent.ts index eb40a27bcd7..8914e0020a0 100644 --- a/tests/cases/fourslash/deleteClassWithEnumPresent.ts +++ b/tests/cases/fourslash/deleteClassWithEnumPresent.ts @@ -5,6 +5,32 @@ goTo.marker(); edit.deleteAtCaret('class Bar { }'.length); + +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "Foo", + "kind": "enum", + "childItems": [ + { + "text": "a", + "kind": "const" + }, + { + "text": "b", + "kind": "const" + }, + { + "text": "c", + "kind": "const" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 5e8666e8670..0a72d295295 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -212,6 +212,7 @@ declare namespace FourSlashInterface { codeFixAtPosition(expectedText: string, errorCode?: number): void; navigationBar(json: any): void; + navigationTree(json: any): void; navigationItemsListCount(count: number, searchValue: string, matchKind?: string, fileName?: string): void; navigationItemsListContains(name: string, kind: string, searchValue: string, matchKind: string, fileName?: string, parentName?: string): void; occurrencesAtPositionContains(range: Range, isWriteAccess?: boolean): void; diff --git a/tests/cases/fourslash/getNavigationBarItems.ts b/tests/cases/fourslash/getNavigationBarItems.ts index 8041ba3b464..4bcfc3e18aa 100644 --- a/tests/cases/fourslash/getNavigationBarItems.ts +++ b/tests/cases/fourslash/getNavigationBarItems.ts @@ -5,6 +5,27 @@ //// ["bar"]: string; ////} +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "C", + "kind": "class", + "childItems": [ + { + "text": "[\"bar\"]", + "kind": "property" + }, + { + "text": "foo", + "kind": "property" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", @@ -31,4 +52,4 @@ verify.navigationBar([ ], "indent": 1 } -]) +]); diff --git a/tests/cases/fourslash/navbar_const.ts b/tests/cases/fourslash/navbar_const.ts index faaedb54482..2d3e3b919d0 100644 --- a/tests/cases/fourslash/navbar_const.ts +++ b/tests/cases/fourslash/navbar_const.ts @@ -2,6 +2,17 @@ //// const c = 0; +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "c", + "kind": "const" + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navbar_contains-no-duplicates.ts b/tests/cases/fourslash/navbar_contains-no-duplicates.ts index ba48c4a93dd..f4b52bb09a1 100644 --- a/tests/cases/fourslash/navbar_contains-no-duplicates.ts +++ b/tests/cases/fourslash/navbar_contains-no-duplicates.ts @@ -27,6 +27,83 @@ //// export var x = 3; //// } +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "ABC", + "kind": "class", + "childItems": [ + { + "text": "foo", + "kind": "method", + "kindModifiers": "public" + } + ] + }, + { + "text": "ABC", + "kind": "module", + "childItems": [ + { + "text": "x", + "kind": "var", + "kindModifiers": "export" + } + ] + }, + { + "text": "Windows", + "kind": "module", + "kindModifiers": "declare", + "childItems": [ + { + "text": "Foundation", + "kind": "module", + "kindModifiers": "export,declare", + "childItems": [ + { + "text": "A", + "kind": "var", + "kindModifiers": "export,declare" + }, + { + "text": "B", + "kind": "var", + "kindModifiers": "export,declare" + }, + { + "text": "Test", + "kind": "class", + "kindModifiers": "export,declare", + "childItems": [ + { + "text": "wow", + "kind": "method", + "kindModifiers": "public,declare" + } + ] + }, + { + "text": "Test", + "kind": "module", + "kindModifiers": "export,declare", + "childItems": [ + { + "text": "Boom", + "kind": "function", + "kindModifiers": "export,declare" + } + ] + } + ] + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navbar_exportDefault.ts b/tests/cases/fourslash/navbar_exportDefault.ts index e547c9129a6..84f50610e6b 100644 --- a/tests/cases/fourslash/navbar_exportDefault.ts +++ b/tests/cases/fourslash/navbar_exportDefault.ts @@ -13,6 +13,17 @@ ////export default function Func { } goTo.file("a.ts"); +verify.navigationTree({ + "text": "\"a\"", + "kind": "module", + "childItems": [ + { + "text": "default", + "kind": "class", + "kindModifiers": "export" + } + ] +}); verify.navigationBar([ { "text": "\"a\"", @@ -34,6 +45,17 @@ verify.navigationBar([ ]); goTo.file("b.ts"); +verify.navigationTree({ + "text": "\"b\"", + "kind": "module", + "childItems": [ + { + "text": "C", + "kind": "class", + "kindModifiers": "export" + } + ] +}); verify.navigationBar([ { "text": "\"b\"", @@ -55,6 +77,17 @@ verify.navigationBar([ ]); goTo.file("c.ts"); +verify.navigationTree({ + "text": "\"c\"", + "kind": "module", + "childItems": [ + { + "text": "default", + "kind": "function", + "kindModifiers": "export" + } + ] +}); verify.navigationBar([ { "text": "\"c\"", @@ -76,6 +109,17 @@ verify.navigationBar([ ]); goTo.file("d.ts"); +verify.navigationTree({ + "text": "\"d\"", + "kind": "module", + "childItems": [ + { + "text": "Func", + "kind": "function", + "kindModifiers": "export" + } + ] +}); verify.navigationBar([ { "text": "\"d\"", diff --git a/tests/cases/fourslash/navbar_let.ts b/tests/cases/fourslash/navbar_let.ts index bd0e702bbf9..77e00ad7300 100644 --- a/tests/cases/fourslash/navbar_let.ts +++ b/tests/cases/fourslash/navbar_let.ts @@ -2,6 +2,17 @@ ////let c = 0; +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "c", + "kind": "let" + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions.ts b/tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions.ts index 30e96739ab0..69fa697f7bc 100644 --- a/tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions.ts +++ b/tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions.ts @@ -26,6 +26,85 @@ //// (class { }); ////}) +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "", + "kind": "function", + "childItems": [ + { + "text": "nest", + "kind": "function", + "childItems": [ + { + "text": "moreNest", + "kind": "function" + } + ] + }, + { + "text": "x", + "kind": "function", + "childItems": [ + { + "text": "xx", + "kind": "function" + } + ] + }, + { + "text": "y", + "kind": "const", + "childItems": [ + { + "text": "foo", + "kind": "function" + } + ] + } + ] + }, + { + "text": "", + "kind": "function", + "childItems": [ + { + "text": "", + "kind": "function" + }, + { + "text": "z", + "kind": "function" + } + ] + }, + { + "text": "classes", + "kind": "function", + "childItems": [ + { + "text": "", + "kind": "class" + }, + { + "text": "cls2", + "kind": "class" + }, + { + "text": "cls3", + "kind": "class" + } + ] + }, + { + "text": "global.cls", + "kind": "class" + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions2.ts b/tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions2.ts index 36b1e07cd8a..f6ce02e0dd0 100644 --- a/tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions2.ts +++ b/tests/cases/fourslash/navigationBarAnonymousClassAndFunctionExpressions2.ts @@ -3,6 +3,39 @@ ////console.log(console.log(class Y {}, class X {}), console.log(class B {}, class A {})); ////console.log(class Cls { meth() {} }); +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "A", + "kind": "class" + }, + { + "text": "B", + "kind": "class" + }, + { + "text": "Cls", + "kind": "class", + "childItems": [ + { + "text": "meth", + "kind": "method" + } + ] + }, + { + "text": "X", + "kind": "class" + }, + { + "text": "Y", + "kind": "class" + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarFunctionIndirectlyInVariableDeclaration.ts b/tests/cases/fourslash/navigationBarFunctionIndirectlyInVariableDeclaration.ts index f14419a121a..a0ed5174f56 100644 --- a/tests/cases/fourslash/navigationBarFunctionIndirectlyInVariableDeclaration.ts +++ b/tests/cases/fourslash/navigationBarFunctionIndirectlyInVariableDeclaration.ts @@ -8,39 +8,64 @@ //// propB: function() {} ////}; -verify.navigationBar([ - { +verify.navigationTree({ "text": "", "kind": "script", "childItems": [ - { - "text": "a", - "kind": "var" - }, - { - "text": "b", - "kind": "var" - }, - { - "text": "propB", - "kind": "function" - } + { + "text": "a", + "kind": "var", + "childItems": [ + { + "text": "propA", + "kind": "function" + } + ] + }, + { + "text": "b", + "kind": "var" + }, + { + "text": "propB", + "kind": "function" + } ] - }, - { - "text": "a", - "kind": "var", - "childItems": [ - { - "text": "propA", - "kind": "function" - } - ], - "indent": 1 - }, - { - "text": "propB", - "kind": "function", - "indent": 1 - } +}); + +verify.navigationBar([ + { + "text": "", + "kind": "script", + "childItems": [ + { + "text": "a", + "kind": "var" + }, + { + "text": "b", + "kind": "var" + }, + { + "text": "propB", + "kind": "function" + } + ] + }, + { + "text": "a", + "kind": "var", + "childItems": [ + { + "text": "propA", + "kind": "function" + } + ], + "indent": 1 + }, + { + "text": "propB", + "kind": "function", + "indent": 1 + } ]); diff --git a/tests/cases/fourslash/navigationBarGetterAndSetter.ts b/tests/cases/fourslash/navigationBarGetterAndSetter.ts index 3ae2e0f60ac..ccf9e7c472e 100644 --- a/tests/cases/fourslash/navigationBarGetterAndSetter.ts +++ b/tests/cases/fourslash/navigationBarGetterAndSetter.ts @@ -8,6 +8,33 @@ //// } ////} +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "X", + "kind": "class", + "childItems": [ + { + "text": "x", + "kind": "getter" + }, + { + "text": "x", + "kind": "setter", + "childItems": [ + { + "text": "f", + "kind": "function" + } + ] + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarImports.ts b/tests/cases/fourslash/navigationBarImports.ts index 43cb99c556a..44f0d46b401 100644 --- a/tests/cases/fourslash/navigationBarImports.ts +++ b/tests/cases/fourslash/navigationBarImports.ts @@ -4,6 +4,29 @@ ////import c = require("m"); ////import * as d from "m"; +verify.navigationTree({ + "text": "\"navigationBarImports\"", + "kind": "module", + "childItems": [ + { + "text": "a", + "kind": "alias" + }, + { + "text": "b", + "kind": "alias" + }, + { + "text": "c", + "kind": "alias" + }, + { + "text": "d", + "kind": "alias" + } + ] +}); + verify.navigationBar([ { "text": "\"navigationBarImports\"", diff --git a/tests/cases/fourslash/navigationBarItemsBindingPatterns.ts b/tests/cases/fourslash/navigationBarItemsBindingPatterns.ts index ff1de04c07e..d2b8b24bcbf 100644 --- a/tests/cases/fourslash/navigationBarItemsBindingPatterns.ts +++ b/tests/cases/fourslash/navigationBarItemsBindingPatterns.ts @@ -6,6 +6,57 @@ ////const bar1, [c, d] ////var {e, x: [f, g]} = {a:1, x:[]}; +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "a", + "kind": "let" + }, + { + "text": "b", + "kind": "let" + }, + { + "text": "bar", + "kind": "var" + }, + { + "text": "bar1", + "kind": "const" + }, + { + "text": "c", + "kind": "const" + }, + { + "text": "d", + "kind": "const" + }, + { + "text": "e", + "kind": "var" + }, + { + "text": "f", + "kind": "var" + }, + { + "text": "foo", + "kind": "var" + }, + { + "text": "foo1", + "kind": "let" + }, + { + "text": "g", + "kind": "var" + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsBindingPatternsInConstructor.ts b/tests/cases/fourslash/navigationBarItemsBindingPatternsInConstructor.ts index 61e34d0f30e..f1250d530d3 100644 --- a/tests/cases/fourslash/navigationBarItemsBindingPatternsInConstructor.ts +++ b/tests/cases/fourslash/navigationBarItemsBindingPatternsInConstructor.ts @@ -11,6 +11,41 @@ //// } ////} +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "A", + "kind": "class", + "childItems": [ + { + "text": "constructor", + "kind": "constructor" + }, + { + "text": "x", + "kind": "property" + } + ] + }, + { + "text": "B", + "kind": "class", + "childItems": [ + { + "text": "constructor", + "kind": "constructor" + }, + { + "text": "x", + "kind": "property" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsEmptyConstructors.ts b/tests/cases/fourslash/navigationBarItemsEmptyConstructors.ts index 2e37f69b456..c300a1207d5 100644 --- a/tests/cases/fourslash/navigationBarItemsEmptyConstructors.ts +++ b/tests/cases/fourslash/navigationBarItemsEmptyConstructors.ts @@ -5,6 +5,23 @@ //// } ////} +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "Test", + "kind": "class", + "childItems": [ + { + "text": "constructor", + "kind": "constructor" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsExports.ts b/tests/cases/fourslash/navigationBarItemsExports.ts index 3ec9b0a7138..1f29ae5af5b 100644 --- a/tests/cases/fourslash/navigationBarItemsExports.ts +++ b/tests/cases/fourslash/navigationBarItemsExports.ts @@ -9,6 +9,26 @@ //// ////export * from "a"; // no bindings here +verify.navigationTree({ + "text": "\"navigationBarItemsExports\"", + "kind": "module", + "childItems": [ + { + "text": "a", + "kind": "alias" + }, + { + "text": "B", + "kind": "alias" + }, + { + "text": "e", + "kind": "alias", + "kindModifiers": "export" + } + ] +}); + verify.navigationBar([ { "text": "\"navigationBarItemsExports\"", diff --git a/tests/cases/fourslash/navigationBarItemsFunctions.ts b/tests/cases/fourslash/navigationBarItemsFunctions.ts index b85e898065c..f43cd3effaf 100644 --- a/tests/cases/fourslash/navigationBarItemsFunctions.ts +++ b/tests/cases/fourslash/navigationBarItemsFunctions.ts @@ -14,6 +14,53 @@ //// var v = 10; ////} +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "baz", + "kind": "function", + "childItems": [ + { + "text": "v", + "kind": "var" + } + ] + }, + { + "text": "foo", + "kind": "function", + "childItems": [ + { + "text": "bar", + "kind": "function", + "childItems": [ + { + "text": "biz", + "kind": "function", + "childItems": [ + { + "text": "z", + "kind": "var" + } + ] + }, + { + "text": "y", + "kind": "var" + } + ] + }, + { + "text": "x", + "kind": "var" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts b/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts index b0238a1ef06..49ca40841d7 100644 --- a/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts +++ b/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts @@ -4,6 +4,23 @@ //// function; ////} +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "f", + "kind": "function", + "childItems": [ + { + "text": "", + "kind": "function" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts b/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts index 4f279446100..8ebfab8519b 100644 --- a/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts +++ b/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts @@ -5,6 +5,27 @@ //// function; ////} +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "", + "kind": "function" + }, + { + "text": "f", + "kind": "function", + "childItems": [ + { + "text": "", + "kind": "function" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsImports.ts b/tests/cases/fourslash/navigationBarItemsImports.ts index ed398ec7f1d..b0ce515a56c 100644 --- a/tests/cases/fourslash/navigationBarItemsImports.ts +++ b/tests/cases/fourslash/navigationBarItemsImports.ts @@ -13,6 +13,44 @@ //// ////import * as ns from "a"; +verify.navigationTree({ + "text": "\"navigationBarItemsImports\"", + "kind": "module", + "childItems": [ + { + "text": "a", + "kind": "alias" + }, + { + "text": "B", + "kind": "alias" + }, + { + "text": "c", + "kind": "alias" + }, + { + "text": "D", + "kind": "alias" + }, + { + "text": "d1", + "kind": "alias" + }, + { + "text": "d2", + "kind": "alias" + }, + { + "text": "e", + "kind": "alias" + }, + { + "text": "ns", + "kind": "alias" + } + ] +}); verify.navigationBar([ { diff --git a/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts b/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts index a49f32d960e..35dff3af14c 100644 --- a/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts +++ b/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts @@ -18,6 +18,77 @@ //// emptyMethod() { } // Non child functions method should not be duplicated ////} +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "Class", + "kind": "class", + "childItems": [ + { + "text": "constructor", + "kind": "constructor", + "childItems": [ + { + "text": "LocalEnumInConstructor", + "kind": "enum", + "childItems": [ + { + "text": "LocalEnumMemberInConstructor", + "kind": "const" + } + ] + }, + { + "text": "LocalFunctionInConstructor", + "kind": "function" + }, + { + "text": "LocalInterfaceInConstrcutor", + "kind": "interface" + } + ] + }, + { + "text": "emptyMethod", + "kind": "method" + }, + { + "text": "method", + "kind": "method", + "childItems": [ + { + "text": "LocalEnumInMethod", + "kind": "enum", + "childItems": [ + { + "text": "LocalEnumMemberInMethod", + "kind": "const" + } + ] + }, + { + "text": "LocalFunctionInMethod", + "kind": "function", + "childItems": [ + { + "text": "LocalFunctionInLocalFunctionInMethod", + "kind": "function" + } + ] + }, + { + "text": "LocalInterfaceInMethod", + "kind": "interface" + } + ] + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsItems.ts b/tests/cases/fourslash/navigationBarItemsItems.ts index e3d8f0e633c..69422dd0cc2 100644 --- a/tests/cases/fourslash/navigationBarItemsItems.ts +++ b/tests/cases/fourslash/navigationBarItemsItems.ts @@ -39,6 +39,114 @@ ////var p: IPoint = new Shapes.Point(3, 4); ////var dist = p.getDist(); +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "dist", + "kind": "var" + }, + { + "text": "IPoint", + "kind": "interface", + "childItems": [ + { + "text": "()", + "kind": "call" + }, + { + "text": "new()", + "kind": "construct" + }, + { + "text": "[]", + "kind": "index" + }, + { + "text": "getDist", + "kind": "method" + }, + { + "text": "prop", + "kind": "property" + } + ] + }, + { + "text": "p", + "kind": "var" + }, + { + "text": "Shapes", + "kind": "module", + "childItems": [ + { + "text": "Point", + "kind": "class", + "kindModifiers": "export", + "childItems": [ + { + "text": "constructor", + "kind": "constructor" + }, + { + "text": "getDist", + "kind": "method" + }, + { + "text": "getOrigin", + "kind": "method", + "kindModifiers": "private,static" + }, + { + "text": "origin", + "kind": "property", + "kindModifiers": "static" + }, + { + "text": "value", + "kind": "getter" + }, + { + "text": "value", + "kind": "setter" + }, + { + "text": "x", + "kind": "property", + "kindModifiers": "public" + }, + { + "text": "y", + "kind": "property", + "kindModifiers": "public" + } + ] + }, + { + "text": "Values", + "kind": "enum", + "childItems": [ + { + "text": "value1", + "kind": "const" + }, + { + "text": "value2", + "kind": "const" + }, + { + "text": "value3", + "kind": "const" + } + ] + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsItems2.ts b/tests/cases/fourslash/navigationBarItemsItems2.ts index 6ab0827e8f9..fa5a5b09fac 100644 --- a/tests/cases/fourslash/navigationBarItemsItems2.ts +++ b/tests/cases/fourslash/navigationBarItemsItems2.ts @@ -6,6 +6,22 @@ goTo.marker(); edit.insertLine("module A"); edit.insert("export class "); +verify.navigationTree({ + "text": "\"navigationBarItemsItems2\"", + "kind": "module", + "childItems": [ + { + "text": "", + "kind": "class", + "kindModifiers": "export" + }, + { + "text": "A", + "kind": "module" + } + ] +}); + // should not crash verify.navigationBar([ { diff --git a/tests/cases/fourslash/navigationBarItemsItemsExternalModules.ts b/tests/cases/fourslash/navigationBarItemsItemsExternalModules.ts index 04091185dd0..705dc2227b3 100644 --- a/tests/cases/fourslash/navigationBarItemsItemsExternalModules.ts +++ b/tests/cases/fourslash/navigationBarItemsItemsExternalModules.ts @@ -4,6 +4,25 @@ //// public s: string; ////} +verify.navigationTree({ + "text": "\"navigationBarItemsItemsExternalModules\"", + "kind": "module", + "childItems": [ + { + "text": "Bar", + "kind": "class", + "kindModifiers": "export", + "childItems": [ + { + "text": "s", + "kind": "property", + "kindModifiers": "public" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "\"navigationBarItemsItemsExternalModules\"", diff --git a/tests/cases/fourslash/navigationBarItemsItemsExternalModules2.ts b/tests/cases/fourslash/navigationBarItemsItemsExternalModules2.ts index 4939091d944..c271050bc52 100644 --- a/tests/cases/fourslash/navigationBarItemsItemsExternalModules2.ts +++ b/tests/cases/fourslash/navigationBarItemsItemsExternalModules2.ts @@ -6,6 +6,30 @@ ////} ////export var x: number; +verify.navigationTree({ + "text": "\"file\"", + "kind": "module", + "childItems": [ + { + "text": "Bar", + "kind": "class", + "kindModifiers": "export", + "childItems": [ + { + "text": "s", + "kind": "property", + "kindModifiers": "public" + } + ] + }, + { + "text": "x", + "kind": "var", + "kindModifiers": "export" + } + ] +}); + verify.navigationBar([ { "text": "\"file\"", diff --git a/tests/cases/fourslash/navigationBarItemsItemsExternalModules3.ts b/tests/cases/fourslash/navigationBarItemsItemsExternalModules3.ts index 5ee760fabc6..91ac533d75c 100644 --- a/tests/cases/fourslash/navigationBarItemsItemsExternalModules3.ts +++ b/tests/cases/fourslash/navigationBarItemsItemsExternalModules3.ts @@ -6,6 +6,30 @@ ////} ////export var x: number; +verify.navigationTree({ + "text": "\"my fil\\\"e\"", + "kind": "module", + "childItems": [ + { + "text": "Bar", + "kind": "class", + "kindModifiers": "export", + "childItems": [ + { + "text": "s", + "kind": "property", + "kindModifiers": "public" + } + ] + }, + { + "text": "x", + "kind": "var", + "kindModifiers": "export" + } + ] +}); + verify.navigationBar([ { "text": "\"my fil\\\"e\"", diff --git a/tests/cases/fourslash/navigationBarItemsItemsModuleVariables.ts b/tests/cases/fourslash/navigationBarItemsItemsModuleVariables.ts index 9b8a65d0430..1beeffddb21 100644 --- a/tests/cases/fourslash/navigationBarItemsItemsModuleVariables.ts +++ b/tests/cases/fourslash/navigationBarItemsItemsModuleVariables.ts @@ -20,6 +20,23 @@ ////} goTo.marker("file1"); // nothing else should show up +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "Module1", + "kind": "module", + "childItems": [ + { + "text": "x", + "kind": "var", + "kindModifiers": "export" + } + ] + } + ] +}); verify.navigationBar([ { "text": "", @@ -46,6 +63,23 @@ verify.navigationBar([ ]); goTo.marker("file2"); +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "Module1.SubModule", + "kind": "module", + "childItems": [ + { + "text": "y", + "kind": "var", + "kindModifiers": "export" + } + ] + } + ] +}); verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsMissingName1.ts b/tests/cases/fourslash/navigationBarItemsMissingName1.ts index 2a445a5e1ed..60123d92e71 100644 --- a/tests/cases/fourslash/navigationBarItemsMissingName1.ts +++ b/tests/cases/fourslash/navigationBarItemsMissingName1.ts @@ -3,6 +3,28 @@ //// foo() {} ////} +verify.navigationTree({ + "text": "\"navigationBarItemsMissingName1\"", + "kind": "module", + "childItems": [ + { + "text": "", + "kind": "function", + "kindModifiers": "export" + }, + { + "text": "C", + "kind": "class", + "childItems": [ + { + "text": "foo", + "kind": "method" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "\"navigationBarItemsMissingName1\"", diff --git a/tests/cases/fourslash/navigationBarItemsMissingName2.ts b/tests/cases/fourslash/navigationBarItemsMissingName2.ts index 9faeb6de902..ce92128c825 100644 --- a/tests/cases/fourslash/navigationBarItemsMissingName2.ts +++ b/tests/cases/fourslash/navigationBarItemsMissingName2.ts @@ -6,6 +6,23 @@ ////} // Anonymous classes are still included. +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "", + "kind": "class", + "childItems": [ + { + "text": "foo", + "kind": "method" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsModules.ts b/tests/cases/fourslash/navigationBarItemsModules.ts index a41c75de550..fd5907e2bc7 100644 --- a/tests/cases/fourslash/navigationBarItemsModules.ts +++ b/tests/cases/fourslash/navigationBarItemsModules.ts @@ -27,6 +27,73 @@ //We have 8 module keywords, and 4 var keywords. //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.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "'X2.Y2.Z2'", + "kind": "module", + "kindModifiers": "declare" + }, + { + "text": "\"X.Y.Z\"", + "kind": "module", + "kindModifiers": "declare" + }, + { + "text": "A", + "kind": "module", + "childItems": [ + { + "text": "B", + "kind": "module", + "childItems": [ + { + "text": "C", + "kind": "module", + "childItems": [ + { + "text": "x", + "kind": "var", + "kindModifiers": "declare" + } + ] + } + ] + }, + { + "text": "z", + "kind": "var", + "kindModifiers": "export" + } + ] + }, + { + "text": "A.B", + "kind": "module", + "childItems": [ + { + "text": "y", + "kind": "var", + "kindModifiers": "export" + } + ] + }, + { + "text": "A.B.C", + "kind": "module", + "childItems": [ + { + "text": "x", + "kind": "var", + "kindModifiers": "export" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts b/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts index dcf8894bb61..793844ac970 100644 --- a/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts +++ b/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts @@ -24,6 +24,56 @@ //// } ////} +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "\"Multiline\\\nMadness\"", + "kind": "module", + "kindModifiers": "declare" + }, + { + "text": "\"Multiline\\r\\nMadness\"", + "kind": "module", + "kindModifiers": "declare" + }, + { + "text": "\"MultilineMadness\"", + "kind": "module", + "kindModifiers": "declare" + }, + { + "text": "Bar", + "kind": "class", + "childItems": [ + { + "text": "'a1\\\\\\r\\nb'", + "kind": "property" + }, + { + "text": "'a2\\\n \\\n b'", + "kind": "method" + } + ] + }, + { + "text": "Foo", + "kind": "interface", + "childItems": [ + { + "text": "\"a1\\\\\\r\\nb\"", + "kind": "property" + }, + { + "text": "\"a2\\\n \\\n b\"", + "kind": "method" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts b/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts index b741084dab5..6e8e0ea350c 100644 --- a/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts +++ b/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts @@ -6,6 +6,43 @@ //// } ////} +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "List", + "kind": "class", + "childItems": [ + { + "text": "constructor", + "kind": "constructor", + "childItems": [ + { + "text": "local", + "kind": "var" + } + ] + }, + { + "text": "a", + "kind": "property", + "kindModifiers": "public" + }, + { + "text": "b", + "kind": "property", + "kindModifiers": "private" + }, + { + "text": "c", + "kind": "property" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsSymbols1.ts b/tests/cases/fourslash/navigationBarItemsSymbols1.ts index ba57916028b..4326d7b4deb 100644 --- a/tests/cases/fourslash/navigationBarItemsSymbols1.ts +++ b/tests/cases/fourslash/navigationBarItemsSymbols1.ts @@ -6,6 +6,31 @@ //// get [Symbol.isConcatSpreadable]() { } ////} +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "C", + "kind": "class", + "childItems": [ + { + "text": "[Symbol.isConcatSpreadable]", + "kind": "getter" + }, + { + "text": "[Symbol.isRegExp]", + "kind": "property" + }, + { + "text": "[Symbol.iterator]", + "kind": "method" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsSymbols2.ts b/tests/cases/fourslash/navigationBarItemsSymbols2.ts index 6812c88c150..64b076aaf0c 100644 --- a/tests/cases/fourslash/navigationBarItemsSymbols2.ts +++ b/tests/cases/fourslash/navigationBarItemsSymbols2.ts @@ -5,6 +5,27 @@ //// [Symbol.iterator](): string; ////} +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "I", + "kind": "interface", + "childItems": [ + { + "text": "[Symbol.isRegExp]", + "kind": "property" + }, + { + "text": "[Symbol.iterator]", + "kind": "method" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsSymbols3.ts b/tests/cases/fourslash/navigationBarItemsSymbols3.ts index 661005b86d9..be001c75e0b 100644 --- a/tests/cases/fourslash/navigationBarItemsSymbols3.ts +++ b/tests/cases/fourslash/navigationBarItemsSymbols3.ts @@ -5,6 +5,17 @@ //// [Symbol.isRegExp] = 0 ////} +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "E", + "kind": "enum" + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarItemsTypeAlias.ts b/tests/cases/fourslash/navigationBarItemsTypeAlias.ts index cba3fb8789c..387a9925d82 100644 --- a/tests/cases/fourslash/navigationBarItemsTypeAlias.ts +++ b/tests/cases/fourslash/navigationBarItemsTypeAlias.ts @@ -2,6 +2,17 @@ ////type T = number | string; +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "T", + "kind": "type" + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarJsDoc.ts b/tests/cases/fourslash/navigationBarJsDoc.ts index 89049e6011a..8efce74d111 100644 --- a/tests/cases/fourslash/navigationBarJsDoc.ts +++ b/tests/cases/fourslash/navigationBarJsDoc.ts @@ -5,6 +5,25 @@ /////** @typedef {(string|number)} */ ////const x = 0; +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "NumberLike", + "kind": "type" + }, + { + "text": "x", + "kind": "const" + }, + { + "text": "x", + "kind": "type" + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarJsDocCommentWithNoTags.ts b/tests/cases/fourslash/navigationBarJsDocCommentWithNoTags.ts index 8746f135e4d..561bdf4a056 100644 --- a/tests/cases/fourslash/navigationBarJsDocCommentWithNoTags.ts +++ b/tests/cases/fourslash/navigationBarJsDocCommentWithNoTags.ts @@ -3,6 +3,18 @@ /////** Test */ ////export const Test = {} +verify.navigationTree({ + "text": "\"navigationBarJsDocCommentWithNoTags\"", + "kind": "module", + "childItems": [ + { + "text": "Test", + "kind": "const", + "kindModifiers": "export" + } + ] +}); + verify.navigationBar([ { "text": "\"navigationBarJsDocCommentWithNoTags\"", diff --git a/tests/cases/fourslash/navigationBarMerging.ts b/tests/cases/fourslash/navigationBarMerging.ts index 2798d186c96..ebf02c94466 100644 --- a/tests/cases/fourslash/navigationBarMerging.ts +++ b/tests/cases/fourslash/navigationBarMerging.ts @@ -11,6 +11,37 @@ //// function bar() {} ////} +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "a", + "kind": "module", + "childItems": [ + { + "text": "bar", + "kind": "function" + }, + { + "text": "foo", + "kind": "function" + } + ] + }, + { + "text": "b", + "kind": "module", + "childItems": [ + { + "text": "foo", + "kind": "function" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", @@ -60,6 +91,22 @@ verify.navigationBar([ ////function a() {} goTo.file("file2.ts"); + +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "a", + "kind": "function" + }, + { + "text": "a", + "kind": "module" + } + ] +}); + verify.navigationBar([ { "text": "", @@ -101,6 +148,34 @@ verify.navigationBar([ ////} goTo.file("file3.ts"); + +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "a", + "kind": "module", + "childItems": [ + { + "text": "A", + "kind": "interface", + "childItems": [ + { + "text": "bar", + "kind": "property" + }, + { + "text": "foo", + "kind": "property" + } + ] + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", @@ -147,6 +222,36 @@ verify.navigationBar([ ////module A.B { export var y; } goTo.file("file4.ts"); + +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "A", + "kind": "module", + "childItems": [ + { + "text": "x", + "kind": "var", + "kindModifiers": "export" + } + ] + }, + { + "text": "A.B", + "kind": "module", + "childItems": [ + { + "text": "y", + "kind": "var", + "kindModifiers": "export" + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/navigationBarNamespaceImportWithNoName.ts b/tests/cases/fourslash/navigationBarNamespaceImportWithNoName.ts index 732e2deb1dd..4c441728fdf 100644 --- a/tests/cases/fourslash/navigationBarNamespaceImportWithNoName.ts +++ b/tests/cases/fourslash/navigationBarNamespaceImportWithNoName.ts @@ -1,4 +1,16 @@ ////import *{} from 'foo'; + +verify.navigationTree({ + "text": "\"navigationBarNamespaceImportWithNoName\"", + "kind": "module", + "childItems": [ + { + "text": "", + "kind": "alias" + } + ] +}); + verify.navigationBar([ { "text": "\"navigationBarNamespaceImportWithNoName\"", diff --git a/tests/cases/fourslash/navigationBarVariables.ts b/tests/cases/fourslash/navigationBarVariables.ts index 93093df1306..98d5e816e8e 100644 --- a/tests/cases/fourslash/navigationBarVariables.ts +++ b/tests/cases/fourslash/navigationBarVariables.ts @@ -4,6 +4,25 @@ ////let y = 1; ////const z = 2; +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "x", + "kind": "var" + }, + { + "text": "y", + "kind": "let" + }, + { + "text": "z", + "kind": "const" + } + ] +}); + verify.navigationBar([ { "text": "", @@ -31,6 +50,26 @@ verify.navigationBar([ ////const [c] = 0; goTo.file("file2.ts"); + +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "a", + "kind": "var" + }, + { + "text": "b", + "kind": "let" + }, + { + "text": "c", + "kind": "const" + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts b/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts index 978db18ab3b..212394159a8 100644 --- a/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts +++ b/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts @@ -10,6 +10,29 @@ //// /** @type {/*1*/NumberLike} */ //// var numberLike; +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "numberLike", + "kind": "var" + }, + { + "text": "NumberLike", + "kind": "type" + }, + { + "text": "NumberLike2", + "kind": "var" + }, + { + "text": "NumberLike2", + "kind": "type" + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/server/navbar01.ts b/tests/cases/fourslash/server/navbar01.ts index a5b22ee1fa9..4a4f59bcf78 100644 --- a/tests/cases/fourslash/server/navbar01.ts +++ b/tests/cases/fourslash/server/navbar01.ts @@ -38,6 +38,114 @@ ////var p: IPoint = new Shapes.Point(3, 4); ////var dist = p.getDist(); +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "dist", + "kind": "var" + }, + { + "text": "IPoint", + "kind": "interface", + "childItems": [ + { + "text": "()", + "kind": "call" + }, + { + "text": "new()", + "kind": "construct" + }, + { + "text": "[]", + "kind": "index" + }, + { + "text": "getDist", + "kind": "method" + }, + { + "text": "prop", + "kind": "property" + } + ] + }, + { + "text": "p", + "kind": "var" + }, + { + "text": "Shapes", + "kind": "module", + "childItems": [ + { + "text": "Point", + "kind": "class", + "kindModifiers": "export", + "childItems": [ + { + "text": "constructor", + "kind": "constructor" + }, + { + "text": "getDist", + "kind": "method" + }, + { + "text": "getOrigin", + "kind": "method", + "kindModifiers": "private,static" + }, + { + "text": "origin", + "kind": "property", + "kindModifiers": "static" + }, + { + "text": "value", + "kind": "getter" + }, + { + "text": "value", + "kind": "setter" + }, + { + "text": "x", + "kind": "property", + "kindModifiers": "public" + }, + { + "text": "y", + "kind": "property", + "kindModifiers": "public" + } + ] + }, + { + "text": "Values", + "kind": "enum", + "childItems": [ + { + "text": "value1", + "kind": "const" + }, + { + "text": "value2", + "kind": "const" + }, + { + "text": "value3", + "kind": "const" + } + ] + } + ] + } + ] +}); + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/shims-pp/getNavigationBarItems.ts b/tests/cases/fourslash/shims-pp/getNavigationBarItems.ts index fba24bdcf77..627d4dac178 100644 --- a/tests/cases/fourslash/shims-pp/getNavigationBarItems.ts +++ b/tests/cases/fourslash/shims-pp/getNavigationBarItems.ts @@ -2,6 +2,17 @@ //// {| "itemName": "c", "kind": "const", "parentName": "" |}const c = 0; +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "c", + "kind": "const" + } + ] +}) + verify.navigationBar([ { "text": "", diff --git a/tests/cases/fourslash/shims/getNavigationBarItems.ts b/tests/cases/fourslash/shims/getNavigationBarItems.ts index fba24bdcf77..627d4dac178 100644 --- a/tests/cases/fourslash/shims/getNavigationBarItems.ts +++ b/tests/cases/fourslash/shims/getNavigationBarItems.ts @@ -2,6 +2,17 @@ //// {| "itemName": "c", "kind": "const", "parentName": "" |}const c = 0; +verify.navigationTree({ + "text": "", + "kind": "script", + "childItems": [ + { + "text": "c", + "kind": "const" + } + ] +}) + verify.navigationBar([ { "text": "",