aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/dirvish-config.el35
-rw-r--r--tests/test-dirvish-config-hard-delete-command.el47
2 files changed, 80 insertions, 2 deletions
diff --git a/modules/dirvish-config.el b/modules/dirvish-config.el
index 8b672764b..b7e33337e 100644
--- a/modules/dirvish-config.el
+++ b/modules/dirvish-config.el
@@ -259,6 +259,37 @@ Examples:
(message "Duplicated: %s → %s"
(file-name-nondirectory file) new-name))))
+;;; ----------------------------- Dirvish Hard Delete ---------------------------
+
+(defun cj/--dirvish-hard-delete-command (files)
+ "Return the `sudo rm -rf' shell command that force-deletes FILES.
+Each path is shell-quoted and the list is preceded by `--' so a
+leading-dash filename can't be misread as an option. Pure helper used by
+`cj/dirvish-hard-delete'."
+ (concat "sudo rm -rf -- "
+ (mapconcat #'shell-quote-argument files " ")))
+
+(defun cj/dirvish-hard-delete ()
+ "Force-delete the marked files (or the file at point) via `sudo rm -rf'.
+This bypasses the trash and is IRREVERSIBLE. Prompts with the exact
+targets named before running."
+ (interactive)
+ (let ((files (dired-get-marked-files)))
+ (unless files
+ (user-error "No file at point"))
+ (let ((targets (mapconcat #'file-name-nondirectory files ", ")))
+ (when (yes-or-no-p
+ (format "Force-delete (sudo rm -rf, NO undo): %s? " targets))
+ (let ((status (shell-command (cj/--dirvish-hard-delete-command files))))
+ ;; Revert either way so the listing reflects whatever was removed,
+ ;; but only claim success when `rm' actually exited 0 -- a failed or
+ ;; cancelled `sudo' must not report files gone that are still there.
+ (revert-buffer)
+ (if (zerop status)
+ (message "Force-deleted: %s" targets)
+ (message "Hard delete failed (exit %d) -- see *Shell Command Output*"
+ status)))))))
+
;;; ------------------------------ Dirvish Print File ---------------------------
(defvar cj/dirvish-print-extensions
@@ -489,8 +520,8 @@ Uses feh on X11, swww on Wayland."
("M-p" . dirvish-peek-toggle)
("M-s" . dirvish-setup-menu)
("TAB" . dirvish-subtree-toggle)
- ("d" . dired-do-delete)
- ("D" . cj/dirvish-duplicate-file)
+ ("d" . cj/dirvish-duplicate-file)
+ ("D" . cj/dirvish-hard-delete)
("f" . cj/dirvish-open-file-manager-here)
("g" . dirvish-quick-access)
("o" . cj/xdg-open)
diff --git a/tests/test-dirvish-config-hard-delete-command.el b/tests/test-dirvish-config-hard-delete-command.el
new file mode 100644
index 000000000..eb12d2830
--- /dev/null
+++ b/tests/test-dirvish-config-hard-delete-command.el
@@ -0,0 +1,47 @@
+;;; test-dirvish-config-hard-delete-command.el --- Tests for cj/--dirvish-hard-delete-command -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; `cj/--dirvish-hard-delete-command' is the pure string builder behind the
+;; forced `sudo rm -rf' hard-delete bound to D in dirvish. It shell-quotes
+;; every path and guards the list with `--' so a leading-dash or space-bearing
+;; filename can't be misread. The interactive command (prompt + shell-command)
+;; is verified live, not here.
+
+;;; Code:
+
+(require 'ert)
+
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+(require 'dirvish-config)
+
+(ert-deftest test-dirvish-config-hard-delete-command-multiple ()
+ "Normal: two paths are quoted and joined behind `sudo rm -rf -- '."
+ (should (equal (cj/--dirvish-hard-delete-command '("/tmp/a.txt" "/tmp/b.txt"))
+ "sudo rm -rf -- /tmp/a.txt /tmp/b.txt")))
+
+(ert-deftest test-dirvish-config-hard-delete-command-single ()
+ "Boundary: a single path still carries the `--' option terminator."
+ (should (equal (cj/--dirvish-hard-delete-command '("/tmp/report.pdf"))
+ "sudo rm -rf -- /tmp/report.pdf")))
+
+(ert-deftest test-dirvish-config-hard-delete-command-spaces-and-dash ()
+ "Boundary: a path with spaces is shell-quoted, and `--' protects a
+leading-dash filename from being read as an option."
+ (let ((cmd (cj/--dirvish-hard-delete-command
+ '("/tmp/my file.txt" "/tmp/-rf"))))
+ ;; `--' precedes the paths so `-rf' is a target, not an option.
+ (should (string-prefix-p "sudo rm -rf -- " cmd))
+ ;; the space-bearing path is quoted (not a bare " " splitting the args).
+ (should (string-match-p (regexp-quote (shell-quote-argument "/tmp/my file.txt"))
+ cmd))
+ (should (string-match-p (regexp-quote (shell-quote-argument "/tmp/-rf"))
+ cmd))))
+
+(ert-deftest test-dirvish-config-hard-delete-command-empty ()
+ "Error: an empty list yields just the prefix (no targets) -- the
+interactive command never reaches here, guarding `No file at point' first."
+ (should (equal (cj/--dirvish-hard-delete-command '())
+ "sudo rm -rf -- ")))
+
+(provide 'test-dirvish-config-hard-delete-command)
+;;; test-dirvish-config-hard-delete-command.el ends here