дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +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
237 рядки
7.8 KiB
TypeScript
237 рядки
7.8 KiB
TypeScript
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();
|
||
};
|
||
}
|