Skip to content

Commit 1dc7ab0

Browse files
committed
Add implementation of getNavigateToItems based on the new compiler
1 parent 11b9118 commit 1dc7ab0

9 files changed

+193
-29
lines changed

src/compiler/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,8 @@ module ts {
237237
Synthetic = 0x00000100, // Synthetic node (for full fidelity)
238238
DeclarationFile = 0x00000200, // Node is a .d.ts file
239239

240-
Modifier = Export | Ambient | Public | Private | Static
240+
Modifier = Export | Ambient | Public | Private | Static,
241+
AccessibilityModifier = Public | Private
241242
}
242243

243244
export interface Node extends TextRange {

src/services/services.ts

Lines changed: 179 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ module ts {
7070
getSourceUnit(): TypeScript.SourceUnitSyntax;
7171
getSyntaxTree(): TypeScript.SyntaxTree;
7272
getScriptSnapshot(): TypeScript.IScriptSnapshot;
73+
getNamedDeclarations(): Declaration[];
7374
update(scriptSnapshot: TypeScript.IScriptSnapshot, version: string, isOpen: boolean, textChangeRange: TypeScript.TextChangeRange): SourceFile;
7475
}
7576

@@ -326,6 +327,7 @@ module ts {
326327

327328
private syntaxTree: TypeScript.SyntaxTree;
328329
private scriptSnapshot: TypeScript.IScriptSnapshot;
330+
private namedDeclarations: Declaration[];
329331

330332
public getSourceUnit(): TypeScript.SourceUnitSyntax {
331333
// If we don't have a script, create one from our parse tree.
@@ -340,6 +342,59 @@ module ts {
340342
return this.getSyntaxTree().lineMap();
341343
}
342344

345+
public getNamedDeclarations() {
346+
if (!this.namedDeclarations) {
347+
var sourceFile = this;
348+
var namedDeclarations: Declaration[] = [];
349+
var isExternalModule = ts.isExternalModule(sourceFile);
350+
351+
forEachChild(sourceFile, function visit(node: Node): boolean {
352+
switch (node.kind) {
353+
case SyntaxKind.ClassDeclaration:
354+
case SyntaxKind.InterfaceDeclaration:
355+
case SyntaxKind.EnumDeclaration:
356+
case SyntaxKind.ModuleDeclaration:
357+
case SyntaxKind.ImportDeclaration:
358+
case SyntaxKind.Method:
359+
case SyntaxKind.FunctionDeclaration:
360+
case SyntaxKind.Constructor:
361+
case SyntaxKind.GetAccessor:
362+
case SyntaxKind.SetAccessor:
363+
case SyntaxKind.TypeLiteral:
364+
if ((<Declaration>node).name) {
365+
namedDeclarations.push(<Declaration>node);
366+
}
367+
forEachChild(node, visit);
368+
break;
369+
370+
case SyntaxKind.VariableStatement:
371+
case SyntaxKind.ModuleBlock:
372+
case SyntaxKind.FunctionBlock:
373+
forEachChild(node, visit);
374+
break;
375+
376+
case SyntaxKind.Parameter:
377+
if (!(node.flags & NodeFlags.AccessibilityModifier)) {
378+
// Only consider properties defined as constructor parameters
379+
break;
380+
}
381+
case SyntaxKind.VariableDeclaration:
382+
case SyntaxKind.EnumMember:
383+
case SyntaxKind.Property:
384+
namedDeclarations.push(<Declaration>node);
385+
break;
386+
}
387+
388+
// do not go any deeper
389+
return undefined;
390+
});
391+
392+
this.namedDeclarations = namedDeclarations;
393+
}
394+
395+
return this.namedDeclarations;
396+
}
397+
343398
public getSyntaxTree(): TypeScript.SyntaxTree {
344399
if (!this.syntaxTree) {
345400
var start = new Date().getTime();
@@ -820,11 +875,11 @@ module ts {
820875
static staticModifier = "static";
821876
}
822877

823-
export class MatchKind {
824-
static none: string = null;
825-
static exact = "exact";
826-
static subString = "substring";
827-
static prefix = "prefix";
878+
enum MatchKind {
879+
none = 0,
880+
exact = 1,
881+
substring = 2,
882+
prefix = 3
828883
}
829884

830885
interface IncrementalParse {
@@ -1983,6 +2038,29 @@ module ts {
19832038
return ScriptElementKind.unknown;
19842039
}
19852040

2041+
function getNodeKind(node: Node): string {
2042+
switch (node.kind) {
2043+
case SyntaxKind.ModuleDeclaration: return ScriptElementKind.moduleElement;
2044+
case SyntaxKind.ClassDeclaration: return ScriptElementKind.classElement;
2045+
case SyntaxKind.InterfaceDeclaration: return ScriptElementKind.interfaceElement;
2046+
case SyntaxKind.EnumDeclaration: return ScriptElementKind.enumElement;
2047+
case SyntaxKind.VariableDeclaration: return ScriptElementKind.variableElement;
2048+
case SyntaxKind.FunctionDeclaration: return ScriptElementKind.functionElement;
2049+
case SyntaxKind.GetAccessor: return ScriptElementKind.memberGetAccessorElement;
2050+
case SyntaxKind.SetAccessor: return ScriptElementKind.memberSetAccessorElement;
2051+
case SyntaxKind.Method: return ScriptElementKind.memberFunctionElement;
2052+
case SyntaxKind.Property: return ScriptElementKind.memberVariableElement;
2053+
case SyntaxKind.IndexSignature: return ScriptElementKind.indexSignatureElement;
2054+
case SyntaxKind.ConstructSignature: return ScriptElementKind.constructSignatureElement;
2055+
case SyntaxKind.CallSignature: return ScriptElementKind.callSignatureElement;
2056+
case SyntaxKind.Constructor: return ScriptElementKind.constructorImplementationElement;
2057+
case SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement;
2058+
case SyntaxKind.EnumMember: return ScriptElementKind.variableElement;
2059+
case SyntaxKind.Parameter: return (node.flags & NodeFlags.AccessibilityModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement;
2060+
return ScriptElementKind.unknown;
2061+
}
2062+
}
2063+
19862064
function getNodeModifiers(node: Node): string {
19872065
var flags = node.flags;
19882066
var result: string[] = [];
@@ -2134,6 +2212,7 @@ module ts {
21342212
return result;
21352213
}
21362214

2215+
/// References and Occurances
21372216
function getOccurrencesAtPosition(filename: string, position: number): ReferenceEntry[] {
21382217
synchronizeHostData();
21392218

@@ -2612,9 +2691,10 @@ module ts {
26122691
return false;
26132692
}
26142693

2615-
/// Search within node "container" for references for a search value, where the search value is defined as a
2616-
/// tuple of(searchSymbol, searchText, searchLocation, and searchMeaning).
2617-
/// searchLocation: a node where the search value
2694+
/** Search within node "container" for references for a search value, where the search value is defined as a
2695+
* tuple of(searchSymbol, searchText, searchLocation, and searchMeaning).
2696+
* searchLocation: a node where the search value
2697+
*/
26182698
function getReferencesInNode(container: Node, searchSymbol: Symbol, searchText: string, searchLocation: Node, searchMeaning: SearchMeaning, result: ReferenceEntry[]): void {
26192699
var sourceFile = container.getSourceFile();
26202700

@@ -2980,12 +3060,13 @@ module ts {
29803060
}
29813061
}
29823062

2983-
/// Given an initial searchMeaning, extracted from a location, widen the search scope based on the declarations
2984-
/// of the corresponding symbol. e.g. if we are searching for "Foo" in value position, but "Foo" references a class
2985-
/// then we need to widen the search to include type positions as well.
2986-
/// On the contrary, if we are searching for "Bar" in type position and we trace bar to an interface, and an uninstantiated
2987-
/// module, we want to keep the search limited to only types, as the two declarations (interface and uninstantiated module)
2988-
/// do not intersect in any of the three spaces.
3063+
/** Given an initial searchMeaning, extracted from a location, widen the search scope based on the declarations
3064+
* of the corresponding symbol. e.g. if we are searching for "Foo" in value position, but "Foo" references a class
3065+
* then we need to widen the search to include type positions as well.
3066+
* On the contrary, if we are searching for "Bar" in type position and we trace bar to an interface, and an uninstantiated
3067+
* module, we want to keep the search limited to only types, as the two declarations (interface and uninstantiated module)
3068+
* do not intersect in any of the three spaces.
3069+
*/
29893070
function getIntersectingMeaningFromDeclarations(meaning: SearchMeaning, declarations: Declaration[]): SearchMeaning {
29903071
if (declarations) {
29913072
do {
@@ -3022,7 +3103,7 @@ module ts {
30223103
return new ReferenceEntry(node.getSourceFile().filename, TypeScript.TextSpan.fromBounds(start, end), isWriteAccess(node));
30233104
}
30243105

3025-
/// A node is considedered a writeAccess iff it is a name of a declaration or a target of an assignment
3106+
/** A node is considedered a writeAccess iff it is a name of a declaration or a target of an assignment */
30263107
function isWriteAccess(node: Node): boolean {
30273108
if (node.kind === SyntaxKind.Identifier && isDeclarationOrFunctionExpressionOrCatchVariableName(node)) {
30283109
return true;
@@ -3042,6 +3123,88 @@ module ts {
30423123
return false;
30433124
}
30443125

3126+
/// NavigateTo
3127+
function getNavigateToItems(searchValue: string): NavigateToItem[] {
3128+
synchronizeHostData();
3129+
3130+
// Split search value in terms array
3131+
var terms = searchValue.split(" ");
3132+
3133+
// default NavigateTo approach: if search term contains only lower-case chars - use case-insensitive search, otherwise switch to case-sensitive version
3134+
var searchTerms = map(terms, t => ({ caseSensitive: hasAnyUpperCaseCharacter(t), term: t }));
3135+
3136+
var items: NavigateToItem[] = [];
3137+
3138+
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
3139+
forEach(program.getSourceFiles(), sourceFile => {
3140+
cancellationToken.throwIfCancellationRequested();
3141+
3142+
var filename = sourceFile.filename;
3143+
var declarations = sourceFile.getNamedDeclarations();
3144+
for (var i = 0, n = declarations.length; i < n; i++) {
3145+
var declaration = declarations[i];
3146+
var name = declaration.name.text;
3147+
var matchKind = getMatchKind(searchTerms, name);
3148+
if (matchKind !== MatchKind.none) {
3149+
var container = <Declaration>getContainerNode(declaration);
3150+
items.push({
3151+
name: name,
3152+
kind: getNodeKind(declaration),
3153+
kindModifiers: getNodeModifiers(declaration),
3154+
matchKind: MatchKind[matchKind],
3155+
fileName: filename,
3156+
textSpan: TypeScript.TextSpan.fromBounds(declaration.getStart(), declaration.getEnd()),
3157+
containerName: container.name ? container.name.text : "",
3158+
containerKind: container.name ? getNodeKind(container) : ""
3159+
});
3160+
}
3161+
}
3162+
});
3163+
3164+
return items;
3165+
3166+
function hasAnyUpperCaseCharacter(s: string): boolean {
3167+
for (var i = 0; i < s.length; ++i) {
3168+
if (s.charAt(i).toLocaleLowerCase() !== s.charAt(i)) {
3169+
return true;
3170+
}
3171+
}
3172+
3173+
return false;
3174+
}
3175+
3176+
function getMatchKind(searchTerms: { caseSensitive: boolean; term: string }[], name: string): MatchKind {
3177+
var matchKind = MatchKind.none;
3178+
3179+
if (name) {
3180+
for (var j = 0, n = searchTerms.length; j < n; j++) {
3181+
var searchTerm = searchTerms[j];
3182+
var nameToSearch = searchTerm.caseSensitive ? name : name.toLocaleLowerCase();
3183+
// in case of case-insensitive search searchTerm.term will already be lower-cased
3184+
var index = nameToSearch.indexOf(searchTerm.term);
3185+
if (index < 0) {
3186+
// Didn't match.
3187+
return MatchKind.none;
3188+
}
3189+
3190+
var termKind = MatchKind.substring;
3191+
if (index === 0) {
3192+
// here we know that match occur at the beginning of the string.
3193+
// if search term and declName has the same length - we have an exact match, otherwise declName have longer length and this will be prefix match
3194+
termKind = name.length === searchTerm.term.length ? MatchKind.exact : MatchKind.prefix;
3195+
}
3196+
3197+
// Update our match kind if we don't have one, or if this match is better.
3198+
if (matchKind === MatchKind.none || termKind < matchKind) {
3199+
matchKind = termKind;
3200+
}
3201+
}
3202+
}
3203+
3204+
return matchKind;
3205+
}
3206+
}
3207+
30453208
/// Syntactic features
30463209
function getSyntaxTree(filename: string): TypeScript.SyntaxTree {
30473210
filename = TypeScript.switchToForwardSlashes(filename);
@@ -3382,7 +3545,7 @@ module ts {
33823545
getImplementorsAtPosition: (filename, position) => [],
33833546
getNameOrDottedNameSpan: getNameOrDottedNameSpan,
33843547
getBreakpointStatementAtPosition: getBreakpointStatementAtPosition,
3385-
getNavigateToItems: (searchValue) => [],
3548+
getNavigateToItems: getNavigateToItems,
33863549
getRenameInfo: (fileName, position): RenameInfo => RenameInfo.CreateError(getLocaleSpecificMessage(Diagnostics.You_cannot_rename_this_element.key)),
33873550
getNavigationBarItems: getNavigationBarItems,
33883551
getOutliningSpans: getOutliningSpans,

tests/cases/fourslash_old/navigationItemsExactMatch.ts renamed to tests/cases/fourslash/navigationItemsExactMatch.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@
66
//// // Class
77
//// {| "itemName": "Point", "kind": "class", "parentName": "Shapes" |}export class Point {
88
//// // Instance member
9-
//// {| "itemName": "origin", "kind": "property", "parentName": "Shapes.Point", "matchKind": "exact"|}private origin = 0.0;
9+
//// {| "itemName": "origin", "kind": "property", "parentName": "Point", "matchKind": "exact"|}private origin = 0.0;
1010
////
11-
//// {| "itemName": "distFromZero", "kind": "property", "parentName": "Shapes.Point", "matchKind": "exact"|}private distFromZero = 0.0;
11+
//// {| "itemName": "distFromZero", "kind": "property", "parentName": "Point", "matchKind": "exact"|}private distFromZero = 0.0;
1212
////
1313
//// // Getter
14-
//// {| "itemName": "distance", "kind": "getter", "parentName": "Shapes.Point", "matchKind": "exact" |}get distance(): number { return 0; }
14+
//// {| "itemName": "distance", "kind": "getter", "parentName": "Point", "matchKind": "exact" |}get distance(): number { return 0; }
1515
//// }
1616
////}
1717
////
1818
////// Local variables
19-
////{| "itemName": "point", "kind": "var", "parentName": "", "matchKind": "exact" |}var point = new Shapes.Point();
19+
////{| "itemName": "point", "kind": "var", "parentName": "", "matchKind": "exact"|}var point = new Shapes.Point();
2020

2121
//// Testing for exact matching of navigationItems
2222

tests/cases/fourslash_old/navigationItemsExactMatch2.ts renamed to tests/cases/fourslash/navigationItemsExactMatch2.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
goTo.marker("file1");
2222
verify.navigationItemsListCount(2, "point", "exact");
23-
verify.navigationItemsListCount(3, "distance", "prefix");
23+
verify.navigationItemsListCount(5, "distance", "prefix");
2424
verify.navigationItemsListCount(1, "origin", "substring");
2525

2626
verify.navigationItemsListCount(0, "square", "exact");

tests/cases/fourslash_old/navigationItemsPrefixMatch.ts renamed to tests/cases/fourslash/navigationItemsPrefixMatch.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
//// // Class
77
//// {| "itemName": "Point", "kind": "class", "parentName": "Shapes" |}export class Point {
88
//// // Instance member
9-
//// {| "itemName": "originality", "kind": "property", "parentName": "Shapes.Point", "matchKind": "prefix"|}private originality = 0.0;
9+
//// {| "itemName": "originality", "kind": "property", "parentName": "Point", "matchKind": "prefix"|}private originality = 0.0;
1010
////
11-
//// {| "itemName": "distanceFromOrig", "kind": "property", "parentName": "Shapes.Point", "matchKind": "prefix"|}private distanceFromOrig = 0.0;
11+
//// {| "itemName": "distanceFromOrig", "kind": "property", "parentName": "Point", "matchKind": "prefix"|}private distanceFromOrig = 0.0;
1212
////
1313
//// // Getter
14-
//// {| "itemName": "distanceFarFarAway", "kind": "getter", "parentName": "Shapes.Point", "matchKind": "prefix" |}get distanceFarFarAway(): number { return 0; }
14+
//// {| "itemName": "distanceFarFarAway", "kind": "getter", "parentName": "Point", "matchKind": "prefix" |}get distanceFarFarAway(): number { return 0; }
1515
//// }
1616
////}
1717
////

tests/cases/fourslash_old/navigationItemsPrefixMatch2.ts renamed to tests/cases/fourslash/navigationItemsPrefixMatch2.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ var notFoundSearchValue = "mPointThatIJustInitiated wrongKeyWord";
2525

2626
goTo.marker("file1");
2727
verify.navigationItemsListCount(3, "origin", "prefix");
28-
verify.navigationItemsListCount(2, "distance", "prefix");
28+
verify.navigationItemsListCount(3, "distance", "prefix");
2929

3030
verify.navigationItemsListCount(0, notFoundSearchValue, "exact");
3131
verify.navigationItemsListCount(0, notFoundSearchValue, "prefix");

tests/cases/fourslash_old/navigationItemsSubStringMatch.ts renamed to tests/cases/fourslash/navigationItemsSubStringMatch.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
//// // Class
77
//// {| "itemName": "Point", "kind": "class", "parentName": "Shapes", "matchKind": "substring" |}export class Point {
88
//// // Instance member
9-
//// {| "itemName": "originPointAttheHorizon", "kind": "property", "parentName": "Shapes.Point", "matchKind": "substring"|}private originPointAttheHorizon = 0.0;
9+
//// {| "itemName": "originPointAttheHorizon", "kind": "property", "parentName": "Point", "matchKind": "substring"|}private originPointAttheHorizon = 0.0;
1010
////
1111
//// // Getter
12-
//// {| "itemName": "distanceFromOrigin", "kind": "getter", "parentName": "Shapes.Point", "matchKind": "substring" |}get distanceFromOrigin(): number { return 0; }
12+
//// {| "itemName": "distanceFromOrigin", "kind": "getter", "parentName": "Point", "matchKind": "substring" |}get distanceFromOrigin(): number { return 0; }
1313
////
1414
//// }
1515
////}

tests/cases/fourslash_old/navigationItemsSubStringMatch2.ts renamed to tests/cases/fourslash/navigationItemsSubStringMatch2.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
////
1515
////var myPointThatIJustInitiated = new Shapes.Point();
1616
////interface IDistance{
17-
//// var INITIATED123;
17+
//// INITIATED123;
1818
//// public horizon(): void;
1919
////}
2020

0 commit comments

Comments
 (0)