Skip to content

Commit a1ce0aa

Browse files
committed
feat: lsp server settings
1 parent 6c9e5f8 commit a1ce0aa

File tree

2 files changed

+291
-32
lines changed

2 files changed

+291
-32
lines changed

src/settings/lspServerDetail.js

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
import serverRegistry from "cm/lsp/serverRegistry";
2+
import settingsPage from "components/settingsPage";
3+
import toast from "components/toast";
4+
import alert from "dialogs/alert";
5+
import prompt from "dialogs/prompt";
6+
import appSettings from "lib/settings";
7+
8+
/**
9+
* Get the current override settings for a server
10+
* @param {string} id Server ID
11+
* @returns {object} Override settings object
12+
*/
13+
function getServerOverride(id) {
14+
return appSettings.value?.lsp?.servers?.[id] || {};
15+
}
16+
17+
/**
18+
* Merge server definition with user overrides
19+
* @param {object} server Server definition from registry
20+
* @returns {object} Merged server configuration
21+
*/
22+
function getMergedConfig(server) {
23+
const override = getServerOverride(server.id);
24+
return {
25+
...server,
26+
enabled: override.enabled ?? server.enabled,
27+
initializationOptions: {
28+
...(server.initializationOptions || {}),
29+
...(override.initializationOptions || {}),
30+
},
31+
clientConfig: {
32+
...(server.clientConfig || {}),
33+
...(override.clientConfig || {}),
34+
builtinExtensions: {
35+
...(server.clientConfig?.builtinExtensions || {}),
36+
...(override.clientConfig?.builtinExtensions || {}),
37+
},
38+
},
39+
};
40+
}
41+
42+
/**
43+
* Update LSP server configuration in app settings
44+
* @param {string} serverId Server ID
45+
* @param {object} partial Partial configuration to update
46+
*/
47+
async function updateServerConfig(serverId, partial) {
48+
const current = JSON.parse(JSON.stringify(appSettings.value.lsp || {}));
49+
current.servers = current.servers || {};
50+
current.servers[serverId] = {
51+
...(current.servers[serverId] || {}),
52+
...partial,
53+
};
54+
55+
await appSettings.update({ lsp: current }, false);
56+
}
57+
58+
/**
59+
* LSP Server detail settings page
60+
* @param {string} serverId - The server ID to show settings for
61+
* @returns {import('components/settingsPage').SettingsPage}
62+
*/
63+
export default function lspServerDetail(serverId) {
64+
const server = serverRegistry.getServer(serverId);
65+
if (!server) {
66+
toast("Server not found");
67+
return null;
68+
}
69+
70+
const merged = getMergedConfig(server);
71+
const title = server.label || server.id;
72+
73+
const items = [];
74+
const builtinExts = merged.clientConfig?.builtinExtensions || {};
75+
76+
// Server enable/disable
77+
items.push({
78+
key: "enabled",
79+
text: "Enabled",
80+
checkbox: merged.enabled,
81+
info: "Enable or disable this language server",
82+
});
83+
84+
// Feature toggles
85+
items.push({
86+
key: "ext_hover",
87+
text: "Hover Information",
88+
checkbox: builtinExts.hover !== false,
89+
info: "Show type information and documentation on hover",
90+
});
91+
92+
items.push({
93+
key: "ext_completion",
94+
text: "Code Completion",
95+
checkbox: builtinExts.completion !== false,
96+
info: "Enable autocomplete suggestions from the server",
97+
});
98+
99+
items.push({
100+
key: "ext_signature",
101+
text: "Signature Help",
102+
checkbox: builtinExts.signature !== false,
103+
info: "Show function parameter hints while typing",
104+
});
105+
106+
items.push({
107+
key: "ext_diagnostics",
108+
text: "Diagnostics",
109+
checkbox: builtinExts.diagnostics !== false,
110+
info: "Show errors and warnings from the language server",
111+
});
112+
113+
items.push({
114+
key: "ext_inlayHints",
115+
text: "Inlay Hints",
116+
checkbox: builtinExts.inlayHints !== false,
117+
info: "Show inline type hints in the editor",
118+
});
119+
120+
items.push({
121+
key: "ext_formatting",
122+
text: "Formatting",
123+
checkbox: builtinExts.formatting !== false,
124+
info: "Enable code formatting from the language server",
125+
});
126+
127+
if (server.launcher?.install?.command) {
128+
items.push({
129+
key: "view_install",
130+
text: "View Install Command",
131+
info: "View the command to install this language server",
132+
});
133+
}
134+
135+
// Advanced options
136+
items.push({
137+
key: "view_init_options",
138+
text: "View Initialization Options",
139+
info: "View the server initialization options as JSON",
140+
});
141+
142+
items.push({
143+
key: "edit_init_options",
144+
text: "Edit Initialization Options",
145+
info: "Edit custom initialization options (JSON)",
146+
});
147+
148+
return settingsPage(title, items, callback);
149+
150+
async function callback(key, value) {
151+
const override = getServerOverride(serverId);
152+
153+
switch (key) {
154+
case "enabled":
155+
await updateServerConfig(serverId, { enabled: value });
156+
// Update the registry so client manager picks it up
157+
serverRegistry.updateServer(serverId, (current) => ({
158+
...current,
159+
enabled: value,
160+
}));
161+
toast(value ? "Server enabled" : "Server disabled");
162+
break;
163+
164+
case "ext_hover":
165+
case "ext_completion":
166+
case "ext_signature":
167+
case "ext_diagnostics":
168+
case "ext_inlayHints":
169+
case "ext_formatting": {
170+
const extKey = key.replace("ext_", "");
171+
const currentClientConfig = override.clientConfig || {};
172+
const currentBuiltins = currentClientConfig.builtinExtensions || {};
173+
174+
await updateServerConfig(serverId, {
175+
clientConfig: {
176+
...currentClientConfig,
177+
builtinExtensions: {
178+
...currentBuiltins,
179+
[extKey]: value,
180+
},
181+
},
182+
});
183+
toast(`${extKey} ${value ? "enabled" : "disabled"}`);
184+
break;
185+
}
186+
187+
case "view_install":
188+
if (server.launcher?.install?.command) {
189+
alert("Install Command", server.launcher.install.command);
190+
}
191+
break;
192+
193+
case "view_init_options": {
194+
const initOpts = merged.initializationOptions || {};
195+
const json = JSON.stringify(initOpts, null, 2);
196+
alert(
197+
"Initialization Options",
198+
`<pre style="overflow: auto; max-height: 60vh; font-size: 12px;">${escapeHtml(json)}</pre>`,
199+
);
200+
break;
201+
}
202+
203+
case "edit_init_options": {
204+
const currentInitOpts = override.initializationOptions || {};
205+
const currentJson = JSON.stringify(currentInitOpts, null, 2);
206+
207+
try {
208+
const result = await prompt(
209+
"Initialization Options (JSON)",
210+
currentJson || "{}",
211+
"textarea",
212+
{
213+
test: (val) => {
214+
try {
215+
JSON.parse(val);
216+
return true;
217+
} catch {
218+
return false;
219+
}
220+
},
221+
},
222+
);
223+
224+
if (result !== null) {
225+
const parsed = JSON.parse(result);
226+
await updateServerConfig(serverId, {
227+
initializationOptions: parsed,
228+
});
229+
toast("Initialization options updated");
230+
}
231+
} catch (error) {
232+
toast("Invalid JSON");
233+
}
234+
break;
235+
}
236+
237+
default:
238+
break;
239+
}
240+
}
241+
}
242+
243+
/**
244+
* Escape HTML entities
245+
* @param {string} text
246+
* @returns {string}
247+
*/
248+
function escapeHtml(text) {
249+
const div = document.createElement("div");
250+
div.textContent = text;
251+
return div.innerHTML;
252+
}

src/settings/lspSettings.js

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,66 @@
11
import serverRegistry from "cm/lsp/serverRegistry";
22
import settingsPage from "components/settingsPage";
33
import appSettings from "lib/settings";
4+
import lspServerDetail from "./lspServerDetail";
45

6+
/**
7+
* Get the current override settings for a server
8+
* @param {string} id Server ID
9+
* @returns {object} Override settings object
10+
*/
511
function getServerOverride(id) {
612
return appSettings.value?.lsp?.servers?.[id] || {};
713
}
814

15+
/**
16+
* LSP Settings page - shows list of all language servers
17+
* @returns {object} Settings page interface
18+
*/
919
export default function lspSettings() {
1020
const title = strings?.lsp_settings || "Language Servers";
1121
const servers = serverRegistry.listServers();
1222

13-
const items = [];
23+
// Sort: enabled servers first, then alphabetically
24+
const sortedServers = servers.sort((a, b) => {
25+
const aEnabled = getServerOverride(a.id).enabled ?? a.enabled;
26+
const bEnabled = getServerOverride(b.id).enabled ?? b.enabled;
1427

15-
for (const server of servers) {
16-
const override = getServerOverride(server.id);
17-
const serverEnabled = override.enabled ?? server.enabled;
18-
const infoParts = [];
19-
if (Array.isArray(server.languages) && server.languages.length) {
20-
infoParts.push(server.languages.join(", "));
28+
if (aEnabled !== bEnabled) {
29+
return bEnabled ? 1 : -1;
2130
}
31+
return a.label.localeCompare(b.label);
32+
});
33+
34+
const items = [];
35+
36+
for (const server of sortedServers) {
37+
// Languages info
38+
const languagesList =
39+
Array.isArray(server.languages) && server.languages.length
40+
? server.languages.join(", ")
41+
: "";
42+
2243
items.push({
2344
key: `server:${server.id}`,
2445
text: server.label,
25-
checkbox: serverEnabled,
26-
info: infoParts.join(" · ") || undefined,
46+
info: languagesList || undefined,
2747
});
2848
}
2949

50+
// Add note
51+
items.push({
52+
note: "Language servers provide IDE features like autocomplete, diagnostics, and hover information. Enable a server for the languages you work with. Make sure the terminal is installed and the server is installed in the proot environment.",
53+
});
54+
3055
return settingsPage(title, items, callback);
3156

32-
async function callback(key, value) {
57+
function callback(key) {
3358
if (key.startsWith("server:")) {
3459
const id = key.split(":")[1];
35-
const override = {
36-
...(appSettings.value.lsp?.servers?.[id] || {}),
37-
enabled: !!value,
38-
};
39-
await updateConfig({ servers: { [id]: override } });
60+
const detailPage = lspServerDetail(id);
61+
if (detailPage) {
62+
detailPage.show();
63+
}
4064
}
4165
}
42-
43-
async function updateConfig(partial) {
44-
const current = JSON.parse(JSON.stringify(appSettings.value.lsp || {}));
45-
if (partial.servers) {
46-
current.servers = {
47-
...(current.servers || {}),
48-
...partial.servers,
49-
};
50-
}
51-
52-
await appSettings.update(
53-
{
54-
lsp: current,
55-
},
56-
false,
57-
);
58-
}
5966
}

0 commit comments

Comments
 (0)