Файли
openidea/src/workbench/toolwindows/actions.ts
T
l0ng-ai 1e00ea5ee2 feat: Third pass aligning core experience with IntelliJ IDEA
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
2026-10-25 21:24:00 +08:00

237 рядки
7.8 KiB
TypeScript
Неформатований Анотація Історія

Цей файл містить неоднозначні символи Юнікоду
Цей файл містить символи Юнікоду, які можна сплутати з іншими символами. Якщо ви вважаєте, що це зроблено навмисно, можете сміливо ігнорувати це попередження. Щоб показати їх, скористайтеся кнопкою Escape.
import { registerAction, registerActions, type Action } from "@/platform/actions";
import { setContext } from "@/platform/context";
import { formatMessage, tx } from "@/platform/i18n";
import { occurrenceNavigator, showStatusInfo, useContributions } from "../contributions";
import { isDistractionFree, setDistractionFree } from "../distractionFree";
import { focusEditor } from "../focus";
import { chooseItem, promptInput } from "../inputDialogs";
import { confirm } from "../messages";
import {
deleteLayout,
layoutNames,
renameLayout,
restoreDefaultLayout,
restoreLayout,
saveLayout,
useToolWindowLayouts,
} from "./layouts";
import { closeSelectedContent } from "./content";
import {
activateToolWindow,
hideActiveToolWindow,
hideAllToolWindows,
hideToolWindow,
jumpToLastToolWindow,
lastUsedToolWindow,
useToolWindowLayout,
toggleMaximizeToolWindow,
} from "./store";
/**
* Next / Previous Occurrence: steps through the results of the focused tool
* window, else of the last active one, else of a shown one, so it also works
* from the editor. The editor gets the focus only when the action ran outside
* the tool window.
*/
export function goToOccurrence(dir: 1 | -1): boolean {
const s = useToolWindowLayout.getState();
for (const id of new Set([s.focused, s.lastActive, lastUsedToolWindow(), ...Object.values(s.visible)])) {
const nav = id ? occurrenceNavigator(id) : null;
if (!nav) continue;
const step = nav.go(dir, s.focused !== id);
if (step) showStatusInfo(formatMessage(tx("{index} of {total}"), { index: step.index, total: step.total }));
return !!step;
}
return false;
}
/** Window › Tool Window Layouts. */
function layoutActions(): Action[] {
const exitDistractionFree = () => {
if (isDistractionFree()) setDistractionFree(false);
};
const pick = (title: string) =>
chooseItem({ title, items: layoutNames(), empty: "No saved layouts. Use Save Current Layout as… first." });
const free = (current?: string) => (v: string) =>
!v.trim() ? "Enter a layout name" : v.trim() !== current && layoutNames().includes(v.trim()) ? "A layout with this name exists" : null;
return [
{
id: "layout.save",
title: "Save Current Layout as…",
category: "Window",
keywords: ["tool window layout", "store layout"],
when: "projectOpen",
run: async () => {
const active = useToolWindowLayouts.getState().active;
const name = await promptInput({
title: "Save Current Layout as",
label: "Layout name:",
initial: active ?? "",
okLabel: "Save",
validate: (v) => (v.trim() ? null : "Enter a layout name"),
});
if (name) saveLayout(name);
},
},
{
id: "layout.restore",
title: "Restore Tool Window Layout",
description: "Restores the saved layout named `arg`, or asks which one.",
category: "Window",
keywords: ["tool window layout"],
when: "projectOpen",
run: async ({ arg }) => {
exitDistractionFree();
const name = typeof arg === "string" ? arg : await pick("Restore Layout");
if (name) restoreLayout(name);
},
},
{
id: "layout.restoreDefault",
title: "Restore Default Layout",
category: "Window",
keywords: ["tool window layout", "reset"],
when: "projectOpen",
run: () => {
exitDistractionFree();
restoreDefaultLayout();
},
},
{
id: "layout.rename",
title: "Rename Tool Window Layout…",
category: "Window",
when: "toolWindowLayoutsSaved",
run: async () => {
const from = await pick("Rename Layout");
if (!from) return;
const to = await promptInput({ title: "Rename Layout", label: "New name:", initial: from, okLabel: "Rename", validate: free(from) });
if (to) renameLayout(from, to);
},
},
{
id: "layout.delete",
title: "Delete Tool Window Layout…",
category: "Window",
when: "toolWindowLayoutsSaved",
run: async () => {
const name = await pick("Delete Layout");
if (name && (await confirm({ title: "Delete Layout", message: `Delete the layout '${name}'?`, ok: "Delete", icon: "warning" })))
deleteLayout(name);
},
},
];
}
/** Registers `toolwindow.<id>` for every tool window plus the generic tool window actions. */
export function installToolWindowActions(): () => void {
const perWindow = new Map<string, { title: string; icon: string; dispose: () => void }>();
const sync = () => {
const windows = useContributions.getState().toolWindows;
for (const [id, entry] of perWindow) {
const tw = windows.get(id);
if (!tw || tw.title !== entry.title || tw.icon !== entry.icon) {
entry.dispose();
perWindow.delete(id);
}
}
for (const [id, tw] of windows) {
if (perWindow.has(id)) continue;
const dispose = registerAction({
id: `toolwindow.${id}`,
title: tw.title,
category: "Tool Windows",
icon: tw.icon,
keywords: ["tool window", "show", "hide"],
run: () => activateToolWindow(id),
});
perWindow.set(id, { title: tw.title, icon: tw.icon, dispose });
}
};
sync();
const unsubscribe = useContributions.subscribe((s, prev) => {
if (s.toolWindows !== prev.toolWindows) sync();
});
const publishLayouts = () => setContext("toolWindowLayoutsSaved", useToolWindowLayouts.getState().layouts.length > 0);
publishLayouts();
const unsubscribeLayouts = useToolWindowLayouts.subscribe(publishLayouts);
const disposeStatic = registerActions([
{ id: "toolwindow.hideAll", title: "Hide All Tool Windows", category: "Window", run: hideAllToolWindows },
{
id: "toolwindow.jumpToLast",
title: "Jump to Last Tool Window",
category: "Window",
keywords: ["tool window", "focus"],
when: "projectOpen",
run: jumpToLastToolWindow,
},
{
// IDEA: also from the editor, hiding the last active window if it is still shown.
id: "toolwindow.hideActive",
title: "Hide Active Tool Window",
category: "Window",
when: "projectOpen && !popupOpen",
run: hideActiveToolWindow,
},
{
// Close Content, as IDEA (⌘W / Ctrl+F4 in a tool window): the selected closable tab, else the window.
id: "toolwindow.closeContent",
title: "Close Tool Window Tab",
category: "Window",
when: "toolWindowFocus && !popupOpen",
run: () => {
const s = useToolWindowLayout.getState();
const id = s.focused ?? s.lastActive;
if (id && !closeSelectedContent(id)) hideToolWindow(id);
},
},
{
id: "toolwindow.focusEditor",
title: "Focus Editor",
category: "Window",
when: "toolWindowFocus && !popupOpen",
run: () => focusEditor(),
},
{
id: "navigate.nextOccurrence",
title: "Next Occurrence",
category: "Navigate",
icon: "arrow-down",
keywords: ["usage", "result", "find"],
when: "projectOpen",
run: () => void goToOccurrence(1),
},
{
id: "navigate.prevOccurrence",
title: "Previous Occurrence",
category: "Navigate",
icon: "arrow-up",
keywords: ["usage", "result", "find"],
when: "projectOpen",
run: () => void goToOccurrence(-1),
},
...layoutActions(),
{
id: "toolwindow.maximize",
title: "Maximize Tool Window",
category: "Window",
when: "toolWindowFocus",
run: () => {
const s = useToolWindowLayout.getState();
const id = s.focused ?? s.lastActive;
if (id) toggleMaximizeToolWindow(id);
},
},
]);
return () => {
unsubscribe();
unsubscribeLayouts();
disposeStatic();
perWindow.forEach((e) => e.dispose());
perWindow.clear();
};
}