mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 17:02:17 +00:00
Root causes: Typo "Change to" replaced one occurrence even for declared names (breaking references); inspections had no Disable / Edit settings controls anywhere; en_US Hunspell does not derive agent nouns like "checkpointer" and compound identifier words were flagged; local inspections (Typo, file references, SQL) never reached Problems. Fix (IDEA): "Typo: Rename to…" renames via LSP rename, falling back to syntax-based local rename, never a silent single replace, and lists identifier-safe words only; intention submenu (→) with Edit inspection profile setting / Disable inspection for every inspection; Problems and Inspection Results get Disable Inspection, Change Severity, Edit Inspection Profile Setting; Typo options Process code / literals / comments; -er/-ers derivation and identifier compounds accepted, common misspellings still flagged. Siblings covered: all LSP and built-in inspections' intentions, file reference and SQL inspections in Problems, severity counter. Verified: ide-spell tests (issue examples + ~50 real misspellings), vitest (286), in-app on macOS: only 'mesage' flagged, submenu, Rename to → 'message' updated both occurrences. Claude-Session: https://claude.ai/code/session_01VUYsdXA2nkF5v6zsC93dpn
106 lines
4.4 KiB
TypeScript
106 lines
4.4 KiB
TypeScript
import { beforeEach, describe, expect, it } from "vitest";
|
|
import type { Diagnostic } from "@/bindings";
|
|
import {
|
|
applyDiagnosticsEvent,
|
|
clearProblems,
|
|
countSeverities,
|
|
fileDiagnostics,
|
|
mergeDiagnostics,
|
|
profiledProblems,
|
|
projectCounts,
|
|
useProblems,
|
|
worstSeverity,
|
|
} from "./problemsStore";
|
|
import { problemDecorations } from "./decorations";
|
|
|
|
const d = (line: number, severity: Diagnostic["severity"] = "error", source: string | null = null): Diagnostic => ({
|
|
range: { start: { line, col: 0 }, end: { line, col: 1 } },
|
|
severity,
|
|
message: `line ${line}`,
|
|
source,
|
|
code: null,
|
|
});
|
|
|
|
describe("mergeDiagnostics", () => {
|
|
it("replaces per (file, source) and keeps other sources", () => {
|
|
let map = mergeDiagnostics(new Map(), "/a", "ra", [d(3), d(1)]);
|
|
map = mergeDiagnostics(map, "/a", "clippy", [d(2, "warning")]);
|
|
expect(fileDiagnostics(map, "/a").map((x) => [x.range.start.line, x.source])).toEqual([
|
|
[1, "ra"],
|
|
[2, "clippy"],
|
|
[3, "ra"],
|
|
]);
|
|
map = mergeDiagnostics(map, "/a", "ra", [d(5, "info")]);
|
|
expect(fileDiagnostics(map, "/a").map((x) => x.range.start.line)).toEqual([2, 5]);
|
|
});
|
|
|
|
it("drops files whose sources are all empty and is a no-op for unknown clears", () => {
|
|
const a = mergeDiagnostics(new Map(), "/a", "ra", [d(1)]);
|
|
expect(mergeDiagnostics(a, "/a", "other", [])).toBe(a);
|
|
expect(mergeDiagnostics(a, "/b", "ra", [])).toBe(a);
|
|
const cleared = mergeDiagnostics(a, "/a", "ra", []);
|
|
expect(cleared.has("/a")).toBe(false);
|
|
});
|
|
|
|
it("keeps an item's own source", () => {
|
|
const map = mergeDiagnostics(new Map(), "/a", "eslint", [d(1, "error", "eslint/no-undef")]);
|
|
expect(fileDiagnostics(map, "/a")[0]!.source).toBe("eslint/no-undef");
|
|
});
|
|
});
|
|
|
|
describe("counts", () => {
|
|
it("counts per severity", () => {
|
|
let map = mergeDiagnostics(new Map(), "/a", "ra", [d(1), d(2, "warning")]);
|
|
map = mergeDiagnostics(map, "/b", "ra", [d(1, "warning"), d(2, "hint")]);
|
|
expect(projectCounts(map)).toEqual({ error: 1, warning: 2, info: 0, hint: 1 });
|
|
expect(countSeverities(fileDiagnostics(map, "/b"))).toEqual({ error: 0, warning: 1, info: 0, hint: 1 });
|
|
expect(worstSeverity(fileDiagnostics(map, "/b"))).toBe("warning");
|
|
expect(worstSeverity([])).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("store + decorations", () => {
|
|
beforeEach(() => clearProblems());
|
|
|
|
it("applies events and decorates only files with errors", () => {
|
|
applyDiagnosticsEvent("/p/src/a.rs", "ra", [d(1), d(2, "warning")]);
|
|
applyDiagnosticsEvent("/p/src/b.rs", "ra", [d(1, "warning")]);
|
|
const deco = problemDecorations(useProblems.getState().map);
|
|
expect([...deco.keys()]).toEqual(["/p/src/a.rs"]);
|
|
expect(deco.get("/p/src/a.rs")).toEqual({ error: true, propagate: true, tooltip: "1 error, 1 warning" });
|
|
});
|
|
});
|
|
|
|
describe("profiledProblems", () => {
|
|
it("applies the inspection profile per producer", () => {
|
|
let raw = mergeDiagnostics(new Map(), "/a", "ra", [d(1), d(2, "warning")]);
|
|
raw = mergeDiagnostics(raw, "/b", "eslint", [d(3)]);
|
|
const out = profiledProblems(raw, { "lsp:eslint": { enabled: false }, "lsp:ra": { severity: "weakWarning" } });
|
|
expect([...out.keys()]).toEqual(["/a"]);
|
|
expect(fileDiagnostics(out, "/a").map((x) => x.severity)).toEqual(["info", "info"]);
|
|
expect(fileDiagnostics(profiledProblems(raw, {}), "/b")).toHaveLength(1);
|
|
});
|
|
});
|
|
|
|
describe("local inspections (issue #42)", () => {
|
|
it("reach Problems and clear there too, with their inspection known", async () => {
|
|
const { installProblemsEvents, producerFor } = await import("./problemsStore");
|
|
const { setFileDiagnostics } = await import("@/editor/lsp/diagnostics");
|
|
const { diagnosticInspection } = await import("@/platform/inspections");
|
|
clearProblems();
|
|
const off = installProblemsEvents();
|
|
try {
|
|
setFileDiagnostics("/p/a.py", "spelling", [{ ...d(0, "info"), message: "Typo: In word 'mesage'", source: "Spelling" }]);
|
|
const [typo] = fileDiagnostics(useProblems.getState().map, "/p/a.py");
|
|
expect(typo).toMatchObject({ message: "Typo: In word 'mesage'", typo: true });
|
|
const producer = producerFor(useProblems.getState().map, "/p/a.py", typo!);
|
|
expect(producer).toBe("spelling");
|
|
expect(diagnosticInspection(producer!, typo!)?.id).toBe("builtin:spelling");
|
|
setFileDiagnostics("/p/a.py", "spelling", []);
|
|
expect(useProblems.getState().map.size).toBe(0);
|
|
} finally {
|
|
off();
|
|
}
|
|
});
|
|
});
|