mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-07 10:16:03 -10:00
feat(vcs): Delete key deletes unversioned files in the Changes view (#1483)
The Changes view's Delete… for unversioned files had no key. When every selected file is unversioned, Delete/Backspace now opens the same confirmation (files go to the Trash) and the menu item shows the platform's delete key; Delete on a changelist group still deletes the changelist. Closes #1477 Co-authored-by: jaw52 <[email protected]>
This commit is contained in:
2 files changed
+35
-5
No files matched your search
@@ -3,6 +3,7 @@
|
||||
* parts also make the Git window's Local Changes / Stash tabs and the Commit dialog.
|
||||
*/
|
||||
import { useWorkflowSettings } from "@/platform/workflowSettings";
|
||||
import { formatBinding, isMacHost } from "@/platform/keymap";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState, type MouseEvent, type PointerEvent } from "react";
|
||||
import { tx } from "@/platform/i18n";
|
||||
import clsx from "clsx";
|
||||
@@ -224,8 +225,15 @@ export function listMenu(id: string, active: boolean, files: readonly FileStatus
|
||||
];
|
||||
}
|
||||
|
||||
/** Paths Delete… applies to: only when every selected file is unversioned (versioned ones are rolled back). */
|
||||
export function deletableUnversioned(nodes: ChangeNode[]): string[] {
|
||||
const files = filesOf(nodes);
|
||||
return files.length && files.every((f) => f.unstaged === "untracked") ? files.map((f) => f.path) : [];
|
||||
}
|
||||
|
||||
export function nodeMenu(nodes: ChangeNode[], repos: readonly string[]): MenuItem[] {
|
||||
const files = filesOf(nodes);
|
||||
const deletable = deletableUnversioned(nodes);
|
||||
const paths = files.map((f) => f.path);
|
||||
const first = nodes.find((n) => n.type === "file");
|
||||
const untracked = files.filter((f) => f.unstaged === "untracked");
|
||||
@@ -248,9 +256,15 @@ export function nodeMenu(nodes: ChangeNode[], repos: readonly string[]): MenuIte
|
||||
...(untracked.length
|
||||
? [
|
||||
{ kind: "action" as const, action: "vcs.addToVcs", arg: untracked.map((f) => f.path) },
|
||||
// Only when every selected file is unversioned: versioned ones are rolled back, not deleted.
|
||||
...(untracked.length === files.length
|
||||
? [{ label: "Delete…", icon: "trash", run: () => void deleteUnversioned(untracked.map((f) => f.path)) }]
|
||||
...(deletable.length
|
||||
? [
|
||||
{
|
||||
label: "Delete…",
|
||||
icon: "trash",
|
||||
shortcut: formatBinding(isMacHost() ? "Backspace" : "Delete"),
|
||||
run: () => void deleteUnversioned(deletable),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
: []),
|
||||
@@ -518,11 +532,17 @@ export function ChangesTree({ mode }: { mode: "commit" | "local" | "dialog" }) {
|
||||
e.stopPropagation();
|
||||
toggle(selectedNodes());
|
||||
} else if ((e.key === "Delete" || e.key === "Backspace") && !e.metaKey) {
|
||||
const group = selectedNodes().find((x) => x.type === "group" && x.changelist);
|
||||
const nodes = selectedNodes();
|
||||
const group = nodes.find((x) => x.type === "group" && x.changelist);
|
||||
const deletable = group ? [] : deletableUnversioned(nodes);
|
||||
if (group?.changelist) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
void executeAction("vcs.changelist.delete", group.changelist);
|
||||
} else if (deletable.length) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
void deleteUnversioned(deletable);
|
||||
}
|
||||
} else if (e.key === "F4") {
|
||||
const n = selectedNodes().find((x) => x.type === "file");
|
||||
|
||||
@@ -13,7 +13,7 @@ vi.mock("../dialogs", async (original) => ({
|
||||
confirmDialog: mocks.confirm,
|
||||
}));
|
||||
|
||||
const { nodeMenu } = await import("../CommitView");
|
||||
const { deletableUnversioned, nodeMenu } = await import("../CommitView");
|
||||
const { deleteUnversioned } = await import("../changeOps");
|
||||
|
||||
const file = (rel: string, unstaged: FileStatus["unstaged"]): ChangeNode => {
|
||||
@@ -36,6 +36,16 @@ describe("Delete… for unversioned files", () => {
|
||||
expect(deleteItem(nodeMenu([file("c.txt", "modified")], ["/p"]))).toBeUndefined();
|
||||
});
|
||||
|
||||
it("shows the Delete key and gives the key handler the same paths", () => {
|
||||
expect(deleteItem(nodeMenu([file("a.txt", "untracked")], ["/p"]))).toMatchObject({ shortcut: "⌫" });
|
||||
expect(deletableUnversioned([file("a.txt", "untracked"), file("b.txt", "untracked")])).toEqual([
|
||||
"/p/a.txt",
|
||||
"/p/b.txt",
|
||||
]);
|
||||
expect(deletableUnversioned([file("a.txt", "untracked"), file("c.txt", "modified")])).toEqual([]);
|
||||
expect(deletableUnversioned([])).toEqual([]);
|
||||
});
|
||||
|
||||
it("asks first, then deletes each file", async () => {
|
||||
mocks.confirm.mockResolvedValueOnce(false);
|
||||
await deleteUnversioned(["/p/a.txt"]);
|
||||
|
||||
Reference in new issue
Block a user