Skip to content

Commit a1f110f

Browse files
author
Andy
committed
Merge pull request #8812 from Microsoft/navbar_root
Always include a root node in the navigation bar.
2 parents 154729d + b2664e7 commit a1f110f

37 files changed

+1586
-462
lines changed

src/harness/fourslash.ts

Lines changed: 16 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1959,69 +1959,23 @@ namespace FourSlash {
19591959
}
19601960
}
19611961

1962-
public verifyNavigationBarCount(expected: number) {
1962+
public verifyNavigationBar(json: any) {
19631963
const items = this.languageService.getNavigationBarItems(this.activeFile.fileName);
1964-
const actual = this.getNavigationBarItemsCount(items);
1965-
1966-
if (expected !== actual) {
1967-
this.raiseError(`verifyNavigationBarCount failed - found: ${actual} navigation items, expected: ${expected}.`);
1968-
}
1969-
}
1970-
1971-
private getNavigationBarItemsCount(items: ts.NavigationBarItem[]) {
1972-
let result = 0;
1973-
if (items) {
1974-
for (let i = 0, n = items.length; i < n; i++) {
1975-
result++;
1976-
result += this.getNavigationBarItemsCount(items[i].childItems);
1977-
}
1978-
}
1979-
1980-
return result;
1981-
}
1982-
1983-
public verifyNavigationBarContains(name: string, kind: string, fileName?: string, parentName?: string, isAdditionalSpan?: boolean, markerPosition?: number) {
1984-
fileName = fileName || this.activeFile.fileName;
1985-
const items = this.languageService.getNavigationBarItems(fileName);
1986-
1987-
if (!items || items.length === 0) {
1988-
this.raiseError("verifyNavigationBarContains failed - found 0 navigation items, expected at least one.");
1989-
}
1990-
1991-
if (this.navigationBarItemsContains(items, name, kind, parentName)) {
1992-
return;
1993-
}
1994-
1995-
const missingItem = { name, kind, parentName };
1996-
this.raiseError(`verifyNavigationBarContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(items, undefined, 2)})`);
1997-
}
1998-
1999-
private navigationBarItemsContains(items: ts.NavigationBarItem[], name: string, kind: string, parentName?: string) {
2000-
function recur(items: ts.NavigationBarItem[], curParentName: string) {
2001-
for (let i = 0; i < items.length; i++) {
2002-
const item = items[i];
2003-
if (item && item.text === name && item.kind === kind && (!parentName || curParentName === parentName)) {
2004-
return true;
2005-
}
2006-
if (recur(item.childItems, item.text)) {
2007-
return true;
2008-
}
2009-
}
2010-
return false;
1964+
if (JSON.stringify(items, replacer) !== JSON.stringify(json)) {
1965+
this.raiseError(`verifyNavigationBar failed - expected: ${JSON.stringify(json, undefined, 2)}, got: ${JSON.stringify(items, replacer, 2)}`);
20111966
}
2012-
return recur(items, "");
2013-
}
2014-
2015-
public verifyNavigationBarChildItem(parent: string, name: string, kind: string) {
2016-
const items = this.languageService.getNavigationBarItems(this.activeFile.fileName);
20171967

2018-
for (let i = 0; i < items.length; i++) {
2019-
const item = items[i];
2020-
if (item.text === parent) {
2021-
if (this.navigationBarItemsContains(item.childItems, name, kind))
2022-
return;
2023-
const missingItem = { name, kind };
2024-
this.raiseError(`verifyNavigationBarChildItem failed - could not find the item: ${JSON.stringify(missingItem)} in the children list: (${JSON.stringify(item.childItems, undefined, 2)})`);
1968+
// Make the data easier to read.
1969+
function replacer(key: string, value: any) {
1970+
switch (key) {
1971+
case "spans":
1972+
// We won't ever check this.
1973+
return undefined;
1974+
case "childItems":
1975+
return value.length === 0 ? undefined : value;
1976+
default:
1977+
// Omit falsy values, those are presumed to be the default.
1978+
return value || undefined;
20251979
}
20261980
}
20271981
}
@@ -3042,23 +2996,8 @@ namespace FourSlashInterface {
30422996
this.DocCommentTemplate(/*expectedText*/ undefined, /*expectedOffset*/ undefined, /*empty*/ true);
30432997
}
30442998

3045-
public navigationBarCount(count: number) {
3046-
this.state.verifyNavigationBarCount(count);
3047-
}
3048-
3049-
// TODO: figure out what to do with the unused arguments.
3050-
public navigationBarContains(
3051-
name: string,
3052-
kind: string,
3053-
fileName?: string,
3054-
parentName?: string,
3055-
isAdditionalSpan?: boolean,
3056-
markerPosition?: number) {
3057-
this.state.verifyNavigationBarContains(name, kind, fileName, parentName, isAdditionalSpan, markerPosition);
3058-
}
3059-
3060-
public navigationBarChildItem(parent: string, name: string, kind: string) {
3061-
this.state.verifyNavigationBarChildItem(parent, name, kind);
2999+
public navigationBar(json: any) {
3000+
this.state.verifyNavigationBar(json);
30623001
}
30633002

30643003
public navigationItemsListCount(count: number, searchValue: string, matchKind?: string) {

src/services/navigationBar.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,10 @@ namespace ts.NavigationBar {
99
return getJsNavigationBarItems(sourceFile, compilerOptions);
1010
}
1111

12-
// If the source file has any child items, then it included in the tree
13-
// and takes lexical ownership of all other top-level items.
14-
let hasGlobalNode = false;
15-
1612
return getItemsWorker(getTopLevelNodes(sourceFile), createTopLevelItem);
1713

1814
function getIndent(node: Node): number {
19-
// If we have a global node in the tree,
20-
// then it adds an extra layer of depth to all subnodes.
21-
let indent = hasGlobalNode ? 1 : 0;
15+
let indent = 1; // Global node is the only one with indent 0.
2216

2317
let current = node.parent;
2418
while (current) {
@@ -141,7 +135,7 @@ namespace ts.NavigationBar {
141135
function sortNodes(nodes: Node[]): Node[] {
142136
return nodes.slice(0).sort((n1: Declaration, n2: Declaration) => {
143137
if (n1.name && n2.name) {
144-
return getPropertyNameForPropertyNameNode(n1.name).localeCompare(getPropertyNameForPropertyNameNode(n2.name));
138+
return localeCompareFix(getPropertyNameForPropertyNameNode(n1.name), getPropertyNameForPropertyNameNode(n2.name));
145139
}
146140
else if (n1.name) {
147141
return 1;
@@ -153,6 +147,16 @@ namespace ts.NavigationBar {
153147
return n1.kind - n2.kind;
154148
}
155149
});
150+
151+
// node 0.10 treats "a" as greater than "B".
152+
// For consistency, sort alphabetically, falling back to which is lower-case.
153+
function localeCompareFix(a: string, b: string) {
154+
const cmp = a.toLowerCase().localeCompare(b.toLowerCase());
155+
if (cmp !== 0)
156+
return cmp;
157+
// Return the *opposite* of the `<` operator, which works the same in node 0.10 and 6.0.
158+
return a < b ? 1 : a > b ? -1 : 0;
159+
}
156160
}
157161

158162
function addTopLevelNodes(nodes: Node[], topLevelNodes: Node[]): void {
@@ -511,11 +515,6 @@ namespace ts.NavigationBar {
511515
function createSourceFileItem(node: SourceFile): ts.NavigationBarItem {
512516
const childItems = getItemsWorker(getChildNodes(node.statements), createChildItem);
513517

514-
if (childItems === undefined || childItems.length === 0) {
515-
return undefined;
516-
}
517-
518-
hasGlobalNode = true;
519518
const rootName = isExternalModule(node)
520519
? "\"" + escapeString(getBaseFileName(removeFileExtension(normalizePath(node.fileName)))) + "\""
521520
: "<global>";

tests/cases/fourslash/deleteClassWithEnumPresent.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,34 @@
55

66
goTo.marker();
77
edit.deleteAtCaret('class Bar { }'.length);
8-
verify.navigationBarContains('Foo', 'enum', 'tests/cases/fourslash/deleteClassWithEnumPresent.ts', '');
8+
verify.navigationBar([
9+
{
10+
"text": "<global>",
11+
"kind": "module",
12+
"childItems": [
13+
{
14+
"text": "Foo",
15+
"kind": "enum"
16+
}
17+
]
18+
},
19+
{
20+
"text": "Foo",
21+
"kind": "enum",
22+
"childItems": [
23+
{
24+
"text": "a",
25+
"kind": "property"
26+
},
27+
{
28+
"text": "b",
29+
"kind": "property"
30+
},
31+
{
32+
"text": "c",
33+
"kind": "property"
34+
}
35+
],
36+
"indent": 1
37+
}
38+
]);

tests/cases/fourslash/fourslash.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,7 @@ declare namespace FourSlashInterface {
175175
DocCommentTemplate(expectedText: string, expectedOffset: number, empty?: boolean): void;
176176
noDocCommentTemplate(): void;
177177

178-
navigationBarCount(count: number): void;
179-
navigationBarContains(name: string, kind: string, fileName?: string, parentName?: string, isAdditionalSpan?: boolean, markerPosition?: number): void;
180-
navigationBarChildItem(parent: string, text: string, kind: string): void;
178+
navigationBar(json: any): void;
181179
navigationItemsListCount(count: number, searchValue: string, matchKind?: string): void;
182180
navigationItemsListContains(name: string, kind: string, searchValue: string, matchKind: string, fileName?: string, parentName?: string): void;
183181
occurrencesAtPositionContains(range: Range, isWriteAccess?: boolean): void;

tests/cases/fourslash/getNavigationBarItems.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,30 @@
55
//// ["bar"]: string;
66
////}
77

8-
verify.navigationBarCount(5);
9-
verify.navigationBarContains("C", "class");
10-
verify.navigationBarChildItem("C", "[\"bar\"]", "property");
11-
verify.navigationBarChildItem("C", "foo", "property");
8+
verify.navigationBar([
9+
{
10+
"text": "<global>",
11+
"kind": "module",
12+
"childItems": [
13+
{
14+
"text": "C",
15+
"kind": "class"
16+
}
17+
]
18+
},
19+
{
20+
"text": "C",
21+
"kind": "class",
22+
"childItems": [
23+
{
24+
"text": "[\"bar\"]",
25+
"kind": "property"
26+
},
27+
{
28+
"text": "foo",
29+
"kind": "property"
30+
}
31+
],
32+
"indent": 1
33+
}
34+
])

tests/cases/fourslash/navbar_const.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
/// <reference path="fourslash.ts" />
22

3-
//// {| "itemName": "c", "kind": "const", "parentName": "<global>" |}const c = 0;
3+
//// const c = 0;
44

5-
test.markers().forEach(marker => {
6-
verify.navigationBarContains(
7-
marker.data.itemName,
8-
marker.data.kind,
9-
marker.fileName,
10-
marker.data.parentName,
11-
marker.data.isAdditionalRange,
12-
marker.position);
13-
});
5+
verify.navigationBar([
6+
{
7+
"text": "<global>",
8+
"kind": "module",
9+
"childItems": [
10+
{
11+
"text": "c",
12+
"kind": "const"
13+
}
14+
]
15+
}
16+
]);

0 commit comments

Comments
 (0)