Skip to content

Cleanup navTo #18150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
1 commit merged into from
Sep 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
276 changes: 137 additions & 139 deletions src/services/navigateTo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,185 +15,183 @@ namespace ts.NavigateTo {
}

forEachEntry(sourceFile.getNamedDeclarations(), (declarations, name) => {
if (declarations) {
// First do a quick check to see if the name of the declaration matches the
// last portion of the (possibly) dotted name they're searching for.
let matches = patternMatcher.getMatchesForLastSegmentOfPattern(name);

if (!matches) {
return; // continue to next named declarations
}

for (const declaration of declarations) {
// It was a match! If the pattern has dots in it, then also see if the
// declaration container matches as well.
if (patternMatcher.patternContainsDots) {
const containers = getContainers(declaration);
if (!containers) {
return true; // Break out of named declarations and go to the next source file.
}

matches = patternMatcher.getMatches(containers, name);

if (!matches) {
return; // continue to next named declarations
}
}

const fileName = sourceFile.fileName;
const matchKind = bestMatchKind(matches);
rawItems.push({ name, fileName, matchKind, isCaseSensitive: allMatchesAreCaseSensitive(matches), declaration });
}
}
getItemsFromNamedDeclaration(patternMatcher, name, declarations, checker, sourceFile.fileName, rawItems);
});
}

// Remove imports when the imported declaration is already in the list and has the same name.
rawItems = filter(rawItems, item => {
const decl = item.declaration;
if (decl.kind === SyntaxKind.ImportClause || decl.kind === SyntaxKind.ImportSpecifier || decl.kind === SyntaxKind.ImportEqualsDeclaration) {
const importer = checker.getSymbolAtLocation((decl as NamedDeclaration).name);
const imported = checker.getAliasedSymbol(importer);
return importer.escapedName !== imported.escapedName;
}
else {
return true;
}
});

rawItems.sort(compareNavigateToItems);
if (maxResultCount !== undefined) {
rawItems = rawItems.slice(0, maxResultCount);
}
return rawItems.map(createNavigateToItem);
}

const items = map(rawItems, createNavigateToItem);

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

function allMatchesAreCaseSensitive(matches: PatternMatch[]): boolean {
Debug.assert(matches.length > 0);
if (!matches) {
return; // continue to next named declarations
}

// This is a case sensitive match, only if all the submatches were case sensitive.
for (const match of matches) {
if (!match.isCaseSensitive) {
return false;
}
for (const declaration of declarations) {
if (!shouldKeepItem(declaration, checker)) {
continue;
}

return true;
}

function tryAddSingleDeclarationName(declaration: Declaration, containers: string[]) {
if (declaration) {
const name = getNameOfDeclaration(declaration);
if (name) {
const text = getTextOfIdentifierOrLiteral(name as (Identifier | LiteralExpression));
if (text !== undefined) {
containers.unshift(text);
}
else if (name.kind === SyntaxKind.ComputedPropertyName) {
return tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ true);
}
else {
// Don't know how to add this.
return false;
}
// It was a match! If the pattern has dots in it, then also see if the
// declaration container matches as well.
let containerMatches = matches;
if (patternMatcher.patternContainsDots) {
containerMatches = patternMatcher.getMatches(getContainers(declaration), name);
if (!containerMatches) {
continue;
}
}

return true;
const matchKind = bestMatchKind(containerMatches);
const isCaseSensitive = allMatchesAreCaseSensitive(containerMatches);
rawItems.push({ name, fileName, matchKind, isCaseSensitive, declaration });
}
}

// Only added the names of computed properties if they're simple dotted expressions, like:
//
// [X.Y.Z]() { }
function tryAddComputedPropertyName(expression: Expression, containers: string[], includeLastPortion: boolean): boolean {
const text = getTextOfIdentifierOrLiteral(expression as LiteralExpression);
if (text !== undefined) {
if (includeLastPortion) {
containers.unshift(text);
}
function shouldKeepItem(declaration: Declaration, checker: ts.TypeChecker): boolean {
switch (declaration.kind) {
case SyntaxKind.ImportClause:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ImportEqualsDeclaration:
const importer = checker.getSymbolAtLocation((declaration as ImportClause | ImportSpecifier | ImportEqualsDeclaration).name);
const imported = checker.getAliasedSymbol(importer);
return importer.escapedName !== imported.escapedName;
default:
return true;
}
}
}

if (expression.kind === SyntaxKind.PropertyAccessExpression) {
const propertyAccess = <PropertyAccessExpression>expression;
if (includeLastPortion) {
containers.unshift(propertyAccess.name.text);
}
function allMatchesAreCaseSensitive(matches: ReadonlyArray<PatternMatch>): boolean {
Debug.assert(matches.length > 0);

return tryAddComputedPropertyName(propertyAccess.expression, containers, /*includeLastPortion*/ true);
// This is a case sensitive match, only if all the submatches were case sensitive.
for (const match of matches) {
if (!match.isCaseSensitive) {
return false;
}

return false;
}

function getContainers(declaration: Declaration) {
const containers: string[] = [];
return true;
}

// First, if we started with a computed property name, then add all but the last
// portion into the container array.
function tryAddSingleDeclarationName(declaration: Declaration, containers: string[]): boolean {
if (declaration) {
const name = getNameOfDeclaration(declaration);
if (name.kind === SyntaxKind.ComputedPropertyName) {
if (!tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ false)) {
return undefined;
if (name) {
const text = getTextOfIdentifierOrLiteral(name as (Identifier | LiteralExpression));
if (text !== undefined) {
containers.unshift(text);
}
else if (name.kind === SyntaxKind.ComputedPropertyName) {
return tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ true);
}
else {
// Don't know how to add this.
return false;
}
}
}

// Now, walk up our containers, adding all their names to the container array.
declaration = getContainerNode(declaration);
return true;
}

while (declaration) {
if (!tryAddSingleDeclarationName(declaration, containers)) {
return undefined;
}
// Only added the names of computed properties if they're simple dotted expressions, like:
//
// [X.Y.Z]() { }
function tryAddComputedPropertyName(expression: Expression, containers: string[], includeLastPortion: boolean): boolean {
const text = getTextOfIdentifierOrLiteral(expression as LiteralExpression);
if (text !== undefined) {
if (includeLastPortion) {
containers.unshift(text);
}
return true;
}

declaration = getContainerNode(declaration);
if (expression.kind === SyntaxKind.PropertyAccessExpression) {
const propertyAccess = <PropertyAccessExpression>expression;
if (includeLastPortion) {
containers.unshift(propertyAccess.name.text);
}

return containers;
return tryAddComputedPropertyName(propertyAccess.expression, containers, /*includeLastPortion*/ true);
}

function bestMatchKind(matches: PatternMatch[]) {
Debug.assert(matches.length > 0);
let bestMatchKind = PatternMatchKind.camelCase;
return false;
}

for (const match of matches) {
const kind = match.kind;
if (kind < bestMatchKind) {
bestMatchKind = kind;
}
}
function getContainers(declaration: Declaration): string[] {
const containers: string[] = [];

return bestMatchKind;
// First, if we started with a computed property name, then add all but the last
// portion into the container array.
const name = getNameOfDeclaration(declaration);
if (name.kind === SyntaxKind.ComputedPropertyName) {
if (!tryAddComputedPropertyName((<ComputedPropertyName>name).expression, containers, /*includeLastPortion*/ false)) {
return undefined;
}
}

function compareNavigateToItems(i1: RawNavigateToItem, i2: RawNavigateToItem) {
// TODO(cyrusn): get the gamut of comparisons that VS already uses here.
// Right now we just sort by kind first, and then by name of the item.
// We first sort case insensitively. So "Aaa" will come before "bar".
// Then we sort case sensitively, so "aaa" will come before "Aaa".
return i1.matchKind - i2.matchKind ||
ts.compareStringsCaseInsensitive(i1.name, i2.name) ||
ts.compareStrings(i1.name, i2.name);
// Now, walk up our containers, adding all their names to the container array.
declaration = getContainerNode(declaration);

while (declaration) {
if (!tryAddSingleDeclarationName(declaration, containers)) {
return undefined;
}

declaration = getContainerNode(declaration);
}

function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem {
const declaration = rawItem.declaration;
const container = <Declaration>getContainerNode(declaration);
const containerName = container && getNameOfDeclaration(container);
return {
name: rawItem.name,
kind: getNodeKind(declaration),
kindModifiers: getNodeModifiers(declaration),
matchKind: PatternMatchKind[rawItem.matchKind],
isCaseSensitive: rawItem.isCaseSensitive,
fileName: rawItem.fileName,
textSpan: createTextSpanFromNode(declaration),
// TODO(jfreeman): What should be the containerName when the container has a computed name?
containerName: containerName ? (<Identifier>containerName).text : "",
containerKind: containerName ? getNodeKind(container) : ScriptElementKind.unknown
};
return containers;
}

function bestMatchKind(matches: ReadonlyArray<PatternMatch>): PatternMatchKind {
Debug.assert(matches.length > 0);
let bestMatchKind = PatternMatchKind.camelCase;

for (const match of matches) {
const kind = match.kind;
if (kind < bestMatchKind) {
bestMatchKind = kind;
}
}

return bestMatchKind;
}

function compareNavigateToItems(i1: RawNavigateToItem, i2: RawNavigateToItem): number {
// TODO(cyrusn): get the gamut of comparisons that VS already uses here.
// Right now we just sort by kind first, and then by name of the item.
// We first sort case insensitively. So "Aaa" will come before "bar".
// Then we sort case sensitively, so "aaa" will come before "Aaa".
return i1.matchKind - i2.matchKind ||
ts.compareStringsCaseInsensitive(i1.name, i2.name) ||
ts.compareStrings(i1.name, i2.name);
}

function createNavigateToItem(rawItem: RawNavigateToItem): NavigateToItem {
const declaration = rawItem.declaration;
const container = <Declaration>getContainerNode(declaration);
const containerName = container && getNameOfDeclaration(container);
return {
name: rawItem.name,
kind: getNodeKind(declaration),
kindModifiers: getNodeModifiers(declaration),
matchKind: PatternMatchKind[rawItem.matchKind],
isCaseSensitive: rawItem.isCaseSensitive,
fileName: rawItem.fileName,
textSpan: createTextSpanFromNode(declaration),
// TODO(jfreeman): What should be the containerName when the container has a computed name?
containerName: containerName ? (<Identifier>containerName).text : "",
containerKind: containerName ? getNodeKind(container) : ScriptElementKind.unknown
};
}
}
4 changes: 2 additions & 2 deletions src/services/patternMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ namespace ts {
// Fully checks a candidate, with an dotted container, against the search pattern.
// The candidate must match the last part of the search pattern, and the dotted container
// must match the preceding segments of the pattern.
getMatches(candidateContainers: string[], candidate: string): PatternMatch[];
getMatches(candidateContainers: string[], candidate: string): PatternMatch[] | undefined;

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

function getMatches(candidateContainers: string[], candidate: string): PatternMatch[] {
function getMatches(candidateContainers: string[], candidate: string): PatternMatch[] | undefined {
if (skipMatch(candidate)) {
return undefined;
}
Expand Down