mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 16:08:05 +00:00
Opening a Python file with none of basedpyright, Pyright, ty and Pyrefly installed raised four install notices at once: each fallback server reports missing after the one before it, and every missing server was prompted. Among interchangeable servers only the first missing one is offered now (events handled one at a time so a fallback sees whether the server before it was offered); a fallback is still offered when the servers before it can't be installed or are turned off. Co-authored-by: andreabasile <7715018+andreabasile@users.noreply.github.com>
106 lines
4.0 KiB
TypeScript
106 lines
4.0 KiB
TypeScript
/**
|
|
* "rust-analyzer is not installed — Install": when a language server reports
|
|
* `missing` and we know how to install it, offer a one-click install that
|
|
* restarts the server when done. Uses the same notification key as the
|
|
* generic code-insight notice so it replaces it.
|
|
*/
|
|
import { executeAction } from "@/platform/actions";
|
|
import { onIdeEvent } from "@/platform/ipc";
|
|
import { notifications } from "@/workbench/contributions";
|
|
import { installTool, refreshTools, SETTINGS_PAGE_ID, toolByName, useTools } from "./store";
|
|
import { tx } from "@/platform/i18n";
|
|
|
|
export const missingKey = (name: string) => `lsp-missing-${name}`;
|
|
|
|
/**
|
|
* `offered`: servers already offered for install. A fallback of one of them (pyright
|
|
* after basedpyright) stays quiet: installing the first is enough, and the Language
|
|
* Servers… page offers the others.
|
|
*/
|
|
export async function promptInstall(
|
|
name: string,
|
|
sticky = true,
|
|
offered: ReadonlySet<string> = new Set(),
|
|
): Promise<boolean> {
|
|
if (!useTools.getState().loaded) await refreshTools();
|
|
const tool = toolByName(name);
|
|
if (!tool || !tool.canInstall || tool.status !== "missing") return false;
|
|
const position = tool.alternatives.indexOf(name);
|
|
if (position > 0 && tool.alternatives.slice(0, position).some((n) => offered.has(n))) return false;
|
|
// An installed alternative (typescript-language-server for vtsls) already serves these files.
|
|
const served = useTools
|
|
.getState()
|
|
.tools.some(
|
|
(t) =>
|
|
t.name !== name &&
|
|
t.kind === tool.kind &&
|
|
t.status !== "missing" &&
|
|
t.languages.some((l) => tool.languages.includes(l)),
|
|
);
|
|
if (tool.kind !== "formatter" && served) return false;
|
|
const key = missingKey(name);
|
|
const langs = tool.languages.length ? tool.languages.join(", ") : "these files";
|
|
notifications().notify({
|
|
group: "tools",
|
|
level: "warning",
|
|
title: tx`${tool.displayName} is not installed`,
|
|
body:
|
|
tool.kind === "formatter"
|
|
? tx`Reformat Code needs ${tool.displayName}. Install it via ${tool.installMethod}?`
|
|
: tx`Code insight for ${langs} needs ${tool.command}. Install it via ${tool.installMethod}?`,
|
|
key,
|
|
sticky,
|
|
actions: [
|
|
{ label: "Install", run: () => void installTool(name, { notificationKey: key }) },
|
|
{ label: "Language Servers…", run: () => void executeAction("file.settings", SETTINGS_PAGE_ID) },
|
|
],
|
|
});
|
|
return true;
|
|
}
|
|
|
|
export function installMissingServerPrompts(): () => void {
|
|
const prompted = new Set<string>();
|
|
const offered = new Set<string>();
|
|
// One at a time: a missing server's fallback reports missing right after it, and
|
|
// must see whether the server's own notice went up.
|
|
let queue = Promise.resolve();
|
|
return onIdeEvent("languageServer", (e) => {
|
|
if (e.status !== "missing" || prompted.has(e.name)) return;
|
|
prompted.add(e.name);
|
|
// Async on purpose: lands after the synchronous generic notice and replaces it.
|
|
queue = queue
|
|
.then(async () => {
|
|
if (await promptInstall(e.name, true, offered)) offered.add(e.name);
|
|
})
|
|
.catch(() => {});
|
|
});
|
|
}
|
|
|
|
/** A system server that lacks a capability can be replaced by the managed tool. */
|
|
export function installServerWarningPrompts(): () => void {
|
|
const prompted = new Set<string>();
|
|
return onIdeEvent("languageServerWarning", (e) => {
|
|
const key = `lsp-warning-${e.name}-${e.installTool}`;
|
|
if (prompted.has(key)) return;
|
|
prompted.add(key);
|
|
void (async () => {
|
|
if (!useTools.getState().loaded) await refreshTools();
|
|
const tool = toolByName(e.installTool);
|
|
notifications().notify({
|
|
group: "lsp",
|
|
level: "warning",
|
|
title: e.name,
|
|
body: tx(e.message),
|
|
key,
|
|
sticky: true,
|
|
actions: [
|
|
...(tool?.canInstall
|
|
? [{ label: "Install", run: () => void installTool(e.installTool, { notificationKey: key }) }]
|
|
: []),
|
|
{ label: "Language Servers…", run: () => void executeAction("file.settings", SETTINGS_PAGE_ID) },
|
|
],
|
|
});
|
|
})();
|
|
});
|
|
}
|