diff options
| -rw-r--r-- | modules/dirvish-config.el | 44 | ||||
| -rw-r--r-- | tests/test-dirvish-config-duplicate-file-name.el | 59 |
2 files changed, 86 insertions, 17 deletions
diff --git a/modules/dirvish-config.el b/modules/dirvish-config.el index fadf09ca..fa5567ad 100644 --- a/modules/dirvish-config.el +++ b/modules/dirvish-config.el @@ -154,33 +154,43 @@ Filters for audio files, prompts for the playlist name, and saves the resulting ;;; ------------------------ Dirvish Duplicate File Copy ------------------------ +(defun cj/--duplicate-file-name (file) + "Return FILE's path with `-copy' inserted before the extension. + +Pure helper used by `cj/dirvish-duplicate-file'. Examples: + /tmp/report.pdf -> /tmp/report-copy.pdf + /home/foo/.bashrc -> /home/foo/.bashrc-copy + doc.txt -> doc-copy.txt + /tmp/archive.tar.gz -> /tmp/archive.tar-copy.gz" + (let* ((dir (file-name-directory file)) + (base (file-name-base file)) + (ext (or (file-name-extension file t) "")) + (new-name (concat base "-copy" ext))) + (if dir + (expand-file-name new-name dir) + new-name))) + (defun cj/dirvish-duplicate-file () - "Duplicate the file at point with '-copy' suffix before the extension. + "Duplicate the file at point with `-copy' suffix before the extension. Examples: report.pdf → report-copy.pdf script.el → script-copy.el README → README-copy" (interactive) - (let* ((file (dired-get-filename nil t)) - (dir (file-name-directory file)) - (base (file-name-base file)) - (ext (file-name-extension file t)) ; includes the dot - (new-name (concat base "-copy" ext)) - (new-path (expand-file-name new-name dir))) + (let ((file (dired-get-filename nil t))) (unless file (user-error "No file at point")) (when (file-directory-p file) (user-error "Cannot duplicate directories, only files")) - - ;; Check if target already exists - (when (file-exists-p new-path) - (unless (y-or-n-p (format "File '%s' already exists. Overwrite? " new-name)) - (user-error "Cancelled"))) - - ;; Copy the file - (copy-file file new-path t) - (revert-buffer) - (message "Duplicated: %s → %s" (file-name-nondirectory file) new-name))) + (let* ((new-path (cj/--duplicate-file-name file)) + (new-name (file-name-nondirectory new-path))) + (when (file-exists-p new-path) + (unless (y-or-n-p (format "File '%s' already exists. Overwrite? " new-name)) + (user-error "Cancelled"))) + (copy-file file new-path t) + (revert-buffer) + (message "Duplicated: %s → %s" + (file-name-nondirectory file) new-name)))) ;;; ----------------------- Dirvish Open File Manager Here ---------------------- diff --git a/tests/test-dirvish-config-duplicate-file-name.el b/tests/test-dirvish-config-duplicate-file-name.el new file mode 100644 index 00000000..8e69f28b --- /dev/null +++ b/tests/test-dirvish-config-duplicate-file-name.el @@ -0,0 +1,59 @@ +;;; test-dirvish-config-duplicate-file-name.el --- Tests for the duplicate-file-name helper -*- lexical-binding: t; -*- + +;;; Commentary: +;; `cj/--duplicate-file-name' is the pure name-mangling half of +;; `cj/dirvish-duplicate-file': given an absolute file path, return the +;; new path with `-copy' inserted before the extension. The interactive +;; wrapper handles the dired side effects (existence check, copy, revert). + +;;; Code: + +(require 'ert) +(require 'package) + +(setq package-user-dir (expand-file-name "elpa" user-emacs-directory)) +(package-initialize) +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(add-to-list 'load-path (expand-file-name "elpa/dirvish-2.3.0/extensions" + user-emacs-directory)) +(require 'user-constants) +(require 'keybindings) +(require 'dirvish-config) + +(ert-deftest test-cj--duplicate-file-name-with-extension () + "Normal: a file with an extension gets `-copy' before the dot." + (should (equal (cj/--duplicate-file-name "/tmp/report.pdf") + "/tmp/report-copy.pdf"))) + +(ert-deftest test-cj--duplicate-file-name-elisp-extension () + "Normal: works for elisp files (the project's daily case)." + (should (equal (cj/--duplicate-file-name "/home/foo/script.el") + "/home/foo/script-copy.el"))) + +(ert-deftest test-cj--duplicate-file-name-no-extension () + "Boundary: an extensionless file appends `-copy' at the end." + (should (equal (cj/--duplicate-file-name "/dir/README") + "/dir/README-copy"))) + +(ert-deftest test-cj--duplicate-file-name-multiple-dots () + "Boundary: only the last dot counts as the extension separator." + (should (equal (cj/--duplicate-file-name "/tmp/archive.tar.gz") + "/tmp/archive.tar-copy.gz"))) + +(ert-deftest test-cj--duplicate-file-name-dotfile () + "Boundary: a leading-dot file (no real extension) keeps the dot in the base." + (should (equal (cj/--duplicate-file-name "/home/foo/.bashrc") + "/home/foo/.bashrc-copy"))) + +(ert-deftest test-cj--duplicate-file-name-relative-path () + "Boundary: relative paths preserve their relative form." + (should (equal (cj/--duplicate-file-name "doc.txt") + "doc-copy.txt"))) + +(ert-deftest test-cj--duplicate-file-name-spaces-in-name () + "Boundary: spaces in the base name are preserved." + (should (equal (cj/--duplicate-file-name "/tmp/my file.txt") + "/tmp/my file-copy.txt"))) + +(provide 'test-dirvish-config-duplicate-file-name) +;;; test-dirvish-config-duplicate-file-name.el ends here |
