Skip to content

Commit 401fc69

Browse files
committed
feat: Add commands to restart and stop LSP servers, enhance LSP client disposal by clearing diagnostics
1 parent a1ce0aa commit 401fc69

File tree

3 files changed

+104
-1
lines changed

3 files changed

+104
-1
lines changed

src/cm/commandRegistry.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import {
5656
import { indentUnit as indentUnitFacet } from "@codemirror/language";
5757
import {
5858
closeLintPanel,
59+
forceLinting,
5960
nextDiagnostic,
6061
openLintPanel,
6162
previousDiagnostic,
@@ -76,6 +77,8 @@ import {
7677
} from "@codemirror/lsp-client";
7778
import { Compartment, EditorSelection } from "@codemirror/state";
7879
import { keymap } from "@codemirror/view";
80+
import { clearDiagnosticsEffect, clientManager } from "cm/lsp";
81+
import toast from "components/toast";
7982
import prompt from "dialogs/prompt";
8083
import actions from "handlers/quickTools";
8184
import keyBindings from "lib/keyBindings";
@@ -938,6 +941,47 @@ function registerLspCommands() {
938941
return lspCloseReferencePanel(resolvedView);
939942
},
940943
});
944+
addCommand({
945+
name: "restartAllLspServers",
946+
description: "Restart all running LSP servers",
947+
readOnly: true,
948+
requiresView: false,
949+
async run() {
950+
const activeClients = clientManager.getActiveClients();
951+
if (!activeClients.length) {
952+
toast("No LSP servers are currently running");
953+
return true;
954+
}
955+
const count = activeClients.length;
956+
toast(`Restarting ${count} LSP server${count > 1 ? "s" : ""}...`);
957+
958+
// Dispose all clients (also clears diagnostics)
959+
await clientManager.dispose();
960+
961+
// Trigger reconnect for active file
962+
editorManager?.restartLsp?.();
963+
return true;
964+
},
965+
});
966+
addCommand({
967+
name: "stopAllLspServers",
968+
description: "Stop all running LSP servers",
969+
readOnly: true,
970+
requiresView: false,
971+
async run() {
972+
const activeClients = clientManager.getActiveClients();
973+
if (!activeClients.length) {
974+
toast("No LSP servers are currently running");
975+
return true;
976+
}
977+
const count = activeClients.length;
978+
979+
// Dispose all clients
980+
await clientManager.dispose();
981+
toast(`Stopped ${count} LSP server${count > 1 ? "s" : ""}`);
982+
return true;
983+
},
984+
});
941985
}
942986

943987
function registerLintCommands() {

src/cm/lsp/clientManager.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ import {
1212
serverDiagnostics,
1313
signatureHelp,
1414
} from "@codemirror/lsp-client";
15-
import { Extension, MapMode } from "@codemirror/state";
15+
import { EditorState, Extension, MapMode } from "@codemirror/state";
1616
import { EditorView, keymap } from "@codemirror/view";
1717
import lspStatusBar from "components/lspStatusBar";
1818
import NotificationManager from "lib/notificationManager";
1919
import Uri from "utils/Uri";
20+
import { clearDiagnosticsEffect } from "./diagnostics";
2021
import { inlayHintsExtension } from "./inlayHints";
2122
import { ensureServerRunning } from "./serverLauncher";
2223
import serverRegistry from "./serverRegistry";
@@ -250,6 +251,54 @@ export class LspClientManager {
250251
}
251252

252253
async dispose(): Promise<void> {
254+
try {
255+
interface FileWithSession {
256+
id?: string;
257+
type?: string;
258+
session?: EditorState;
259+
}
260+
261+
interface EditorManagerLike {
262+
files?: FileWithSession[];
263+
editor?: EditorView;
264+
activeFile?: FileWithSession;
265+
}
266+
267+
const em = (globalThis as Record<string, unknown>).editorManager as
268+
| EditorManagerLike
269+
| undefined;
270+
271+
if (em?.editor) {
272+
try {
273+
em.editor.dispatch({ effects: clearDiagnosticsEffect() });
274+
if (em.activeFile?.type === "editor") {
275+
em.activeFile.session = em.editor.state;
276+
}
277+
} catch {
278+
/* View may be disposed */
279+
}
280+
}
281+
282+
if (em?.files) {
283+
for (const file of em.files) {
284+
if (file?.type !== "editor" || file.id === em.activeFile?.id)
285+
continue;
286+
const session = file.session;
287+
if (session && typeof session.update === "function") {
288+
try {
289+
file.session = session.update({
290+
effects: clearDiagnosticsEffect(),
291+
}).state;
292+
} catch {
293+
/* State update failed */
294+
}
295+
}
296+
}
297+
}
298+
} catch {
299+
/* Ignore errors */
300+
}
301+
253302
const disposeOps: Promise<void>[] = [];
254303
for (const [key, state] of this.#clients.entries()) {
255304
disposeOps.push(state.dispose());

src/lib/editorManager.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,16 @@ async function EditorManager($header, $body) {
11231123
events.emit(detailedEvent, ...detailedEventArgs);
11241124
}
11251125
},
1126+
/**
1127+
* Restart LSP for the active file
1128+
* Useful after stopping/restarting language servers
1129+
*/
1130+
restartLsp() {
1131+
const activeFile = manager.activeFile;
1132+
if (activeFile?.type === "editor") {
1133+
void configureLspForFile(activeFile);
1134+
}
1135+
},
11261136
};
11271137

11281138
if (typeof document !== "undefined") {

0 commit comments

Comments
 (0)