Skip to content

Commit 499ba2f

Browse files
author
wellwei
committed
fix: keep IDE commands responsive after CDB removal
1 parent 0740431 commit 499ba2f

9 files changed

Lines changed: 144 additions & 40 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# 更新日志
22

3+
## 未发布
4+
5+
- 修复缺少 CDB 时 IDE 命令看似无响应的问题:配置 clangd、刷新编译数据库、检查模块支持和一键配置现在会立即显示进度并打开 `mcpp` 输出频道;clangd 重启与 `mcpp build --configure-only` 增加超时,避免异步操作永久占用 IDE 队列。
6+
- 增加删除 `compile_commands.json` 后刷新 CDB、配置 clangd、检查模块支持和一键配置的 Extension Host 回归测试。
7+
38
## 0.3.0 - 2026-08-11
49

510
- 将编译数据库刷新迁移到 `mcpp build --configure-only`:不解析 stdout 人类文本,以退出码

‎src/cliController.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ export class McppCliController {
632632
} catch {
633633
// 输出频道可能已经在窗口重载时释放。
634634
}
635-
await vscode.window.showErrorMessage(`mcpp:${message}`);
635+
void vscode.window.showErrorMessage(`mcpp:${message}`);
636636
}
637637
};
638638
}
@@ -679,15 +679,15 @@ export class McppCliController {
679679
const result = await runProcess(executable, args, workingDirectory(project));
680680
this.appendShortCommand("查看工具链", executable, args, result);
681681
if (result.exitCode !== 0) {
682-
await vscode.window.showErrorMessage(
682+
void vscode.window.showErrorMessage(
683683
`mcpp toolchain list 失败(退出码 ${result.exitCode})。请查看 mcpp 输出频道。`,
684684
);
685685
return undefined;
686686
}
687687

688688
const inventory = parseToolchainList(`${result.stdout}${result.stderr.length > 0 ? `\n${result.stderr}` : ""}`);
689689
if (!inventory.recognized) {
690-
await vscode.window.showErrorMessage(
690+
void vscode.window.showErrorMessage(
691691
"无法识别当前 mcpp toolchain list 输出;原始输出已保留在 mcpp 输出频道,请检查 mcpp 版本。",
692692
);
693693
return undefined;

‎src/configureOnly.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import { runProcess, type ProcessResult, type ProcessRunner } from "./process";
22

33
export const configureOnlyArguments = ["build", "--configure-only"] as const;
4+
// 配置阶段可能解析工具链和依赖,但不能无限期占住 IDE 操作队列。
5+
export const configureOnlyTimeoutMs = 5 * 60_000;
46

57
export function runConfigureOnly(
68
projectRoot: string,
79
executable = "mcpp",
810
runner: ProcessRunner = runProcess,
911
): Promise<ProcessResult> {
10-
return runner(executable, [...configureOnlyArguments], projectRoot);
12+
return runner(
13+
executable,
14+
[...configureOnlyArguments],
15+
projectRoot,
16+
{ timeoutMs: configureOnlyTimeoutMs },
17+
);
1118
}

‎src/extension.ts‎

Lines changed: 51 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
shouldRenderProjectStatus,
4848
shouldUseWorkspaceClangd,
4949
statusCommandForCapability,
50+
withTimeout,
5051
workspaceAllowsToolExecution,
5152
type ModuleSupportState,
5253
} from "./workflow";
@@ -306,12 +307,23 @@ async function maybeDisableCppTools(project: McppProjectDiscovery): Promise<void
306307
}
307308
}
308309

309-
async function restartClangd(): Promise<boolean> {
310+
const CLANGD_RESTART_TIMEOUT_MS = 15_000;
311+
312+
async function restartClangd(output: vscode.OutputChannel): Promise<boolean> {
310313
if (vscode.extensions.getExtension("llvm-vs-code-extensions.vscode-clangd") === undefined) {
311314
return false;
312315
}
313316
try {
314-
await vscode.commands.executeCommand("clangd.restart");
317+
const timedOut = {};
318+
const result = await withTimeout(
319+
vscode.commands.executeCommand("clangd.restart"),
320+
CLANGD_RESTART_TIMEOUT_MS,
321+
timedOut,
322+
);
323+
if (result === timedOut) {
324+
appendOutputLine(output, `[clangd] 重启命令超过 ${CLANGD_RESTART_TIMEOUT_MS / 1000} 秒未完成,已释放 IDE 操作队列。`);
325+
return false;
326+
}
315327
return true;
316328
} catch {
317329
// clangd 扩展可能尚未激活,但配置仍然已经写入。
@@ -329,7 +341,7 @@ async function configureClangd(
329341
const interactive = mode === "interactive";
330342
if (context.analysis.capability === "syntax-only") {
331343
if (interactive) {
332-
await vscode.window.showWarningMessage(
344+
void vscode.window.showWarningMessage(
333345
`${context.analysis.kind.toUpperCase()} 模块产物无法由 clangd 读取。语法高亮仍然可用,但模块语义诊断需要 LLVM mcpp 工具链。`,
334346
);
335347
}
@@ -338,7 +350,7 @@ async function configureClangd(
338350
}
339351
if (context.analysis.capability !== "full" || context.analysis.compilerPath === undefined) {
340352
if (interactive) {
341-
await vscode.window.showWarningMessage(
353+
void vscode.window.showWarningMessage(
342354
`${context.analysis.reason} 请先运行“mcpp: 刷新编译数据库”。`,
343355
);
344356
}
@@ -347,7 +359,7 @@ async function configureClangd(
347359
if (!workspaceAllowsToolExecution(vscode.workspace.isTrusted)) {
348360
const message = "当前工作区未受信任,mcpp 不会执行 CDB 中的编译器或 clangd,也不会接管 clangd 配置。";
349361
if (interactive) {
350-
await vscode.window.showWarningMessage(message);
362+
void vscode.window.showWarningMessage(message);
351363
} else {
352364
appendOutputLine(output, `[自动配置] ${message}`);
353365
}
@@ -358,7 +370,7 @@ async function configureClangd(
358370
if (clangd === undefined) {
359371
const message = "没有找到可用的 clangd。请安装与 mcpp LLVM 编译器来自同一 revision 的 clangd,或设置 mcpp.clangd.path;clangd 可以来自 xlings llvm-tools,也可以独立安装。";
360372
if (interactive) {
361-
await vscode.window.showErrorMessage(message);
373+
void vscode.window.showErrorMessage(message);
362374
} else {
363375
appendOutputLine(output, `[自动配置] ${message}`);
364376
}
@@ -399,11 +411,11 @@ async function configureClangd(
399411
await maybeDisableCppTools(context.project);
400412
}
401413
const restartRequired = shouldRestartClangd(plan.changed, interactive, forceRestart);
402-
const restartSucceeded = restartRequired ? await restartClangd() : false;
414+
const restartSucceeded = restartRequired ? await restartClangd(output) : false;
403415
if (!configurationReadyAfterRestart(restartRequired, restartSucceeded)) {
404416
const message = "clangd 配置已写入,但无法重启语言服务器。请查看 mcpp 输出频道,或手动执行 clangd 重启命令。";
405417
if (interactive) {
406-
await vscode.window.showErrorMessage(message);
418+
void vscode.window.showErrorMessage(message);
407419
} else {
408420
appendOutputLine(output, `[自动配置] ${message}`);
409421
}
@@ -412,11 +424,11 @@ async function configureClangd(
412424
updateStatusBar(status, context);
413425

414426
if (!clangd.comparison.compatible) {
415-
await vscode.window.showWarningMessage(
427+
void vscode.window.showWarningMessage(
416428
`clangd 已配置,但 LLVM 身份与 mcpp 编译器不匹配:${clangd.comparison.reason}。`,
417429
);
418430
} else if (interactive) {
419-
await vscode.window.showInformationMessage("mcpp 已为当前工作区配置匹配的 clangd。");
431+
void vscode.window.showInformationMessage("mcpp 已为当前工作区配置匹配的 clangd。");
420432
} else if (plan.changed) {
421433
appendOutputLine(output, `[自动配置] clangd.path = ${plan.path}`);
422434
}
@@ -546,7 +558,7 @@ async function runModuleSupportCheck(
546558
storeModuleStatus(status, context, { state: "unavailable", message });
547559
}
548560
if (interactive) {
549-
await vscode.window.showWarningMessage(message);
561+
void vscode.window.showWarningMessage(message);
550562
}
551563
return context.analysis.capability === "full"
552564
? { state: "unavailable", message }
@@ -559,7 +571,7 @@ async function runModuleSupportCheck(
559571
const moduleStatus = { state: "unavailable", message } as const;
560572
storeModuleStatus(status, context, moduleStatus);
561573
if (interactive) {
562-
await vscode.window.showWarningMessage(message);
574+
void vscode.window.showWarningMessage(message);
563575
} else {
564576
appendOutputLine(output, `[自动检查] ${message}`);
565577
}
@@ -583,7 +595,7 @@ async function runModuleSupportCheck(
583595
return undefined;
584596
}
585597
if (interactive) {
586-
await vscode.window.showErrorMessage(message);
598+
void vscode.window.showErrorMessage(message);
587599
} else {
588600
appendOutputLine(output, `[自动检查] ${message}`);
589601
}
@@ -630,9 +642,9 @@ async function runModuleSupportCheck(
630642
}
631643
if (interactive) {
632644
if (moduleStatus.state === "available") {
633-
await vscode.window.showInformationMessage(message);
645+
void vscode.window.showInformationMessage(message);
634646
} else {
635-
await vscode.window.showErrorMessage(message);
647+
void vscode.window.showErrorMessage(message);
636648
}
637649
} else {
638650
appendOutputLine(output, `[自动检查] ${message}`);
@@ -751,7 +763,7 @@ async function autoConfigureModulesWizard(
751763
appendOutputLine(output, "[一键配置] 开始一键配置模块代码提示...");
752764

753765
if (!vscode.workspace.isTrusted) {
754-
await vscode.window.showWarningMessage(moduleSetupBlockedMessage("untrusted"));
766+
void vscode.window.showWarningMessage(moduleSetupBlockedMessage("untrusted"));
755767
return;
756768
}
757769
const inventory = await cliController.readToolchainInventory(context.project);
@@ -765,7 +777,7 @@ async function autoConfigureModulesWizard(
765777
cliController.isBusy(),
766778
);
767779
if (decision.kind === "blocked") {
768-
await vscode.window.showWarningMessage(moduleSetupBlockedMessage(decision.reason));
780+
void vscode.window.showWarningMessage(moduleSetupBlockedMessage(decision.reason));
769781
return;
770782
}
771783

@@ -861,14 +873,14 @@ async function autoConfigureModulesWizard(
861873

862874
if (outcome.state === "succeeded") {
863875
if (outcome.degraded) {
864-
await vscode.window.showWarningMessage("构建失败,语言服务已刷新。请查看任务终端获取构建错误。");
876+
void vscode.window.showWarningMessage("构建失败,语言服务已刷新。请查看任务终端获取构建错误。");
865877
} else {
866-
await vscode.window.showInformationMessage("mcpp 模块代码提示一键配置完成。");
878+
void vscode.window.showInformationMessage("mcpp 模块代码提示一键配置完成。");
867879
}
868880
} else if (outcome.state === "cancelled") {
869-
await vscode.window.showWarningMessage(`一键配置已取消(${outcome.stage})。`);
881+
void vscode.window.showWarningMessage(`一键配置已取消(${outcome.stage})。`);
870882
} else {
871-
await vscode.window.showErrorMessage(`一键配置失败(${outcome.stage})。${outcome.steps.at(-1)?.detail ?? "请查看 mcpp 输出频道。"}`);
883+
void vscode.window.showErrorMessage(`一键配置失败(${outcome.stage})。${outcome.steps.at(-1)?.detail ?? "请查看 mcpp 输出频道。"}`);
872884
}
873885
}
874886

@@ -931,6 +943,10 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
931943
void vscode.window.showErrorMessage(`mcpp:${message}`);
932944
}
933945
};
946+
const showInteractiveIdeStart = (title: string): void => {
947+
appendOutputLine(output, `\n[IDE] ${title}:已接收,正在等待 IDE 操作队列。`);
948+
output.show(true);
949+
};
934950
let cliController: McppCliController;
935951
const runConfigureOnlyForProject = async (
936952
project: McppProjectDiscovery,
@@ -939,7 +955,7 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
939955
if (!workspaceAllowsToolExecution(vscode.workspace.isTrusted)) {
940956
appendOutputLine(output, "[CDB 配置] 工作区未受信任,跳过 mcpp build --configure-only。");
941957
if (interactive) {
942-
await vscode.window.showWarningMessage(
958+
void vscode.window.showWarningMessage(
943959
"当前工作区未受信任,不会刷新编译数据库。请先信任工作区。",
944960
);
945961
}
@@ -949,7 +965,7 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
949965
if (result === undefined) {
950966
appendOutputLine(output, "[CDB 配置] 已有 mcpp 操作正在运行,本次刷新已跳过。");
951967
if (interactive) {
952-
await vscode.window.showWarningMessage(
968+
void vscode.window.showWarningMessage(
953969
"已有 mcpp 操作正在运行,暂不能刷新编译数据库。请等待当前操作完成后重试。",
954970
);
955971
}
@@ -1204,7 +1220,7 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
12041220
if (kind === "build") {
12051221
if (reconciled.context?.analysis.capability === "syntax-only") {
12061222
const buildMessage = completion.state === "succeeded" ? "mcpp 构建完成" : "mcpp 构建失败";
1207-
await vscode.window.showWarningMessage(
1223+
void vscode.window.showWarningMessage(
12081224
`${buildMessage};${reconciled.context.analysis.reason}。模块语法高亮仍然可用。`,
12091225
);
12101226
return;
@@ -1215,18 +1231,18 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
12151231
reconciled.configured,
12161232
);
12171233
if (outcome.level === "information") {
1218-
await vscode.window.showInformationMessage(outcome.message);
1234+
void vscode.window.showInformationMessage(outcome.message);
12191235
} else if (outcome.level === "warning") {
1220-
await vscode.window.showWarningMessage(outcome.message);
1236+
void vscode.window.showWarningMessage(outcome.message);
12211237
} else {
1222-
await vscode.window.showErrorMessage(outcome.message);
1238+
void vscode.window.showErrorMessage(outcome.message);
12231239
}
12241240
return;
12251241
}
12261242

12271243
if (completion.state === "succeeded") {
12281244
const label = kind === "run" ? "运行" : kind === "test" ? "测试" : "清理";
1229-
await vscode.window.showInformationMessage(`mcpp ${label}完成;clangd/CDB 状态已重新检查。`);
1245+
void vscode.window.showInformationMessage(`mcpp ${label}完成;clangd/CDB 状态已重新检查。`);
12301246
}
12311247
};
12321248
cliController = new McppCliController({
@@ -1251,6 +1267,7 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
12511267
await vscode.window.showWarningMessage("当前工作区没有找到 mcpp.toml。");
12521268
return;
12531269
}
1270+
showInteractiveIdeStart("配置 clangd");
12541271
await executeWithWorkspaceClangd(async () => {
12551272
if (!shouldUseWorkspaceClangd(findCurrentProject()?.root, project.root)) {
12561273
return;
@@ -1269,6 +1286,7 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
12691286
await vscode.window.showWarningMessage("当前工作区没有找到 mcpp.toml。");
12701287
return;
12711288
}
1289+
showInteractiveIdeStart("刷新编译数据库");
12721290
await executeWithWorkspaceClangd(async () => {
12731291
const hadUsableDatabase = hasUsableCompilationDatabase(project);
12741292
const result = await runConfigureOnlyForProject(project, true);
@@ -1284,11 +1302,11 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
12841302
reconciled.configured,
12851303
);
12861304
if (outcome.level === "information") {
1287-
await vscode.window.showInformationMessage(outcome.message);
1305+
void vscode.window.showInformationMessage(outcome.message);
12881306
} else if (outcome.level === "warning") {
1289-
await vscode.window.showWarningMessage(outcome.message);
1307+
void vscode.window.showWarningMessage(outcome.message);
12901308
} else {
1291-
await vscode.window.showErrorMessage(outcome.message);
1309+
void vscode.window.showErrorMessage(outcome.message);
12921310
}
12931311
});
12941312
})),
@@ -1298,6 +1316,7 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
12981316
await vscode.window.showWarningMessage("当前工作区没有找到 mcpp.toml。");
12991317
return;
13001318
}
1319+
showInteractiveIdeStart("检查模块支持");
13011320
await executeWithWorkspaceClangd(async () => {
13021321
if (!shouldUseWorkspaceClangd(findCurrentProject()?.root, project.root)) {
13031322
return;
@@ -1401,6 +1420,7 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
14011420
await vscode.window.showWarningMessage("当前工作区没有找到 mcpp.toml。");
14021421
return;
14031422
}
1423+
showInteractiveIdeStart("一键配置模块代码提示");
14041424
await executeWithWorkspaceClangd(async () => {
14051425
if (!shouldUseWorkspaceClangd(findCurrentProject()?.root, project.root)) {
14061426
return;

‎src/workflow.ts‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,26 @@ export interface CompilationDatabaseWatcher<T, E> {
1616
onDidDelete(listener: (event: E) => void): T;
1717
}
1818

19+
export async function withTimeout<T>(
20+
operation: PromiseLike<T>,
21+
timeoutMs: number,
22+
fallback: T,
23+
): Promise<T> {
24+
let timer: ReturnType<typeof setTimeout> | undefined;
25+
try {
26+
return await Promise.race([
27+
Promise.resolve(operation),
28+
new Promise<T>((resolve) => {
29+
timer = setTimeout(() => resolve(fallback), timeoutMs);
30+
}),
31+
]);
32+
} finally {
33+
if (timer !== undefined) {
34+
clearTimeout(timer);
35+
}
36+
}
37+
}
38+
1939
export function registerCompilationDatabaseReconciliation<T, E>(
2040
watcher: CompilationDatabaseWatcher<T, E>,
2141
requestReconciliation: (event: E, forceRestart: boolean) => void,

‎test/artifacts.test.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,16 @@ test("configure-only shares the project operation lock", () => {
144144
assert.match(method, /finally\s*\{[\s\S]*finishProject\(project\.root, token\)/);
145145
});
146146

147+
test("IDE commands expose progress and bound clangd restart waits", () => {
148+
const source = readFileSync(path.join(root, "src/extension.ts"), "utf8");
149+
assert.match(source, /const CLANGD_RESTART_TIMEOUT_MS = 15_000/);
150+
assert.match(source, /withTimeout\([\s\S]*CLANGD_RESTART_TIMEOUT_MS/);
151+
for (const title of ["配置 clangd", "刷新编译数据库", "检查模块支持", "一键配置模块代码提示"]) {
152+
assert.match(source, new RegExp(`showInteractiveIdeStart\\("${title}"\\)`));
153+
}
154+
assert.match(source, /output\.show\(true\)/);
155+
});
156+
147157
test("ships syntax-only C++ highlighting for the exact build.mcpp filename", () => {
148158
const manifest = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8")) as PackageManifest;
149159
const associations = manifest.contributes?.configurationDefaults?.["files.associations"] as

0 commit comments

Comments
 (0)