Файли
openidea/src/features/vcs/mergeActions.ts
T
ntbang0901 f752f2db08 feat(vcs): make the merge editor work like IntelliJ's merge tool (#902)
The result pane starts from the base and every change, conflicting or not, gets Accept / Ignore next to its side; accepting one side of a conflict still lets the other side be appended, and the modifier key resolves using that side directly. Adds Resolve Simple Conflicts (word-level), Apply Non-Conflicting Changes from the left, right or both (optionally on opening), Revert Conflict Resolution, Accept Yours / Accept Theirs / Cancel / Apply, Compare Contents between any two versions, and undo back to the conflicted state. CRLF and UTF-16 files are written back exactly, deleted sides and binary files are handled, and the commands are registered as merge.* actions.

Refs #826

Co-authored-by: ntbang0901 <[email protected]>
2026-11-03 23:37:38 +08:00

77 рядки
2.8 KiB
TypeScript

/** Commands target the merge editor that opened a toolbar menu or Find Action. */
import { registerActions, type Action } from "@/platform/actions";
import { setContext } from "@/platform/context";
const COMMANDS = [
["applyLeft", "Apply Non-Conflicting Changes from the Left Side", "diff-apply-left"],
["applyAll", "Apply All Non-Conflicting Changes", "diff-apply-all"],
["applyRight", "Apply Non-Conflicting Changes from the Right Side", "diff-apply-right"],
["resolveSimple", "Resolve Simple Conflicts", "diff-magic-toolbar"],
["revert", "Revert Conflict Resolution", "diff-revert"],
["acceptYours", "Accept Yours"],
["acceptTheirs", "Accept Theirs"],
["apply", "Apply Merge Result"],
["cancel", "Cancel Merge"],
["reload", "Reload from Disk", "refresh"],
["compare", "Compare Contents", "diff"],
["settings", "Merge Settings", "settings"],
["autoApply", "Automatically Apply Non-Conflicting Changes"],
["compareYoursResult", "Yours and Result"],
["compareResultTheirs", "Result and Theirs"],
["compareYoursTheirs", "Yours and Theirs"],
["compareBaseYours", "Base and Yours"],
["compareBaseResult", "Base and Result"],
["compareBaseTheirs", "Base and Theirs"],
["accept", "Accept"],
["resolveUsingYours", "Resolve Using Yours"],
["resolveUsingTheirs", "Resolve Using Theirs"],
["ignore", "Ignore", "diff-remove"],
["resolveAutomatically", "Resolve Automatically", "diff-magic"],
["annotate", "Annotate with Git Blame", "changelist"],
["cut", "Cut"],
["copy", "Copy", "copy"],
["paste", "Paste"],
["selectAll", "Select All"],
["change", "Resolve Merge Change"],
] as const;
export type MergeCommand = (typeof COMMANDS)[number][0];
export interface MergeActionHost {
enabled(command: MergeCommand): boolean;
run(command: MergeCommand, arg?: unknown): void | Promise<void>;
autoApply(): boolean;
}
let focused: MergeActionHost | null = null;
export function setFocusedMergeEditor(host: MergeActionHost | null) {
focused = host;
setContext("mergeEditorFocus", !!host);
for (const [command] of COMMANDS) setContext(`merge.${command}`, host?.enabled(command) ?? false);
}
export function refreshMergeActions(host: MergeActionHost) {
if (focused === host) setFocusedMergeEditor(host);
}
export function clearFocusedMergeEditor(host: MergeActionHost) {
if (focused === host) setFocusedMergeEditor(null);
}
export function registerMergeActions(): () => void {
return registerActions(
COMMANDS.map(([command, title, icon]): Action => ({
id: `merge.${command}`,
title,
icon,
category: "Merge",
when: `mergeEditorFocus && merge.${command}`,
keywords: ["merge", "conflict"],
hidden: command === "change",
toggled: command === "autoApply" ? () => focused?.autoApply() ?? false : undefined,
run: ({ arg }) => focused?.run(command, arg),
})),
);
}