Skip to content

Commit 5c1ef11

Browse files
Merge pull request microsoft#2084 from Microsoft/limitNavigateTo
Allow clients to limit the amount of data that navigate to returns.
2 parents 791e913 + 4b271d2 commit 5c1ef11

12 files changed

Lines changed: 186 additions & 161 deletions

Jakefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ var servicesSources = [
6565
return path.join(compilerDirectory, f);
6666
}).concat([
6767
"breakpoints.ts",
68+
"navigateTo.ts",
6869
"navigationBar.ts",
6970
"outliningElementsCollector.ts",
7071
"services.ts",

src/services/navigateTo.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
module ts.NavigateTo {
2+
type RawNavigateToItem = { name: string; fileName: string; matchKind: MatchKind; declaration: Declaration };
3+
4+
enum MatchKind {
5+
none = 0,
6+
exact = 1,
7+
substring = 2,
8+
prefix = 3
9+
}
10+
11+
export function getNavigateToItems(program: Program, cancellationToken: CancellationTokenObject, searchValue: string, maxResultCount: number): NavigateToItem[]{
12+
// Split search value in terms array
13+
var terms = searchValue.split(" ");
14+
15+
// default NavigateTo approach: if search term contains only lower-case chars - use case-insensitive search, otherwise switch to case-sensitive version
16+
var searchTerms = map(terms, t => ({ caseSensitive: hasAnyUpperCaseCharacter(t), term: t }));
17+
18+
var rawItems: RawNavigateToItem[] = [];
19+
20+
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
21+
forEach(program.getSourceFiles(), sourceFile => {
22+
cancellationToken.throwIfCancellationRequested();
23+
24+
var fileName = sourceFile.fileName;
25+
var declarations = sourceFile.getNamedDeclarations();
26+
for (var i = 0, n = declarations.length; i < n; i++) {
27+
var declaration = declarations[i];
28+
// TODO(jfreeman): Skip this declaration if it has a computed name
29+
var name = (<Identifier>declaration.name).text;
30+
var matchKind = getMatchKind(searchTerms, name);
31+
if (matchKind !== MatchKind.none) {
32+
rawItems.push({ name, fileName, matchKind, declaration });
33+
}
34+
}
35+
});
36+
37+
rawItems.sort((i1, i2) => i1.matchKind - i2.matchKind);
38+
if (maxResultCount !== undefined) {
39+
rawItems = rawItems.slice(0, maxResultCount);
40+
}
41+
42+
var items = map(rawItems, createNavigateToItem);
43+
44+
return items;
45+
46+
function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem {
47+
var declaration = rawItem.declaration;
48+
var container = <Declaration>getContainerNode(declaration);
49+
return {
50+
name: rawItem.name,
51+
kind: getNodeKind(declaration),
52+
kindModifiers: getNodeModifiers(declaration),
53+
matchKind: MatchKind[rawItem.matchKind],
54+
fileName: rawItem.fileName,
55+
textSpan: createTextSpanFromBounds(declaration.getStart(), declaration.getEnd()),
56+
// TODO(jfreeman): What should be the containerName when the container has a computed name?
57+
containerName: container && container.name ? (<Identifier>container.name).text : "",
58+
containerKind: container && container.name ? getNodeKind(container) : ""
59+
};
60+
}
61+
62+
function hasAnyUpperCaseCharacter(s: string): boolean {
63+
for (var i = 0, n = s.length; i < n; i++) {
64+
var c = s.charCodeAt(i);
65+
if ((CharacterCodes.A <= c && c <= CharacterCodes.Z) ||
66+
(c >= CharacterCodes.maxAsciiCharacter && s.charAt(i).toLocaleLowerCase() !== s.charAt(i))) {
67+
return true;
68+
}
69+
}
70+
71+
return false;
72+
}
73+
74+
function getMatchKind(searchTerms: { caseSensitive: boolean; term: string }[], name: string): MatchKind {
75+
var matchKind = MatchKind.none;
76+
77+
if (name) {
78+
for (var j = 0, n = searchTerms.length; j < n; j++) {
79+
var searchTerm = searchTerms[j];
80+
var nameToSearch = searchTerm.caseSensitive ? name : name.toLocaleLowerCase();
81+
// in case of case-insensitive search searchTerm.term will already be lower-cased
82+
var index = nameToSearch.indexOf(searchTerm.term);
83+
if (index < 0) {
84+
// Didn't match.
85+
return MatchKind.none;
86+
}
87+
88+
var termKind = MatchKind.substring;
89+
if (index === 0) {
90+
// here we know that match occur at the beginning of the string.
91+
// 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
92+
termKind = name.length === searchTerm.term.length ? MatchKind.exact : MatchKind.prefix;
93+
}
94+
95+
// Update our match kind if we don't have one, or if this match is better.
96+
if (matchKind === MatchKind.none || termKind < matchKind) {
97+
matchKind = termKind;
98+
}
99+
}
100+
}
101+
102+
return matchKind;
103+
}
104+
}
105+
}

src/services/services.ts

Lines changed: 60 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
/// <reference path='breakpoints.ts' />
44
/// <reference path='outliningElementsCollector.ts' />
5+
/// <reference path='navigateTo.ts' />
56
/// <reference path='navigationBar.ts' />
67
/// <reference path='signatureHelp.ts' />
78
/// <reference path='utilities.ts' />
@@ -900,7 +901,7 @@ module ts {
900901
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[];
901902
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[];
902903

903-
getNavigateToItems(searchValue: string): NavigateToItem[];
904+
getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
904905
getNavigationBarItems(fileName: string): NavigationBarItem[];
905906

906907
getOutliningSpans(fileName: string): OutliningSpan[];
@@ -1378,13 +1379,6 @@ module ts {
13781379
public static typeAlias = "type alias name";
13791380
}
13801381

1381-
enum MatchKind {
1382-
none = 0,
1383-
exact = 1,
1384-
substring = 2,
1385-
prefix = 3
1386-
}
1387-
13881382
/// Language Service
13891383

13901384
interface CompletionSession {
@@ -1992,6 +1986,62 @@ module ts {
19921986
});
19931987
}
19941988

1989+
/* @internal */ export function getContainerNode(node: Node): Node {
1990+
while (true) {
1991+
node = node.parent;
1992+
if (!node) {
1993+
return undefined;
1994+
}
1995+
switch (node.kind) {
1996+
case SyntaxKind.SourceFile:
1997+
case SyntaxKind.MethodDeclaration:
1998+
case SyntaxKind.MethodSignature:
1999+
case SyntaxKind.FunctionDeclaration:
2000+
case SyntaxKind.FunctionExpression:
2001+
case SyntaxKind.GetAccessor:
2002+
case SyntaxKind.SetAccessor:
2003+
case SyntaxKind.ClassDeclaration:
2004+
case SyntaxKind.InterfaceDeclaration:
2005+
case SyntaxKind.EnumDeclaration:
2006+
case SyntaxKind.ModuleDeclaration:
2007+
return node;
2008+
}
2009+
}
2010+
}
2011+
2012+
/* @internal */ export function getNodeKind(node: Node): string {
2013+
switch (node.kind) {
2014+
case SyntaxKind.ModuleDeclaration: return ScriptElementKind.moduleElement;
2015+
case SyntaxKind.ClassDeclaration: return ScriptElementKind.classElement;
2016+
case SyntaxKind.InterfaceDeclaration: return ScriptElementKind.interfaceElement;
2017+
case SyntaxKind.TypeAliasDeclaration: return ScriptElementKind.typeElement;
2018+
case SyntaxKind.EnumDeclaration: return ScriptElementKind.enumElement;
2019+
case SyntaxKind.VariableDeclaration:
2020+
return isConst(node)
2021+
? ScriptElementKind.constElement
2022+
: isLet(node)
2023+
? ScriptElementKind.letElement
2024+
: ScriptElementKind.variableElement;
2025+
case SyntaxKind.FunctionDeclaration: return ScriptElementKind.functionElement;
2026+
case SyntaxKind.GetAccessor: return ScriptElementKind.memberGetAccessorElement;
2027+
case SyntaxKind.SetAccessor: return ScriptElementKind.memberSetAccessorElement;
2028+
case SyntaxKind.MethodDeclaration:
2029+
case SyntaxKind.MethodSignature:
2030+
return ScriptElementKind.memberFunctionElement;
2031+
case SyntaxKind.PropertyDeclaration:
2032+
case SyntaxKind.PropertySignature:
2033+
return ScriptElementKind.memberVariableElement;
2034+
case SyntaxKind.IndexSignature: return ScriptElementKind.indexSignatureElement;
2035+
case SyntaxKind.ConstructSignature: return ScriptElementKind.constructSignatureElement;
2036+
case SyntaxKind.CallSignature: return ScriptElementKind.callSignatureElement;
2037+
case SyntaxKind.Constructor: return ScriptElementKind.constructorImplementationElement;
2038+
case SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement;
2039+
case SyntaxKind.EnumMember: return ScriptElementKind.variableElement;
2040+
case SyntaxKind.Parameter: return (node.flags & NodeFlags.AccessibilityModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement;
2041+
}
2042+
return ScriptElementKind.unknown;
2043+
}
2044+
19952045
export function createLanguageService(host: LanguageServiceHost, documentRegistry: DocumentRegistry = createDocumentRegistry()): LanguageService {
19962046
var syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
19972047
var ruleProvider: formatting.RulesProvider;
@@ -2722,29 +2772,6 @@ module ts {
27222772
}
27232773
}
27242774

2725-
function getContainerNode(node: Node): Node {
2726-
while (true) {
2727-
node = node.parent;
2728-
if (!node) {
2729-
return undefined;
2730-
}
2731-
switch (node.kind) {
2732-
case SyntaxKind.SourceFile:
2733-
case SyntaxKind.MethodDeclaration:
2734-
case SyntaxKind.MethodSignature:
2735-
case SyntaxKind.FunctionDeclaration:
2736-
case SyntaxKind.FunctionExpression:
2737-
case SyntaxKind.GetAccessor:
2738-
case SyntaxKind.SetAccessor:
2739-
case SyntaxKind.ClassDeclaration:
2740-
case SyntaxKind.InterfaceDeclaration:
2741-
case SyntaxKind.EnumDeclaration:
2742-
case SyntaxKind.ModuleDeclaration:
2743-
return node;
2744-
}
2745-
}
2746-
}
2747-
27482775
// TODO(drosen): use contextual SemanticMeaning.
27492776
function getSymbolKind(symbol: Symbol, typeResolver: TypeChecker, location: Node): string {
27502777
var flags = symbol.getFlags();
@@ -2831,39 +2858,6 @@ module ts {
28312858
return ScriptElementKind.unknown;
28322859
}
28332860

2834-
function getNodeKind(node: Node): string {
2835-
switch (node.kind) {
2836-
case SyntaxKind.ModuleDeclaration: return ScriptElementKind.moduleElement;
2837-
case SyntaxKind.ClassDeclaration: return ScriptElementKind.classElement;
2838-
case SyntaxKind.InterfaceDeclaration: return ScriptElementKind.interfaceElement;
2839-
case SyntaxKind.TypeAliasDeclaration: return ScriptElementKind.typeElement;
2840-
case SyntaxKind.EnumDeclaration: return ScriptElementKind.enumElement;
2841-
case SyntaxKind.VariableDeclaration:
2842-
return isConst(node)
2843-
? ScriptElementKind.constElement
2844-
: isLet(node)
2845-
? ScriptElementKind.letElement
2846-
: ScriptElementKind.variableElement;
2847-
case SyntaxKind.FunctionDeclaration: return ScriptElementKind.functionElement;
2848-
case SyntaxKind.GetAccessor: return ScriptElementKind.memberGetAccessorElement;
2849-
case SyntaxKind.SetAccessor: return ScriptElementKind.memberSetAccessorElement;
2850-
case SyntaxKind.MethodDeclaration:
2851-
case SyntaxKind.MethodSignature:
2852-
return ScriptElementKind.memberFunctionElement;
2853-
case SyntaxKind.PropertyDeclaration:
2854-
case SyntaxKind.PropertySignature:
2855-
return ScriptElementKind.memberVariableElement;
2856-
case SyntaxKind.IndexSignature: return ScriptElementKind.indexSignatureElement;
2857-
case SyntaxKind.ConstructSignature: return ScriptElementKind.constructSignatureElement;
2858-
case SyntaxKind.CallSignature: return ScriptElementKind.callSignatureElement;
2859-
case SyntaxKind.Constructor: return ScriptElementKind.constructorImplementationElement;
2860-
case SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement;
2861-
case SyntaxKind.EnumMember: return ScriptElementKind.variableElement;
2862-
case SyntaxKind.Parameter: return (node.flags & NodeFlags.AccessibilityModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement;
2863-
}
2864-
return ScriptElementKind.unknown;
2865-
}
2866-
28672861
function getSymbolModifiers(symbol: Symbol): string {
28682862
return symbol && symbol.declarations && symbol.declarations.length > 0
28692863
? getNodeModifiers(symbol.declarations[0])
@@ -4663,89 +4657,10 @@ module ts {
46634657
}
46644658

46654659
/// NavigateTo
4666-
function getNavigateToItems(searchValue: string): NavigateToItem[] {
4660+
function getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[] {
46674661
synchronizeHostData();
46684662

4669-
// Split search value in terms array
4670-
var terms = searchValue.split(" ");
4671-
4672-
// default NavigateTo approach: if search term contains only lower-case chars - use case-insensitive search, otherwise switch to case-sensitive version
4673-
var searchTerms = map(terms, t => ({ caseSensitive: hasAnyUpperCaseCharacter(t), term: t }));
4674-
4675-
var items: NavigateToItem[] = [];
4676-
4677-
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
4678-
forEach(program.getSourceFiles(), sourceFile => {
4679-
cancellationToken.throwIfCancellationRequested();
4680-
4681-
var fileName = sourceFile.fileName;
4682-
var declarations = sourceFile.getNamedDeclarations();
4683-
for (var i = 0, n = declarations.length; i < n; i++) {
4684-
var declaration = declarations[i];
4685-
// TODO(jfreeman): Skip this declaration if it has a computed name
4686-
var name = (<Identifier>declaration.name).text;
4687-
var matchKind = getMatchKind(searchTerms, name);
4688-
if (matchKind !== MatchKind.none) {
4689-
var container = <Declaration>getContainerNode(declaration);
4690-
items.push({
4691-
name: name,
4692-
kind: getNodeKind(declaration),
4693-
kindModifiers: getNodeModifiers(declaration),
4694-
matchKind: MatchKind[matchKind],
4695-
fileName: fileName,
4696-
textSpan: createTextSpanFromBounds(declaration.getStart(), declaration.getEnd()),
4697-
// TODO(jfreeman): What should be the containerName when the container has a computed name?
4698-
containerName: container && container.name ? (<Identifier>container.name).text : "",
4699-
containerKind: container && container.name ? getNodeKind(container) : ""
4700-
});
4701-
}
4702-
}
4703-
});
4704-
4705-
return items;
4706-
4707-
function hasAnyUpperCaseCharacter(s: string): boolean {
4708-
for (var i = 0, n = s.length; i < n; i++) {
4709-
var c = s.charCodeAt(i);
4710-
if ((CharacterCodes.A <= c && c <= CharacterCodes.Z) ||
4711-
(c >= CharacterCodes.maxAsciiCharacter && s.charAt(i).toLocaleLowerCase() !== s.charAt(i))) {
4712-
return true;
4713-
}
4714-
}
4715-
4716-
return false;
4717-
}
4718-
4719-
function getMatchKind(searchTerms: { caseSensitive: boolean; term: string }[], name: string): MatchKind {
4720-
var matchKind = MatchKind.none;
4721-
4722-
if (name) {
4723-
for (var j = 0, n = searchTerms.length; j < n; j++) {
4724-
var searchTerm = searchTerms[j];
4725-
var nameToSearch = searchTerm.caseSensitive ? name : name.toLocaleLowerCase();
4726-
// in case of case-insensitive search searchTerm.term will already be lower-cased
4727-
var index = nameToSearch.indexOf(searchTerm.term);
4728-
if (index < 0) {
4729-
// Didn't match.
4730-
return MatchKind.none;
4731-
}
4732-
4733-
var termKind = MatchKind.substring;
4734-
if (index === 0) {
4735-
// here we know that match occur at the beginning of the string.
4736-
// 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
4737-
termKind = name.length === searchTerm.term.length ? MatchKind.exact : MatchKind.prefix;
4738-
}
4739-
4740-
// Update our match kind if we don't have one, or if this match is better.
4741-
if (matchKind === MatchKind.none || termKind < matchKind) {
4742-
matchKind = termKind;
4743-
}
4744-
}
4745-
}
4746-
4747-
return matchKind;
4748-
}
4663+
return ts.NavigateTo.getNavigateToItems(program, cancellationToken, searchValue, maxResultCount);
47494664
}
47504665

47514666
function containErrors(diagnostics: Diagnostic[]): boolean {

src/services/shims.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ module ts {
138138
* Returns a JSON-encoded value of the type:
139139
* { name: string; kind: string; kindModifiers: string; containerName: string; containerKind: string; matchKind: string; fileName: string; textSpan: { start: number; length: number}; } [] = [];
140140
*/
141-
getNavigateToItems(searchValue: string): string;
141+
getNavigateToItems(searchValue: string, maxResultCount?: number): string;
142142

143143
/**
144144
* Returns a JSON-encoded value of the type:
@@ -628,11 +628,11 @@ module ts {
628628
/// NAVIGATE TO
629629

630630
/** Return a list of symbols that are interesting to navigate to */
631-
public getNavigateToItems(searchValue: string): string {
631+
public getNavigateToItems(searchValue: string, maxResultCount?: number): string {
632632
return this.forwardJSONCall(
633-
"getNavigateToItems('" + searchValue + "')",
633+
"getNavigateToItems('" + searchValue + "', " + maxResultCount+ ")",
634634
() => {
635-
var items = this.languageService.getNavigateToItems(searchValue);
635+
var items = this.languageService.getNavigateToItems(searchValue, maxResultCount);
636636
return items;
637637
});
638638
}

0 commit comments

Comments
 (0)