mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 08:07:49 +00:00
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 <186315048+a-rookie-of-C-language@users.noreply.github.com>
This commit is contained in:
co-authored by
a-rookie-of-C-language
parent
0c54266d03
commit
f8d6f9df77
@@ -15,12 +15,14 @@ import type { EditorView } from "@codemirror/view";
|
||||
import {
|
||||
commands,
|
||||
isCancelled,
|
||||
isNoServer,
|
||||
nextRequestId,
|
||||
unavailableText,
|
||||
type Pos,
|
||||
type RequestId,
|
||||
type WorkspaceEdit,
|
||||
} from "@/platform/ipc";
|
||||
import { executeAction } from "@/platform/actions";
|
||||
import { whenSynced, type EditorDocContext } from "@/editor/api";
|
||||
import { structure } from "@/editor/structure";
|
||||
import { docOf, incompleteNote, isUnavailable, lspRequest, reportLspError } from "./lsp";
|
||||
@@ -78,7 +80,10 @@ export async function startRename(view: EditorView) {
|
||||
range = await lspRequest(() => commands.lspPrepareRename(doc.doc, pos));
|
||||
} catch (e) {
|
||||
if (isCancelled(e)) return;
|
||||
if (!(await startSyntaxRename(view, doc, pos, e))) reportLspError(view, e, true);
|
||||
if (await startSyntaxRename(view, doc, pos, e)) return;
|
||||
// No server covers this file (a plain text file): Shift+F6 renames the file, like IDEA.
|
||||
if (isNoServer(e) && (await executeAction("project.rename"))) return;
|
||||
reportLspError(view, e, true);
|
||||
return;
|
||||
}
|
||||
if (!range) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -46,6 +46,11 @@ export function unavailableText(e: unknown, fallback = "No language server avail
|
||||
return e instanceof IpcError && e.kind === "unavailable" && LACKS_FEATURE.test(e.message) ? e.message : fallback;
|
||||
}
|
||||
|
||||
/** True when `unavailable` means no server is running for the file, rather than a running server lacking the feature. */
|
||||
export function isNoServer(e: unknown): boolean {
|
||||
return e instanceof IpcError && e.kind === "unavailable" && !LACKS_FEATURE.test(e.message);
|
||||
}
|
||||
|
||||
// Ids are shared by every window of the backend and outlive a reload there (a cancel that
|
||||
// arrives before its request began is remembered), so each page starts at a random point.
|
||||
let nextId = 1 + Math.floor(Math.random() * 0x7000_0000);
|
||||
|
||||
Reference in New Issue
Block a user