Skip to content

Commit 07e3c7f

Browse files
committed
Merge pull request #677 from Microsoft/navigateTo
Add implementation for getNavigateToItems for the new compiler
2 parents fdc9b9d + 50a4926 commit 07e3c7f

12 files changed

+200
-35
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5211,8 +5211,7 @@ module ts {
52115211
var otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor;
52125212
var otherAccessor = <AccessorDeclaration>getDeclarationOfKind(node.symbol, otherKind);
52135213
if (otherAccessor) {
5214-
var visibilityFlags = NodeFlags.Private | NodeFlags.Public;
5215-
if (((node.flags & visibilityFlags) !== (otherAccessor.flags & visibilityFlags))) {
5214+
if (((node.flags & NodeFlags.AccessibilityModifier) !== (otherAccessor.flags & NodeFlags.AccessibilityModifier))) {
52165215
error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility);
52175216
}
52185217

src/compiler/emitter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,7 +1431,7 @@ module ts {
14311431

14321432
function emitParameterPropertyAssignments(node: ConstructorDeclaration) {
14331433
forEach(node.parameters, param => {
1434-
if (param.flags & (NodeFlags.Public | NodeFlags.Private)) {
1434+
if (param.flags & NodeFlags.AccessibilityModifier) {
14351435
writeLine();
14361436
emitStart(param);
14371437
emitStart(param.name);
@@ -2630,7 +2630,7 @@ module ts {
26302630
function emitParameterProperties(constructorDeclaration: ConstructorDeclaration) {
26312631
if (constructorDeclaration) {
26322632
forEach(constructorDeclaration.parameters, param => {
2633-
if (param.flags & (NodeFlags.Public | NodeFlags.Private)) {
2633+
if (param.flags & NodeFlags.AccessibilityModifier) {
26342634
emitPropertyDeclaration(param);
26352635
}
26362636
});

src/compiler/parser.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3239,7 +3239,7 @@ module ts {
32393239

32403240
switch (modifierToken) {
32413241
case SyntaxKind.PublicKeyword:
3242-
if (flags & NodeFlags.Private || flags & NodeFlags.Public) {
3242+
if (flags & NodeFlags.AccessibilityModifier) {
32433243
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics.Accessibility_modifier_already_seen);
32443244
}
32453245
else if (flags & NodeFlags.Static) {
@@ -3252,7 +3252,7 @@ module ts {
32523252
break;
32533253

32543254
case SyntaxKind.PrivateKeyword:
3255-
if (flags & NodeFlags.Private || flags & NodeFlags.Public) {
3255+
if (flags & NodeFlags.AccessibilityModifier) {
32563256
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics.Accessibility_modifier_already_seen);
32573257
}
32583258
else if (flags & NodeFlags.Static) {

src/compiler/types.ts

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

242-
Modifier = Export | Ambient | Public | Private | Static
242+
Modifier = Export | Ambient | Public | Private | Static,
243+
AccessibilityModifier = Public | Private
243244
}
244245

245246
export interface Node extends TextRange {

src/services/services.ts

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

@@ -327,6 +328,7 @@ module ts {
327328

328329
private syntaxTree: TypeScript.SyntaxTree;
329330
private scriptSnapshot: TypeScript.IScriptSnapshot;
331+
private namedDeclarations: Declaration[];
330332

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

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

853-
export class MatchKind {
854-
static none: string = null;
855-
static exact = "exact";
856-
static subString = "substring";
857-
static prefix = "prefix";
908+
enum MatchKind {
909+
none = 0,
910+
exact = 1,
911+
substring = 2,
912+
prefix = 3
858913
}
859914

860915
interface IncrementalParse {
@@ -2035,6 +2090,29 @@ module ts {
20352090
return ScriptElementKind.unknown;
20362091
}
20372092

2093+
function getNodeKind(node: Node): string {
2094+
switch (node.kind) {
2095+
case SyntaxKind.ModuleDeclaration: return ScriptElementKind.moduleElement;
2096+
case SyntaxKind.ClassDeclaration: return ScriptElementKind.classElement;
2097+
case SyntaxKind.InterfaceDeclaration: return ScriptElementKind.interfaceElement;
2098+
case SyntaxKind.EnumDeclaration: return ScriptElementKind.enumElement;
2099+
case SyntaxKind.VariableDeclaration: return ScriptElementKind.variableElement;
2100+
case SyntaxKind.FunctionDeclaration: return ScriptElementKind.functionElement;
2101+
case SyntaxKind.GetAccessor: return ScriptElementKind.memberGetAccessorElement;
2102+
case SyntaxKind.SetAccessor: return ScriptElementKind.memberSetAccessorElement;
2103+
case SyntaxKind.Method: return ScriptElementKind.memberFunctionElement;
2104+
case SyntaxKind.Property: return ScriptElementKind.memberVariableElement;
2105+
case SyntaxKind.IndexSignature: return ScriptElementKind.indexSignatureElement;
2106+
case SyntaxKind.ConstructSignature: return ScriptElementKind.constructSignatureElement;
2107+
case SyntaxKind.CallSignature: return ScriptElementKind.callSignatureElement;
2108+
case SyntaxKind.Constructor: return ScriptElementKind.constructorImplementationElement;
2109+
case SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement;
2110+
case SyntaxKind.EnumMember: return ScriptElementKind.variableElement;
2111+
case SyntaxKind.Parameter: return (node.flags & NodeFlags.AccessibilityModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement;
2112+
return ScriptElementKind.unknown;
2113+
}
2114+
}
2115+
20382116
function getNodeModifiers(node: Node): string {
20392117
var flags = node.flags;
20402118
var result: string[] = [];
@@ -2186,6 +2264,7 @@ module ts {
21862264
return result;
21872265
}
21882266

2267+
/// References and Occurances
21892268
function getOccurrencesAtPosition(filename: string, position: number): ReferenceEntry[] {
21902269
synchronizeHostData();
21912270

@@ -2732,9 +2811,10 @@ module ts {
27322811
return false;
27332812
}
27342813

2735-
/// Search within node "container" for references for a search value, where the search value is defined as a
2736-
/// tuple of(searchSymbol, searchText, searchLocation, and searchMeaning).
2737-
/// searchLocation: a node where the search value
2814+
/** Search within node "container" for references for a search value, where the search value is defined as a
2815+
* tuple of(searchSymbol, searchText, searchLocation, and searchMeaning).
2816+
* searchLocation: a node where the search value
2817+
*/
27382818
function getReferencesInNode(container: Node, searchSymbol: Symbol, searchText: string, searchLocation: Node, searchMeaning: SearchMeaning, result: ReferenceEntry[]): void {
27392819
var sourceFile = container.getSourceFile();
27402820

@@ -3100,12 +3180,13 @@ module ts {
31003180
}
31013181
}
31023182

3103-
/// Given an initial searchMeaning, extracted from a location, widen the search scope based on the declarations
3104-
/// of the corresponding symbol. e.g. if we are searching for "Foo" in value position, but "Foo" references a class
3105-
/// then we need to widen the search to include type positions as well.
3106-
/// On the contrary, if we are searching for "Bar" in type position and we trace bar to an interface, and an uninstantiated
3107-
/// module, we want to keep the search limited to only types, as the two declarations (interface and uninstantiated module)
3108-
/// do not intersect in any of the three spaces.
3183+
/** Given an initial searchMeaning, extracted from a location, widen the search scope based on the declarations
3184+
* of the corresponding symbol. e.g. if we are searching for "Foo" in value position, but "Foo" references a class
3185+
* then we need to widen the search to include type positions as well.
3186+
* On the contrary, if we are searching for "Bar" in type position and we trace bar to an interface, and an uninstantiated
3187+
* module, we want to keep the search limited to only types, as the two declarations (interface and uninstantiated module)
3188+
* do not intersect in any of the three spaces.
3189+
*/
31093190
function getIntersectingMeaningFromDeclarations(meaning: SearchMeaning, declarations: Declaration[]): SearchMeaning {
31103191
if (declarations) {
31113192
do {
@@ -3142,7 +3223,7 @@ module ts {
31423223
return new ReferenceEntry(node.getSourceFile().filename, TypeScript.TextSpan.fromBounds(start, end), isWriteAccess(node));
31433224
}
31443225

3145-
/// A node is considered a writeAccess iff it is a name of a declaration or a target of an assignment
3226+
/** A node is considered a writeAccess iff it is a name of a declaration or a target of an assignment */
31463227
function isWriteAccess(node: Node): boolean {
31473228
if (node.kind === SyntaxKind.Identifier && isDeclarationOrFunctionExpressionOrCatchVariableName(node)) {
31483229
return true;
@@ -3162,6 +3243,90 @@ module ts {
31623243
return false;
31633244
}
31643245

3246+
/// NavigateTo
3247+
function getNavigateToItems(searchValue: string): NavigateToItem[] {
3248+
synchronizeHostData();
3249+
3250+
// Split search value in terms array
3251+
var terms = searchValue.split(" ");
3252+
3253+
// default NavigateTo approach: if search term contains only lower-case chars - use case-insensitive search, otherwise switch to case-sensitive version
3254+
var searchTerms = map(terms, t => ({ caseSensitive: hasAnyUpperCaseCharacter(t), term: t }));
3255+
3256+
var items: NavigateToItem[] = [];
3257+
3258+
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
3259+
forEach(program.getSourceFiles(), sourceFile => {
3260+
cancellationToken.throwIfCancellationRequested();
3261+
3262+
var filename = sourceFile.filename;
3263+
var declarations = sourceFile.getNamedDeclarations();
3264+
for (var i = 0, n = declarations.length; i < n; i++) {
3265+
var declaration = declarations[i];
3266+
var name = declaration.name.text;
3267+
var matchKind = getMatchKind(searchTerms, name);
3268+
if (matchKind !== MatchKind.none) {
3269+
var container = <Declaration>getContainerNode(declaration);
3270+
items.push({
3271+
name: name,
3272+
kind: getNodeKind(declaration),
3273+
kindModifiers: getNodeModifiers(declaration),
3274+
matchKind: MatchKind[matchKind],
3275+
fileName: filename,
3276+
textSpan: TypeScript.TextSpan.fromBounds(declaration.getStart(), declaration.getEnd()),
3277+
containerName: container.name ? container.name.text : "",
3278+
containerKind: container.name ? getNodeKind(container) : ""
3279+
});
3280+
}
3281+
}
3282+
});
3283+
3284+
return items;
3285+
3286+
function hasAnyUpperCaseCharacter(s: string): boolean {
3287+
for (var i = 0, n = s.length; i < n; i++) {
3288+
var c = s.charCodeAt(i);
3289+
if ((CharacterCodes.A <= c && c <= CharacterCodes.Z) ||
3290+
(c >= CharacterCodes.maxAsciiCharacter && s.charAt(i).toLocaleLowerCase() !== s.charAt(i))) {
3291+
return true;
3292+
}
3293+
}
3294+
3295+
return false;
3296+
}
3297+
3298+
function getMatchKind(searchTerms: { caseSensitive: boolean; term: string }[], name: string): MatchKind {
3299+
var matchKind = MatchKind.none;
3300+
3301+
if (name) {
3302+
for (var j = 0, n = searchTerms.length; j < n; j++) {
3303+
var searchTerm = searchTerms[j];
3304+
var nameToSearch = searchTerm.caseSensitive ? name : name.toLocaleLowerCase();
3305+
// in case of case-insensitive search searchTerm.term will already be lower-cased
3306+
var index = nameToSearch.indexOf(searchTerm.term);
3307+
if (index < 0) {
3308+
// Didn't match.
3309+
return MatchKind.none;
3310+
}
3311+
3312+
var termKind = MatchKind.substring;
3313+
if (index === 0) {
3314+
// here we know that match occur at the beginning of the string.
3315+
// 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
3316+
termKind = name.length === searchTerm.term.length ? MatchKind.exact : MatchKind.prefix;
3317+
}
3318+
3319+
// Update our match kind if we don't have one, or if this match is better.
3320+
if (matchKind === MatchKind.none || termKind < matchKind) {
3321+
matchKind = termKind;
3322+
}
3323+
}
3324+
}
3325+
3326+
return matchKind;
3327+
}
3328+
}
3329+
31653330
/// Syntactic features
31663331
function getSyntaxTree(filename: string): TypeScript.SyntaxTree {
31673332
filename = TypeScript.switchToForwardSlashes(filename);
@@ -3692,7 +3857,7 @@ module ts {
36923857
getImplementorsAtPosition: (filename, position) => [],
36933858
getNameOrDottedNameSpan: getNameOrDottedNameSpan,
36943859
getBreakpointStatementAtPosition: getBreakpointStatementAtPosition,
3695-
getNavigateToItems: (searchValue) => [],
3860+
getNavigateToItems: getNavigateToItems,
36963861
getRenameInfo: (fileName, position): RenameInfo => RenameInfo.CreateError(getLocaleSpecificMessage(Diagnostics.You_cannot_rename_this_element.key)),
36973862
getNavigationBarItems: getNavigationBarItems,
36983863
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");

0 commit comments

Comments
 (0)