mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Factor out FindAllReferences.Core and use Definition/Entry to allow findReferencedSymbols and getImplementationsAtPosition to return different results
This commit is contained in:
1 parent
ec558b8080
commit
94f6839aec
7 files changed
+278
-197
No files matched your search
@@ -1849,7 +1849,6 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface ExternalModuleReference extends Node {
|
||||
parent: ImportEqualsDeclaration;
|
||||
kind: SyntaxKind.ExternalModuleReference;
|
||||
parent?: ImportEqualsDeclaration;
|
||||
expression?: Expression;
|
||||
@@ -1909,7 +1908,6 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface NamedExports extends Node {
|
||||
parent: ExportDeclaration;
|
||||
kind: SyntaxKind.NamedExports;
|
||||
parent?: ExportDeclaration;
|
||||
elements: NodeArray<ExportSpecifier>;
|
||||
@@ -1925,7 +1923,6 @@ namespace ts {
|
||||
}
|
||||
|
||||
export interface ExportSpecifier extends Declaration {
|
||||
parent: NamedExports;
|
||||
kind: SyntaxKind.ExportSpecifier;
|
||||
parent?: NamedExports;
|
||||
propertyName?: Identifier; // Name preceding "as" keyword (or undefined when "as" is absent)
|
||||
|
||||
@@ -992,7 +992,7 @@ namespace FourSlash {
|
||||
|
||||
for (const startRange of toArray(startRanges)) {
|
||||
this.goToRangeStart(startRange);
|
||||
const fullActual = this.findReferencesAtCaret().map<ReferenceJson>(({ definition, references }) => ({
|
||||
const fullActual = ts.map<ts.ReferencedSymbol, ReferenceJson>(this.findReferencesAtCaret(), ({ definition, references }) => ({
|
||||
definition: definition.displayParts.map(d => d.text).join(""),
|
||||
ranges: references
|
||||
}));
|
||||
@@ -2383,7 +2383,7 @@ namespace FourSlash {
|
||||
else {
|
||||
if (actual === undefined) {
|
||||
this.raiseError(`${name} failed - expected the template {newText: "${expected.newText}", caretOffset: "${expected.caretOffset}"} but got nothing instead`);
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (actual.newText !== expected.newText) {
|
||||
|
||||
@@ -21,19 +21,15 @@ namespace ts.DocumentHighlights {
|
||||
return referenceEntries && convertReferencedSymbols(referenceEntries);
|
||||
}
|
||||
|
||||
function convertReferencedSymbols(referenceEntries: ReferenceEntry[]): DocumentHighlights[] {
|
||||
function convertReferencedSymbols(referenceEntries: FindAllReferences.Entry[]): DocumentHighlights[] {
|
||||
const fileNameToDocumentHighlights = createMap<HighlightSpan[]>();
|
||||
for (const referenceEntry of referenceEntries) {
|
||||
const fileName = referenceEntry.fileName;
|
||||
for (const entry of referenceEntries) {
|
||||
const { fileName, span } = FindAllReferences.toHighlightSpan(entry);
|
||||
let highlightSpans = fileNameToDocumentHighlights.get(fileName);
|
||||
if (!highlightSpans) {
|
||||
fileNameToDocumentHighlights.set(fileName, highlightSpans = []);
|
||||
}
|
||||
|
||||
highlightSpans.push({
|
||||
textSpan: referenceEntry.textSpan,
|
||||
kind: referenceEntry.isWriteAccess ? HighlightSpanKind.writtenReference : HighlightSpanKind.reference
|
||||
});
|
||||
highlightSpans.push(span);
|
||||
}
|
||||
|
||||
return arrayFrom(fileNameToDocumentHighlights.entries(), ([fileName, highlightSpans ]) => ({ fileName, highlightSpans }));
|
||||
|
||||
+255
-183
@@ -2,6 +2,33 @@
|
||||
|
||||
/* @internal */
|
||||
namespace ts.FindAllReferences {
|
||||
export interface SymbolAndEntries {
|
||||
definition: Definition | undefined;
|
||||
references: Entry[];
|
||||
}
|
||||
|
||||
type Definition =
|
||||
| { type: "symbol"; symbol: Symbol; node: Node; }
|
||||
| { type: "label"; node: Identifier; }
|
||||
| { type: "keyword"; node: ts.Node; }
|
||||
| { type: "this"; node: ts.Node; }
|
||||
| { type: "string"; node: ts.StringLiteral };
|
||||
|
||||
export type Entry = NodeEntry | SpanEntry;
|
||||
export interface NodeEntry {
|
||||
type: "node";
|
||||
node: Node;
|
||||
isInString: boolean;
|
||||
}
|
||||
interface SpanEntry {
|
||||
type: "span";
|
||||
fileName: string;
|
||||
textSpan: TextSpan;
|
||||
}
|
||||
export function nodeEntry(node: ts.Node, isInString = false): NodeEntry {
|
||||
return { type: "node", node, isInString };
|
||||
}
|
||||
|
||||
export interface Options {
|
||||
readonly findInStrings?: boolean;
|
||||
readonly findInComments?: boolean;
|
||||
@@ -16,29 +43,41 @@ namespace ts.FindAllReferences {
|
||||
|
||||
export function findReferencedSymbols(checker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], sourceFile: SourceFile, position: number): ReferencedSymbol[] | undefined {
|
||||
const referencedSymbols = findAllReferencedSymbols(checker, cancellationToken, sourceFiles, sourceFile, position);
|
||||
// Only include referenced symbols that have a valid definition.
|
||||
return filter(referencedSymbols, rs => !!rs.definition);
|
||||
|
||||
if (!referencedSymbols || !referencedSymbols.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const out: ReferencedSymbol[] = [];
|
||||
for (const { definition, references } of referencedSymbols) {
|
||||
// Only include referenced symbols that have a valid definition.
|
||||
if (definition) {
|
||||
out.push({ definition: definitionToReferencedSymbolDefinitionInfo(definition, checker), references: references.map(toReferenceEntry) });
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
export function getImplementationsAtPosition(checker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], sourceFile: SourceFile, position: number): ImplementationLocation[] {
|
||||
const node = getTouchingPropertyName(sourceFile, position);
|
||||
const referenceEntries = getImplementationReferenceEntries(checker, cancellationToken, sourceFiles, node);
|
||||
return map(referenceEntries, ({ textSpan, fileName }) => ({ textSpan, fileName }));
|
||||
return map(referenceEntries, entry => toImplementationLocation(entry, checker));
|
||||
}
|
||||
|
||||
function getImplementationReferenceEntries(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], node: Node): ReferenceEntry[] {
|
||||
function getImplementationReferenceEntries(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], node: Node): Entry[] | undefined {
|
||||
// If invoked directly on a shorthand property assignment, then return
|
||||
// the declaration of the symbol being assigned (not the symbol being assigned to).
|
||||
if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
|
||||
const result: ReferenceEntry[] = [];
|
||||
getReferenceEntriesForShorthandPropertyAssignment(node, typeChecker, node => result.push(getReferenceEntryFromNode(node)));
|
||||
const result: NodeEntry[] = [];
|
||||
Core.getReferenceEntriesForShorthandPropertyAssignment(node, typeChecker, node => result.push(nodeEntry(node)));
|
||||
return result;
|
||||
}
|
||||
else if (node.kind === SyntaxKind.SuperKeyword || isSuperProperty(node.parent)) {
|
||||
// References to and accesses on the super keyword only have one possible implementation, so no
|
||||
// need to "Find all References"
|
||||
const symbol = typeChecker.getSymbolAtLocation(node);
|
||||
return symbol.valueDeclaration && [getReferenceEntryFromNode(symbol.valueDeclaration)];
|
||||
return symbol.valueDeclaration && [nodeEntry(symbol.valueDeclaration)];
|
||||
}
|
||||
else {
|
||||
// Perform "Find all References" and retrieve only those that are implementations
|
||||
@@ -47,30 +86,188 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
|
||||
export function findReferencedEntries(checker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], sourceFile: SourceFile, position: number, options?: Options): ReferenceEntry[] | undefined {
|
||||
return flattenEntries(findAllReferencedSymbols(checker, cancellationToken, sourceFiles, sourceFile, position, options));
|
||||
const x = flattenEntries(findAllReferencedSymbols(checker, cancellationToken, sourceFiles, sourceFile, position, options));
|
||||
return map(x, toReferenceEntry);
|
||||
}
|
||||
|
||||
function findAllReferencedSymbols(checker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], sourceFile: SourceFile, position: number, options?: Options): ReferencedSymbol[] | undefined {
|
||||
export function getReferenceEntriesForNode(node: Node, sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken, options: Options = {}): Entry[] | undefined {
|
||||
return flattenEntries(Core.getReferencedSymbolsForNode(node, sourceFiles, checker, cancellationToken, options));
|
||||
}
|
||||
|
||||
function findAllReferencedSymbols(checker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], sourceFile: SourceFile, position: number, options?: Options): SymbolAndEntries[] | undefined {
|
||||
const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
|
||||
return getReferencedSymbolsForNode(node, sourceFiles, checker, cancellationToken, options);
|
||||
return Core.getReferencedSymbolsForNode(node, sourceFiles, checker, cancellationToken, options);
|
||||
}
|
||||
|
||||
export function getReferenceEntriesForNode(node: Node, sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken, options: Options = {}): ReferenceEntry[] | undefined {
|
||||
return flattenEntries(getReferencedSymbolsForNode(node, sourceFiles, checker, cancellationToken, options));
|
||||
}
|
||||
|
||||
function flattenEntries(referenceSymbols: ReferencedSymbol[]): ReferenceEntry[] {
|
||||
function flattenEntries(referenceSymbols: SymbolAndEntries[]): Entry[] {
|
||||
return referenceSymbols && flatMap(referenceSymbols, r => r.references);
|
||||
}
|
||||
|
||||
function definitionToReferencedSymbolDefinitionInfo(def: Definition, checker: TypeChecker): ReferencedSymbolDefinitionInfo | undefined {
|
||||
const info = (() => {
|
||||
switch (def.type) {
|
||||
case "symbol": {
|
||||
const { symbol, node } = def;
|
||||
const declarations = symbol.declarations;
|
||||
if (!declarations || declarations.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const { displayParts, kind } = getDefinitionKindAndDisplayParts(symbol, node, checker);
|
||||
const name = displayParts.map(p => p.text).join("");
|
||||
return { node, name, kind, displayParts };
|
||||
}
|
||||
case "label": {
|
||||
const { node } = def;
|
||||
return { node, name: node.text, kind: ScriptElementKind.label, displayParts: [displayPart(node.text, SymbolDisplayPartKind.text)] };
|
||||
}
|
||||
case "keyword": {
|
||||
const { node } = def;
|
||||
const name = tokenToString(node.kind);
|
||||
return { node, name, kind: ScriptElementKind.keyword, displayParts: [{ text: name, kind: ScriptElementKind.keyword }] };
|
||||
}
|
||||
case "this": {
|
||||
const { node } = def;
|
||||
const symbol = checker.getSymbolAtLocation(node);
|
||||
const displayParts = symbol && SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(
|
||||
checker, symbol, node.getSourceFile(), getContainerNode(node), node).displayParts;
|
||||
return { node, name: "this", kind: ScriptElementKind.variableElement, displayParts };
|
||||
}
|
||||
case "string": {
|
||||
const { node } = def;
|
||||
return { node, name: node.text, kind: ScriptElementKind.variableElement, displayParts: [displayPart(getTextOfNode(node), SymbolDisplayPartKind.stringLiteral)] };
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
if (!info) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { node, name, kind, displayParts } = info;
|
||||
const sourceFile = node.getSourceFile();
|
||||
return {
|
||||
containerKind: "",
|
||||
containerName: "",
|
||||
fileName: sourceFile.fileName,
|
||||
kind,
|
||||
name,
|
||||
textSpan: createTextSpanFromNode(node, sourceFile),
|
||||
displayParts
|
||||
};
|
||||
}
|
||||
|
||||
function getDefinitionKindAndDisplayParts(symbol: Symbol, node: Node, checker: TypeChecker): { displayParts: SymbolDisplayPart[], kind: string } {
|
||||
const { displayParts, symbolKind } =
|
||||
SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, node.getSourceFile(), getContainerNode(node), node);
|
||||
return { displayParts, kind: symbolKind };
|
||||
}
|
||||
|
||||
function toReferenceEntry(entry: Entry): ReferenceEntry {
|
||||
if (entry.type === "span") {
|
||||
return { textSpan: entry.textSpan, fileName: entry.fileName, isWriteAccess: false, isDefinition: false };
|
||||
}
|
||||
|
||||
const { node, isInString } = entry;
|
||||
return {
|
||||
fileName: node.getSourceFile().fileName,
|
||||
textSpan: getTextSpan(node),
|
||||
isWriteAccess: isWriteAccess(node),
|
||||
isDefinition: isDeclarationName(node) || isLiteralComputedPropertyDeclarationName(node),
|
||||
isInString: isInString ? true : undefined
|
||||
};
|
||||
}
|
||||
|
||||
function toImplementationLocation(entry: Entry, checker: ts.TypeChecker): ImplementationLocation {
|
||||
if (entry.type === "node") {
|
||||
const { node } = entry;
|
||||
return { textSpan: getTextSpan(node), fileName: node.getSourceFile().fileName, ...implementationKindDisplayParts(node, checker) };
|
||||
} else {
|
||||
const { textSpan, fileName } = entry;
|
||||
return { textSpan, fileName, kind: ScriptElementKind.unknown, displayParts: [] };
|
||||
}
|
||||
}
|
||||
|
||||
function implementationKindDisplayParts(node: ts.Node, checker: ts.TypeChecker): { kind: string, displayParts: SymbolDisplayPart[] } {
|
||||
const symbol = checker.getSymbolAtLocation(isDeclaration(node) && node.name ? node.name : node);
|
||||
if (symbol) {
|
||||
return getDefinitionKindAndDisplayParts(symbol, node, checker);
|
||||
}
|
||||
else if (node.kind === SyntaxKind.ObjectLiteralExpression) {
|
||||
return {
|
||||
kind: ScriptElementKind.interfaceElement,
|
||||
displayParts: [punctuationPart(SyntaxKind.OpenParenToken), textPart("object literal"), punctuationPart(SyntaxKind.CloseParenToken)]
|
||||
};
|
||||
}
|
||||
else if (node.kind === SyntaxKind.ClassExpression) {
|
||||
return {
|
||||
kind: ScriptElementKind.localClassElement,
|
||||
displayParts: [punctuationPart(SyntaxKind.OpenParenToken), textPart("anonymous local class"), punctuationPart(SyntaxKind.CloseParenToken)]
|
||||
};
|
||||
}
|
||||
else {
|
||||
return { kind: getNodeKind(node), displayParts: [] };
|
||||
}
|
||||
}
|
||||
|
||||
export function toHighlightSpan(entry: FindAllReferences.Entry): { fileName: string, span: HighlightSpan } {
|
||||
if (entry.type === "span") {
|
||||
const { fileName, textSpan } = entry;
|
||||
return { fileName, span: { textSpan, kind: HighlightSpanKind.reference } };
|
||||
}
|
||||
|
||||
const { node, isInString } = entry;
|
||||
const fileName = entry.node.getSourceFile().fileName;
|
||||
const writeAccess = isWriteAccess(node);
|
||||
const span: HighlightSpan = {
|
||||
textSpan: getTextSpan(node),
|
||||
kind: writeAccess ? HighlightSpanKind.writtenReference : HighlightSpanKind.reference,
|
||||
isInString: isInString ? true : undefined
|
||||
};
|
||||
return { fileName, span };
|
||||
}
|
||||
|
||||
function getTextSpan(node: Node): TextSpan {
|
||||
let start = node.getStart();
|
||||
let end = node.getEnd();
|
||||
if (node.kind === SyntaxKind.StringLiteral) {
|
||||
start += 1;
|
||||
end -= 1;
|
||||
}
|
||||
return createTextSpanFromBounds(start, end);
|
||||
}
|
||||
|
||||
/** A node is considered a writeAccess iff it is a name of a declaration or a target of an assignment */
|
||||
function isWriteAccess(node: Node): boolean {
|
||||
if (node.kind === SyntaxKind.Identifier && isDeclarationName(node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const parent = node.parent;
|
||||
if (parent) {
|
||||
if (parent.kind === SyntaxKind.PostfixUnaryExpression || parent.kind === SyntaxKind.PrefixUnaryExpression) {
|
||||
return true;
|
||||
}
|
||||
else if (parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>parent).left === node) {
|
||||
const operator = (<BinaryExpression>parent).operatorToken.kind;
|
||||
return SyntaxKind.FirstAssignment <= operator && operator <= SyntaxKind.LastAssignment;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Encapsulates the core find-all-references algorithm. */
|
||||
/* @internal */
|
||||
namespace ts.FindAllReferences.Core {
|
||||
/** Core find-all-references algorithm. Handles special cases before delegating to `getReferencedSymbolsForSymbol`. */
|
||||
function getReferencedSymbolsForNode(node: Node, sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken, options: Options = {}): ReferencedSymbol[] | undefined {
|
||||
export function getReferencedSymbolsForNode(node: Node, sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken, options: Options = {}): SymbolAndEntries[] | undefined {
|
||||
if (node.kind === ts.SyntaxKind.SourceFile) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!options.implementations) {
|
||||
const special = getReferencedSymbolsSpecial(node, sourceFiles, checker, cancellationToken);
|
||||
const special = getReferencedSymbolsSpecial(node, sourceFiles, cancellationToken);
|
||||
if (special) {
|
||||
return special;
|
||||
}
|
||||
@@ -97,7 +294,7 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
|
||||
/** getReferencedSymbols for special node kinds. */
|
||||
function getReferencedSymbolsSpecial(node: Node, sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken): ReferencedSymbol[] | undefined {
|
||||
function getReferencedSymbolsSpecial(node: Node, sourceFiles: SourceFile[], cancellationToken: CancellationToken): SymbolAndEntries[] | undefined {
|
||||
if (isTypeKeyword(node.kind)) {
|
||||
return getAllReferencesForKeyword(sourceFiles, node.kind, cancellationToken);
|
||||
}
|
||||
@@ -117,24 +314,24 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
|
||||
if (isThis(node)) {
|
||||
return getReferencesForThisKeyword(node, sourceFiles, checker, cancellationToken);
|
||||
return getReferencesForThisKeyword(node, sourceFiles, cancellationToken);
|
||||
}
|
||||
|
||||
if (node.kind === SyntaxKind.SuperKeyword) {
|
||||
return getReferencesForSuperKeyword(node, checker, cancellationToken);
|
||||
return getReferencesForSuperKeyword(node, cancellationToken);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Core find-all-references algorithm for a normal symbol. */
|
||||
function getReferencedSymbolsForSymbol(symbol: Symbol, node: Node, sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken, options: Options): ReferencedSymbol[] {
|
||||
function getReferencedSymbolsForSymbol(symbol: Symbol, node: Node, sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken, options: Options): SymbolAndEntries[] {
|
||||
symbol = skipPastExportOrImportSpecifier(symbol, node, checker);
|
||||
|
||||
// Compute the meaning from the location and the symbol it references
|
||||
const searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), symbol.declarations);
|
||||
|
||||
const result: ReferencedSymbol[] = [];
|
||||
const result: SymbolAndEntries[] = [];
|
||||
const state = createState(sourceFiles, node, checker, cancellationToken, searchMeaning, options, result);
|
||||
const search = state.createSearch(node, symbol, /*comingFrom*/undefined, { allSearchSymbols: populateSearchSymbolSet(symbol, node, checker, options.implementations) });
|
||||
|
||||
@@ -248,8 +445,8 @@ namespace ts.FindAllReferences {
|
||||
markSeenReExportRHS(rhs: Identifier): boolean;
|
||||
}
|
||||
|
||||
function createState(sourceFiles: SourceFile[], originalLocation: Node, checker: TypeChecker, cancellationToken: CancellationToken, searchMeaning: SemanticMeaning, options: Options, result: Push<ReferencedSymbol>): State {
|
||||
const symbolIdToReferences: ReferenceEntry[][] = [];
|
||||
function createState(sourceFiles: SourceFile[], originalLocation: Node, checker: TypeChecker, cancellationToken: CancellationToken, searchMeaning: SemanticMeaning, options: Options, result: Push<SymbolAndEntries>): State {
|
||||
const symbolIdToReferences: Entry[][] = [];
|
||||
const inheritsFromCache = createMap<boolean>();
|
||||
// Source file ID → symbol ID → Whether the symbol has been searched for in the source file.
|
||||
const sourceFileToSeenSymbols: Array<Array<true>> = [];
|
||||
@@ -284,15 +481,15 @@ namespace ts.FindAllReferences {
|
||||
let references = symbolIdToReferences[symbolId];
|
||||
if (!references) {
|
||||
references = symbolIdToReferences[symbolId] = [];
|
||||
result.push({ definition: getDefinition(referenceSymbol, searchLocation, checker), references });
|
||||
result.push({ definition: { type: "symbol", symbol: referenceSymbol, node: searchLocation }, references });
|
||||
}
|
||||
return node => references.push(getReferenceEntryFromNode(node));
|
||||
return node => references.push({ type: "node", node, isInString: false });
|
||||
}
|
||||
|
||||
function addStringOrCommentReference(fileName: string, textSpan: TextSpan): void {
|
||||
result.push({
|
||||
definition: undefined,
|
||||
references: [{ fileName, textSpan, isWriteAccess: false, isDefinition: false }]
|
||||
references: [{ type: "span", fileName, textSpan }]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -361,26 +558,6 @@ namespace ts.FindAllReferences {
|
||||
return getNameTable(sourceFile).get(escapedName) !== undefined;
|
||||
}
|
||||
|
||||
function getDefinition(symbol: Symbol, node: Node, checker: TypeChecker): ReferencedSymbolDefinitionInfo | undefined {
|
||||
const declarations = symbol.declarations;
|
||||
if (!declarations || declarations.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { displayParts, symbolKind } =
|
||||
SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, node.getSourceFile(), getContainerNode(node), node);
|
||||
const name = displayParts.map(p => p.text).join("");
|
||||
return {
|
||||
containerKind: "",
|
||||
containerName: "",
|
||||
name,
|
||||
kind: symbolKind,
|
||||
fileName: declarations[0].getSourceFile().fileName,
|
||||
textSpan: createTextSpan(declarations[0].getStart(), 0),
|
||||
displayParts
|
||||
};
|
||||
}
|
||||
|
||||
function getPropertySymbolOfDestructuringAssignment(location: Node, checker: TypeChecker): Symbol | undefined {
|
||||
return isArrayLiteralOrObjectLiteralDestructuringPattern(location.parent.parent) &&
|
||||
checker.getPropertySymbolOfDestructuringAssignment(<Identifier>location);
|
||||
@@ -509,8 +686,8 @@ namespace ts.FindAllReferences {
|
||||
return positions;
|
||||
}
|
||||
|
||||
function getLabelReferencesInNode(container: Node, targetLabel: Identifier, cancellationToken: CancellationToken): ReferencedSymbol[] {
|
||||
const references: ReferenceEntry[] = [];
|
||||
function getLabelReferencesInNode(container: Node, targetLabel: Identifier, cancellationToken: CancellationToken): SymbolAndEntries[] {
|
||||
const references: Entry[] = [];
|
||||
const sourceFile = container.getSourceFile();
|
||||
const labelName = targetLabel.text;
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, labelName, container.getStart(), container.getEnd(), cancellationToken);
|
||||
@@ -525,21 +702,11 @@ namespace ts.FindAllReferences {
|
||||
// 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)) {
|
||||
references.push(getReferenceEntryFromNode(node));
|
||||
references.push(nodeEntry(node));
|
||||
}
|
||||
}
|
||||
|
||||
const definition: ReferencedSymbolDefinitionInfo = {
|
||||
containerKind: "",
|
||||
containerName: "",
|
||||
fileName: targetLabel.getSourceFile().fileName,
|
||||
kind: ScriptElementKind.label,
|
||||
name: labelName,
|
||||
textSpan: createTextSpanFromNode(targetLabel, sourceFile),
|
||||
displayParts: [displayPart(labelName, SymbolDisplayPartKind.text)]
|
||||
};
|
||||
|
||||
return [{ definition, references }];
|
||||
return [{ definition: { type: "label", node: targetLabel }, references }];
|
||||
}
|
||||
|
||||
function isValidReferencePosition(node: Node, searchSymbolName: string): boolean {
|
||||
@@ -561,41 +728,22 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
|
||||
function getAllReferencesForKeyword(sourceFiles: SourceFile[], keywordKind: ts.SyntaxKind, cancellationToken: CancellationToken): ReferencedSymbol[] {
|
||||
const name = tokenToString(keywordKind);
|
||||
const references: ReferenceEntry[] = [];
|
||||
function getAllReferencesForKeyword(sourceFiles: SourceFile[], keywordKind: ts.SyntaxKind, cancellationToken: CancellationToken): SymbolAndEntries[] {
|
||||
const references: NodeEntry[] = [];
|
||||
for (const sourceFile of sourceFiles) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
addReferencesForKeywordInFile(sourceFile, keywordKind, name, cancellationToken, references);
|
||||
addReferencesForKeywordInFile(sourceFile, keywordKind, tokenToString(keywordKind), cancellationToken, references);
|
||||
}
|
||||
|
||||
if (!references.length) return undefined;
|
||||
|
||||
const definition: ReferencedSymbolDefinitionInfo = {
|
||||
containerKind: "",
|
||||
containerName: "",
|
||||
fileName: references[0].fileName,
|
||||
kind: ScriptElementKind.keyword,
|
||||
name,
|
||||
textSpan: references[0].textSpan,
|
||||
displayParts: [{ text: name, kind: ScriptElementKind.keyword }]
|
||||
};
|
||||
|
||||
return [{ definition, references }];
|
||||
return references.length ? [{ definition: { type: "keyword", node: references[0].node }, references }] : undefined;
|
||||
}
|
||||
|
||||
function addReferencesForKeywordInFile(sourceFile: SourceFile, kind: SyntaxKind, searchText: string, cancellationToken: CancellationToken, references: Push<ReferenceEntry>): void {
|
||||
function addReferencesForKeywordInFile(sourceFile: SourceFile, kind: SyntaxKind, searchText: string, cancellationToken: CancellationToken, references: Push<NodeEntry>): void {
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, sourceFile.getStart(), sourceFile.getEnd(), cancellationToken);
|
||||
for (const position of possiblePositions) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
const referenceLocation = getTouchingPropertyName(sourceFile, position);
|
||||
if (referenceLocation.kind === kind) {
|
||||
references.push({
|
||||
textSpan: createTextSpanFromNode(referenceLocation),
|
||||
fileName: sourceFile.fileName,
|
||||
isWriteAccess: false,
|
||||
isDefinition: false,
|
||||
});
|
||||
references.push(nodeEntry(referenceLocation));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -683,7 +831,7 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
|
||||
if (!propertyName) {
|
||||
addRef()
|
||||
addRef();
|
||||
}
|
||||
else if (referenceLocation === propertyName) {
|
||||
// For `export { foo as bar } from "baz"`, "`foo`" will be added from the singleReferences for import searches of the original export.
|
||||
@@ -1034,7 +1182,7 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
|
||||
function getReferencesForSuperKeyword(superKeyword: Node, checker: TypeChecker, cancellationToken: CancellationToken): ReferencedSymbol[] {
|
||||
function getReferencesForSuperKeyword(superKeyword: Node, cancellationToken: CancellationToken): SymbolAndEntries[] {
|
||||
let searchSpaceNode = getSuperContainer(superKeyword, /*stopOnFunctions*/ false);
|
||||
if (!searchSpaceNode) {
|
||||
return undefined;
|
||||
@@ -1057,7 +1205,7 @@ namespace ts.FindAllReferences {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const references: ReferenceEntry[] = [];
|
||||
const references: Entry[] = [];
|
||||
|
||||
const sourceFile = searchSpaceNode.getSourceFile();
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "super", searchSpaceNode.getStart(), searchSpaceNode.getEnd(), cancellationToken);
|
||||
@@ -1076,15 +1224,14 @@ namespace ts.FindAllReferences {
|
||||
// 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 && (ModifierFlags.Static & getModifierFlags(container)) === staticFlag && container.parent.symbol === searchSpaceNode.symbol) {
|
||||
references.push(getReferenceEntryFromNode(node));
|
||||
references.push(nodeEntry(node));
|
||||
}
|
||||
}
|
||||
|
||||
const definition = getDefinition(searchSpaceNode.symbol, superKeyword, checker);
|
||||
return [{ definition, references }];
|
||||
return [{ definition: { type: "symbol", symbol: searchSpaceNode.symbol, node: superKeyword }, references }];
|
||||
}
|
||||
|
||||
function getReferencesForThisKeyword(thisOrSuperKeyword: Node, sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken): ReferencedSymbol[] {
|
||||
function getReferencesForThisKeyword(thisOrSuperKeyword: Node, sourceFiles: SourceFile[], cancellationToken: CancellationToken): SymbolAndEntries[] {
|
||||
let searchSpaceNode = getThisContainer(thisOrSuperKeyword, /* includeArrowFunctions */ false);
|
||||
|
||||
// Whether 'this' occurs in a static context within a class.
|
||||
@@ -1119,7 +1266,7 @@ namespace ts.FindAllReferences {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const references: ReferenceEntry[] = [];
|
||||
const references: Entry[] = [];
|
||||
|
||||
let possiblePositions: number[];
|
||||
if (searchSpaceNode.kind === SyntaxKind.SourceFile) {
|
||||
@@ -1134,25 +1281,12 @@ namespace ts.FindAllReferences {
|
||||
getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, references);
|
||||
}
|
||||
|
||||
const thisOrSuperSymbol = checker.getSymbolAtLocation(thisOrSuperKeyword);
|
||||
|
||||
const displayParts = thisOrSuperSymbol && SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(
|
||||
checker, thisOrSuperSymbol, thisOrSuperKeyword.getSourceFile(), getContainerNode(thisOrSuperKeyword), thisOrSuperKeyword).displayParts;
|
||||
|
||||
return [{
|
||||
definition: {
|
||||
containerKind: "",
|
||||
containerName: "",
|
||||
fileName: thisOrSuperKeyword.getSourceFile().fileName,
|
||||
kind: ScriptElementKind.variableElement,
|
||||
name: "this",
|
||||
textSpan: createTextSpanFromNode(thisOrSuperKeyword),
|
||||
displayParts
|
||||
},
|
||||
references: references
|
||||
definition: { type: "this", node: thisOrSuperKeyword },
|
||||
references
|
||||
}];
|
||||
|
||||
function getThisReferencesInFile(sourceFile: SourceFile, searchSpaceNode: Node, possiblePositions: number[], result: ReferenceEntry[]): void {
|
||||
function getThisReferencesInFile(sourceFile: SourceFile, searchSpaceNode: Node, possiblePositions: number[], result: Entry[]): void {
|
||||
forEach(possiblePositions, position => {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
@@ -1167,13 +1301,13 @@ namespace ts.FindAllReferences {
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.FunctionDeclaration:
|
||||
if (searchSpaceNode.symbol === container.symbol) {
|
||||
result.push(getReferenceEntryFromNode(node));
|
||||
result.push(nodeEntry(node));
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.MethodDeclaration:
|
||||
case SyntaxKind.MethodSignature:
|
||||
if (isObjectLiteralMethod(searchSpaceNode) && searchSpaceNode.symbol === container.symbol) {
|
||||
result.push(getReferenceEntryFromNode(node));
|
||||
result.push(nodeEntry(node));
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.ClassExpression:
|
||||
@@ -1181,12 +1315,12 @@ namespace ts.FindAllReferences {
|
||||
// Make sure the container belongs to the same class
|
||||
// and has the appropriate static modifier from the original container.
|
||||
if (container.parent && searchSpaceNode.symbol === container.parent.symbol && (getModifierFlags(container) & ModifierFlags.Static) === staticFlag) {
|
||||
result.push(getReferenceEntryFromNode(node));
|
||||
result.push(nodeEntry(node));
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.SourceFile:
|
||||
if (container.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>container)) {
|
||||
result.push(getReferenceEntryFromNode(node));
|
||||
result.push(nodeEntry(node));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1194,7 +1328,7 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
|
||||
function getReferencesForStringLiteral(node: StringLiteral, sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken): ReferencedSymbol[] {
|
||||
function getReferencesForStringLiteral(node: StringLiteral, sourceFiles: SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken): SymbolAndEntries[] {
|
||||
const type = getStringLiteralTypeForNode(node, checker);
|
||||
|
||||
if (!type) {
|
||||
@@ -1202,7 +1336,7 @@ namespace ts.FindAllReferences {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const references: ReferenceEntry[] = [];
|
||||
const references: NodeEntry[] = [];
|
||||
|
||||
for (const sourceFile of sourceFiles) {
|
||||
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, type.text, sourceFile.getStart(), sourceFile.getEnd(), cancellationToken);
|
||||
@@ -1210,19 +1344,11 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
|
||||
return [{
|
||||
definition: {
|
||||
containerKind: "",
|
||||
containerName: "",
|
||||
fileName: node.getSourceFile().fileName,
|
||||
kind: ScriptElementKind.variableElement,
|
||||
name: type.text,
|
||||
textSpan: createTextSpanFromNode(node),
|
||||
displayParts: [displayPart(getTextOfNode(node), SymbolDisplayPartKind.stringLiteral)]
|
||||
},
|
||||
references: references
|
||||
definition: { type: "string", node },
|
||||
references
|
||||
}];
|
||||
|
||||
function getReferencesForStringLiteralInFile(sourceFile: SourceFile, searchType: Type, possiblePositions: number[], references: ReferenceEntry[]): void {
|
||||
function getReferencesForStringLiteralInFile(sourceFile: SourceFile, searchType: Type, possiblePositions: number[], references: Push<NodeEntry>): void {
|
||||
for (const position of possiblePositions) {
|
||||
cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
@@ -1233,7 +1359,7 @@ namespace ts.FindAllReferences {
|
||||
|
||||
const type = getStringLiteralTypeForNode(<StringLiteral>node, checker);
|
||||
if (type === searchType) {
|
||||
references.push(getReferenceEntryFromNode(node));
|
||||
references.push(nodeEntry(node, /*isInString*/true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1537,7 +1663,7 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
|
||||
function getReferenceEntriesForShorthandPropertyAssignment(node: Node, checker: TypeChecker, addReference: (node: Node) => void): void {
|
||||
export function getReferenceEntriesForShorthandPropertyAssignment(node: Node, checker: TypeChecker, addReference: (node: Node) => void): void {
|
||||
const refSymbol = checker.getSymbolAtLocation(node);
|
||||
const shorthandSymbol = checker.getShorthandAssignmentValueSymbol(refSymbol.valueDeclaration);
|
||||
|
||||
@@ -1550,45 +1676,6 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
}
|
||||
|
||||
function getReferenceEntryFromNode(node: Node): ReferenceEntry {
|
||||
return {
|
||||
fileName: node.getSourceFile().fileName,
|
||||
textSpan: getTextSpan(node),
|
||||
isWriteAccess: isWriteAccess(node),
|
||||
isDefinition: isDeclarationName(node) || isLiteralComputedPropertyDeclarationName(node)
|
||||
};
|
||||
}
|
||||
|
||||
function getTextSpan(node: Node): TextSpan {
|
||||
let start = node.getStart();
|
||||
let end = node.getEnd();
|
||||
if (node.kind === SyntaxKind.StringLiteral) {
|
||||
start += 1;
|
||||
end -= 1;
|
||||
}
|
||||
return createTextSpanFromBounds(start, end);
|
||||
}
|
||||
|
||||
/** A node is considered a writeAccess iff it is a name of a declaration or a target of an assignment */
|
||||
function isWriteAccess(node: Node): boolean {
|
||||
if (node.kind === SyntaxKind.Identifier && isDeclarationName(node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const parent = node.parent;
|
||||
if (parent) {
|
||||
if (parent.kind === SyntaxKind.PostfixUnaryExpression || parent.kind === SyntaxKind.PrefixUnaryExpression) {
|
||||
return true;
|
||||
}
|
||||
else if (parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>parent).left === node) {
|
||||
const operator = (<BinaryExpression>parent).operatorToken.kind;
|
||||
return SyntaxKind.FirstAssignment <= operator && operator <= SyntaxKind.LastAssignment;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function forEachDescendantOfKind(node: Node, kind: SyntaxKind, action: (node: Node) => void): void {
|
||||
forEachChild(node, child => {
|
||||
if (child.kind === kind) {
|
||||
@@ -1639,19 +1726,4 @@ namespace ts.FindAllReferences {
|
||||
return getSymbolsForClassAndInterfaceComponents(<UnionOrIntersectionType>localParentType);
|
||||
}
|
||||
}
|
||||
|
||||
/** True if the symbol is for an external module, as opposed to a namespace. */
|
||||
export function isExternalModuleSymbol(moduleSymbol: Symbol): boolean {
|
||||
Debug.assert(!!(moduleSymbol.flags & SymbolFlags.Module));
|
||||
return moduleSymbol.name.charCodeAt(0) === CharacterCodes.doubleQuote;
|
||||
}
|
||||
|
||||
/** Returns `true` the first time it encounters a node and `false` afterwards. */
|
||||
export function nodeSeenTracker<T extends Node>(): (node: T) => boolean {
|
||||
const seen: Array<true> = [];
|
||||
return node => {
|
||||
const id = getNodeId(node);
|
||||
return !seen[id] && (seen[id] = true);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -491,7 +491,7 @@ namespace ts.FindAllReferences {
|
||||
/** If at an export specifier, go to the symbol it refers to. */
|
||||
function skipExportSpecifierSymbol(symbol: Symbol, checker: TypeChecker): Symbol {
|
||||
// For `export { foo } from './bar", there's nothing to skip, because it does not create a new alias. But `export { foo } does.
|
||||
for (const declaration of symbol.declarations) {
|
||||
if (symbol.declarations) for (const declaration of symbol.declarations) {
|
||||
if (isExportSpecifier(declaration) && !(declaration as ExportSpecifier).propertyName && !(declaration as ExportSpecifier).parent.parent.moduleSpecifier) {
|
||||
return checker.getExportSpecifierLocalTargetSymbol(declaration);
|
||||
}
|
||||
|
||||
@@ -479,6 +479,7 @@ namespace ts {
|
||||
displayParts: SymbolDisplayPart[];
|
||||
}
|
||||
|
||||
//!!! internal implementation details leaked!!!
|
||||
export interface ReferencedSymbolOf<T extends DocumentSpan> {
|
||||
definition: ReferencedSymbolDefinitionInfo;
|
||||
references: T[];
|
||||
|
||||
@@ -1132,6 +1132,21 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** True if the symbol is for an external module, as opposed to a namespace. */
|
||||
export function isExternalModuleSymbol(moduleSymbol: Symbol): boolean {
|
||||
Debug.assert(!!(moduleSymbol.flags & SymbolFlags.Module));
|
||||
return moduleSymbol.name.charCodeAt(0) === CharacterCodes.doubleQuote;
|
||||
}
|
||||
|
||||
/** Returns `true` the first time it encounters a node and `false` afterwards. */
|
||||
export function nodeSeenTracker<T extends Node>(): (node: T) => boolean {
|
||||
const seen: Array<true> = [];
|
||||
return node => {
|
||||
const id = getNodeId(node);
|
||||
return !seen[id] && (seen[id] = true);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Display-part writer helpers
|
||||
|
||||
Reference in new issue
Block a user