diff --git a/src/server/session.ts b/src/server/session.ts index 89231ede0070f..fdafff2aace6c 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -2164,15 +2164,18 @@ namespace ts.server { return result; } - private toProtocolCallHierarchyItem(item: CallHierarchyItem, scriptInfo?: ScriptInfo): protocol.CallHierarchyItem { + private getScriptInfoFromProjectService(file: string) { + const normalizedFile = toNormalizedPath(file); + const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(normalizedFile); if (!scriptInfo) { - const file = toNormalizedPath(item.file); - scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file); - if (!scriptInfo) { - this.projectService.logErrorForScriptInfoNotFound(file); - return Errors.ThrowNoProject(); - } + this.projectService.logErrorForScriptInfoNotFound(normalizedFile); + return Errors.ThrowNoProject(); } + return scriptInfo; + } + + private toProtocolCallHierarchyItem(item: CallHierarchyItem): protocol.CallHierarchyItem { + const scriptInfo = this.getScriptInfoFromProjectService(item.file); return { name: item.name, kind: item.kind, @@ -2182,7 +2185,8 @@ namespace ts.server { }; } - private toProtocolCallHierarchyIncomingCall(incomingCall: CallHierarchyIncomingCall, scriptInfo: ScriptInfo): protocol.CallHierarchyIncomingCall { + private toProtocolCallHierarchyIncomingCall(incomingCall: CallHierarchyIncomingCall): protocol.CallHierarchyIncomingCall { + const scriptInfo = this.getScriptInfoFromProjectService(incomingCall.from.file); return { from: this.toProtocolCallHierarchyItem(incomingCall.from), fromSpans: incomingCall.fromSpans.map(fromSpan => toProtocolTextSpan(fromSpan, scriptInfo)) @@ -2202,29 +2206,21 @@ namespace ts.server { if (scriptInfo) { const position = this.getPosition(args, scriptInfo); const result = project.getLanguageService().prepareCallHierarchy(file, position); - return result && mapOneOrMany(result, item => this.toProtocolCallHierarchyItem(item, scriptInfo)); + return result && mapOneOrMany(result, item => this.toProtocolCallHierarchyItem(item)); } return undefined; } private provideCallHierarchyIncomingCalls(args: protocol.FileLocationRequestArgs): protocol.CallHierarchyIncomingCall[] { const { file, project } = this.getFileAndProject(args); - const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file); - if (!scriptInfo) { - this.projectService.logErrorForScriptInfoNotFound(file); - return Errors.ThrowNoProject(); - } + const scriptInfo = this.getScriptInfoFromProjectService(file); const incomingCalls = project.getLanguageService().provideCallHierarchyIncomingCalls(file, this.getPosition(args, scriptInfo)); - return incomingCalls.map(call => this.toProtocolCallHierarchyIncomingCall(call, scriptInfo)); + return incomingCalls.map(call => this.toProtocolCallHierarchyIncomingCall(call)); } private provideCallHierarchyOutgoingCalls(args: protocol.FileLocationRequestArgs): protocol.CallHierarchyOutgoingCall[] { const { file, project } = this.getFileAndProject(args); - const scriptInfo = this.projectService.getScriptInfoForNormalizedPath(file); - if (!scriptInfo) { - this.projectService.logErrorForScriptInfoNotFound(file); - return Errors.ThrowNoProject(); - } + const scriptInfo = this.getScriptInfoFromProjectService(file); const outgoingCalls = project.getLanguageService().provideCallHierarchyOutgoingCalls(file, this.getPosition(args, scriptInfo)); return outgoingCalls.map(call => this.toProtocolCallHierarchyOutgoingCall(call, scriptInfo)); } diff --git a/src/services/callHierarchy.ts b/src/services/callHierarchy.ts index 838f9355d3dca..efe748841c2b6 100644 --- a/src/services/callHierarchy.ts +++ b/src/services/callHierarchy.ts @@ -260,16 +260,18 @@ namespace ts.CallHierarchy { range: TextRange; } - function convertEntryToCallSite(entry: FindAllReferences.Entry, _originalNode: Node, typeChecker: TypeChecker): CallSite | undefined { + function convertEntryToCallSite(entry: FindAllReferences.Entry): CallSite | undefined { if (entry.kind === FindAllReferences.EntryKind.Node) { - if (isCallOrNewExpressionTarget(entry.node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true) - || isTaggedTemplateTag(entry.node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true) - || isDecoratorTarget(entry.node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true) - || isJsxOpeningLikeElementTagName(entry.node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true) - || isRightSideOfPropertyAccess(entry.node) - || isArgumentExpressionOfElementAccess(entry.node)) { - const ancestor = findAncestor(entry.node, isValidCallHierarchyDeclaration) || entry.node.getSourceFile(); - return { declaration: firstOrOnly(findImplementationOrAllInitialDeclarations(typeChecker, ancestor)), range: createTextRangeFromNode(entry.node, entry.node.getSourceFile()) }; + const { node } = entry; + if (isCallOrNewExpressionTarget(node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true) + || isTaggedTemplateTag(node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true) + || isDecoratorTarget(node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true) + || isJsxOpeningLikeElementTagName(node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true) + || isRightSideOfPropertyAccess(node) + || isArgumentExpressionOfElementAccess(node)) { + const sourceFile = node.getSourceFile(); + const ancestor = findAncestor(node, isValidCallHierarchyDeclaration) || sourceFile; + return { declaration: ancestor, range: createTextRangeFromNode(node, sourceFile) }; } } } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 9320464f11133..0e09f5469fe13 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -9551,6 +9551,7 @@ declare namespace ts.server { private configurePlugin; private getSmartSelectionRange; private mapSelectionRange; + private getScriptInfoFromProjectService; private toProtocolCallHierarchyItem; private toProtocolCallHierarchyIncomingCall; private toProtocolCallHierarchyOutgoingCall; diff --git a/tests/baselines/reference/callHierarchyCrossFile.callHierarchy.txt b/tests/baselines/reference/callHierarchyCrossFile.callHierarchy.txt new file mode 100644 index 0000000000000..e301fbdf8b14d --- /dev/null +++ b/tests/baselines/reference/callHierarchyCrossFile.callHierarchy.txt @@ -0,0 +1,63 @@ +╭ name: createModelReference +├ kind: function +├ file: /a.ts +├ span: +│ ╭ /a.ts:1:1-1:42 +│ │ 1: export function createModelReference() {} +│ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +│ ╰ +├ selectionSpan: +│ ╭ /a.ts:1:17-1:37 +│ │ 1: export function createModelReference() {} +│ │ ^^^^^^^^^^^^^^^^^^^^ +│ ╰ +├ incoming: +│ ╭ from: +│ │ ╭ name: openElementsAtEditor +│ │ ├ kind: function +│ │ ├ file: /b.ts +│ │ ├ span: +│ │ │ ╭ /b.ts:2:1-4:2 +│ │ │ │ 2: function openElementsAtEditor() { +│ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +│ │ │ │ 3: createModelReference(); +│ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^ +│ │ │ │ 4: } +│ │ │ │ ^ +│ │ │ ╰ +│ │ ├ selectionSpan: +│ │ │ ╭ /b.ts:2:10-2:30 +│ │ │ │ 2: function openElementsAtEditor() { +│ │ │ │ ^^^^^^^^^^^^^^^^^^^^ +│ │ │ ╰ +│ │ ╰ incoming: none +│ ├ fromSpans: +│ │ ╭ /b.ts:3:3-3:23 +│ │ │ 3: createModelReference(); +│ │ │ ^^^^^^^^^^^^^^^^^^^^ +│ ╰ ╰ +│ ╭ from: +│ │ ╭ name: registerDefaultLanguageCommand +│ │ ├ kind: function +│ │ ├ file: /c.ts +│ │ ├ span: +│ │ │ ╭ /c.ts:2:1-4:2 +│ │ │ │ 2: function registerDefaultLanguageCommand() { +│ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +│ │ │ │ 3: createModelReference(); +│ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^ +│ │ │ │ 4: } +│ │ │ │ ^ +│ │ │ ╰ +│ │ ├ selectionSpan: +│ │ │ ╭ /c.ts:2:10-2:40 +│ │ │ │ 2: function registerDefaultLanguageCommand() { +│ │ │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +│ │ │ ╰ +│ │ ╰ incoming: none +│ ├ fromSpans: +│ │ ╭ /c.ts:3:3-3:23 +│ │ │ 3: createModelReference(); +│ │ │ ^^^^^^^^^^^^^^^^^^^^ +│ ╰ ╰ +╰ outgoing: none diff --git a/tests/cases/fourslash/callHierarchyCrossFile.ts b/tests/cases/fourslash/callHierarchyCrossFile.ts new file mode 100644 index 0000000000000..240c2b258015b --- /dev/null +++ b/tests/cases/fourslash/callHierarchyCrossFile.ts @@ -0,0 +1,19 @@ +/// + +// @filename: /a.ts +////export function /**/createModelReference() {} + +// @filename: /b.ts +////import { createModelReference } from "./a"; +////function openElementsAtEditor() { +//// createModelReference(); +////} + +// @filename: /c.ts +////import { createModelReference } from "./a"; +////function registerDefaultLanguageCommand() { +//// createModelReference(); +////} + +goTo.marker(); +verify.baselineCallHierarchy();