Skip to content

Commit 1edb0a8

Browse files
andlrcatscott
authored andcommitted
fix(server): Respect the client capabilities "textDocument.{declaration,typeDefinition}.linkSupport." (#1875)
If the client doesn't explicitly tells the server that they have this capability then a `Location` should be send instead of `LocationLink` as a reply to a "Goto Declaration Request". See the relevant documentation: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_declaration (cherry picked from commit 8751840)
1 parent 9ad2d05 commit 1edb0a8

File tree

2 files changed

+56
-2
lines changed

2 files changed

+56
-2
lines changed

integration/lsp/test_utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ export function initializeServer(client: MessageConnection): Promise<lsp.Initial
7373
}
7474
},
7575
moniker: {},
76+
definition: {linkSupport: true},
77+
typeDefinition: {linkSupport: true}
7678
}
7779
},
7880
/**

server/src/session.ts

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -889,7 +889,8 @@ export class Session {
889889
return htmlLS.getFoldingRanges(virtualHtmlDoc);
890890
}
891891

892-
private onDefinition(params: lsp.TextDocumentPositionParams): lsp.LocationLink[]|null {
892+
private onDefinition(params: lsp.TextDocumentPositionParams):
893+
lsp.Location[]|lsp.LocationLink[]|null {
893894
const lsInfo = this.getLSAndScriptInfo(params.textDocument);
894895
if (lsInfo === null) {
895896
return null;
@@ -900,11 +901,20 @@ export class Session {
900901
if (!definition || !definition.definitions) {
901902
return null;
902903
}
904+
905+
const clientSupportsLocationLinks =
906+
this.clientCapabilities.textDocument?.definition?.linkSupport ?? false;
907+
908+
if (!clientSupportsLocationLinks) {
909+
return this.tsDefinitionsToLspLocations(definition.definitions);
910+
}
911+
903912
const originSelectionRange = tsTextSpanToLspRange(scriptInfo, definition.textSpan);
904913
return this.tsDefinitionsToLspLocationLinks(definition.definitions, originSelectionRange);
905914
}
906915

907-
private onTypeDefinition(params: lsp.TextDocumentPositionParams): lsp.LocationLink[]|null {
916+
private onTypeDefinition(params: lsp.TextDocumentPositionParams):
917+
lsp.Location[]|lsp.LocationLink[]|null {
908918
const lsInfo = this.getLSAndScriptInfo(params.textDocument);
909919
if (lsInfo === null) {
910920
return null;
@@ -915,6 +925,14 @@ export class Session {
915925
if (!definitions) {
916926
return null;
917927
}
928+
929+
const clientSupportsLocationLinks =
930+
this.clientCapabilities.textDocument?.typeDefinition?.linkSupport ?? false;
931+
932+
if (!clientSupportsLocationLinks) {
933+
return this.tsDefinitionsToLspLocations(definitions);
934+
}
935+
918936
return this.tsDefinitionsToLspLocationLinks(definitions);
919937
}
920938

@@ -998,6 +1016,40 @@ export class Session {
9981016
});
9991017
}
10001018

1019+
private tsDefinitionsToLspLocations(definitions: readonly ts.DefinitionInfo[]): lsp.Location[] {
1020+
const results: lsp.Location[] = [];
1021+
for (const d of definitions) {
1022+
const scriptInfo = this.projectService.getScriptInfo(d.fileName);
1023+
1024+
// Some definitions, like definitions of CSS files, may not be recorded files with a
1025+
// `scriptInfo` but are still valid definitions because they are files that exist. In this
1026+
// case, check to make sure that the text span of the definition is zero so that the file
1027+
// doesn't have to be read; if the span is non-zero, we can't do anything with this
1028+
// definition.
1029+
if (!scriptInfo && d.textSpan.length > 0) {
1030+
continue;
1031+
}
1032+
1033+
let mappedInfo = d;
1034+
let range = EMPTY_RANGE;
1035+
if (scriptInfo) {
1036+
const project = this.getDefaultProjectForScriptInfo(scriptInfo);
1037+
mappedInfo = project ? getMappedDefinitionInfo(d, project) : mappedInfo;
1038+
// After the DTS file maps to original source file, the `scriptInfo` should be updated.
1039+
const originalScriptInfo =
1040+
this.projectService.getScriptInfo(mappedInfo.fileName) ?? scriptInfo;
1041+
range = tsTextSpanToLspRange(originalScriptInfo, mappedInfo.textSpan);
1042+
}
1043+
1044+
const uri = filePathToUri(mappedInfo.fileName);
1045+
results.push({
1046+
uri,
1047+
range,
1048+
});
1049+
}
1050+
return results;
1051+
}
1052+
10011053
private tsDefinitionsToLspLocationLinks(
10021054
definitions: readonly ts.DefinitionInfo[],
10031055
originSelectionRange?: lsp.Range): lsp.LocationLink[] {

0 commit comments

Comments
 (0)