Skip to content

Commit 8dc66e4

Browse files
author
Andy
authored
Cleanup navTo (#18150)
1 parent 89eb06e commit 8dc66e4

File tree

2 files changed

+139
-141
lines changed

2 files changed

+139
-141
lines changed

src/services/navigateTo.ts

Lines changed: 137 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -21,185 +21,183 @@ namespace ts.NavigateTo {
2121
}
2222

2323
forEachEntry(sourceFile.getNamedDeclarations(), (declarations, name) => {
24-
if (declarations) {
25-
// First do a quick check to see if the name of the declaration matches the
26-
// last portion of the (possibly) dotted name they're searching for.
27-
let matches = patternMatcher.getMatchesForLastSegmentOfPattern(name);
28-
29-
if (!matches) {
30-
return; // continue to next named declarations
31-
}
32-
33-
for (const declaration of declarations) {
34-
// It was a match! If the pattern has dots in it, then also see if the
35-
// declaration container matches as well.
36-
if (patternMatcher.patternContainsDots) {
37-
const containers = getContainers(declaration);
38-
if (!containers) {
39-
return true; // Break out of named declarations and go to the next source file.
40-
}
41-
42-
matches = patternMatcher.getMatches(containers, name);
43-
44-
if (!matches) {
45-
return; // continue to next named declarations
46-
}
47-
}
48-
49-
const fileName = sourceFile.fileName;
50-
const matchKind = bestMatchKind(matches);
51-
rawItems.push({ name, fileName, matchKind, isCaseSensitive: allMatchesAreCaseSensitive(matches), declaration });
52-
}
53-
}
24+
getItemsFromNamedDeclaration(patternMatcher, name, declarations, checker, sourceFile.fileName, rawItems);
5425
});
5526
}
5627

57-
// Remove imports when the imported declaration is already in the list and has the same name.
58-
rawItems = filter(rawItems, item => {
59-
const decl = item.declaration;
60-
if (decl.kind === SyntaxKind.ImportClause || decl.kind === SyntaxKind.ImportSpecifier || decl.kind === SyntaxKind.ImportEqualsDeclaration) {
61-
const importer = checker.getSymbolAtLocation((decl as NamedDeclaration).name);
62-
const imported = checker.getAliasedSymbol(importer);
63-
return importer.escapedName !== imported.escapedName;
64-
}
65-
else {
66-
return true;
67-
}
68-
});
69-
7028
rawItems.sort(compareNavigateToItems);
7129
if (maxResultCount !== undefined) {
7230
rawItems = rawItems.slice(0, maxResultCount);
7331
}
32+
return rawItems.map(createNavigateToItem);
33+
}
7434

75-
const items = map(rawItems, createNavigateToItem);
76-
77-
return items;
35+
function getItemsFromNamedDeclaration(patternMatcher: PatternMatcher, name: string, declarations: ReadonlyArray<Declaration>, checker: TypeChecker, fileName: string, rawItems: Push<RawNavigateToItem>): void {
36+
// First do a quick check to see if the name of the declaration matches the
37+
// last portion of the (possibly) dotted name they're searching for.
38+
const matches = patternMatcher.getMatchesForLastSegmentOfPattern(name);
7839

79-
function allMatchesAreCaseSensitive(matches: PatternMatch[]): boolean {
80-
Debug.assert(matches.length > 0);
40+
if (!matches) {
41+
return; // continue to next named declarations
42+
}
8143

82-
// This is a case sensitive match, only if all the submatches were case sensitive.
83-
for (const match of matches) {
84-
if (!match.isCaseSensitive) {
85-
return false;
86-
}
44+
for (const declaration of declarations) {
45+
if (!shouldKeepItem(declaration, checker)) {
46+
continue;
8747
}
8848

89-
return true;
90-
}
91-
92-
function tryAddSingleDeclarationName(declaration: Declaration, containers: string[]) {
93-
if (declaration) {
94-
const name = getNameOfDeclaration(declaration);
95-
if (name) {
96-
const text = getTextOfIdentifierOrLiteral(name as (Identifier | LiteralExpression));
97-
if (text !== undefined) {
98-
containers.unshift(text);
99-
}
100-
else if (name.kind === SyntaxKind.ComputedPropertyName) {
101-
return tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ true);
102-
}
103-
else {
104-
// Don't know how to add this.
105-
return false;
106-
}
49+
// It was a match! If the pattern has dots in it, then also see if the
50+
// declaration container matches as well.
51+
let containerMatches = matches;
52+
if (patternMatcher.patternContainsDots) {
53+
containerMatches = patternMatcher.getMatches(getContainers(declaration), name);
54+
if (!containerMatches) {
55+
continue;
10756
}
10857
}
10958

110-
return true;
59+
const matchKind = bestMatchKind(containerMatches);
60+
const isCaseSensitive = allMatchesAreCaseSensitive(containerMatches);
61+
rawItems.push({ name, fileName, matchKind, isCaseSensitive, declaration });
11162
}
63+
}
11264

113-
// Only added the names of computed properties if they're simple dotted expressions, like:
114-
//
115-
// [X.Y.Z]() { }
116-
function tryAddComputedPropertyName(expression: Expression, containers: string[], includeLastPortion: boolean): boolean {
117-
const text = getTextOfIdentifierOrLiteral(expression as LiteralExpression);
118-
if (text !== undefined) {
119-
if (includeLastPortion) {
120-
containers.unshift(text);
121-
}
65+
function shouldKeepItem(declaration: Declaration, checker: ts.TypeChecker): boolean {
66+
switch (declaration.kind) {
67+
case SyntaxKind.ImportClause:
68+
case SyntaxKind.ImportSpecifier:
69+
case SyntaxKind.ImportEqualsDeclaration:
70+
const importer = checker.getSymbolAtLocation((declaration as ImportClause | ImportSpecifier | ImportEqualsDeclaration).name);
71+
const imported = checker.getAliasedSymbol(importer);
72+
return importer.escapedName !== imported.escapedName;
73+
default:
12274
return true;
123-
}
75+
}
76+
}
12477

125-
if (expression.kind === SyntaxKind.PropertyAccessExpression) {
126-
const propertyAccess = <PropertyAccessExpression>expression;
127-
if (includeLastPortion) {
128-
containers.unshift(propertyAccess.name.text);
129-
}
78+
function allMatchesAreCaseSensitive(matches: ReadonlyArray<PatternMatch>): boolean {
79+
Debug.assert(matches.length > 0);
13080

131-
return tryAddComputedPropertyName(propertyAccess.expression, containers, /*includeLastPortion*/ true);
81+
// This is a case sensitive match, only if all the submatches were case sensitive.
82+
for (const match of matches) {
83+
if (!match.isCaseSensitive) {
84+
return false;
13285
}
133-
134-
return false;
13586
}
13687

137-
function getContainers(declaration: Declaration) {
138-
const containers: string[] = [];
88+
return true;
89+
}
13990

140-
// First, if we started with a computed property name, then add all but the last
141-
// portion into the container array.
91+
function tryAddSingleDeclarationName(declaration: Declaration, containers: string[]): boolean {
92+
if (declaration) {
14293
const name = getNameOfDeclaration(declaration);
143-
if (name.kind === SyntaxKind.ComputedPropertyName) {
144-
if (!tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ false)) {
145-
return undefined;
94+
if (name) {
95+
const text = getTextOfIdentifierOrLiteral(name as (Identifier | LiteralExpression));
96+
if (text !== undefined) {
97+
containers.unshift(text);
98+
}
99+
else if (name.kind === SyntaxKind.ComputedPropertyName) {
100+
return tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ true);
101+
}
102+
else {
103+
// Don't know how to add this.
104+
return false;
146105
}
147106
}
107+
}
148108

149-
// Now, walk up our containers, adding all their names to the container array.
150-
declaration = getContainerNode(declaration);
109+
return true;
110+
}
151111

152-
while (declaration) {
153-
if (!tryAddSingleDeclarationName(declaration, containers)) {
154-
return undefined;
155-
}
112+
// Only added the names of computed properties if they're simple dotted expressions, like:
113+
//
114+
// [X.Y.Z]() { }
115+
function tryAddComputedPropertyName(expression: Expression, containers: string[], includeLastPortion: boolean): boolean {
116+
const text = getTextOfIdentifierOrLiteral(expression as LiteralExpression);
117+
if (text !== undefined) {
118+
if (includeLastPortion) {
119+
containers.unshift(text);
120+
}
121+
return true;
122+
}
156123

157-
declaration = getContainerNode(declaration);
124+
if (expression.kind === SyntaxKind.PropertyAccessExpression) {
125+
const propertyAccess = <PropertyAccessExpression>expression;
126+
if (includeLastPortion) {
127+
containers.unshift(propertyAccess.name.text);
158128
}
159129

160-
return containers;
130+
return tryAddComputedPropertyName(propertyAccess.expression, containers, /*includeLastPortion*/ true);
161131
}
162132

163-
function bestMatchKind(matches: PatternMatch[]) {
164-
Debug.assert(matches.length > 0);
165-
let bestMatchKind = PatternMatchKind.camelCase;
133+
return false;
134+
}
166135

167-
for (const match of matches) {
168-
const kind = match.kind;
169-
if (kind < bestMatchKind) {
170-
bestMatchKind = kind;
171-
}
172-
}
136+
function getContainers(declaration: Declaration): string[] {
137+
const containers: string[] = [];
173138

174-
return bestMatchKind;
139+
// First, if we started with a computed property name, then add all but the last
140+
// portion into the container array.
141+
const name = getNameOfDeclaration(declaration);
142+
if (name.kind === SyntaxKind.ComputedPropertyName) {
143+
if (!tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ false)) {
144+
return undefined;
145+
}
175146
}
176147

177-
function compareNavigateToItems(i1: RawNavigateToItem, i2: RawNavigateToItem) {
178-
// TODO(cyrusn): get the gamut of comparisons that VS already uses here.
179-
// Right now we just sort by kind first, and then by name of the item.
180-
// We first sort case insensitively. So "Aaa" will come before "bar".
181-
// Then we sort case sensitively, so "aaa" will come before "Aaa".
182-
return i1.matchKind - i2.matchKind ||
183-
ts.compareStringsCaseInsensitive(i1.name, i2.name) ||
184-
ts.compareStrings(i1.name, i2.name);
148+
// Now, walk up our containers, adding all their names to the container array.
149+
declaration = getContainerNode(declaration);
150+
151+
while (declaration) {
152+
if (!tryAddSingleDeclarationName(declaration, containers)) {
153+
return undefined;
154+
}
155+
156+
declaration = getContainerNode(declaration);
185157
}
186158

187-
function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem {
188-
const declaration = rawItem.declaration;
189-
const container = <Declaration>getContainerNode(declaration);
190-
const containerName = container && getNameOfDeclaration(container);
191-
return {
192-
name: rawItem.name,
193-
kind: getNodeKind(declaration),
194-
kindModifiers: getNodeModifiers(declaration),
195-
matchKind: PatternMatchKind[rawItem.matchKind],
196-
isCaseSensitive: rawItem.isCaseSensitive,
197-
fileName: rawItem.fileName,
198-
textSpan: createTextSpanFromNode(declaration),
199-
// TODO(jfreeman): What should be the containerName when the container has a computed name?
200-
containerName: containerName ? (<Identifier>containerName).text : "",
201-
containerKind: containerName ? getNodeKind(container) : ScriptElementKind.unknown
202-
};
159+
return containers;
160+
}
161+
162+
function bestMatchKind(matches: ReadonlyArray<PatternMatch>): PatternMatchKind {
163+
Debug.assert(matches.length > 0);
164+
let bestMatchKind = PatternMatchKind.camelCase;
165+
166+
for (const match of matches) {
167+
const kind = match.kind;
168+
if (kind < bestMatchKind) {
169+
bestMatchKind = kind;
170+
}
203171
}
172+
173+
return bestMatchKind;
174+
}
175+
176+
function compareNavigateToItems(i1: RawNavigateToItem, i2: RawNavigateToItem): number {
177+
// TODO(cyrusn): get the gamut of comparisons that VS already uses here.
178+
// Right now we just sort by kind first, and then by name of the item.
179+
// We first sort case insensitively. So "Aaa" will come before "bar".
180+
// Then we sort case sensitively, so "aaa" will come before "Aaa".
181+
return i1.matchKind - i2.matchKind ||
182+
ts.compareStringsCaseInsensitive(i1.name, i2.name) ||
183+
ts.compareStrings(i1.name, i2.name);
184+
}
185+
186+
function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem {
187+
const declaration = rawItem.declaration;
188+
const container = <Declaration>getContainerNode(declaration);
189+
const containerName = container && getNameOfDeclaration(container);
190+
return {
191+
name: rawItem.name,
192+
kind: getNodeKind(declaration),
193+
kindModifiers: getNodeModifiers(declaration),
194+
matchKind: PatternMatchKind[rawItem.matchKind],
195+
isCaseSensitive: rawItem.isCaseSensitive,
196+
fileName: rawItem.fileName,
197+
textSpan: createTextSpanFromNode(declaration),
198+
// TODO(jfreeman): What should be the containerName when the container has a computed name?
199+
containerName: containerName ? (<Identifier>containerName).text : "",
200+
containerKind: containerName ? getNodeKind(container) : ScriptElementKind.unknown
201+
};
204202
}
205203
}

src/services/patternMatcher.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ namespace ts {
4747
// Fully checks a candidate, with an dotted container, against the search pattern.
4848
// The candidate must match the last part of the search pattern, and the dotted container
4949
// must match the preceding segments of the pattern.
50-
getMatches(candidateContainers: string[], candidate: string): PatternMatch[];
50+
getMatches(candidateContainers: string[], candidate: string): PatternMatch[] | undefined;
5151

5252
// Whether or not the pattern contained dots or not. Clients can use this to determine
5353
// If they should call getMatches, or if getMatchesForLastSegmentOfPattern is sufficient.
@@ -139,7 +139,7 @@ namespace ts {
139139
return matchSegment(candidate, lastOrUndefined(dotSeparatedSegments));
140140
}
141141

142-
function getMatches(candidateContainers: string[], candidate: string): PatternMatch[] {
142+
function getMatches(candidateContainers: string[], candidate: string): PatternMatch[] | undefined {
143143
if (skipMatch(candidate)) {
144144
return undefined;
145145
}

0 commit comments

Comments
 (0)