dsh-vscode-mode 0.1.46 → 0.1.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +88 -6
- package/lib/client.js.map +1 -1
- package/lib/index.js +485 -29
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/ui/LspSettings.ts +83 -5
- package/src/lsp/config.ts +5 -1
- package/src/lsp/dotnetProvision.ts +224 -0
- package/src/lsp/envRequirements.ts +92 -0
- package/src/lsp/providers.ts +198 -25
- package/src/lsp/rpc.ts +24 -1
- package/src/lsp/server.ts +8 -1
- package/src/shared/lsp.ts +18 -0
- package/src/shared/rpc.ts +5 -1
package/lib/client.js
CHANGED
|
@@ -5804,8 +5804,8 @@ window.__ModuleLoader__.load({
|
|
|
5804
5804
|
hint: "优先使用已安装 EmmyLua;未安装时回退 LuaLS 或 PATH 中的 lua-language-server"
|
|
5805
5805
|
}, {
|
|
5806
5806
|
id: "csharp",
|
|
5807
|
-
label: "C#(Roslyn / OmniSharp)",
|
|
5808
|
-
hint: "
|
|
5807
|
+
label: "C#(Roslyn / DotRush / OmniSharp)",
|
|
5808
|
+
hint: "优先自动发现 ms-dotnettools.csharp / DotRush 扩展(DotRush 需 .NET 10 运行时);也可手动指定"
|
|
5809
5809
|
}];
|
|
5810
5810
|
const PHASE_LABEL = {
|
|
5811
5811
|
idle: "未启动",
|
|
@@ -5839,8 +5839,17 @@ window.__ModuleLoader__.load({
|
|
|
5839
5839
|
label: "更新"
|
|
5840
5840
|
}
|
|
5841
5841
|
];
|
|
5842
|
-
/**
|
|
5843
|
-
function
|
|
5842
|
+
/** 环境安装按钮文案(按安装进行态)。 */
|
|
5843
|
+
function envButtonLabel(req, envStates) {
|
|
5844
|
+
const state = (envStates ?? []).find((s) => s.id === req.id);
|
|
5845
|
+
if (state?.phase === "downloading") return "下载中…";
|
|
5846
|
+
if (state?.phase === "extracting") return "安装中…";
|
|
5847
|
+
if (state?.phase === "done") return "已完成";
|
|
5848
|
+
if (state?.phase === "failed") return "重试";
|
|
5849
|
+
return "一键安装";
|
|
5850
|
+
}
|
|
5851
|
+
/** 语言卡片:状态 + 缺失环境提示(一键安装/官网下载) + 启用开关 + 命令/路径配置 + 重新检测。 */
|
|
5852
|
+
function LangCard({ lang, config, status, busy, envStates, onInstallEnv, onToggle, onSave, onRedetect }) {
|
|
5844
5853
|
const [pathDraft, setPathDraft] = react.default.useState(config?.path ?? "");
|
|
5845
5854
|
const [commandDraft, setCommandDraft] = react.default.useState(config?.command ?? "");
|
|
5846
5855
|
react.default.useEffect(() => {
|
|
@@ -5852,7 +5861,23 @@ window.__ModuleLoader__.load({
|
|
|
5852
5861
|
className: "vsm-switch " + (config?.enabled !== false ? "on" : ""),
|
|
5853
5862
|
onClick: () => onToggle(lang.id, config?.enabled !== false ? false : true),
|
|
5854
5863
|
"aria-label": "启用/禁用"
|
|
5855
|
-
}, config?.enabled !== false ? "●" : "○"))), react.default.createElement("div", { className: "vsm-mcp-meta" }, PHASE_LABEL[phase] ?? phase, " · ", SOURCE_LABEL[status?.source] ?? status?.source, status?.providerName ? " · " + status.providerName : "", status?.version ? " · v" + status.version : "", phase === "ready" && status?.root ? " · " + String(status.root).split(/[\\/]/).pop() : ""), status?.reason ? react.default.createElement("div", { className: "vsm-mcp-error" }, status.reason) : null, react.default.createElement("div", { className: "vsm-lsp-
|
|
5864
|
+
}, config?.enabled !== false ? "●" : "○"))), react.default.createElement("div", { className: "vsm-mcp-meta" }, PHASE_LABEL[phase] ?? phase, " · ", SOURCE_LABEL[status?.source] ?? status?.source, status?.providerName ? " · " + status.providerName : "", status?.version ? " · v" + status.version : "", phase === "ready" && status?.root ? " · " + String(status.root).split(/[\\/]/).pop() : ""), status?.reason ? react.default.createElement("div", { className: "vsm-mcp-error" }, status.reason) : null, Array.isArray(status?.missingEnv) && status.missingEnv.length ? react.default.createElement("div", { className: "vsm-lsp-hint" }, "缺少运行环境(一键安装后自动生效):", status.missingEnv.map((req) => react.default.createElement("div", {
|
|
5865
|
+
key: req.id,
|
|
5866
|
+
className: "vsm-mcp-actions"
|
|
5867
|
+
}, react.default.createElement("span", null, req.label, req.detail ? "(" + req.detail + ")" : ""), req.installable ? react.default.createElement("button", {
|
|
5868
|
+
className: "vsm-primary vsm-small",
|
|
5869
|
+
disabled: busy === "env:" + req.id,
|
|
5870
|
+
onClick: () => onInstallEnv(lang.id, req)
|
|
5871
|
+
}, envButtonLabel(req, envStates)) : null, req.manualUrl ? react.default.createElement("a", {
|
|
5872
|
+
href: req.manualUrl,
|
|
5873
|
+
target: "_blank",
|
|
5874
|
+
rel: "noreferrer",
|
|
5875
|
+
style: {
|
|
5876
|
+
color: "inherit",
|
|
5877
|
+
textDecoration: "underline",
|
|
5878
|
+
alignSelf: "center"
|
|
5879
|
+
}
|
|
5880
|
+
}, req.manualLabel ?? "官网下载") : null))) : null, react.default.createElement("div", { className: "vsm-lsp-form" }, react.default.createElement("label", null, react.default.createElement("span", null, "可执行文件路径(绝对路径,优先)"), react.default.createElement("input", {
|
|
5856
5881
|
value: pathDraft,
|
|
5857
5882
|
placeholder: "如 C:/.../lua-language-server.exe",
|
|
5858
5883
|
onChange: (e) => setPathDraft(e.target.value)
|
|
@@ -5908,6 +5933,8 @@ window.__ModuleLoader__.load({
|
|
|
5908
5933
|
const [busy, setBusy] = react.default.useState("");
|
|
5909
5934
|
const [error, setError] = react.default.useState("");
|
|
5910
5935
|
const [loading, setLoading] = react.default.useState(true);
|
|
5936
|
+
const [envStates, setEnvStates] = react.default.useState([]);
|
|
5937
|
+
const [envLang, setEnvLang] = react.default.useState("");
|
|
5911
5938
|
const refreshServers = react.default.useCallback(() => {
|
|
5912
5939
|
Promise.all([rpc("edrv.lsp.configGet", {}), rpc("edrv.lsp.status", {})]).then(([cfg, st]) => {
|
|
5913
5940
|
if (cfg?.ok) setConfig(cfg.config ?? {});
|
|
@@ -5922,6 +5949,12 @@ window.__ModuleLoader__.load({
|
|
|
5922
5949
|
setError("");
|
|
5923
5950
|
}).catch((e) => setError(String(e)));
|
|
5924
5951
|
}, []);
|
|
5952
|
+
const refreshEnvStates = react.default.useCallback(() => {
|
|
5953
|
+
return rpc("edrv.lsp.envState", {}).then((res) => {
|
|
5954
|
+
if (res?.ok) setEnvStates(res.states ?? []);
|
|
5955
|
+
return res?.states ?? [];
|
|
5956
|
+
}).catch(() => []);
|
|
5957
|
+
}, []);
|
|
5925
5958
|
const refresh = react.default.useCallback(() => {
|
|
5926
5959
|
setLoading(true);
|
|
5927
5960
|
Promise.all([refreshServers(), refreshExt()]).finally(() => setLoading(false));
|
|
@@ -5941,9 +5974,56 @@ window.__ModuleLoader__.load({
|
|
|
5941
5974
|
setServers((prev) => Array.isArray(list) ? [...prev.filter((s) => s.languageId !== languageId), ...list] : prev);
|
|
5942
5975
|
};
|
|
5943
5976
|
/** 重检测后通知编辑器:与 host 重新同步已打开的模型(触发重新 acquire)。 */
|
|
5944
|
-
const notifyResync = (languageId) => {
|
|
5977
|
+
const notifyResync = react.default.useCallback((languageId) => {
|
|
5945
5978
|
window.dispatchEvent(new CustomEvent("edrv:lsp-redetect", { detail: { languageId } }));
|
|
5979
|
+
}, []);
|
|
5980
|
+
/** 一键安装缺失环境:启动内置安装器(成功后进入轮询,完成自动重同步)。 */
|
|
5981
|
+
const installEnv = (languageId, req) => {
|
|
5982
|
+
const key = "env:" + req.id;
|
|
5983
|
+
setBusy(key);
|
|
5984
|
+
setError("");
|
|
5985
|
+
setEnvLang(languageId);
|
|
5986
|
+
rpc("edrv.lsp.envInstall", {
|
|
5987
|
+
languageId,
|
|
5988
|
+
id: req.id
|
|
5989
|
+
}).then((res) => {
|
|
5990
|
+
if (!res?.ok) {
|
|
5991
|
+
setError(res?.error ?? "无法启动安装");
|
|
5992
|
+
return;
|
|
5993
|
+
}
|
|
5994
|
+
refreshEnvStates();
|
|
5995
|
+
refreshServers();
|
|
5996
|
+
}).catch((e) => setError(String(e))).finally(() => setBusy(""));
|
|
5946
5997
|
};
|
|
5998
|
+
react.default.useEffect(() => {
|
|
5999
|
+
if (!(envStates ?? []).some((s) => s.phase === "downloading" || s.phase === "extracting")) return;
|
|
6000
|
+
const timer = setInterval(() => {
|
|
6001
|
+
refreshEnvStates();
|
|
6002
|
+
refreshServers();
|
|
6003
|
+
}, 2e3);
|
|
6004
|
+
return () => clearInterval(timer);
|
|
6005
|
+
}, [
|
|
6006
|
+
envStates,
|
|
6007
|
+
refreshEnvStates,
|
|
6008
|
+
refreshServers
|
|
6009
|
+
]);
|
|
6010
|
+
const prevEnvPhases = react.default.useRef({});
|
|
6011
|
+
react.default.useEffect(() => {
|
|
6012
|
+
const states = envStates ?? [];
|
|
6013
|
+
for (const s of states) {
|
|
6014
|
+
const prev = prevEnvPhases.current[s.id];
|
|
6015
|
+
if (prev && prev !== "done" && s.phase === "done" && envLang) {
|
|
6016
|
+
refreshServers();
|
|
6017
|
+
notifyResync(envLang);
|
|
6018
|
+
}
|
|
6019
|
+
prevEnvPhases.current[s.id] = s.phase;
|
|
6020
|
+
}
|
|
6021
|
+
}, [
|
|
6022
|
+
envStates,
|
|
6023
|
+
envLang,
|
|
6024
|
+
refreshServers,
|
|
6025
|
+
notifyResync
|
|
6026
|
+
]);
|
|
5947
6027
|
const saveLang = (languageId, path, command) => {
|
|
5948
6028
|
setBusy(languageId);
|
|
5949
6029
|
setError("");
|
|
@@ -6064,6 +6144,8 @@ window.__ModuleLoader__.load({
|
|
|
6064
6144
|
config: config[lang.id] ?? {},
|
|
6065
6145
|
status: servers.find((s) => s.languageId === lang.id),
|
|
6066
6146
|
busy,
|
|
6147
|
+
envStates,
|
|
6148
|
+
onInstallEnv: installEnv,
|
|
6067
6149
|
onToggle: toggleLang,
|
|
6068
6150
|
onSave: saveLang,
|
|
6069
6151
|
onRedetect: redetectLang
|