дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
Project & files - Project view modes (Project Files / Open Files / All Changed Files / Scratches); External Libraries for Python, JDK (src.zip), Node, Cargo, Go, Maven/Gradle jars; read-only jar/zip browsing - ⌘N New popup with type search and more file kinds; New Project, Rename Project, Reload All from Disk, Export/Import Settings, Power Save Mode - File type associations, extra content roots and exclude patterns (indexed and watched without reopening the project) Editor & navigation - Find bar: search scope (comments/strings), Preserve Case, Exclude, multiline, history; Find in Files: Module scope, custom pattern scopes, multiline - Search Everywhere filters, Assign Shortcut, settings results; named bookmark lists, file/folder bookmarks; Structure filters and Show Inherited; Recent Locations timestamps and breadcrumbs; Code Vision author - Completion options, editor tab placement/layout, inline completion (LSP 3.18) - Spell checking (Typo inspection, ide-spell) and Inspections settings with custom profiles; Code Style schemes, formatter markers, Do not format Run, VCS, services, database - Typed run configurations (Shell, Program, npm, Gradle, Maven, Docker, Compound), Modify options, Before launch, env dialog, Current File, Emulate terminal (PTY) - Git log graph/view options, Go to Hash/Branch/Tag, date filter keeps the graph; Pull/Merge/Rebase dialogs, Manage Remotes, Worktrees - Generic Services host (Docker connections/registries, run sessions); TODO scope/changes tabs with changelists; Coverage report import - Data sources: wire-compatible vendors, URL import, SSL certs, Options, Schemas and Advanced tabs, color and comment Workbench - Run Anything, Customize Main Toolbar, named tool window layouts, menu completeness, Code Cleanup, macros, "Don't Show Again", editor banners - Chinese translations for the new UI Claude-Session: https://claude.ai/code/session_01SaPPsVPuk6ZTNsoUCNCbMv
145 рядки
5.8 KiB
TypeScript
145 рядки
5.8 KiB
TypeScript
import { afterEach, describe, expect, it } from "vitest";
|
|
import type { Diagnostic } from "@/bindings";
|
|
import {
|
|
activeProfileId,
|
|
applyProfile,
|
|
applyProfileMixed,
|
|
copyProfileName,
|
|
effectiveProfileId,
|
|
isTypoStyled,
|
|
listProfiles,
|
|
newProfileKey,
|
|
overridesOf,
|
|
profileLabel,
|
|
validateProfileName,
|
|
inspectionProfile,
|
|
producerInspectionId,
|
|
subscribeInspectionProfile,
|
|
validateInspections,
|
|
} from "./inspections";
|
|
import { useProject } from "./project";
|
|
|
|
const diag = (code: string | null, severity: Diagnostic["severity"] = "warning", server?: string): Diagnostic => ({
|
|
range: { start: { line: 0, col: 0 }, end: { line: 0, col: 1 } },
|
|
severity,
|
|
message: `m ${code}`,
|
|
source: "Ruff",
|
|
code,
|
|
server,
|
|
});
|
|
|
|
describe("inspection profiles", () => {
|
|
afterEach(() => useProject.setState({ project: null, settings: null }));
|
|
|
|
it("validates leniently", () => {
|
|
expect(
|
|
validateInspections({
|
|
profile: "project",
|
|
default: { "lsp:ruff": { enabled: false, severity: "loud" }, empty: {}, x: { severity: "typo" } },
|
|
project: 3,
|
|
}),
|
|
).toEqual({
|
|
profile: "project",
|
|
default: { "lsp:ruff": { enabled: false }, x: { severity: "typo" } },
|
|
project: {},
|
|
ideProfiles: {},
|
|
projectProfiles: {},
|
|
});
|
|
expect(validateInspections(undefined).profile).toBe("default");
|
|
expect(effectiveProfileId(validateInspections({ profile: "project" }), false)).toBe("default");
|
|
});
|
|
|
|
it("maps built-in producers to their inspections", () => {
|
|
expect(producerInspectionId("file-references")).toBe("builtin:file-references");
|
|
expect(producerInspectionId("ruff")).toBe("lsp:ruff");
|
|
});
|
|
|
|
it("drops disabled inspections and remaps severities", () => {
|
|
const items = [diag("F401"), diag("E501", "error"), diag(null, "info")];
|
|
expect(applyProfile({}, "ruff", items)).toBe(items);
|
|
expect(applyProfile({ "lsp:ruff": { enabled: false } }, "ruff", items)).toEqual([]);
|
|
const out = applyProfile(
|
|
{
|
|
"lsp:ruff": { severity: "error" },
|
|
"lsp:ruff:F401": { severity: "weakWarning" },
|
|
"lsp:ruff:E501": { enabled: false },
|
|
},
|
|
"ruff",
|
|
items,
|
|
);
|
|
expect(out.map((d) => [d.code, d.severity])).toEqual([
|
|
["F401", "info"],
|
|
[null, "error"],
|
|
]);
|
|
expect(applyProfile({ "lsp:ruff": { severity: "none" } }, "ruff", [diag("x")])[0]!.severity).toBe("hint");
|
|
});
|
|
|
|
it("groups mixed results by reporting server", () => {
|
|
const out = applyProfileMixed({ "lsp:ruff": { enabled: false } }, [
|
|
diag("a", "warning", "ruff"),
|
|
diag("b", "warning", "ty"),
|
|
]);
|
|
expect(out.map((d) => d.code)).toEqual(["b"]);
|
|
});
|
|
|
|
it("follows the project's choice of profile and notifies on content changes", () => {
|
|
const calls: number[] = [];
|
|
const off = subscribeInspectionProfile(() => calls.push(1));
|
|
const settings = {
|
|
inspections: { profile: "project", default: { a: { enabled: false } }, project: { b: { enabled: false } } },
|
|
};
|
|
useProject.setState({ settings: settings as never });
|
|
expect(activeProfileId()).toBe("default");
|
|
expect(inspectionProfile()).toEqual({ a: { enabled: false } });
|
|
useProject.setState({ project: { root: "/p" } as never });
|
|
expect(activeProfileId()).toBe("project");
|
|
expect(inspectionProfile()).toEqual({ b: { enabled: false } });
|
|
useProject.setState({ settings: JSON.parse(JSON.stringify(settings)) });
|
|
off();
|
|
expect(calls.length).toBe(2);
|
|
});
|
|
|
|
it("gives the Typo inspection its default level and marks typo-level diagnostics", () => {
|
|
const typo = { ...diag(null, "info"), source: "Spelling" };
|
|
const [shown] = applyProfile({}, "spelling", [typo]);
|
|
expect(shown!.severity).toBe("info");
|
|
expect(isTypoStyled(shown!)).toBe(true);
|
|
const [asError] = applyProfile({ "builtin:spelling": { severity: "error" } }, "spelling", [typo]);
|
|
expect(asError!.severity).toBe("error");
|
|
expect(isTypoStyled(asError!)).toBe(false);
|
|
expect(applyProfile({ "builtin:spelling": { enabled: false } }, "spelling", [typo])).toEqual([]);
|
|
// Any inspection can be shown at the Typo level.
|
|
expect(isTypoStyled(applyProfile({ "lsp:ruff": { severity: "typo" } }, "ruff", [diag("x")])[0]!)).toBe(true);
|
|
});
|
|
|
|
it("lists, labels and resolves copied profiles", () => {
|
|
const s = validateInspections({
|
|
profile: "ide:strict",
|
|
default: { a: { enabled: false } },
|
|
ideProfiles: { strict: { name: "Strict", overrides: { b: { severity: "error" } } }, bad: 3 },
|
|
projectProfiles: { team: { name: "Team", overrides: {} } },
|
|
});
|
|
expect(s.ideProfiles.bad).toEqual({ name: "bad", overrides: {} });
|
|
expect(listProfiles(s, true).map((p) => [p.id, p.label, p.level])).toEqual([
|
|
["project", "Project Default", "project"],
|
|
["project:team", "Team", "project"],
|
|
["default", "Default", "ide"],
|
|
["ide:bad", "bad", "ide"],
|
|
["ide:strict", "Strict", "ide"],
|
|
]);
|
|
expect(listProfiles(s, false).map((p) => p.id)).toEqual(["default", "ide:bad", "ide:strict"]);
|
|
expect(effectiveProfileId(s, false)).toBe("ide:strict");
|
|
expect(effectiveProfileId({ ...s, profile: "project:team" }, false)).toBe("default");
|
|
expect(effectiveProfileId({ ...s, profile: "ide:gone" }, true)).toBe("default");
|
|
expect(overridesOf(s, "ide:strict")).toEqual({ b: { severity: "error" } });
|
|
expect(profileLabel(s, "project")).toBe("Project Default");
|
|
expect(validateProfileName("Strict", "ide", s)).toMatch(/already exists/);
|
|
expect(validateProfileName("Strict", "project", s)).toBeNull();
|
|
expect(validateProfileName("Strict", "ide", s, "ide:strict")).toBeNull();
|
|
expect(validateProfileName(" ", "ide", s)).toMatch(/empty/);
|
|
expect(copyProfileName("Strict", "ide", s)).toBe("Strict copy");
|
|
expect(newProfileKey("Strict copy!", s.ideProfiles)).toBe("strict-copy");
|
|
expect(newProfileKey("Strict", s.ideProfiles)).toBe("strict-2");
|
|
});
|
|
});
|