Skip to content

Commit ad20f13

Browse files
committed
Notify sourcekit-lsp of active document changes
`sourcekit-lsp` recently added an LSP extension method to notify the server of the active documnent, so it doesn't need to infer it from information in other requests. This capability was added in swiftlang/sourcekit-lsp#1989.
1 parent a67d049 commit ad20f13

File tree

5 files changed

+124
-20
lines changed

5 files changed

+124
-20
lines changed

src/TestExplorer/LSPTestDiscovery.ts

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ import {
2020
WorkspaceTestsRequest,
2121
} from "../sourcekit-lsp/extensions";
2222
import { SwiftPackage, TargetType } from "../SwiftPackage";
23-
import { LanguageClientManager } from "../sourcekit-lsp/LanguageClientManager";
23+
import {
24+
checkExperimentalCapability,
25+
LanguageClientManager,
26+
} from "../sourcekit-lsp/LanguageClientManager";
2427
import { LanguageClient } from "vscode-languageclient/node";
2528

2629
/**
@@ -45,7 +48,7 @@ export class LSPTestDiscovery {
4548
return await this.languageClient.useLanguageClient(async (client, token) => {
4649
// Only use the lsp for this request if it supports the
4750
// textDocument/tests method, and is at least version 2.
48-
if (this.checkExperimentalCapability(client, TextDocumentTestsRequest.method, 2)) {
51+
if (checkExperimentalCapability(client, TextDocumentTestsRequest.method, 2)) {
4952
const testsInDocument = await client.sendRequest(
5053
TextDocumentTestsRequest.type,
5154
{ textDocument: { uri: document.toString() } },
@@ -66,7 +69,7 @@ export class LSPTestDiscovery {
6669
return await this.languageClient.useLanguageClient(async (client, token) => {
6770
// Only use the lsp for this request if it supports the
6871
// workspace/tests method, and is at least version 2.
69-
if (this.checkExperimentalCapability(client, WorkspaceTestsRequest.method, 2)) {
72+
if (checkExperimentalCapability(client, WorkspaceTestsRequest.method, 2)) {
7073
const tests = await client.sendRequest(WorkspaceTestsRequest.type, token);
7174
return this.transformToTestClass(client, swiftPackage, tests);
7275
} else {
@@ -75,23 +78,6 @@ export class LSPTestDiscovery {
7578
});
7679
}
7780

78-
/**
79-
* Returns `true` if the LSP supports the supplied `method` at or
80-
* above the supplied `minVersion`.
81-
*/
82-
private checkExperimentalCapability(
83-
client: LanguageClient,
84-
method: string,
85-
minVersion: number
86-
) {
87-
const experimentalCapability = client.initializeResult?.capabilities.experimental;
88-
if (!experimentalCapability) {
89-
throw new Error(`${method} requests not supported`);
90-
}
91-
const targetCapability = experimentalCapability[method];
92-
return (targetCapability?.version ?? -1) >= minVersion;
93-
}
94-
9581
/**
9682
* Convert from `LSPTestItem[]` to `TestDiscovery.TestClass[]`,
9783
* updating the format of the location.

src/sourcekit-lsp/LanguageClientManager.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { activateGetReferenceDocument } from "./getReferenceDocument";
4747
import { uriConverters } from "./uriConverters";
4848
import { LanguageClientFactory } from "./LanguageClientFactory";
4949
import { SourceKitLogMessageNotification, SourceKitLogMessageParams } from "./extensions";
50+
import { activateDidChangeActiveDocument } from "./didChangeActiveDocument";
5051

5152
/**
5253
* Manages the creation and destruction of Language clients as we move between
@@ -136,6 +137,7 @@ export class LanguageClientManager implements vscode.Disposable {
136137
private legacyInlayHints?: vscode.Disposable;
137138
private peekDocuments?: vscode.Disposable;
138139
private getReferenceDocument?: vscode.Disposable;
140+
private didChangeActiveDocument?: vscode.Disposable;
139141
private restartedPromise?: Promise<void>;
140142
private currentWorkspaceFolder?: vscode.Uri;
141143
private waitingOnRestartCount: number;
@@ -666,6 +668,13 @@ export class LanguageClientManager implements vscode.Disposable {
666668
};
667669
}
668670

671+
if (this.swiftVersion.isGreaterThanOrEqual(new Version(6, 1, 0))) {
672+
options = {
673+
...options,
674+
"window/didChangeActiveDocument": true, // the client can send `window/didChangeActiveDocument` notifications
675+
};
676+
}
677+
669678
if (configuration.swiftSDK !== "") {
670679
options = {
671680
...options,
@@ -715,6 +724,8 @@ export class LanguageClientManager implements vscode.Disposable {
715724
this.peekDocuments = activatePeekDocuments(client);
716725
this.getReferenceDocument = activateGetReferenceDocument(client);
717726
this.workspaceContext.subscriptions.push(this.getReferenceDocument);
727+
this.didChangeActiveDocument = activateDidChangeActiveDocument(client);
728+
this.workspaceContext.subscriptions.push(this.didChangeActiveDocument);
718729
})
719730
.catch(reason => {
720731
this.workspaceContext.outputChannel.log(`${reason}`);
@@ -846,3 +857,20 @@ type SourceKitDocumentSelector = {
846857
scheme: string;
847858
language: string;
848859
}[];
860+
861+
/**
862+
* Returns `true` if the LSP supports the supplied `method` at or
863+
* above the supplied `minVersion`.
864+
*/
865+
export function checkExperimentalCapability(
866+
client: LanguageClient,
867+
method: string,
868+
minVersion: number
869+
) {
870+
const experimentalCapability = client.initializeResult?.capabilities.experimental;
871+
if (!experimentalCapability) {
872+
throw new Error(`${method} requests not supported`);
873+
}
874+
const targetCapability = experimentalCapability[method];
875+
return (targetCapability?.version ?? -1) >= minVersion;
876+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VS Code Swift open source project
4+
//
5+
// Copyright (c) 2024 the VS Code Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import * as vscode from "vscode";
16+
import * as langclient from "vscode-languageclient/node";
17+
import { checkExperimentalCapability } from "./LanguageClientManager";
18+
import { DidChangeActiveDocumentNotification } from "./extensions/DidChangeActiveDocumentRequest";
19+
20+
export function activateDidChangeActiveDocument(
21+
client: langclient.LanguageClient
22+
): vscode.Disposable {
23+
const disposable = vscode.window.onDidChangeActiveTextEditor(event => {
24+
if (
25+
event &&
26+
checkExperimentalCapability(client, DidChangeActiveDocumentNotification.method, 1)
27+
) {
28+
client.sendNotification(DidChangeActiveDocumentNotification.method, {
29+
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(
30+
event.document
31+
),
32+
});
33+
}
34+
});
35+
36+
// Fire an inital notification
37+
const activeEditor = vscode.window.activeTextEditor;
38+
if (
39+
activeEditor &&
40+
checkExperimentalCapability(client, DidChangeActiveDocumentNotification.method, 1)
41+
) {
42+
client.sendNotification(DidChangeActiveDocumentNotification.method, {
43+
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(
44+
activeEditor.document
45+
),
46+
});
47+
}
48+
return disposable;
49+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VS Code Swift open source project
4+
//
5+
// Copyright (c) 2021-2024 the VS Code Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import { MessageDirection, NotificationType, TextDocumentIdentifier } from "vscode-languageclient";
16+
17+
// We use namespaces to store request information just like vscode-languageclient
18+
/* eslint-disable @typescript-eslint/no-namespace */
19+
20+
export interface DidChangeActiveDocumentParams {
21+
/**
22+
* The document that is being displayed in the active editor.
23+
*/
24+
textDocument: TextDocumentIdentifier;
25+
}
26+
27+
/**
28+
* Notify the server that the active document has changed.
29+
*/
30+
export namespace DidChangeActiveDocumentNotification {
31+
export const method = "window/didChangeActiveDocument" as const;
32+
export const messageDirection: MessageDirection = MessageDirection.clientToServer;
33+
export const type = new NotificationType<DidChangeActiveDocumentParams>(method);
34+
}

test/unit-tests/sourcekit-lsp/LanguageClientManager.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,13 @@ suite("LanguageClientManager Suite", () => {
123123
dispose: mockFn(),
124124
})
125125
),
126+
initializeResult: {
127+
capabilities: {
128+
experimental: {
129+
"window/didChangeActiveDocument": true,
130+
},
131+
},
132+
},
126133
start: mockFn(s =>
127134
s.callsFake(async () => {
128135
const oldState = languageClientMock.state;

0 commit comments

Comments
 (0)