diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d50e440c5e9..4275816f723 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7,6 +7,15 @@ module ts { /* @internal */ export let checkTime = 0; + /* @internal */ + export function getSymbolId(symbol: Symbol): number { + if (!symbol.id) { + symbol.id = nextSymbolId++; + } + + return symbol.id; + } + export function createTypeChecker(host: TypeCheckerHost, produceDiagnostics: boolean): TypeChecker { let Symbol = objectAllocator.getSymbolConstructor(); let Type = objectAllocator.getTypeConstructor(); @@ -250,8 +259,8 @@ module ts { function getSymbolLinks(symbol: Symbol): SymbolLinks { if (symbol.flags & SymbolFlags.Transient) return symbol; - if (!symbol.id) symbol.id = nextSymbolId++; - return symbolLinks[symbol.id] || (symbolLinks[symbol.id] = {}); + var id = getSymbolId(symbol); + return symbolLinks[id] || (symbolLinks[id] = {}); } function getNodeLinks(node: Node): NodeLinks { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 68ac8d916fb..299ba52bdeb 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -330,6 +330,9 @@ module Harness.LanguageService { getReferencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] { return unwrapJSONCallResult(this.shim.getReferencesAtPosition(fileName, position)); } + findReferences(fileName: string, position: number): ts.ReferencedSymbol[] { + return unwrapJSONCallResult(this.shim.findReferences(fileName, position)); + } getOccurrencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] { return unwrapJSONCallResult(this.shim.getOccurrencesAtPosition(fileName, position)); } diff --git a/src/server/client.ts b/src/server/client.ts index 07c742db4f8..875e3ffdb5b 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -300,6 +300,11 @@ module ts.server { }); } + findReferences(fileName: string, position: number): ReferencedSymbol[]{ + // Not yet implemented. + return []; + } + getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] { var lineOffset = this.positionToOneBasedLineOffset(fileName, position); var args: protocol.FileLocationRequestArgs = { diff --git a/src/services/services.ts b/src/services/services.ts index 5ff7c578aa0..2918e2910fb 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -937,6 +937,7 @@ module ts { getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[]; getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[]; + findReferences(fileName: string, position: number): ReferencedSymbol[]; getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; getNavigationBarItems(fileName: string): NavigationBarItem[]; @@ -1042,6 +1043,11 @@ module ts { containerName: string; } + export interface ReferencedSymbol { + definition: DefinitionInfo; + references: ReferenceEntry[]; + } + export enum SymbolDisplayPartKind { aliasName, className, @@ -3438,6 +3444,111 @@ module ts { }; } + function createDefinitionInfo(node: Node, symbolKind: string, symbolName: string, containerName: string): DefinitionInfo { + return { + fileName: node.getSourceFile().fileName, + textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()), + kind: symbolKind, + name: symbolName, + containerKind: undefined, + containerName + }; + } + + function getDefinitionInfos(node: Node, symbol: Symbol): DefinitionInfo[] { + // If this is an alias, and the request came at the declaration location + // get the aliased symbol instead. This allows for goto def on an import e.g. + // import {A, B} from "mod"; + // to jump to the implementation directly. + if (symbol.flags & SymbolFlags.Alias) { + let declaration = symbol.declarations[0]; + if (node.kind === SyntaxKind.Identifier && node.parent === declaration) { + symbol = typeInfoResolver.getAliasedSymbol(symbol); + } + } + + // Because name in short-hand property assignment has two different meanings: property name and property value, + // using go-to-definition at such position should go to the variable declaration of the property value rather than + // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition + // is performed at the location of property access, we would like to go to definition of the property in the short-hand + // assignment. This case and others are handled by the following code. + if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) { + let shorthandSymbol = typeInfoResolver.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); + if (!shorthandSymbol) { + return []; + } + + let shorthandDeclarations = shorthandSymbol.getDeclarations(); + let shorthandSymbolKind = getSymbolKind(shorthandSymbol, typeInfoResolver, node); + let shorthandSymbolName = typeInfoResolver.symbolToString(shorthandSymbol); + let shorthandContainerName = typeInfoResolver.symbolToString(symbol.parent, node); + return map(shorthandDeclarations, + declaration => createDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName)); + } + + let result: DefinitionInfo[] = []; + let declarations = symbol.getDeclarations(); + let symbolName = typeInfoResolver.symbolToString(symbol); // Do not get scoped name, just the name of the symbol + let symbolKind = getSymbolKind(symbol, typeInfoResolver, node); + let containerSymbol = symbol.parent; + let containerName = containerSymbol ? typeInfoResolver.symbolToString(containerSymbol, node) : ""; + + if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && + !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { + // Just add all the declarations. + forEach(declarations, declaration => { + result.push(createDefinitionInfo(declaration, symbolKind, symbolName, containerName)); + }); + } + + return result; + + function tryAddConstructSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) { + // Applicable only if we are in a new expression, or we are on a constructor declaration + // and in either case the symbol has a construct signature definition, i.e. class + if (isNewExpressionTarget(location) || location.kind === SyntaxKind.ConstructorKeyword) { + if (symbol.flags & SymbolFlags.Class) { + let classDeclaration = symbol.getDeclarations()[0]; + Debug.assert(classDeclaration && classDeclaration.kind === SyntaxKind.ClassDeclaration); + + return tryAddSignature(classDeclaration.members, /*selectConstructors*/ true, symbolKind, symbolName, containerName, result); + } + } + return false; + } + + function tryAddCallSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) { + if (isCallExpressionTarget(location) || isNewExpressionTarget(location) || isNameOfFunctionDeclaration(location)) { + return tryAddSignature(symbol.declarations, /*selectConstructors*/ false, symbolKind, symbolName, containerName, result); + } + return false; + } + + function tryAddSignature(signatureDeclarations: Declaration[], selectConstructors: boolean, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) { + let declarations: Declaration[] = []; + let definition: Declaration; + + forEach(signatureDeclarations, d => { + if ((selectConstructors && d.kind === SyntaxKind.Constructor) || + (!selectConstructors && (d.kind === SyntaxKind.FunctionDeclaration || d.kind === SyntaxKind.MethodDeclaration || d.kind === SyntaxKind.MethodSignature))) { + declarations.push(d); + if ((d).body) definition = d; + } + }); + + if (definition) { + result.push(createDefinitionInfo(definition, symbolKind, symbolName, containerName)); + return true; + } + else if (declarations.length) { + result.push(createDefinitionInfo(declarations[declarations.length - 1], symbolKind, symbolName, containerName)); + return true; + } + + return false; + } + } + /// Goto definition function getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[] { synchronizeHostData(); @@ -3453,7 +3564,7 @@ module ts { if (isJumpStatementTarget(node)) { let labelName = (node).text; let label = getTargetLabel((node.parent), (node).text); - return label ? [getDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined; + return label ? [createDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined; } /// Triple slash reference comments @@ -3481,109 +3592,7 @@ module ts { return undefined; } - // If this is an alias, and the request came at the declaration location - // get the aliased symbol instead. This allows for goto def on an import e.g. - // import {A, B} from "mod"; - // to jump to the implementation directelly. - if (symbol.flags & SymbolFlags.Alias) { - let declaration = symbol.declarations[0]; - if (node.kind === SyntaxKind.Identifier && node.parent === declaration) { - symbol = typeInfoResolver.getAliasedSymbol(symbol); - } - } - - - // Because name in short-hand property assignment has two different meanings: property name and property value, - // using go-to-definition at such position should go to the variable declaration of the property value rather than - // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition - // is performed at the location of property access, we would like to go to definition of the property in the short-hand - // assignment. This case and others are handled by the following code. - if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) { - let shorthandSymbol = typeInfoResolver.getShorthandAssignmentValueSymbol(symbol.valueDeclaration); - if (!shorthandSymbol) { - return []; - } - - let shorthandDeclarations = shorthandSymbol.getDeclarations(); - let shorthandSymbolKind = getSymbolKind(shorthandSymbol, typeInfoResolver, node); - let shorthandSymbolName = typeInfoResolver.symbolToString(shorthandSymbol); - let shorthandContainerName = typeInfoResolver.symbolToString(symbol.parent, node); - return map(shorthandDeclarations, - declaration => getDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName)); - } - - let result: DefinitionInfo[] = []; - let declarations = symbol.getDeclarations(); - let symbolName = typeInfoResolver.symbolToString(symbol); // Do not get scoped name, just the name of the symbol - let symbolKind = getSymbolKind(symbol, typeInfoResolver, node); - let containerSymbol = symbol.parent; - let containerName = containerSymbol ? typeInfoResolver.symbolToString(containerSymbol, node) : ""; - - if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && - !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { - // Just add all the declarations. - forEach(declarations, declaration => { - result.push(getDefinitionInfo(declaration, symbolKind, symbolName, containerName)); - }); - } - - return result; - - function getDefinitionInfo(node: Node, symbolKind: string, symbolName: string, containerName: string): DefinitionInfo { - return { - fileName: node.getSourceFile().fileName, - textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()), - kind: symbolKind, - name: symbolName, - containerKind: undefined, - containerName - }; - } - - function tryAddSignature(signatureDeclarations: Declaration[], selectConstructors: boolean, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) { - let declarations: Declaration[] = []; - let definition: Declaration; - - forEach(signatureDeclarations, d => { - if ((selectConstructors && d.kind === SyntaxKind.Constructor) || - (!selectConstructors && (d.kind === SyntaxKind.FunctionDeclaration || d.kind === SyntaxKind.MethodDeclaration || d.kind === SyntaxKind.MethodSignature))) { - declarations.push(d); - if ((d).body) definition = d; - } - }); - - if (definition) { - result.push(getDefinitionInfo(definition, symbolKind, symbolName, containerName)); - return true; - } - else if (declarations.length) { - result.push(getDefinitionInfo(declarations[declarations.length - 1], symbolKind, symbolName, containerName)); - return true; - } - - return false; - } - - function tryAddConstructSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) { - // Applicable only if we are in a new expression, or we are on a constructor declaration - // and in either case the symbol has a construct signature definition, i.e. class - if (isNewExpressionTarget(location) || location.kind === SyntaxKind.ConstructorKeyword) { - if (symbol.flags & SymbolFlags.Class) { - let classDeclaration = symbol.getDeclarations()[0]; - Debug.assert(classDeclaration && classDeclaration.kind === SyntaxKind.ClassDeclaration); - - return tryAddSignature(classDeclaration.members, /*selectConstructors*/ true, symbolKind, symbolName, containerName, result); - } - } - return false; - } - - function tryAddCallSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) { - if (isCallExpressionTarget(location) || isNewExpressionTarget(location) || isNameOfFunctionDeclaration(location)) { - return tryAddSignature(symbol.declarations, /*selectConstructors*/ false, symbolKind, symbolName, containerName, result); - } - return false; - } + return getDefinitionInfos(node, symbol); } /// References and Occurrences @@ -3599,7 +3608,7 @@ module ts { if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword || isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) { - return getReferencesForNode(node, [sourceFile], /*searchOnlyInCurrentFile*/ true, /*findInStrings:*/ false, /*findInComments:*/ false); + return convertReferences(getReferencesForNode(node, [sourceFile], /*searchOnlyInCurrentFile*/ true, /*findInStrings:*/ false, /*findInComments:*/ false)); } switch (node.kind) { @@ -3899,7 +3908,7 @@ module ts { return map(keywords, getReferenceEntryFromNode); } - function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement) { + function getSwitchCaseDefaultOccurrences(switchStatement: SwitchStatement): ReferenceEntry[] { let keywords: Node[] = []; pushKeywordIf(keywords, switchStatement.getFirstToken(), SyntaxKind.SwitchKeyword); @@ -4022,7 +4031,7 @@ module ts { } } - function getModifierOccurrences(modifier: SyntaxKind, declaration: Node) { + function getModifierOccurrences(modifier: SyntaxKind, declaration: Node): ReferenceEntry[] { let container = declaration.parent; // Make sure we only highlight the keyword when it makes sense to do so. @@ -4127,15 +4136,33 @@ module ts { } } - function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[] { - return findReferences(fileName, position, findInStrings, findInComments); + function convertReferences(referenceSymbols: ReferencedSymbol[]): ReferenceEntry[]{ + if (!referenceSymbols) { + return undefined; + } + + let referenceEntries: ReferenceEntry[] = []; + for (let referenceSymbol of referenceSymbols) { + addRange(referenceEntries, referenceSymbol.references); + } + return referenceEntries; + } + + function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): RenameLocation[]{ + var referenceSymbols = findReferencesWorker(fileName, position, findInStrings, findInComments); + return convertReferences(referenceSymbols); } function getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] { - return findReferences(fileName, position, /*findInStrings:*/ false, /*findInComments:*/ false); + var referenceSymbols = findReferencesWorker(fileName, position, /*findInStrings:*/ false, /*findInComments:*/ false); + return convertReferences(referenceSymbols); } - function findReferences(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ReferenceEntry[] { + function findReferences(fileName: string, position: number): ReferencedSymbol[] { + return filter(findReferencesWorker(fileName, position, /*findInStrings:*/ false, /*findInComments:*/ false), rs => !!rs.definition); + } + + function findReferencesWorker(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): ReferencedSymbol[] { synchronizeHostData(); let sourceFile = getValidSourceFile(fileName); @@ -4158,14 +4185,14 @@ module ts { return getReferencesForNode(node, program.getSourceFiles(), /*searchOnlyInCurrentFile*/ false, findInStrings, findInComments); } - function getReferencesForNode(node: Node, sourceFiles: SourceFile[], searchOnlyInCurrentFile: boolean, findInStrings: boolean, findInComments: boolean): ReferenceEntry[] { + function getReferencesForNode(node: Node, sourceFiles: SourceFile[], searchOnlyInCurrentFile: boolean, findInStrings: boolean, findInComments: boolean): ReferencedSymbol[]{ // Labels if (isLabelName(node)) { if (isJumpStatementTarget(node)) { let labelDefinition = getTargetLabel((node.parent), (node).text); // if we have a label definition, look within its statement for references, if not, then - // the label is undefined, just return a set of one for the current node. - return labelDefinition ? getLabelReferencesInNode(labelDefinition.parent, labelDefinition) : [getReferenceEntryFromNode(node)]; + // the label is undefined and we have no results.. + return labelDefinition ? getLabelReferencesInNode(labelDefinition.parent, labelDefinition) : undefined; } else { // it is a label definition and not a target, search within the parent labeledStatement @@ -4185,9 +4212,8 @@ module ts { // Could not find a symbol e.g. unknown identifier if (!symbol) { - // Even if we did not find a symbol, we have an identifier, so there is at least - // one reference that we know of. return that instead of undefined. - return [getReferenceEntryFromNode(node)]; + // Can't have references to something that we have no symbol for. + return undefined; } let declarations = symbol.declarations; @@ -4197,7 +4223,7 @@ module ts { return undefined; } - let result: ReferenceEntry[]; + let result: ReferencedSymbol[]; // Compute the meaning from the location and the symbol it references let searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), declarations); @@ -4209,15 +4235,18 @@ module ts { // otherwise we'll need to search globally (i.e. include each file). let scope = getSymbolScope(symbol); + // Maps from a symbol ID to the ReferencedSymbol entry in 'result'. + let symbolToIndex: number[] = []; + if (scope) { result = []; - getReferencesInNode(scope, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result); + getReferencesInNode(scope, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex); } else { if (searchOnlyInCurrentFile) { Debug.assert(sourceFiles.length === 1); result = []; - getReferencesInNode(sourceFiles[0], symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result); + getReferencesInNode(sourceFiles[0], symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex); } else { let internedName = getInternedName(symbol, node, declarations) @@ -4228,7 +4257,7 @@ module ts { if (lookUp(nameTable, internedName)) { result = result || []; - getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result); + getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex); } }); } @@ -4393,8 +4422,8 @@ module ts { return positions; } - function getLabelReferencesInNode(container: Node, targetLabel: Identifier): ReferenceEntry[] { - let result: ReferenceEntry[] = []; + function getLabelReferencesInNode(container: Node, targetLabel: Identifier): ReferencedSymbol[] { + let references: ReferenceEntry[] = []; let sourceFile = container.getSourceFile(); let labelName = targetLabel.text; let possiblePositions = getPossibleSymbolReferencePositions(sourceFile, labelName, container.getStart(), container.getEnd()); @@ -4409,10 +4438,20 @@ module ts { // Only pick labels that are either the target label, or have a target that is the target label if (node === targetLabel || (isJumpStatementTarget(node) && getTargetLabel(node, labelName) === targetLabel)) { - result.push(getReferenceEntryFromNode(node)); + references.push(getReferenceEntryFromNode(node)); } }); - return result; + + var definition: DefinitionInfo = { + containerKind: "", + containerName: "", + fileName: targetLabel.getSourceFile().fileName, + kind: ScriptElementKind.label, + name: labelName, + textSpan: createTextSpanFromBounds(targetLabel.getStart(), targetLabel.getEnd()) + } + + return [{ definition, references }]; } function isValidReferencePosition(node: Node, searchSymbolName: string): boolean { @@ -4452,7 +4491,9 @@ module ts { searchMeaning: SemanticMeaning, findInStrings: boolean, findInComments: boolean, - result: ReferenceEntry[]): void { + result: ReferencedSymbol[], + symbolToIndex: number[]): void { + let sourceFile = container.getSourceFile(); let tripleSlashDirectivePrefixRegex = /^\/\/\/\s*= 0) { - result.push(getReferenceEntryFromNode(referenceSymbolDeclaration.name)); + var referencedSymbol = getReferencedSymbol(shorthandValueSymbol); + referencedSymbol.references.push(getReferenceEntryFromNode(referenceSymbolDeclaration.name)); } } }); } + return; + + function getReferencedSymbol(symbol: Symbol): ReferencedSymbol { + var symbolId = getSymbolId(symbol); + var index = symbolToIndex[symbolId]; + if (index === undefined) { + index = result.length; + symbolToIndex[symbolId] = index; + + result.push({ + definition: getDefinitionInfos(node, symbol)[0], + references: [] + }); + } + + return result[index]; + } + function isInString(position: number) { let token = getTokenAtPosition(sourceFile, position); return token && token.kind === SyntaxKind.StringLiteral && position > token.getStart(); @@ -4532,7 +4598,7 @@ module ts { } } - function getReferencesForSuperKeyword(superKeyword: Node): ReferenceEntry[] { + function getReferencesForSuperKeyword(superKeyword: Node): ReferencedSymbol[] { let searchSpaceNode = getSuperContainer(superKeyword, /*includeFunctions*/ false); if (!searchSpaceNode) { return undefined; @@ -4555,7 +4621,7 @@ module ts { return undefined; } - let result: ReferenceEntry[] = []; + let references: ReferenceEntry[] = []; let sourceFile = searchSpaceNode.getSourceFile(); let possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "super", searchSpaceNode.getStart(), searchSpaceNode.getEnd()); @@ -4574,14 +4640,16 @@ module ts { // Now make sure the owning class is the same as the search-space // and has the same static qualifier as the original 'super's owner. if (container && (NodeFlags.Static & container.flags) === staticFlag && container.parent.symbol === searchSpaceNode.symbol) { - result.push(getReferenceEntryFromNode(node)); + references.push(getReferenceEntryFromNode(node)); } }); - return result; + var definitions = getDefinitionInfos(superKeyword, searchSpaceNode.symbol); + var definition = definitions[0]; + return [{ definition, references }]; } - function getReferencesForThisKeyword(thisOrSuperKeyword: Node, sourceFiles: SourceFile[]): ReferenceEntry[] { + function getReferencesForThisKeyword(thisOrSuperKeyword: Node, sourceFiles: SourceFile[]): ReferencedSymbol[] { let searchSpaceNode = getThisContainer(thisOrSuperKeyword, /* includeArrowFunctions */ false); // Whether 'this' occurs in a static context within a class. @@ -4616,22 +4684,32 @@ module ts { return undefined; } - let result: ReferenceEntry[] = []; + let references: ReferenceEntry[] = []; let possiblePositions: number[]; if (searchSpaceNode.kind === SyntaxKind.SourceFile) { forEach(sourceFiles, sourceFile => { possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", sourceFile.getStart(), sourceFile.getEnd()); - getThisReferencesInFile(sourceFile, sourceFile, possiblePositions, result); + getThisReferencesInFile(sourceFile, sourceFile, possiblePositions, references); }); } else { let sourceFile = searchSpaceNode.getSourceFile(); possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", searchSpaceNode.getStart(), searchSpaceNode.getEnd()); - getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, result); + getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, references); } - return result; + return [{ + definition: { + containerKind: "", + containerName: "", + fileName: node.getSourceFile().fileName, + kind: ScriptElementKind.variableElement, + name: "this", + textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()) + }, + references: references + }]; function getThisReferencesInFile(sourceFile: SourceFile, searchSpaceNode: Node, possiblePositions: number[], result: ReferenceEntry[]): void { forEach(possiblePositions, position => { @@ -4754,16 +4832,18 @@ module ts { } } - function isRelatableToSearchSet(searchSymbols: Symbol[], referenceSymbol: Symbol, referenceLocation: Node): boolean { + function getRelatedSymbol(searchSymbols: Symbol[], referenceSymbol: Symbol, referenceLocation: Node): Symbol { if (searchSymbols.indexOf(referenceSymbol) >= 0) { - return true; + return referenceSymbol; } // If the reference symbol is an alias, check if what it is aliasing is one of the search // symbols. - if (isImportOrExportSpecifierImportSymbol(referenceSymbol) && - searchSymbols.indexOf(typeInfoResolver.getAliasedSymbol(referenceSymbol)) >= 0) { - return true; + if (isImportOrExportSpecifierImportSymbol(referenceSymbol)) { + var aliasedSymbol = typeInfoResolver.getAliasedSymbol(referenceSymbol); + if (searchSymbols.indexOf(aliasedSymbol) >= 0) { + return aliasedSymbol; + } } // If the reference location is in an object literal, try to get the contextual type for the @@ -4771,7 +4851,7 @@ module ts { // compare to our searchSymbol if (isNameOfPropertyAssignment(referenceLocation)) { return forEach(getPropertySymbolsFromContextualType(referenceLocation), contextualSymbol => { - return forEach(typeInfoResolver.getRootSymbols(contextualSymbol), s => searchSymbols.indexOf(s) >= 0); + return forEach(typeInfoResolver.getRootSymbols(contextualSymbol), s => searchSymbols.indexOf(s) >= 0 ? s : undefined); }); } @@ -4780,7 +4860,7 @@ module ts { return forEach(typeInfoResolver.getRootSymbols(referenceSymbol), rootSymbol => { // if it is in the list, then we are done if (searchSymbols.indexOf(rootSymbol) >= 0) { - return true; + return rootSymbol; } // Finally, try all properties with the same name in any type the containing type extended or implemented, and @@ -4788,10 +4868,10 @@ module ts { if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { let result: Symbol[] = []; getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result); - return forEach(result, s => searchSymbols.indexOf(s) >= 0); + return forEach(result, s => searchSymbols.indexOf(s) >= 0 ? s : undefined); } - return false; + return undefined; }); } @@ -5747,6 +5827,7 @@ module ts { getQuickInfoAtPosition, getDefinitionAtPosition, getReferencesAtPosition, + findReferences, getOccurrencesAtPosition, getNameOrDottedNameSpan, getBreakpointStatementAtPosition, diff --git a/src/services/shims.ts b/src/services/shims.ts index 63e3c9e23ae..adcec5ada2b 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -127,6 +127,12 @@ module ts { * { fileName: string; textSpan: { start: number; length: number}; isWriteAccess: boolean }[] */ getReferencesAtPosition(fileName: string, position: number): string; + + /** + * Returns a JSON-encoded value of the type: + * { definition: ; references: [] }[] + */ + findReferences(fileName: string, position: number): string; /** * Returns a JSON-encoded value of the type: @@ -555,11 +561,6 @@ module ts { /// GET REFERENCES - /** - * Return references to a symbol at the requested position. - * References are separated by "\n". - * Each reference is a "fileindex min lim" sub-string. - */ public getReferencesAtPosition(fileName: string, position: number): string { return this.forwardJSONCall( "getReferencesAtPosition('" + fileName + "', " + position + ")", @@ -568,6 +569,14 @@ module ts { }); } + public findReferences(fileName: string, position: number): string { + return this.forwardJSONCall( + "findReferences('" + fileName + "', " + position + ")", + () => { + return this.languageService.findReferences(fileName, position); + }); + } + public getOccurrencesAtPosition(fileName: string, position: number): string { return this.forwardJSONCall( "getOccurrencesAtPosition('" + fileName + "', " + position + ")", diff --git a/tests/baselines/reference/APISample_compile.js b/tests/baselines/reference/APISample_compile.js index b8f4c7cdd13..0fd72e80158 100644 --- a/tests/baselines/reference/APISample_compile.js +++ b/tests/baselines/reference/APISample_compile.js @@ -1589,6 +1589,7 @@ declare module "typescript" { getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[]; getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[]; + findReferences(fileName: string, position: number): ReferencedSymbol[]; getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; getNavigationBarItems(fileName: string): NavigationBarItem[]; getOutliningSpans(fileName: string): OutliningSpan[]; @@ -1675,6 +1676,10 @@ declare module "typescript" { containerKind: string; containerName: string; } + interface ReferencedSymbol { + definition: DefinitionInfo; + references: ReferenceEntry[]; + } enum SymbolDisplayPartKind { aliasName = 0, className = 1, diff --git a/tests/baselines/reference/APISample_compile.types b/tests/baselines/reference/APISample_compile.types index e1d9aa58961..28763a896dd 100644 --- a/tests/baselines/reference/APISample_compile.types +++ b/tests/baselines/reference/APISample_compile.types @@ -5154,6 +5154,12 @@ declare module "typescript" { >position : number >ReferenceEntry : ReferenceEntry + findReferences(fileName: string, position: number): ReferencedSymbol[]; +>findReferences : (fileName: string, position: number) => ReferencedSymbol[] +>fileName : string +>position : number +>ReferencedSymbol : ReferencedSymbol + getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; >getNavigateToItems : (searchValue: string, maxResultCount?: number) => NavigateToItem[] >searchValue : string @@ -5424,6 +5430,17 @@ declare module "typescript" { containerName: string; >containerName : string + } + interface ReferencedSymbol { +>ReferencedSymbol : ReferencedSymbol + + definition: DefinitionInfo; +>definition : DefinitionInfo +>DefinitionInfo : DefinitionInfo + + references: ReferenceEntry[]; +>references : ReferenceEntry[] +>ReferenceEntry : ReferenceEntry } enum SymbolDisplayPartKind { >SymbolDisplayPartKind : SymbolDisplayPartKind diff --git a/tests/baselines/reference/APISample_linter.js b/tests/baselines/reference/APISample_linter.js index 4e3af8d1be0..a947821455d 100644 --- a/tests/baselines/reference/APISample_linter.js +++ b/tests/baselines/reference/APISample_linter.js @@ -1620,6 +1620,7 @@ declare module "typescript" { getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[]; getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[]; + findReferences(fileName: string, position: number): ReferencedSymbol[]; getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; getNavigationBarItems(fileName: string): NavigationBarItem[]; getOutliningSpans(fileName: string): OutliningSpan[]; @@ -1706,6 +1707,10 @@ declare module "typescript" { containerKind: string; containerName: string; } + interface ReferencedSymbol { + definition: DefinitionInfo; + references: ReferenceEntry[]; + } enum SymbolDisplayPartKind { aliasName = 0, className = 1, diff --git a/tests/baselines/reference/APISample_linter.types b/tests/baselines/reference/APISample_linter.types index 7cf542f0b40..34dc65aa351 100644 --- a/tests/baselines/reference/APISample_linter.types +++ b/tests/baselines/reference/APISample_linter.types @@ -5300,6 +5300,12 @@ declare module "typescript" { >position : number >ReferenceEntry : ReferenceEntry + findReferences(fileName: string, position: number): ReferencedSymbol[]; +>findReferences : (fileName: string, position: number) => ReferencedSymbol[] +>fileName : string +>position : number +>ReferencedSymbol : ReferencedSymbol + getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; >getNavigateToItems : (searchValue: string, maxResultCount?: number) => NavigateToItem[] >searchValue : string @@ -5570,6 +5576,17 @@ declare module "typescript" { containerName: string; >containerName : string + } + interface ReferencedSymbol { +>ReferencedSymbol : ReferencedSymbol + + definition: DefinitionInfo; +>definition : DefinitionInfo +>DefinitionInfo : DefinitionInfo + + references: ReferenceEntry[]; +>references : ReferenceEntry[] +>ReferenceEntry : ReferenceEntry } enum SymbolDisplayPartKind { >SymbolDisplayPartKind : SymbolDisplayPartKind diff --git a/tests/baselines/reference/APISample_transform.js b/tests/baselines/reference/APISample_transform.js index 261ad5b25f6..00c2c63ac10 100644 --- a/tests/baselines/reference/APISample_transform.js +++ b/tests/baselines/reference/APISample_transform.js @@ -1621,6 +1621,7 @@ declare module "typescript" { getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[]; getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[]; + findReferences(fileName: string, position: number): ReferencedSymbol[]; getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; getNavigationBarItems(fileName: string): NavigationBarItem[]; getOutliningSpans(fileName: string): OutliningSpan[]; @@ -1707,6 +1708,10 @@ declare module "typescript" { containerKind: string; containerName: string; } + interface ReferencedSymbol { + definition: DefinitionInfo; + references: ReferenceEntry[]; + } enum SymbolDisplayPartKind { aliasName = 0, className = 1, diff --git a/tests/baselines/reference/APISample_transform.types b/tests/baselines/reference/APISample_transform.types index eb4c7ab122f..c5b769888db 100644 --- a/tests/baselines/reference/APISample_transform.types +++ b/tests/baselines/reference/APISample_transform.types @@ -5250,6 +5250,12 @@ declare module "typescript" { >position : number >ReferenceEntry : ReferenceEntry + findReferences(fileName: string, position: number): ReferencedSymbol[]; +>findReferences : (fileName: string, position: number) => ReferencedSymbol[] +>fileName : string +>position : number +>ReferencedSymbol : ReferencedSymbol + getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; >getNavigateToItems : (searchValue: string, maxResultCount?: number) => NavigateToItem[] >searchValue : string @@ -5520,6 +5526,17 @@ declare module "typescript" { containerName: string; >containerName : string + } + interface ReferencedSymbol { +>ReferencedSymbol : ReferencedSymbol + + definition: DefinitionInfo; +>definition : DefinitionInfo +>DefinitionInfo : DefinitionInfo + + references: ReferenceEntry[]; +>references : ReferenceEntry[] +>ReferenceEntry : ReferenceEntry } enum SymbolDisplayPartKind { >SymbolDisplayPartKind : SymbolDisplayPartKind diff --git a/tests/baselines/reference/APISample_watcher.js b/tests/baselines/reference/APISample_watcher.js index a821bc5011d..91039f112b7 100644 --- a/tests/baselines/reference/APISample_watcher.js +++ b/tests/baselines/reference/APISample_watcher.js @@ -1658,6 +1658,7 @@ declare module "typescript" { getDefinitionAtPosition(fileName: string, position: number): DefinitionInfo[]; getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[]; getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[]; + findReferences(fileName: string, position: number): ReferencedSymbol[]; getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; getNavigationBarItems(fileName: string): NavigationBarItem[]; getOutliningSpans(fileName: string): OutliningSpan[]; @@ -1744,6 +1745,10 @@ declare module "typescript" { containerKind: string; containerName: string; } + interface ReferencedSymbol { + definition: DefinitionInfo; + references: ReferenceEntry[]; + } enum SymbolDisplayPartKind { aliasName = 0, className = 1, diff --git a/tests/baselines/reference/APISample_watcher.types b/tests/baselines/reference/APISample_watcher.types index 47e5a0147fd..09322aaa771 100644 --- a/tests/baselines/reference/APISample_watcher.types +++ b/tests/baselines/reference/APISample_watcher.types @@ -5423,6 +5423,12 @@ declare module "typescript" { >position : number >ReferenceEntry : ReferenceEntry + findReferences(fileName: string, position: number): ReferencedSymbol[]; +>findReferences : (fileName: string, position: number) => ReferencedSymbol[] +>fileName : string +>position : number +>ReferencedSymbol : ReferencedSymbol + getNavigateToItems(searchValue: string, maxResultCount?: number): NavigateToItem[]; >getNavigateToItems : (searchValue: string, maxResultCount?: number) => NavigateToItem[] >searchValue : string @@ -5693,6 +5699,17 @@ declare module "typescript" { containerName: string; >containerName : string + } + interface ReferencedSymbol { +>ReferencedSymbol : ReferencedSymbol + + definition: DefinitionInfo; +>definition : DefinitionInfo +>DefinitionInfo : DefinitionInfo + + references: ReferenceEntry[]; +>references : ReferenceEntry[] +>ReferenceEntry : ReferenceEntry } enum SymbolDisplayPartKind { >SymbolDisplayPartKind : SymbolDisplayPartKind diff --git a/tests/cases/fourslash/findAllRefsInsideWithBlock.ts b/tests/cases/fourslash/findAllRefsInsideWithBlock.ts index 394d30d89d3..fc8a94eaa2d 100644 --- a/tests/cases/fourslash/findAllRefsInsideWithBlock.ts +++ b/tests/cases/fourslash/findAllRefsInsideWithBlock.ts @@ -13,4 +13,4 @@ goTo.marker('1'); verify.referencesCountIs(3); goTo.marker('2'); -verify.referencesCountIs(1); \ No newline at end of file +verify.referencesCountIs(0); \ No newline at end of file diff --git a/tests/cases/fourslash/getOccurrencesOfUndefinedSymbol.ts b/tests/cases/fourslash/getOccurrencesOfUndefinedSymbol.ts index 4893a6079d1..96c3cdc2e39 100644 --- a/tests/cases/fourslash/getOccurrencesOfUndefinedSymbol.ts +++ b/tests/cases/fourslash/getOccurrencesOfUndefinedSymbol.ts @@ -17,4 +17,4 @@ // "any" should not be highlighted goTo.marker(); -verify.occurrencesAtPositionCount(1); +verify.occurrencesAtPositionCount(0); diff --git a/tests/cases/fourslash/getOccurrencesSwitchCaseDefaultBroken.ts b/tests/cases/fourslash/getOccurrencesSwitchCaseDefaultBroken.ts index 018280a4032..25ee54f8115 100644 --- a/tests/cases/fourslash/getOccurrencesSwitchCaseDefaultBroken.ts +++ b/tests/cases/fourslash/getOccurrencesSwitchCaseDefaultBroken.ts @@ -39,8 +39,6 @@ for (var i = 1; i <= test.markers().length; i++) { verify.occurrencesAtPositionCount(8); break; case 4: - case 5: - case 8: verify.occurrencesAtPositionCount(1); break; case 6: @@ -48,6 +46,8 @@ for (var i = 1; i <= test.markers().length; i++) { case 9: verify.occurrencesAtPositionCount(8); break; + case 5: + case 8: case 10: case 11: case 12: diff --git a/tests/cases/fourslash/getOccurrencesThisNegatives2.ts b/tests/cases/fourslash/getOccurrencesThisNegatives2.ts index 65fb37d09b4..0a8b5860feb 100644 --- a/tests/cases/fourslash/getOccurrencesThisNegatives2.ts +++ b/tests/cases/fourslash/getOccurrencesThisNegatives2.ts @@ -143,5 +143,5 @@ test.markers().forEach(m => { goTo.position(m.position, m.fileName) - verify.occurrencesAtPositionCount(1); + verify.occurrencesAtPositionCount(0); }); diff --git a/tests/cases/fourslash/getOccurrencesTryCatchFinallyBroken.ts b/tests/cases/fourslash/getOccurrencesTryCatchFinallyBroken.ts index c866ed96398..01a202a677c 100644 --- a/tests/cases/fourslash/getOccurrencesTryCatchFinallyBroken.ts +++ b/tests/cases/fourslash/getOccurrencesTryCatchFinallyBroken.ts @@ -36,6 +36,8 @@ for (var i = 1; i <= test.markers().length; i++) { switch (i) { case 1: + verify.occurrencesAtPositionCount(0); + break; case 2: case 3: verify.occurrencesAtPositionCount(1); diff --git a/tests/cases/fourslash/localGetReferences.ts b/tests/cases/fourslash/localGetReferences.ts index 6d5f541dd51..e5873efeeb4 100644 --- a/tests/cases/fourslash/localGetReferences.ts +++ b/tests/cases/fourslash/localGetReferences.ts @@ -223,7 +223,7 @@ verify.referencesCountIs(7); // References to unresolved symbol. goTo.marker("12"); -verify.referencesCountIs(1); +verify.referencesCountIs(0); // References to no context. goTo.marker("13"); diff --git a/tests/cases/fourslash/referencesForIllegalAssignment.ts b/tests/cases/fourslash/referencesForIllegalAssignment.ts index 81a45f64153..403bf0432a7 100644 --- a/tests/cases/fourslash/referencesForIllegalAssignment.ts +++ b/tests/cases/fourslash/referencesForIllegalAssignment.ts @@ -6,10 +6,10 @@ ////ba/*4*/r = b/*5*/ar + 1; goTo.marker("1"); -verify.referencesCountIs(1); +verify.referencesCountIs(0); goTo.marker("2"); -verify.referencesCountIs(1); +verify.referencesCountIs(0); goTo.marker("3"); verify.referencesCountIs(3); diff --git a/tests/cases/fourslash/referencesForIndexProperty2.ts b/tests/cases/fourslash/referencesForIndexProperty2.ts index fd9cf0e7183..9dcba3163c6 100644 --- a/tests/cases/fourslash/referencesForIndexProperty2.ts +++ b/tests/cases/fourslash/referencesForIndexProperty2.ts @@ -6,4 +6,4 @@ ////a[/*1*/"blah"]; goTo.marker("1"); -verify.referencesCountIs(1); \ No newline at end of file +verify.referencesCountIs(0); \ No newline at end of file diff --git a/tests/cases/fourslash/referencesForLabel2.ts b/tests/cases/fourslash/referencesForLabel2.ts index 054ce562864..ccb441d60b9 100644 --- a/tests/cases/fourslash/referencesForLabel2.ts +++ b/tests/cases/fourslash/referencesForLabel2.ts @@ -9,4 +9,4 @@ ////} goTo.marker("1"); -verify.referencesCountIs(1); +verify.referencesCountIs(0);