dsh-vscode-mode 0.15.8 → 0.16.0
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/README.md +6 -4
- package/lib/client.js +312 -69
- package/lib/client.js.map +1 -1
- package/lib/index.js +262 -17
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +4 -0
- package/src/client/state/editorFindState.ts +15 -10
- package/src/client/state/editorViewState.ts +7 -4
- package/src/client/styles/editor.css +3 -0
- package/src/client/ui/EditorPane.ts +23 -3
- package/src/client/ui/EditorView.ts +117 -15
- package/src/client/ui/McpSettings.ts +36 -0
- package/src/client/updateNotice.ts +64 -0
- package/src/dap/discovery.ts +5 -3
- package/src/dap/provider.ts +26 -0
- package/src/lsp/providers.ts +9 -5
- package/src/lsp/server.ts +26 -1
- package/src/rpc.ts +9 -0
- package/src/selfUpdate.ts +188 -0
- package/src/shared/rpc.ts +4 -0
package/lib/index.js
CHANGED
|
@@ -10306,6 +10306,181 @@ async function storedOpenBase(dir, fallback) {
|
|
|
10306
10306
|
return fallback;
|
|
10307
10307
|
}
|
|
10308
10308
|
}
|
|
10309
|
+
//#endregion
|
|
10310
|
+
//#region src/selfUpdate.ts
|
|
10311
|
+
/**
|
|
10312
|
+
* dsh-vscode-mode host — 自我更新:registry 最新版本检查与官方 pluginManager「先删后装」编排。
|
|
10313
|
+
* 职责:compareVersion 语义化版本比较(纯函数可单测)、latestVersionOf 经 subprocess 运行
|
|
10314
|
+
* `pnpm view <pkg> version --json`(与 installBundle 同一套 pnpm/registry 配置)、
|
|
10315
|
+
* startSelfUpdate 把 removeBundle→installBundle 编排放进宿主级 setTimeout(先回 RPC 响应,
|
|
10316
|
+
* 定时器回调不受本插件卸载影响,规避「删除自身后 install 不再执行」的自愈陷阱)。
|
|
10317
|
+
* 注意:不 import @deepseek-ai/dsh-plugin-manager(鸭子类型引用 ctx.get('pluginManager')),
|
|
10318
|
+
* 老宿主缺失该服务时返回明确错误引导官方插件管理页。
|
|
10319
|
+
* 作者 ddj 2026-10-16
|
|
10320
|
+
*/
|
|
10321
|
+
/** 防并发重入标志:一旦启动编排,本次进程内不再接受第二次更新请求。 */
|
|
10322
|
+
let updating = false;
|
|
10323
|
+
/**
|
|
10324
|
+
* 语义化版本比较:核心段按 `.` 数值比较、段数不足补 0;预发布版(-beta 等)低于同号正式版,
|
|
10325
|
+
* 构建元数据(+)与预发布粒度不参与排序。
|
|
10326
|
+
* @private
|
|
10327
|
+
* @author ddj 2026年10月16号
|
|
10328
|
+
* @param a 左版本号
|
|
10329
|
+
* @param b 右版本号
|
|
10330
|
+
* @returns a>b 为 1,a<b 为 -1,相等为 0
|
|
10331
|
+
*/
|
|
10332
|
+
function compareVersion$1(a, b) {
|
|
10333
|
+
const parse = (text) => {
|
|
10334
|
+
const value = String(text ?? "").trim().replace(/^[^\d]*/, "");
|
|
10335
|
+
const cut = value.search(/[-+]/);
|
|
10336
|
+
return {
|
|
10337
|
+
nums: (cut >= 0 ? value.slice(0, cut) : value).split(".").map((part) => {
|
|
10338
|
+
const num = Number.parseInt(part, 10);
|
|
10339
|
+
return Number.isFinite(num) ? num : 0;
|
|
10340
|
+
}),
|
|
10341
|
+
pre: cut >= 0 && value[cut] === "-"
|
|
10342
|
+
};
|
|
10343
|
+
};
|
|
10344
|
+
const left = parse(a);
|
|
10345
|
+
const right = parse(b);
|
|
10346
|
+
const total = Math.max(left.nums.length, right.nums.length);
|
|
10347
|
+
for (let index = 0; index < total; index += 1) {
|
|
10348
|
+
const diff = (left.nums[index] ?? 0) - (right.nums[index] ?? 0);
|
|
10349
|
+
if (diff !== 0) return diff > 0 ? 1 : -1;
|
|
10350
|
+
}
|
|
10351
|
+
if (left.pre !== right.pre) return left.pre ? -1 : 1;
|
|
10352
|
+
return 0;
|
|
10353
|
+
}
|
|
10354
|
+
/**
|
|
10355
|
+
* 用 profileContext.packageManager(缺省 pnpm)查询 npm registry 上本包最新版本。
|
|
10356
|
+
* @private
|
|
10357
|
+
* @author ddj 2026年10月16号
|
|
10358
|
+
* @param ctx DSH host 上下文
|
|
10359
|
+
* @returns registry 最新版本号
|
|
10360
|
+
* @throws pnpm 不存在、退出非零或输出不可解析
|
|
10361
|
+
*/
|
|
10362
|
+
async function latestVersionOf(ctx) {
|
|
10363
|
+
const dir = findProfileDir(ctx);
|
|
10364
|
+
if (!dir) throw new Error("无法定位当前 profile,无法查询最新版本");
|
|
10365
|
+
const runner = ctx.get("profileContext")?.packageManager;
|
|
10366
|
+
return parseVersionJson(await new Promise((resolvePromise, rejectPromise) => {
|
|
10367
|
+
let spawned = false;
|
|
10368
|
+
try {
|
|
10369
|
+
const handle = ctx.get("subprocess").spawn({
|
|
10370
|
+
argv: [
|
|
10371
|
+
runner?.command ?? "pnpm",
|
|
10372
|
+
...runner?.args ?? [],
|
|
10373
|
+
"view",
|
|
10374
|
+
PLUGIN_NAME,
|
|
10375
|
+
"version",
|
|
10376
|
+
"--json"
|
|
10377
|
+
],
|
|
10378
|
+
cwd: dir,
|
|
10379
|
+
env: childEnv(runner?.env),
|
|
10380
|
+
stdio: {
|
|
10381
|
+
stdout: "pipe",
|
|
10382
|
+
stderr: "pipe",
|
|
10383
|
+
stdin: "ignore"
|
|
10384
|
+
},
|
|
10385
|
+
graceMs: 2e4
|
|
10386
|
+
});
|
|
10387
|
+
spawned = true;
|
|
10388
|
+
readSpawnText(handle, resolvePromise, rejectPromise);
|
|
10389
|
+
} catch (error) {
|
|
10390
|
+
if (!spawned) rejectPromise(error instanceof Error ? error : new Error(String(error)));
|
|
10391
|
+
}
|
|
10392
|
+
}));
|
|
10393
|
+
}
|
|
10394
|
+
/**
|
|
10395
|
+
* 读取 spawn 句柄的 stdout 文本并等待退出(非零退出或空输出抛错)。
|
|
10396
|
+
* @private
|
|
10397
|
+
* @author ddj 2026年10月16号
|
|
10398
|
+
* @param handle subprocess.spawn 返回句柄
|
|
10399
|
+
* @param resolvePromise 成功回传 stdout 全文
|
|
10400
|
+
* @param rejectPromise 失败回传错误
|
|
10401
|
+
*/
|
|
10402
|
+
function readSpawnText(handle, resolvePromise, rejectPromise) {
|
|
10403
|
+
handle.done.then((outcome) => {
|
|
10404
|
+
const text = handle.collected?.stdout?.readFrom(0)?.text ?? "";
|
|
10405
|
+
if (outcome?.exitCode !== 0 || !text.trim()) {
|
|
10406
|
+
rejectPromise(/* @__PURE__ */ new Error("pnpm view 失败(exit " + String(outcome?.exitCode) + "):" + text.slice(0, 200)));
|
|
10407
|
+
return;
|
|
10408
|
+
}
|
|
10409
|
+
resolvePromise(text);
|
|
10410
|
+
}, rejectPromise);
|
|
10411
|
+
}
|
|
10412
|
+
/**
|
|
10413
|
+
* 解析 `pnpm view --json` 输出:JSON 字符串("1.2.3")或直接版本字面量均接受。
|
|
10414
|
+
* @private
|
|
10415
|
+
* @author ddj 2026年10月16号
|
|
10416
|
+
* @param text stdout 全文
|
|
10417
|
+
* @returns 版本号
|
|
10418
|
+
* @throws 输出不含可识别版本
|
|
10419
|
+
*/
|
|
10420
|
+
function parseVersionJson(text) {
|
|
10421
|
+
const trimmed = text.trim();
|
|
10422
|
+
try {
|
|
10423
|
+
const value = JSON.parse(trimmed);
|
|
10424
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
10425
|
+
} catch {}
|
|
10426
|
+
if (/^\d+(\.\d+){0,3}([-+].*)?$/.test(trimmed)) return trimmed;
|
|
10427
|
+
throw new Error("无法解析 pnpm view 输出:" + trimmed.slice(0, 120));
|
|
10428
|
+
}
|
|
10429
|
+
/**
|
|
10430
|
+
* 检查 registry 是否有比当前安装版本更新的版本。
|
|
10431
|
+
* @public
|
|
10432
|
+
* @author ddj 2026年10月16号
|
|
10433
|
+
* @param ctx DSH host 上下文
|
|
10434
|
+
* @returns 当前版本 / 最新版本 / 是否有更新
|
|
10435
|
+
*/
|
|
10436
|
+
async function checkUpdate(ctx) {
|
|
10437
|
+
const current = pluginVersionOf();
|
|
10438
|
+
try {
|
|
10439
|
+
const latest = await latestVersionOf(ctx);
|
|
10440
|
+
return {
|
|
10441
|
+
ok: true,
|
|
10442
|
+
current,
|
|
10443
|
+
latest,
|
|
10444
|
+
hasUpdate: latest !== "" && compareVersion$1(latest, current) > 0
|
|
10445
|
+
};
|
|
10446
|
+
} catch (error) {
|
|
10447
|
+
return {
|
|
10448
|
+
ok: false,
|
|
10449
|
+
current,
|
|
10450
|
+
error: String(error)
|
|
10451
|
+
};
|
|
10452
|
+
}
|
|
10453
|
+
}
|
|
10454
|
+
/**
|
|
10455
|
+
* 启动「先删后装」自我更新编排:校验通过后把编排放进宿主级 setTimeout(延迟 1s 让 RPC 响应
|
|
10456
|
+
* 先送达客户端),立即返回 started;定时器回调不受本插件卸载影响。install 失败时
|
|
10457
|
+
* pluginManager 会恢复 profile 的 package.json/pnpm-lock.yaml,插件保持未安装状态,
|
|
10458
|
+
* 由用户在官方插件管理页重装恢复。
|
|
10459
|
+
* @public
|
|
10460
|
+
* @author ddj 2026年10月16号
|
|
10461
|
+
* @param ctx DSH host 上下文
|
|
10462
|
+
* @returns started 是否已启动编排;失败时 ok=false
|
|
10463
|
+
* @throws 无(失败走返回值 error)
|
|
10464
|
+
*/
|
|
10465
|
+
async function startSelfUpdate(ctx) {
|
|
10466
|
+
if (updating) return {
|
|
10467
|
+
ok: false,
|
|
10468
|
+
error: "更新流程已在进行中,请重启 DSH 后观察结果"
|
|
10469
|
+
};
|
|
10470
|
+
const manager = ctx.get("pluginManager");
|
|
10471
|
+
if (!manager || typeof manager.removeBundle !== "function" || typeof manager.installBundle !== "function") return {
|
|
10472
|
+
ok: false,
|
|
10473
|
+
error: "当前宿主无 pluginManager 服务,请在官方插件管理页卸载后重新安装以升级"
|
|
10474
|
+
};
|
|
10475
|
+
updating = true;
|
|
10476
|
+
setTimeout(() => {
|
|
10477
|
+
manager.removeBundle(PLUGIN_NAME).then(() => manager.installBundle(PLUGIN_NAME)).catch(() => {});
|
|
10478
|
+
}, 1e3);
|
|
10479
|
+
return {
|
|
10480
|
+
ok: true,
|
|
10481
|
+
started: true
|
|
10482
|
+
};
|
|
10483
|
+
}
|
|
10309
10484
|
/**
|
|
10310
10485
|
* 路径归一:去首尾空白 + 反斜杠转 '/'(Windows 输入等价处理)。
|
|
10311
10486
|
* @author ddj 2026年09月22号
|
|
@@ -12827,6 +13002,26 @@ function buildHandlers(ctx, registry, searcher = newSearcher(ctx), contentSearch
|
|
|
12827
13002
|
};
|
|
12828
13003
|
}
|
|
12829
13004
|
},
|
|
13005
|
+
"vscode.updateCheck": async () => {
|
|
13006
|
+
try {
|
|
13007
|
+
return await checkUpdate(ctx);
|
|
13008
|
+
} catch (error) {
|
|
13009
|
+
return {
|
|
13010
|
+
ok: false,
|
|
13011
|
+
error: String(error)
|
|
13012
|
+
};
|
|
13013
|
+
}
|
|
13014
|
+
},
|
|
13015
|
+
"vscode.updateSelf": async () => {
|
|
13016
|
+
try {
|
|
13017
|
+
return await startSelfUpdate(ctx);
|
|
13018
|
+
} catch (error) {
|
|
13019
|
+
return {
|
|
13020
|
+
ok: false,
|
|
13021
|
+
error: String(error)
|
|
13022
|
+
};
|
|
13023
|
+
}
|
|
13024
|
+
},
|
|
12830
13025
|
"edrv.integration.status": async () => {
|
|
12831
13026
|
try {
|
|
12832
13027
|
return {
|
|
@@ -14225,6 +14420,25 @@ function isDeclarationForm(word, queryChain) {
|
|
|
14225
14420
|
*/
|
|
14226
14421
|
const MAX_DOC_VERSION = 2147483647;
|
|
14227
14422
|
/**
|
|
14423
|
+
* 找工作区根目录下的第一个 .sln(字典序)。
|
|
14424
|
+
*
|
|
14425
|
+
* 背景:Roslyn LSP 启动后若收不到 solution/project 通知,会停留在「杂项文件」模式——
|
|
14426
|
+
* 工作区内自有符号可解析,但 Unity 引擎程序集 / NuGet 包等元数据符号的
|
|
14427
|
+
* definition/hover 一律返回空(实测 2026-10-16,GameObject.Find 全空)。
|
|
14428
|
+
* 需在握手后发 Roslyn 专有 `solution/open` 通知加载项目上下文。
|
|
14429
|
+
* @author ddj 2026年10月19号
|
|
14430
|
+
* @param root 工作区根(绝对路径)
|
|
14431
|
+
* @returns sln 绝对路径;根目录无 sln 返回 null
|
|
14432
|
+
*/
|
|
14433
|
+
async function findSolutionFile(root) {
|
|
14434
|
+
try {
|
|
14435
|
+
const names = (await readdir(root)).filter((name) => name.toLowerCase().endsWith(".sln")).sort();
|
|
14436
|
+
return names[0] ? join(root, names[0]) : null;
|
|
14437
|
+
} catch {
|
|
14438
|
+
return null;
|
|
14439
|
+
}
|
|
14440
|
+
}
|
|
14441
|
+
/**
|
|
14228
14442
|
* 使用合法、递增的 Int32 文档版本;旧客户端的毫秒时间戳不能直发给 Roslyn。
|
|
14229
14443
|
* @author ddj 2026年09月24号
|
|
14230
14444
|
* @param incoming 客户端传来的版本
|
|
@@ -14485,7 +14699,11 @@ function createLspServer(spec, root, languageId, logger) {
|
|
|
14485
14699
|
onLog: (line) => log("lsp: " + line)
|
|
14486
14700
|
});
|
|
14487
14701
|
const caps = await client.initialize(rootUri, [rootUri]);
|
|
14488
|
-
if (languageId === "csharp")
|
|
14702
|
+
if (languageId === "csharp") {
|
|
14703
|
+
client.notify("workspace/didChangeConfiguration", { settings: { dotrush: { roslyn: {} } } });
|
|
14704
|
+
const slnPath = await findSolutionFile(root);
|
|
14705
|
+
if (slnPath) client.notify("solution/open", { solution: pathToFileUri(slnPath) });
|
|
14706
|
+
}
|
|
14489
14707
|
capabilities = caps;
|
|
14490
14708
|
return true;
|
|
14491
14709
|
} catch (error) {
|
|
@@ -15909,8 +16127,8 @@ function resolveLuaProvider(manual, home = dshHome()) {
|
|
|
15909
16127
|
});
|
|
15910
16128
|
}
|
|
15911
16129
|
/**
|
|
15912
|
-
* 解析 C# provider(优先级:手动 > 扩展源 > 自动发现;
|
|
15913
|
-
*
|
|
16130
|
+
* 解析 C# provider(优先级:手动 > 扩展源 > 自动发现;DotRush 优先——自带 sln 加载与
|
|
16131
|
+
* 反编译跳转;Roslyn dll 需 dotnet,但无 C# Dev Kit 时不加载项目;或 OmniSharp)。
|
|
15914
16132
|
* @author ddj 2026年08月27号
|
|
15915
16133
|
* @param manual 手动配置
|
|
15916
16134
|
* @param home DSH home(缺省真实;测试可注入临时目录)
|
|
@@ -15932,17 +16150,6 @@ function resolveCSharpProvider(manual, home = dshHome()) {
|
|
|
15932
16150
|
const discovered = candidateCSharpServers(h);
|
|
15933
16151
|
const sdkRoot = findSdkRoot();
|
|
15934
16152
|
const msbuildEnv = sdkRoot ? sdkEnvOf(sdkRoot) : void 0;
|
|
15935
|
-
if (discovered.roslynDll && discovered.dotnet) return {
|
|
15936
|
-
...base,
|
|
15937
|
-
kind: "discover",
|
|
15938
|
-
argv: [
|
|
15939
|
-
discovered.dotnet,
|
|
15940
|
-
discovered.roslynDll,
|
|
15941
|
-
"--stdio"
|
|
15942
|
-
],
|
|
15943
|
-
env: msbuildEnv,
|
|
15944
|
-
ready: true
|
|
15945
|
-
};
|
|
15946
16153
|
let dotrushReason = null;
|
|
15947
16154
|
let provisionMajor;
|
|
15948
16155
|
if (discovered.dotrushDll) {
|
|
@@ -15960,6 +16167,17 @@ function resolveCSharpProvider(manual, home = dshHome()) {
|
|
|
15960
16167
|
dotrushReason = launch.reason;
|
|
15961
16168
|
provisionMajor = major ?? void 0;
|
|
15962
16169
|
}
|
|
16170
|
+
if (discovered.roslynDll && discovered.dotnet) return {
|
|
16171
|
+
...base,
|
|
16172
|
+
kind: "discover",
|
|
16173
|
+
argv: [
|
|
16174
|
+
discovered.dotnet,
|
|
16175
|
+
discovered.roslynDll,
|
|
16176
|
+
"--stdio"
|
|
16177
|
+
],
|
|
16178
|
+
env: msbuildEnv,
|
|
16179
|
+
ready: true
|
|
16180
|
+
};
|
|
15963
16181
|
if (discovered.omnisharpExe) return {
|
|
15964
16182
|
...base,
|
|
15965
16183
|
kind: "discover",
|
|
@@ -22515,7 +22733,8 @@ function listProcesses() {
|
|
|
22515
22733
|
* ~/.vscode-server/extensions、插件扩展目录),读各扩展 package.json 的
|
|
22516
22734
|
* contributes.debuggers[] 声明,解析为 DapAdapterDecl:
|
|
22517
22735
|
* - program 按平台取 windows/linux/osx 覆盖,相对扩展根解析;
|
|
22518
|
-
* - runtime='node' → 由 node 跑 js
|
|
22736
|
+
* - runtime='node' → 由 node 跑 js 入口;runtime='dotnet' → 由 dotnet 跑 dll 入口;
|
|
22737
|
+
* 无 runtime → program 即可执行(args 为清单声明);
|
|
22519
22738
|
* - 同 type 多扩展并存 → 按「清单版本降序(numeric) → 路径升序」取唯一(与 LSP 候选口径一致);
|
|
22520
22739
|
* - 入口不存在 / runtime 不支持 → available=false + reason(如 DotRush 按需下载的 clrdbg,
|
|
22521
22740
|
* 诚实标注不假成功,调用方据此在下拉置灰并提示原因)。
|
|
@@ -22597,9 +22816,9 @@ function declOf(extDir, entry) {
|
|
|
22597
22816
|
const languages = stringsOf(entry.languages);
|
|
22598
22817
|
const resolved = resolveProgram(extDir, entry);
|
|
22599
22818
|
const runtime = typeof entry.runtime === "string" && entry.runtime ? entry.runtime : void 0;
|
|
22600
|
-
const runtimeBad = runtime !== void 0 && runtime !== "node";
|
|
22819
|
+
const runtimeBad = runtime !== void 0 && runtime !== "node" && runtime !== "dotnet";
|
|
22601
22820
|
const available = resolved.available && !runtimeBad;
|
|
22602
|
-
const reason = !resolved.available ? resolved.reason : runtimeBad ? "暂不支持运行时 \"" + runtime + "\"(仅支持 node 与原生可执行)" : void 0;
|
|
22821
|
+
const reason = !resolved.available ? resolved.reason : runtimeBad ? "暂不支持运行时 \"" + runtime + "\"(仅支持 node、dotnet 与原生可执行)" : void 0;
|
|
22603
22822
|
return {
|
|
22604
22823
|
type,
|
|
22605
22824
|
label: typeof entry.label === "string" && entry.label ? entry.label : type,
|
|
@@ -22665,6 +22884,8 @@ function stringsOf(value) {
|
|
|
22665
22884
|
* dsh-vscode-mode host — 调试适配器规格解析:按调试类型从扩展清单声明(discovery)
|
|
22666
22885
|
* 解析 spawn 规格,通用不限定语言:
|
|
22667
22886
|
* - runtime='node' → node 跑 js 入口(emmylua 形态,等价旧 process.execPath 硬编码);
|
|
22887
|
+
* - runtime='dotnet' → dotnet 跑 dll 入口(DotRush monodbg 形态;按 dll runtimeconfig
|
|
22888
|
+
* 要求的运行时大版本找满足的 dotnet,对齐 LSP 侧 dotrushLaunch 口径);
|
|
22668
22889
|
* - 无 runtime → program 即原生可执行(clrdbg/monodbg 形态,args 为清单声明如 --interpreter=vscode);
|
|
22669
22890
|
* - 其它 runtime 明确不支持(decl.available=false 带原因,不做假启动)。
|
|
22670
22891
|
* 旧版仅探测 tangzx.emmylua 两型(attach/new),由清单驱动后自然覆盖并扩展到任意适配器。
|
|
@@ -22693,6 +22914,15 @@ function resolveAdapterSpec(type, force = false, home) {
|
|
|
22693
22914
|
command: process.execPath,
|
|
22694
22915
|
args: [decl.program, ...decl.args]
|
|
22695
22916
|
};
|
|
22917
|
+
if (decl.runtime === "dotnet") {
|
|
22918
|
+
const dotnet = dotnetLaunch(decl.extensionPath, decl.program);
|
|
22919
|
+
if (!dotnet) return null;
|
|
22920
|
+
return {
|
|
22921
|
+
...base,
|
|
22922
|
+
command: dotnet,
|
|
22923
|
+
args: [decl.program, ...decl.args]
|
|
22924
|
+
};
|
|
22925
|
+
}
|
|
22696
22926
|
if (!decl.runtime) return {
|
|
22697
22927
|
...base,
|
|
22698
22928
|
command: decl.program,
|
|
@@ -22700,6 +22930,21 @@ function resolveAdapterSpec(type, force = false, home) {
|
|
|
22700
22930
|
};
|
|
22701
22931
|
return null;
|
|
22702
22932
|
}
|
|
22933
|
+
/**
|
|
22934
|
+
* 为 dotnet 型适配器找启动用 dotnet:优先扩展内置 SDK,其次满足 dll runtimeconfig
|
|
22935
|
+
* 要求的运行时大版本,兜底任意 dotnet(对齐 LSP 侧 dotrushLaunch 口径)。
|
|
22936
|
+
* @author ddj 2026年10月19号
|
|
22937
|
+
* @param extensionPath 扩展根目录
|
|
22938
|
+
* @param program 适配器 dll 绝对路径
|
|
22939
|
+
* @returns dotnet 可执行路径;找不到满足条件的返回 null
|
|
22940
|
+
*/
|
|
22941
|
+
function dotnetLaunch(extensionPath, program) {
|
|
22942
|
+
const sdkDotnet = join(extensionPath, "extension", "Sdk", "dotnet" + (process.platform === "win32" ? ".exe" : ""));
|
|
22943
|
+
if (existsSync(sdkDotnet)) return sdkDotnet;
|
|
22944
|
+
const major = runtimeMajorOf(dirname(program));
|
|
22945
|
+
if (major !== null) return dotnetWithRuntime(major) ?? findDotnet();
|
|
22946
|
+
return findDotnet();
|
|
22947
|
+
}
|
|
22703
22948
|
//#endregion
|
|
22704
22949
|
//#region src/dap/protocol.ts
|
|
22705
22950
|
/** 客户端 → 适配器请求帧构造。 */
|