Files
openidea/src/features/settings/pages/editor/codeStyleScheme.tsx
T
Jaw 70f10e741c fix(i18n): Translate the remaining UI text (Git, hierarchy, pull requests, dialogs, aria labels) (#114)
Hard-coded UI text, titles and aria-labels in about 110 components (Git panels, hierarchy, pull requests, dialogs, settings, welcome) now go through tx; spliced fragments became whole sentences (fileCount, noneFoundLabel, hierarchyTitle) and pull request states use keyed messages. 225 zh entries added, stale fragment entries removed.

Also: tx used as a tag without interpolations (tx`No checks`) now looks the text up exactly instead of as a template, so it is translated like tx("No checks"); the text-catalog test checks such tags as plain literals. Check and test counts reuse the existing "{count} passed" / "{failed} failing" entries.

Claude-Session: https://claude.ai/code/session_01VUYsdXA2nkF5v6zsC93dpn
Co-authored-by: jaw52 <53564781+jaw52@users.noreply.github.com>
2026-10-28 12:18:46 +08:00

151 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Code Style › Scheme (IDEA's Default = IDE-level, Project = stored in the
* project) and the Formatter tab. The scheme is the scope shared by Code Style
* and its language pages (`scopeGroup`); "Copy to Project" / "Copy to IDE"
* stage every Code Style value in the other scope.
*/
import { useState } from "react";
import clsx from "clsx";
import { Button, T, type SelectOption } from "@/ui";
import { useProject } from "@/platform/project";
import { GlobListEditor, Row, Select } from "../../components/Form";
import { usePageContext, useSetting } from "../../components/context";
import { setPageScope, stage, stagedValue, useSettingsDialog, type Scope } from "../../model/staging";
import { EditorSections, sectionLabels, type EditorSection } from "./kit";
import styles from "./CodeStyle.module.css";
import { tx } from "@/platform/i18n";
export const CODE_STYLE_SCOPE = "editor.codeStyle";
/** What a Code Style scheme holds. */
export const CODE_STYLE_KEYS = [
"editor.tabSize",
"editor.insertSpaces",
"editor.codeStyle.lineSeparator",
"editor.codeStyle.hardWrapAt",
"editor.codeStyle.visualGuides",
"editor.codeStyle.detectIndents",
"editor.codeStyle.editorconfig",
"editor.codeStyle.languages",
"editor.codeStyle.doNotFormat",
"editor.codeStyle.formatterTagsEnabled",
"editor.codeStyle.formatterOffTag",
"editor.codeStyle.formatterOnTag",
"editor.codeStyle.formatterTagsRegexp",
] as const;
/** Stages the `from` scheme's values in the `to` scheme (`page` owns the staged edits). */
export function copyScheme(page: string, from: Scope, to: Scope) {
const s = useSettingsDialog.getState();
for (const key of CODE_STYLE_KEYS) {
const value = stagedValue(s, from, key);
if (value !== undefined) stage(page, to, key, value);
}
}
export function SchemeRow() {
const { pageId, scope } = usePageContext();
const hasProject = useProject((s) => !!s.project);
const newProjects = useSettingsDialog((s) => s.newProjects);
const canProject = hasProject && !newProjects;
const options: SelectOption<Scope>[] = [
{ value: "global", label: "Default (IDE)" },
...(canProject ? [{ value: "project" as const, label: "Project" }] : []),
];
const other: Scope = scope === "global" ? "project" : "global";
return (
<Row
label="Scheme"
hint={
scope === "project" ? "Stored in the project." : "Stored in the IDE; used by projects without their own scheme."
}
>
<span className={styles.scheme}>
<Select<Scope>
value={scope}
options={options}
onChange={(v) => setPageScope(CODE_STYLE_SCOPE, v)}
aria-label="Scheme"
/>
{canProject && (
<Button
size="small"
onClick={() => {
copyScheme(pageId, scope, other);
setPageScope(CODE_STYLE_SCOPE, other);
}}
>
<T>{scope === "global" ? "Copy to Project" : "Copy to IDE"}</T>
</Button>
)}
</span>
</Row>
);
}
function DoNotFormatRow() {
const [globs, setGlobs] = useSetting<string[]>("editor.codeStyle.doNotFormat");
return (
<Row label="Do not format" stacked hint="File name patterns and folders (glob), e.g. *.min.js, generated/**.">
<GlobListEditor value={globs ?? []} onChange={setGlobs} placeholder="*.min.js" aria-label="Do not format" />
</Row>
);
}
export const FORMATTER_SECTIONS: EditorSection[] = [
{
items: [
{ t: "custom", label: "Do not format", keywords: ["glob", "exclude from formatting"], component: DoNotFormatRow },
{
t: "check",
key: "editor.codeStyle.formatterTagsEnabled",
label: "Turn formatter on/off with markers in code comments",
children: [
{ t: "text", key: "editor.codeStyle.formatterOffTag", label: "Off", placeholder: "@formatter:off" },
{ t: "text", key: "editor.codeStyle.formatterOnTag", label: "On", placeholder: "@formatter:on" },
{
t: "check",
key: "editor.codeStyle.formatterTagsRegexp",
label: "Enable regular expressions in formatter markers",
},
],
},
],
},
];
type Tab = "general" | "formatter";
/** Code Style: Scheme, then the General / Formatter tabs. */
export function CodeStyleMain({ general }: { general: EditorSection[] }) {
const [tab, setTab] = useState<Tab>("general");
const tabs: Array<[Tab, string]> = [
["general", "General"],
["formatter", "Formatter"],
];
return (
<div className={styles.page}>
<SchemeRow />
<div className={styles.tabs} role="tablist" aria-label={tx("Code Style")}>
{tabs.map(([id, label]) => (
<button
key={id}
type="button"
role="tab"
aria-selected={tab === id}
className={clsx(styles.tab, tab === id && styles.tabOn)}
onClick={() => setTab(id)}
>
<T>{label}</T>
</button>
))}
</div>
<div role="tabpanel">
<EditorSections sections={tab === "general" ? general : FORMATTER_SECTIONS} />
</div>
</div>
);
}
export const formatterLabels = () => ["Scheme", "Formatter", ...sectionLabels(FORMATTER_SECTIONS)];