Merge pull request #2084 from Microsoft/limitNavigateTo

Allow clients to limit the amount of data that navigate to returns.
This commit is contained in:
CyrusNajmabadi 2015-02-20 16:42:06 -08:00
commit 5c1ef11d69
12 changed files with 186 additions and 161 deletions

View File

@ -65,6 +65,7 @@ var servicesSources = [
return path.join(compilerDirectory, f);
}).concat([
"breakpoints.ts",
"navigateTo.ts",
"navigationBar.ts",
"outliningElementsCollector.ts",
"services.ts",

105
src/services/navigateTo.ts Normal file
View File

@ -0,0 +1,105 @@
module ts.NavigateTo {
type RawNavigateToItem = { name: string; fileName: string; matchKind: MatchKind; declaration: Declaration };
enum MatchKind {
none = 0,
exact = 1,
substring = 2,
prefix = 3
}
export function getNavigateToItems(program: Program, cancellationToken: CancellationTokenObject, searchValue: string, maxResultCount: number): NavigateToItem[]{
// Split search value in terms array
var terms = searchValue.split(" ");
// default NavigateTo approach: if search term contains only lower-case chars - use case-insensitive search, otherwise switch to case-sensitive version
var searchTerms = map(terms, t => ({ caseSensitive: hasAnyUpperCaseCharacter(t), term: t }));
var rawItems: RawNavigateToItem[] = [];
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
forEach(program.getSourceFiles(), sourceFile => {
cancellationToken.throwIfCancellationRequested();
var fileName = sourceFile.fileName;
var declarations = sourceFile.getNamedDeclarations();
for (var i = 0, n = declarations.length; i < n; i++) {
var declaration = declarations[i];
// TODO(jfreeman): Skip this declaration if it has a computed name
var name = (<Identifier>declaration.name).text;
var matchKind = getMatchKind(searchTerms, name);
if (matchKind !== MatchKind.none) {
rawItems.push({ name, fileName, matchKind, declaration });
}
}
});
rawItems.sort((i1, i2) => i1.matchKind - i2.matchKind);
if (maxResultCount !== undefined) {
rawItems = rawItems.slice(0, maxResultCount);
}
var items = map(rawItems, createNavigateToItem);
return items;
function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem {
var declaration = rawItem.declaration;
var container = <Declaration>getContainerNode(declaration);
return {
name: rawItem.name,
kind: getNodeKind(declaration),
kindModifiers: getNodeModifiers(declaration),
matchKind: MatchKind[rawItem.matchKind],
fileName: rawItem.fileName,
textSpan: createTextSpanFromBounds(declaration.getStart(), declaration.getEnd()),
// TODO(jfreeman): What should be the containerName when the container has a computed name?
containerName: container && container.name ? (<Identifier>container.name).text : "",
containerKind: container && container.name ? getNodeKind(container) : ""
};
}
function hasAnyUpperCaseCharacter(s: string): boolean {
for (var i = 0, n = s.length; i < n; i++) {
var c = s.charCodeAt(i);
if ((CharacterCodes.A <= c && c <= CharacterCodes.Z) ||
(c >= CharacterCodes.maxAsciiCharacter && s.charAt(i).toLocaleLowerCase() !== s.charAt(i))) {
return true;
}
}
return false;
}
function getMatchKind(searchTerms: { caseSensitive: boolean; term: string }[], name: string): MatchKind {
var matchKind = MatchKind.none;
if (name) {
for (var j = 0, n = searchTerms.length; j < n; j++) {
var searchTerm = searchTerms[j];
var nameToSearch = searchTerm.caseSensitive ? name : name.toLocaleLowerCase();
// in case of case-insensitive search searchTerm.term will already be lower-cased
var index = nameToSearch.indexOf(searchTerm.term);
if (index < 0) {
// Didn't match.
return MatchKind.none;
}
var termKind = MatchKind.substring;
if (index === 0) {
// here we know that match occur at the beginning of the string.
// 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
termKind = name.length === searchTerm.term.length ? MatchKind.exact : MatchKind.prefix;
}
// Update our match kind if we don't have one, or if this match is better.
if (matchKind === MatchKind.none || termKind < matchKind) {
matchKind = termKind;
}
}
}
return matchKind;
}
}
}

View File

@ -2,6 +2,7 @@
/// <reference path='breakpoints.ts' />
/// <reference path='outliningElementsCollector.ts' />
/// <reference path='navigateTo.ts' />
/// <reference path='navigationBar.ts' />
/// <reference path='signatureHelp.ts' />
/// <reference path='utilities.ts' />
@ -900,7 +901,7 @@ module ts {
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[];
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[];
getNavigateToItems(searchValue: string): NavigateToItem[];
getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
getNavigationBarItems(fileName: string): NavigationBarItem[];
getOutliningSpans(fileName: string): OutliningSpan[];
@ -1378,13 +1379,6 @@ module ts {
public static typeAlias = "type alias name";
}
enum MatchKind {
none = 0,
exact = 1,
substring = 2,
prefix = 3
}
/// Language Service
interface CompletionSession {
@ -1992,6 +1986,62 @@ module ts {
});
}
/* @internal */ export function getContainerNode(node: Node): Node {
while (true) {
node = node.parent;
if (!node) {
return undefined;
}
switch (node.kind) {
case SyntaxKind.SourceFile:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ModuleDeclaration:
return node;
}
}
}
/* @internal */ export function getNodeKind(node: Node): string {
switch (node.kind) {
case SyntaxKind.ModuleDeclaration: return ScriptElementKind.moduleElement;
case SyntaxKind.ClassDeclaration: return ScriptElementKind.classElement;
case SyntaxKind.InterfaceDeclaration: return ScriptElementKind.interfaceElement;
case SyntaxKind.TypeAliasDeclaration: return ScriptElementKind.typeElement;
case SyntaxKind.EnumDeclaration: return ScriptElementKind.enumElement;
case SyntaxKind.VariableDeclaration:
return isConst(node)
? ScriptElementKind.constElement
: isLet(node)
? ScriptElementKind.letElement
: ScriptElementKind.variableElement;
case SyntaxKind.FunctionDeclaration: return ScriptElementKind.functionElement;
case SyntaxKind.GetAccessor: return ScriptElementKind.memberGetAccessorElement;
case SyntaxKind.SetAccessor: return ScriptElementKind.memberSetAccessorElement;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
return ScriptElementKind.memberFunctionElement;
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
return ScriptElementKind.memberVariableElement;
case SyntaxKind.IndexSignature: return ScriptElementKind.indexSignatureElement;
case SyntaxKind.ConstructSignature: return ScriptElementKind.constructSignatureElement;
case SyntaxKind.CallSignature: return ScriptElementKind.callSignatureElement;
case SyntaxKind.Constructor: return ScriptElementKind.constructorImplementationElement;
case SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement;
case SyntaxKind.EnumMember: return ScriptElementKind.variableElement;
case SyntaxKind.Parameter: return (node.flags & NodeFlags.AccessibilityModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement;
}
return ScriptElementKind.unknown;
}
export function createLanguageService(host: LanguageServiceHost, documentRegistry: DocumentRegistry = createDocumentRegistry()): LanguageService {
var syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
var ruleProvider: formatting.RulesProvider;
@ -2722,29 +2772,6 @@ module ts {
}
}
function getContainerNode(node: Node): Node {
while (true) {
node = node.parent;
if (!node) {
return undefined;
}
switch (node.kind) {
case SyntaxKind.SourceFile:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ModuleDeclaration:
return node;
}
}
}
// TODO(drosen): use contextual SemanticMeaning.
function getSymbolKind(symbol: Symbol, typeResolver: TypeChecker, location: Node): string {
var flags = symbol.getFlags();
@ -2831,39 +2858,6 @@ module ts {
return ScriptElementKind.unknown;
}
function getNodeKind(node: Node): string {
switch (node.kind) {
case SyntaxKind.ModuleDeclaration: return ScriptElementKind.moduleElement;
case SyntaxKind.ClassDeclaration: return ScriptElementKind.classElement;
case SyntaxKind.InterfaceDeclaration: return ScriptElementKind.interfaceElement;
case SyntaxKind.TypeAliasDeclaration: return ScriptElementKind.typeElement;
case SyntaxKind.EnumDeclaration: return ScriptElementKind.enumElement;
case SyntaxKind.VariableDeclaration:
return isConst(node)
? ScriptElementKind.constElement
: isLet(node)
? ScriptElementKind.letElement
: ScriptElementKind.variableElement;
case SyntaxKind.FunctionDeclaration: return ScriptElementKind.functionElement;
case SyntaxKind.GetAccessor: return ScriptElementKind.memberGetAccessorElement;
case SyntaxKind.SetAccessor: return ScriptElementKind.memberSetAccessorElement;
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
return ScriptElementKind.memberFunctionElement;
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
return ScriptElementKind.memberVariableElement;
case SyntaxKind.IndexSignature: return ScriptElementKind.indexSignatureElement;
case SyntaxKind.ConstructSignature: return ScriptElementKind.constructSignatureElement;
case SyntaxKind.CallSignature: return ScriptElementKind.callSignatureElement;
case SyntaxKind.Constructor: return ScriptElementKind.constructorImplementationElement;
case SyntaxKind.TypeParameter: return ScriptElementKind.typeParameterElement;
case SyntaxKind.EnumMember: return ScriptElementKind.variableElement;
case SyntaxKind.Parameter: return (node.flags & NodeFlags.AccessibilityModifier) ? ScriptElementKind.memberVariableElement : ScriptElementKind.parameterElement;
}
return ScriptElementKind.unknown;
}
function getSymbolModifiers(symbol: Symbol): string {
return symbol && symbol.declarations && symbol.declarations.length > 0
? getNodeModifiers(symbol.declarations[0])
@ -4663,89 +4657,10 @@ module ts {
}
/// NavigateTo
function getNavigateToItems(searchValue: string): NavigateToItem[] {
function getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[] {
synchronizeHostData();
// Split search value in terms array
var terms = searchValue.split(" ");
// default NavigateTo approach: if search term contains only lower-case chars - use case-insensitive search, otherwise switch to case-sensitive version
var searchTerms = map(terms, t => ({ caseSensitive: hasAnyUpperCaseCharacter(t), term: t }));
var items: NavigateToItem[] = [];
// Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
forEach(program.getSourceFiles(), sourceFile => {
cancellationToken.throwIfCancellationRequested();
var fileName = sourceFile.fileName;
var declarations = sourceFile.getNamedDeclarations();
for (var i = 0, n = declarations.length; i < n; i++) {
var declaration = declarations[i];
// TODO(jfreeman): Skip this declaration if it has a computed name
var name = (<Identifier>declaration.name).text;
var matchKind = getMatchKind(searchTerms, name);
if (matchKind !== MatchKind.none) {
var container = <Declaration>getContainerNode(declaration);
items.push({
name: name,
kind: getNodeKind(declaration),
kindModifiers: getNodeModifiers(declaration),
matchKind: MatchKind[matchKind],
fileName: fileName,
textSpan: createTextSpanFromBounds(declaration.getStart(), declaration.getEnd()),
// TODO(jfreeman): What should be the containerName when the container has a computed name?
containerName: container && container.name ? (<Identifier>container.name).text : "",
containerKind: container && container.name ? getNodeKind(container) : ""
});
}
}
});
return items;
function hasAnyUpperCaseCharacter(s: string): boolean {
for (var i = 0, n = s.length; i < n; i++) {
var c = s.charCodeAt(i);
if ((CharacterCodes.A <= c && c <= CharacterCodes.Z) ||
(c >= CharacterCodes.maxAsciiCharacter && s.charAt(i).toLocaleLowerCase() !== s.charAt(i))) {
return true;
}
}
return false;
}
function getMatchKind(searchTerms: { caseSensitive: boolean; term: string }[], name: string): MatchKind {
var matchKind = MatchKind.none;
if (name) {
for (var j = 0, n = searchTerms.length; j < n; j++) {
var searchTerm = searchTerms[j];
var nameToSearch = searchTerm.caseSensitive ? name : name.toLocaleLowerCase();
// in case of case-insensitive search searchTerm.term will already be lower-cased
var index = nameToSearch.indexOf(searchTerm.term);
if (index < 0) {
// Didn't match.
return MatchKind.none;
}
var termKind = MatchKind.substring;
if (index === 0) {
// here we know that match occur at the beginning of the string.
// 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
termKind = name.length === searchTerm.term.length ? MatchKind.exact : MatchKind.prefix;
}
// Update our match kind if we don't have one, or if this match is better.
if (matchKind === MatchKind.none || termKind < matchKind) {
matchKind = termKind;
}
}
}
return matchKind;
}
return ts.NavigateTo.getNavigateToItems(program, cancellationToken, searchValue, maxResultCount);
}
function containErrors(diagnostics: Diagnostic[]): boolean {

View File

@ -138,7 +138,7 @@ module ts {
* Returns a JSON-encoded value of the type:
* { name: string; kind: string; kindModifiers: string; containerName: string; containerKind: string; matchKind: string; fileName: string; textSpan: { start: number; length: number}; } [] = [];
*/
getNavigateToItems(searchValue: string): string;
getNavigateToItems(searchValue: string, maxResultCount?: number): string;
/**
* Returns a JSON-encoded value of the type:
@ -628,11 +628,11 @@ module ts {
/// NAVIGATE TO
/** Return a list of symbols that are interesting to navigate to */
public getNavigateToItems(searchValue: string): string {
public getNavigateToItems(searchValue: string, maxResultCount?: number): string {
return this.forwardJSONCall(
"getNavigateToItems('" + searchValue + "')",
"getNavigateToItems('" + searchValue + "', " + maxResultCount+ ")",
() => {
var items = this.languageService.getNavigateToItems(searchValue);
var items = this.languageService.getNavigateToItems(searchValue, maxResultCount);
return items;
});
}

View File

@ -1541,7 +1541,7 @@ declare module "typescript" {
getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[];
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[];
getNavigateToItems(searchValue: string): NavigateToItem[];
getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
getNavigationBarItems(fileName: string): NavigationBarItem[];
getOutliningSpans(fileName: string): OutliningSpan[];
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];

View File

@ -4986,9 +4986,10 @@ declare module "typescript" {
>position : number
>ReferenceEntry : ReferenceEntry
getNavigateToItems(searchValue: string): NavigateToItem[];
>getNavigateToItems : (searchValue: string) => NavigateToItem[]
getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
>getNavigateToItems : (searchValue: string, maxResultCount?: number) => NavigateToItem[]
>searchValue : string
>maxResultCount : number
>NavigateToItem : NavigateToItem
getNavigationBarItems(fileName: string): NavigationBarItem[];

View File

@ -1572,7 +1572,7 @@ declare module "typescript" {
getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[];
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[];
getNavigateToItems(searchValue: string): NavigateToItem[];
getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
getNavigationBarItems(fileName: string): NavigationBarItem[];
getOutliningSpans(fileName: string): OutliningSpan[];
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];

View File

@ -5130,9 +5130,10 @@ declare module "typescript" {
>position : number
>ReferenceEntry : ReferenceEntry
getNavigateToItems(searchValue: string): NavigateToItem[];
>getNavigateToItems : (searchValue: string) => NavigateToItem[]
getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
>getNavigateToItems : (searchValue: string, maxResultCount?: number) => NavigateToItem[]
>searchValue : string
>maxResultCount : number
>NavigateToItem : NavigateToItem
getNavigationBarItems(fileName: string): NavigationBarItem[];

View File

@ -1573,7 +1573,7 @@ declare module "typescript" {
getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[];
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[];
getNavigateToItems(searchValue: string): NavigateToItem[];
getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
getNavigationBarItems(fileName: string): NavigationBarItem[];
getOutliningSpans(fileName: string): OutliningSpan[];
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];

View File

@ -5082,9 +5082,10 @@ declare module "typescript" {
>position : number
>ReferenceEntry : ReferenceEntry
getNavigateToItems(searchValue: string): NavigateToItem[];
>getNavigateToItems : (searchValue: string) => NavigateToItem[]
getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
>getNavigateToItems : (searchValue: string, maxResultCount?: number) => NavigateToItem[]
>searchValue : string
>maxResultCount : number
>NavigateToItem : NavigateToItem
getNavigationBarItems(fileName: string): NavigationBarItem[];

View File

@ -1610,7 +1610,7 @@ declare module "typescript" {
getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[];
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[];
getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[];
getNavigateToItems(searchValue: string): NavigateToItem[];
getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
getNavigationBarItems(fileName: string): NavigationBarItem[];
getOutliningSpans(fileName: string): OutliningSpan[];
getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[];

View File

@ -5255,9 +5255,10 @@ declare module "typescript" {
>position : number
>ReferenceEntry : ReferenceEntry
getNavigateToItems(searchValue: string): NavigateToItem[];
>getNavigateToItems : (searchValue: string) => NavigateToItem[]
getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[];
>getNavigateToItems : (searchValue: string, maxResultCount?: number) => NavigateToItem[]
>searchValue : string
>maxResultCount : number
>NavigateToItem : NavigateToItem
getNavigationBarItems(fileName: string): NavigationBarItem[];