fix(editor): Shift+F6 renames the file when no language server covers it (#653)

In files no language server covers (Markdown, .properties, plain text), Shift+F6 only showed a transient "no running language server supports rename" hint at the caret, so the default rename shortcut looked broken.

Root cause: when prepare_rename reported no server and the syntax fallback declined, startRename fell through to the error hint. It now hands off to project.rename, which opens the file rename dialog for the active file; a running server that lacks rename keeps its hint.

Verified on macOS: Shift+F6 in .md and .properties editors renames the file, symbol rename in TypeScript is unchanged, and Shift+F6 in the project view still renames the selected file.

Fixes #651

Co-authored-by: a-rookie-of-C-language <[email protected]>
Цей коміт міститься в:
arookieofcтаa-rookie-of-C-language authored and GitHub committed 2026-11-01 15:45:35 +08:00
1 джерело 0c54266d03
коміт f8d6f9df77
3 files changed
+46 -1

No files matched your search

+35
Переглянути файл
@@ -30,6 +30,13 @@ vi.mock("@/platform/ipc", async (orig) => {
return { ...actual, isTauri: () => true, commands: { ...actual.commands, ...cmd } };
});
const executeAction = vi.hoisted(() => vi.fn());
vi.mock("@/platform/actions", async (orig) => {
const actual = await orig<typeof import("@/platform/actions")>();
executeAction.mockImplementation(actual.executeAction);
return { ...actual, executeAction };
});
const openFile = vi.fn(async () => {});
const { provideEditorService } = await import("@/workbench/contributions");
provideEditorService({ openFile } as never);
@@ -488,6 +495,34 @@ describe("rename wiring", () => {
});
});
it("renames the file itself when no server covers it (a plain text file)", async () => {
cmd.lspPrepareRename.mockReturnValueOnce(
Promise.resolve({
status: "error",
error: { kind: "unavailable", message: "no running language server supports rename" } as IdeError,
}),
);
cmd.syntaxSupport.mockReturnValueOnce(ok({ tree: false, locals: false }));
executeAction.mockResolvedValueOnce(true);
const view = editor("# notes", "markdown", 2);
await startRename(view);
expect(executeAction).toHaveBeenCalledWith("project.rename");
expect(document.querySelector(".cm-lsp-hint")).toBeNull();
});
it("keeps the hint when a server runs but lacks rename (no file fallback)", async () => {
cmd.lspPrepareRename.mockReturnValueOnce(
Promise.resolve({
status: "error",
error: { kind: "unavailable", message: "jdtls doesn't support rename" } as IdeError,
}),
);
const view = editor("class A {}", "java", 6);
await startRename(view);
expect(executeAction).not.toHaveBeenCalledWith("project.rename");
expect(document.querySelector(".cm-lsp-hint")?.textContent).toContain("jdtls doesn't support rename");
});
it("respects a server refusal over valid code, but falls back over broken code", async () => {
cmd.lspPrepareRename.mockReturnValueOnce(ok(null));
const view = editor("const x = 1;", "typescript", 6);