Skip to content

Commit 89350b3

Browse files
Merge pull request #7178 from tinganho/navigationBarItemTopLevel
Navigation bar items in methods
2 parents 8dc3b2e + b7c3547 commit 89350b3

File tree

2 files changed

+110
-8
lines changed

2 files changed

+110
-8
lines changed

src/services/navigationBar.ts

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,20 @@ namespace ts.NavigationBar {
160160
for (let node of nodes) {
161161
switch (node.kind) {
162162
case SyntaxKind.ClassDeclaration:
163+
topLevelNodes.push(node);
164+
for (const member of (<ClassDeclaration>node).members) {
165+
if (member.kind === SyntaxKind.MethodDeclaration || member.kind === SyntaxKind.Constructor) {
166+
type FunctionLikeMember = MethodDeclaration | ConstructorDeclaration;
167+
if ((<FunctionLikeMember>member).body) {
168+
// We do not include methods that does not have child functions in it, because of duplications.
169+
if (hasNamedFunctionDeclarations((<Block>(<FunctionLikeMember>member).body).statements)) {
170+
topLevelNodes.push(member);
171+
}
172+
addTopLevelNodes((<Block>(<MethodDeclaration>member).body).statements, topLevelNodes);
173+
}
174+
}
175+
}
176+
break;
163177
case SyntaxKind.EnumDeclaration:
164178
case SyntaxKind.InterfaceDeclaration:
165179
topLevelNodes.push(node);
@@ -182,23 +196,40 @@ namespace ts.NavigationBar {
182196
}
183197
}
184198

185-
function isTopLevelFunctionDeclaration(functionDeclaration: FunctionLikeDeclaration) {
199+
function hasNamedFunctionDeclarations(nodes: NodeArray<Statement>): boolean {
200+
for (let s of nodes) {
201+
if (s.kind === SyntaxKind.FunctionDeclaration && !isEmpty((<FunctionDeclaration>s).name.text)) {
202+
return true;
203+
}
204+
}
205+
return false;
206+
}
207+
208+
function isTopLevelFunctionDeclaration(functionDeclaration: FunctionLikeDeclaration): boolean {
186209
if (functionDeclaration.kind === SyntaxKind.FunctionDeclaration) {
187210
// A function declaration is 'top level' if it contains any function declarations
188211
// within it.
189212
if (functionDeclaration.body && functionDeclaration.body.kind === SyntaxKind.Block) {
190213
// Proper function declarations can only have identifier names
191-
if (forEach((<Block>functionDeclaration.body).statements,
192-
s => s.kind === SyntaxKind.FunctionDeclaration && !isEmpty((<FunctionDeclaration>s).name.text))) {
193-
214+
if (hasNamedFunctionDeclarations((<Block>functionDeclaration.body).statements)) {
194215
return true;
195216
}
196217

197-
// Or if it is not parented by another function. i.e all functions
198-
// at module scope are 'top level'.
218+
// Or if it is not parented by another function. I.e all functions at module scope are 'top level'.
199219
if (!isFunctionBlock(functionDeclaration.parent)) {
200220
return true;
201221
}
222+
223+
// Or if it is nested inside class methods and constructors.
224+
else {
225+
// We have made sure that a grand parent node exists with 'isFunctionBlock()' above.
226+
const grandParentKind = functionDeclaration.parent.parent.kind;
227+
if (grandParentKind === SyntaxKind.MethodDeclaration ||
228+
grandParentKind === SyntaxKind.Constructor) {
229+
230+
return true;
231+
}
232+
}
202233
}
203234
}
204235

@@ -376,6 +407,10 @@ namespace ts.NavigationBar {
376407
case SyntaxKind.ClassDeclaration:
377408
return createClassItem(<ClassDeclaration>node);
378409

410+
case SyntaxKind.MethodDeclaration:
411+
case SyntaxKind.Constructor:
412+
return createMemberFunctionLikeItem(<MethodDeclaration | ConstructorDeclaration>node);
413+
379414
case SyntaxKind.EnumDeclaration:
380415
return createEnumItem(<EnumDeclaration>node);
381416

@@ -424,11 +459,11 @@ namespace ts.NavigationBar {
424459
getIndent(node));
425460
}
426461

427-
function createFunctionItem(node: FunctionDeclaration) {
462+
function createFunctionItem(node: FunctionDeclaration): ts.NavigationBarItem {
428463
if (node.body && node.body.kind === SyntaxKind.Block) {
429464
let childItems = getItemsWorker(sortNodes((<Block>node.body).statements), createChildItem);
430465

431-
return getNavigationBarItem(!node.name ? "default": node.name.text ,
466+
return getNavigationBarItem(!node.name ? "default": node.name.text,
432467
ts.ScriptElementKind.functionElement,
433468
getNodeModifiers(node),
434469
[getNodeSpan(node)],
@@ -439,6 +474,31 @@ namespace ts.NavigationBar {
439474
return undefined;
440475
}
441476

477+
function createMemberFunctionLikeItem(node: MethodDeclaration | ConstructorDeclaration): ts.NavigationBarItem {
478+
if (node.body && node.body.kind === SyntaxKind.Block) {
479+
let childItems = getItemsWorker(sortNodes((<Block>node.body).statements), createChildItem);
480+
let scriptElementKind: string;
481+
let memberFunctionName: string;
482+
if (node.kind === SyntaxKind.MethodDeclaration) {
483+
memberFunctionName = getPropertyNameForPropertyNameNode(node.name);
484+
scriptElementKind = ts.ScriptElementKind.memberFunctionElement;
485+
}
486+
else {
487+
memberFunctionName = "constructor";
488+
scriptElementKind = ts.ScriptElementKind.constructorImplementationElement;
489+
}
490+
491+
return getNavigationBarItem(memberFunctionName,
492+
scriptElementKind,
493+
getNodeModifiers(node),
494+
[getNodeSpan(node)],
495+
childItems,
496+
getIndent(node));
497+
}
498+
499+
return undefined;
500+
}
501+
442502
function createSourceFileItem(node: SourceFile): ts.NavigationBarItem {
443503
let childItems = getItemsWorker(getChildNodes(node.statements), createChildItem);
444504

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/// <reference path="fourslash.ts"/>
2+
3+
////class Class {
4+
//// constructor() {
5+
//// {| "itemName": "LocalFunctionInConstructor", "kind": "function", "parentName": "Class"|}function LocalFunctionInConstructor() {
6+
////
7+
//// }
8+
////
9+
//// {| "itemName": "LocalInterfaceInConstrcutor", "kind": "interface", "parentName": "foo"|}interface LocalInterfaceInConstrcutor {
10+
//// }
11+
////
12+
//// enum LocalEnumInConstructor {
13+
//// {| "itemName": "LocalEnumMemberInConstructor", "kind": "property", "parentName": "LocalEnumInConstructor"|}LocalEnumMemberInConstructor,
14+
//// }
15+
//// }
16+
////
17+
//// method() {
18+
//// {| "itemName": "LocalFunctionInMethod", "kind": "function", "parentName": "foo"|}function LocalFunctionInMethod() {
19+
//// {| "itemName": "LocalFunctionInLocalFunctionInMethod", "kind": "function", "parentName": "bar"|}function LocalFunctionInLocalFunctionInMethod() {
20+
////
21+
//// }
22+
//// }
23+
////
24+
//// {| "itemName": "LocalInterfaceInMethod", "kind": "interface", "parentName": "foo"|}interface LocalInterfaceInMethod {
25+
//// }
26+
////
27+
//// enum LocalEnumInMethod {
28+
//// {| "itemName": "LocalEnumMemberInMethod", "kind": "property", "parentName": "foo"|}LocalEnumMemberInMethod,
29+
//// }
30+
//// }
31+
////
32+
//// emptyMethod() { // Non child functions method should not be duplicated
33+
////
34+
//// }
35+
////}
36+
37+
test.markers().forEach((marker) => {
38+
verify.getScriptLexicalStructureListContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName);
39+
});
40+
41+
// no other items
42+
verify.getScriptLexicalStructureListCount(17);

0 commit comments

Comments
 (0)