diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 2f928f52c7b62..b4c7c5a2c24c1 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -133,7 +133,7 @@ namespace ts.NavigationBar { /** Call after calling `startNode` and adding children to it. */ function endNode(): void { if (parent.children) { - mergeChildren(parent.children); + mergeChildren(parent.children, parent); sortChildren(parent.children); } parent = parentsStack.pop()!; @@ -304,7 +304,7 @@ namespace ts.NavigationBar { } /** Merge declarations of the same kind. */ - function mergeChildren(children: NavigationBarNode[]): void { + function mergeChildren(children: NavigationBarNode[], node: NavigationBarNode): void { const nameToItems = createMap(); filterMutate(children, child => { const declName = getNameOfDeclaration(child.node); @@ -322,7 +322,7 @@ namespace ts.NavigationBar { if (itemsWithSameName instanceof Array) { for (const itemWithSameName of itemsWithSameName) { - if (tryMerge(itemWithSameName, child)) { + if (tryMerge(itemWithSameName, child, node)) { return false; } } @@ -331,7 +331,7 @@ namespace ts.NavigationBar { } else { const itemWithSameName = itemsWithSameName; - if (tryMerge(itemWithSameName, child)) { + if (tryMerge(itemWithSameName, child, node)) { return false; } nameToItems.set(name, [itemWithSameName, child]); @@ -340,8 +340,8 @@ namespace ts.NavigationBar { }); } - function tryMerge(a: NavigationBarNode, b: NavigationBarNode): boolean { - if (shouldReallyMerge(a.node, b.node)) { + function tryMerge(a: NavigationBarNode, b: NavigationBarNode, parent: NavigationBarNode): boolean { + if (shouldReallyMerge(a.node, b.node, parent)) { merge(a, b); return true; } @@ -349,8 +349,8 @@ namespace ts.NavigationBar { } /** a and b have the same name, but they may not be mergeable. */ - function shouldReallyMerge(a: Node, b: Node): boolean { - if (a.kind !== b.kind) { + function shouldReallyMerge(a: Node, b: Node, parent: NavigationBarNode): boolean { + if (a.kind !== b.kind || a.parent !== b.parent && !(isOwnChild(a, parent) && isOwnChild(b, parent))) { return false; } switch (a.kind) { @@ -366,6 +366,13 @@ namespace ts.NavigationBar { } } + // We want to merge own children like `I` in in `module A { interface I {} } module A { interface I {} }` + // We don't want to merge unrelated children like `m` in `const o = { a: { m() {} }, b: { m() {} } };` + function isOwnChild(n: Node, parent: NavigationBarNode): boolean { + const par = isModuleBlock(n.parent) ? n.parent.parent : n.parent; + return par === parent.node || contains(parent.additionalNodes, par); + } + // 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 { @@ -383,7 +390,7 @@ namespace ts.NavigationBar { target.children = concatenate(target.children, source.children); if (target.children) { - mergeChildren(target.children); + mergeChildren(target.children, target); sortChildren(target.children); } } diff --git a/tests/cases/fourslash/navigationBarMerging_grandchildren.ts b/tests/cases/fourslash/navigationBarMerging_grandchildren.ts new file mode 100644 index 0000000000000..732b1794b6818 --- /dev/null +++ b/tests/cases/fourslash/navigationBarMerging_grandchildren.ts @@ -0,0 +1,44 @@ +/// + +////const o = { +//// a: { +//// m() {}, +//// }, +//// b: { +//// m() {}, +//// }, +////} + +verify.navigationTree({ + text: "", + kind: "script", + childItems: [ + { + text: "o", + kind: "const", + childItems: [ + { text: "m", kind: "method" }, + { text: "m", kind: "method" }, + ], + }, + ] +}); + +verify.navigationBar([ + { + text: "", + kind: "script", + childItems: [ + { text: "o", kind: "const" }, + ], + }, + { + text: "o", + kind: "const", + childItems: [ + { text: "m", kind: "method" }, + { text: "m", kind: "method" }, + ], + indent: 1, + }, +]);