diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 8f09e98e3fb..12a97433615 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -5,13 +5,14 @@ namespace ts.FindAllReferences { readonly references: readonly Entry[]; } - export const enum DefinitionKind { Symbol, Label, Keyword, This, String } + export const enum DefinitionKind { Symbol, Label, Keyword, This, String, TripleSlashReference } export type Definition = | { readonly type: DefinitionKind.Symbol; readonly symbol: Symbol } | { readonly type: DefinitionKind.Label; readonly node: Identifier } | { readonly type: DefinitionKind.Keyword; readonly node: Node } | { readonly type: DefinitionKind.This; readonly node: Node } - | { readonly type: DefinitionKind.String; readonly node: StringLiteralLike }; + | { readonly type: DefinitionKind.String; readonly node: StringLiteralLike } + | { readonly type: DefinitionKind.TripleSlashReference; readonly reference: FileReference, readonly file: SourceFile }; export const enum EntryKind { Span, Node, StringLiteral, SearchedLocalFoundProperty, SearchedPropertyFoundLocal } export type NodeEntryKind = EntryKind.Node | EntryKind.StringLiteral | EntryKind.SearchedLocalFoundProperty | EntryKind.SearchedPropertyFoundLocal; @@ -298,17 +299,16 @@ namespace ts.FindAllReferences { } function definitionToReferencedSymbolDefinitionInfo(def: Definition, checker: TypeChecker, originalNode: Node): ReferencedSymbolDefinitionInfo { - const info = (() => { + const info = ((): { sourceFile: SourceFile, textSpan: TextSpan, name: string, kind: ScriptElementKind, displayParts: SymbolDisplayPart[], context?: Node | ContextWithStartAndEndNode } => { switch (def.type) { case DefinitionKind.Symbol: { const { symbol } = def; const { displayParts, kind } = getDefinitionKindAndDisplayParts(symbol, checker, originalNode); const name = displayParts.map(p => p.text).join(""); const declaration = symbol.declarations && firstOrUndefined(symbol.declarations); + const node = declaration ? (getNameOfDeclaration(declaration) || declaration) : originalNode; return { - node: declaration ? - getNameOfDeclaration(declaration) || declaration : - originalNode, + ...getFileAndTextSpanFromNode(node), name, kind, displayParts, @@ -317,32 +317,44 @@ namespace ts.FindAllReferences { } case DefinitionKind.Label: { const { node } = def; - return { node, name: node.text, kind: ScriptElementKind.label, displayParts: [displayPart(node.text, SymbolDisplayPartKind.text)] }; + return { ...getFileAndTextSpanFromNode(node), name: node.text, kind: ScriptElementKind.label, displayParts: [displayPart(node.text, SymbolDisplayPartKind.text)] }; } case DefinitionKind.Keyword: { const { node } = def; const name = tokenToString(node.kind)!; - return { node, name, kind: ScriptElementKind.keyword, displayParts: [{ text: name, kind: ScriptElementKind.keyword }] }; + return { ...getFileAndTextSpanFromNode(node), name, kind: ScriptElementKind.keyword, displayParts: [{ text: name, kind: ScriptElementKind.keyword }] }; } case DefinitionKind.This: { const { node } = def; const symbol = checker.getSymbolAtLocation(node); const displayParts = symbol && SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind( checker, symbol, node.getSourceFile(), getContainerNode(node), node).displayParts || [textPart("this")]; - return { node, name: "this", kind: ScriptElementKind.variableElement, displayParts }; + return { ...getFileAndTextSpanFromNode(node), name: "this", kind: ScriptElementKind.variableElement, displayParts }; } case DefinitionKind.String: { const { node } = def; - return { node, name: node.text, kind: ScriptElementKind.variableElement, displayParts: [displayPart(getTextOfNode(node), SymbolDisplayPartKind.stringLiteral)] }; + return { + ...getFileAndTextSpanFromNode(node), + name: node.text, + kind: ScriptElementKind.variableElement, + displayParts: [displayPart(getTextOfNode(node), SymbolDisplayPartKind.stringLiteral)] + }; + } + case DefinitionKind.TripleSlashReference: { + return { + textSpan: createTextSpanFromRange(def.reference), + sourceFile: def.file, + name: def.reference.fileName, + kind: ScriptElementKind.string, + displayParts: [displayPart(`"${def.reference.fileName}"`, SymbolDisplayPartKind.stringLiteral)] + }; } default: return Debug.assertNever(def); } })(); - const { node, name, kind, displayParts, context } = info; - const sourceFile = node.getSourceFile(); - const textSpan = getTextSpan(isComputedPropertyName(node) ? node.expression : node, sourceFile); + const { sourceFile, textSpan, name, kind, displayParts, context } = info; return { containerKind: ScriptElementKind.unknown, containerName: "", @@ -355,6 +367,14 @@ namespace ts.FindAllReferences { }; } + function getFileAndTextSpanFromNode(node: Node) { + const sourceFile = node.getSourceFile(); + return { + sourceFile, + textSpan: getTextSpan(isComputedPropertyName(node) ? node.expression : node, sourceFile) + }; + } + function getDefinitionKindAndDisplayParts(symbol: Symbol, checker: TypeChecker, node: Node): { displayParts: SymbolDisplayPart[], kind: ScriptElementKind } { const meaning = Core.getIntersectingMeaningFromDeclarations(node, symbol); const enclosingDeclaration = symbol.declarations && firstOrUndefined(symbol.declarations) || node; @@ -603,9 +623,22 @@ namespace ts.FindAllReferences { node = getAdjustedRenameLocation(node); } if (isSourceFile(node)) { - const reference = GoToDefinition.getReferenceAtPosition(node, position, program); - const moduleSymbol = reference && program.getTypeChecker().getMergedSymbol(reference.file.symbol); - return moduleSymbol && getReferencedSymbolsForModule(program, moduleSymbol, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet); + const resolvedRef = GoToDefinition.getReferenceAtPosition(node, position, program); + if (!resolvedRef) { + return undefined; + } + const moduleSymbol = program.getTypeChecker().getMergedSymbol(resolvedRef.file.symbol); + if (moduleSymbol) { + return getReferencedSymbolsForModule(program, moduleSymbol, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet); + } + const fileIncludeReasons = program.getFileIncludeReasons(); + if (!fileIncludeReasons) { + return undefined; + } + return [{ + definition: { type: DefinitionKind.TripleSlashReference, reference: resolvedRef.reference, file: node }, + references: getReferencesForNonModule(resolvedRef.file, fileIncludeReasons, program) || emptyArray + }]; } if (!options.implementations) { diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 3567716cb1c..faaf1077354 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -1,9 +1,9 @@ /* @internal */ namespace ts.GoToDefinition { export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number): readonly DefinitionInfo[] | undefined { - const reference = getReferenceAtPosition(sourceFile, position, program); - if (reference) { - return [getDefinitionInfoForFileReference(reference.fileName, reference.file.fileName)]; + const resolvedRef = getReferenceAtPosition(sourceFile, position, program); + if (resolvedRef) { + return [getDefinitionInfoForFileReference(resolvedRef.reference.fileName, resolvedRef.file.fileName)]; } const node = getTouchingPropertyName(sourceFile, position); @@ -108,24 +108,24 @@ namespace ts.GoToDefinition { || (!isCallLikeExpression(calledDeclaration.parent) && s === calledDeclaration.parent.symbol); } - export function getReferenceAtPosition(sourceFile: SourceFile, position: number, program: Program): { fileName: string, file: SourceFile } | undefined { + export function getReferenceAtPosition(sourceFile: SourceFile, position: number, program: Program): { reference: FileReference, file: SourceFile } | undefined { const referencePath = findReferenceInPosition(sourceFile.referencedFiles, position); if (referencePath) { const file = program.getSourceFileFromReference(sourceFile, referencePath); - return file && { fileName: referencePath.fileName, file }; + return file && { reference: referencePath, file }; } const typeReferenceDirective = findReferenceInPosition(sourceFile.typeReferenceDirectives, position); if (typeReferenceDirective) { const reference = program.getResolvedTypeReferenceDirectives().get(typeReferenceDirective.fileName); const file = reference && program.getSourceFile(reference.resolvedFileName!); // TODO:GH#18217 - return file && { fileName: typeReferenceDirective.fileName, file }; + return file && { reference: typeReferenceDirective, file }; } const libReferenceDirective = findReferenceInPosition(sourceFile.libReferenceDirectives, position); if (libReferenceDirective) { const file = program.getLibFileFromReference(libReferenceDirective); - return file && { fileName: libReferenceDirective.fileName, file }; + return file && { reference: libReferenceDirective, file }; } return undefined; diff --git a/tests/baselines/reference/findAllReferencesTripleSlash.baseline.jsonc b/tests/baselines/reference/findAllReferencesTripleSlash.baseline.jsonc new file mode 100644 index 00000000000..d53c912d4fb --- /dev/null +++ b/tests/baselines/reference/findAllReferencesTripleSlash.baseline.jsonc @@ -0,0 +1,99 @@ +// === /c.js === +// require([|"./b"|]); +// require("globals"); + +// === /a.ts === +// /// +// /// + +[ + { + "definition": { + "containerKind": "", + "containerName": "", + "fileName": "/a.ts", + "kind": "string", + "name": "b.ts", + "textSpan": { + "start": 21, + "length": 4 + }, + "displayParts": [ + { + "text": "\"b.ts\"", + "kind": "stringLiteral" + } + ] + }, + "references": [ + { + "textSpan": { + "start": 21, + "length": 4 + }, + "fileName": "/a.ts", + "isWriteAccess": false, + "isDefinition": false + }, + { + "textSpan": { + "start": 8, + "length": 5 + }, + "fileName": "/c.js", + "isWriteAccess": false, + "isDefinition": false + } + ] + } +] + +// === /c.js === +// require("./b"); +// require([|"globals"|]); + +// === /a.ts === +// /// +// /// + +[ + { + "definition": { + "containerKind": "", + "containerName": "", + "fileName": "/a.ts", + "kind": "string", + "name": "globals", + "textSpan": { + "start": 52, + "length": 7 + }, + "displayParts": [ + { + "text": "\"globals\"", + "kind": "stringLiteral" + } + ] + }, + "references": [ + { + "textSpan": { + "start": 52, + "length": 7 + }, + "fileName": "/a.ts", + "isWriteAccess": false, + "isDefinition": false + }, + { + "textSpan": { + "start": 24, + "length": 9 + }, + "fileName": "/c.js", + "isWriteAccess": false, + "isDefinition": false + } + ] + } +] \ No newline at end of file diff --git a/tests/cases/fourslash/findAllReferencesTripleSlash.ts b/tests/cases/fourslash/findAllReferencesTripleSlash.ts new file mode 100644 index 00000000000..956371d0ce8 --- /dev/null +++ b/tests/cases/fourslash/findAllReferencesTripleSlash.ts @@ -0,0 +1,19 @@ +/// + +// @checkJs: true + +// @Filename: /node_modules/@types/globals/index.d.ts +//// declare const someAmbientGlobal: unknown; + +// @Filename: /a.ts +//// /// +//// /// + +// @Filename: /b.ts +//// console.log("b.ts"); + +// @Filename: /c.js +//// require("./b"); +//// require("globals"); + +verify.baselineFindAllReferences("1", "2");