From f5b3baf09ecc3bada521b5db836c7039252e2088 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Fri, 14 Apr 2017 22:16:28 -0400 Subject: [PATCH] feature/evil: generalize custom file modifiers + unit test --- modules/feature/evil/autoload/evil.el | 81 +++++++++++++++++---------- test/modules/feature/test-evil.el | 61 +++++++++++++++----- 2 files changed, 97 insertions(+), 45 deletions(-) diff --git a/modules/feature/evil/autoload/evil.el b/modules/feature/evil/autoload/evil.el index bb7b720da..8eada6638 100644 --- a/modules/feature/evil/autoload/evil.el +++ b/modules/feature/evil/autoload/evil.el @@ -30,52 +30,73 @@ (defun +evil*ex-replace-special-filenames (file-name) "Replace special symbols in FILE-NAME. Modified to include other substitution flags. See http://vimdoc.sourceforge.net/htmldoc/cmdline.html#filename-modifiers." - (let ((regexp (concat "\\(?:^\\|[^\\\\]\\)" - "\\([#%\\w]\\)" - "\\(\\(?::\\(?:[PphtreS~.]\\|g?s[^: $]+\\)\\)*\\)")) - case-fold-search) - ;; TODO Remove s.el dependency so I can offer it upstream - (dolist (match (s-match-strings-all regexp file-name)) - (let ((flags (split-string (cl-caddr match) ":" t)) - (path (file-relative-name - (pcase (cadr match) - ("%" (buffer-file-name)) - ("#" (and (other-buffer) (buffer-file-name (other-buffer)))) - (_ file-name)) - default-directory)) + (let* (case-fold-search + (regexp (concat "\\(?:^\\|[^\\\\]\\)" + "\\([#%]\\)" + "\\(\\(?::\\(?:[PphtreS~.]\\|g?s[^:\t\n ]+\\)\\)*\\)")) + (matches + (let ((all-strings ()) + (i 0)) + (while (and (< i (length file-name)) + (string-match regexp file-name i)) + (setq i (1+ (match-beginning 0))) + (let (strings) + (push (dotimes (i (/ (length (match-data)) 2) (nreverse strings)) + (push (match-string i file-name) strings)) + all-strings))) + (nreverse all-strings)))) + (dolist (match matches) + (let ((flags (split-string (car (cdr (cdr match))) ":" t)) + (path (and buffer-file-name + (pcase (car (cdr match)) + ("%" (file-relative-name buffer-file-name)) + ("#" (save-excursion (other-window 1) (file-relative-name buffer-file-name)))))) flag global) - (when path + (if (not path) + (setq path "") (while flags (setq flag (pop flags)) (when (string-suffix-p "\\" flag) (setq flag (concat flag (pop flags)))) (when (string-prefix-p "gs" flag) (setq global t - flag (string-remove-prefix "g" flag))) + flag (substring flag 1))) (setq path (or (pcase (substring flag 0 1) - ("P" (doom-project-root)) ("p" (expand-file-name path)) - ("~" (file-relative-name path "~")) + ("~" (concat "~/" (file-relative-name path "~"))) ("." (file-relative-name path default-directory)) - ("h" (directory-file-name path)) ; FIXME - ("t" (file-name-nondirectory (directory-file-name path))) ; FIXME + ("t" (file-name-nondirectory (directory-file-name path))) ("r" (file-name-sans-extension path)) ("e" (file-name-extension path)) - ("s" (let* ((args (evil-delimited-arguments (substring flag 1) 2)) - (pattern (evil-transform-vim-style-regexp (car args))) - (replace (cadr args))) - (replace-regexp-in-string - (if global pattern (concat "\\(" pattern "\\).*\\'")) - (evil-transform-vim-style-regexp replace) path t t - (unless global 1)))) ("S" (shell-quote-argument path)) + ("h" + (let ((parent (file-name-directory (expand-file-name path)))) + (unless (equal (file-truename path) + (file-truename parent)) + (if (file-name-absolute-p path) + (directory-file-name parent) + (file-relative-name parent))))) + ("s" + (when-let (args (evil-delimited-arguments (substring flag 1) 2)) + (let ((pattern (evil-transform-vim-style-regexp (car args))) + (replace (cadr args))) + (replace-regexp-in-string + (if global pattern (concat "\\(" pattern "\\).*\\'")) + (evil-transform-vim-style-regexp replace) path t t + (unless global 1))))) + ("P" + (let ((default-directory (file-name-directory (expand-file-name path)))) + (doom-project-root))) (_ path)) ""))) - (setq file-name - (replace-regexp-in-string (format "\\(?:^\\|[^\\\\]\\)\\(%s\\)" - (regexp-quote (string-trim-left (car match)))) - path file-name t t 1))))) + ;; strip trailing slash, if applicable + (when (and (not (string= path "")) (equal (substring path -1) "/")) + (setq path (substring path 0 -1)))) + (setq file-name + (replace-regexp-in-string (format "\\(?:^\\|[^\\\\]\\)\\(%s\\)" + (regexp-quote (string-trim-left (car match)))) + path file-name t t 1)))) (setq file-name (replace-regexp-in-string regexp "\\1" file-name t)))) diff --git a/test/modules/feature/test-evil.el b/test/modules/feature/test-evil.el index 3290d9176..bb8809d0b 100644 --- a/test/modules/feature/test-evil.el +++ b/test/modules/feature/test-evil.el @@ -2,20 +2,51 @@ (require! :feature evil t) +(defalias 'do-it '+evil*ex-replace-special-filenames) + (def-test-group! feature/evil - :disabled t (ert-deftest custom-file-modifiers () - (let ((buffer-file-name "~/.emacs.d/test/modules/feature/test-evil.el") - (default-directory "~/.emacs.d/test/modules")) - (should (equal (+evil*ex-replace-special-filenames "%") "feature/test-evil.el")) - (should (equal (+evil*ex-replace-special-filenames "%:r") "feature/test-evil")) - (should (equal (+evil*ex-replace-special-filenames "%:e") "el")) - ;; TODO :h - ;; TODO :t - ;; TODO :. - ;; TODO :~ - ;; TODO :s - ;; TODO :S - (should (equal (+evil*ex-replace-special-filenames "%:P") (doom-project-root))) - (should (equal (file-truename (+evil*ex-replace-special-filenames "%:p")) - (file-truename buffer-file-name)))))) + (let ((buffer-file-name "~/.emacs.d/test/modules/feature/test-evil.el") + (default-directory "~/.emacs.d/test/modules/")) + (should (equal (do-it "%") "feature/test-evil.el")) + (should (equal (do-it "%:r") "feature/test-evil")) + (should (equal (do-it "%:r.elc") "feature/test-evil.elc")) + (should (equal (do-it "%:e") "el")) + (should (equal (do-it "%:p") (expand-file-name buffer-file-name))) + (should (equal (do-it "%:h") "feature")) + (should (equal (do-it "%:t") "test-evil.el")) + (should (equal (do-it "%:.") "feature/test-evil.el")) + (should (equal (do-it "%:~") "~/.emacs.d/test/modules/feature/test-evil.el")) + (should (equal (do-it "%:s?e?x?") "fxature/test-evil.el")) + (should (equal (do-it "%:gs?e?x?") "fxaturx/txst-xvil.xl")) + (should (equal (do-it "%:P/") (doom-project-root))) + (should (equal (file-truename (do-it "%:p")) + (file-truename buffer-file-name))))) + + (ert-deftest custom-nested-file-modifiers () + (let ((buffer-file-name "~/vim/src/version.c") + (default-directory "~/vim/")) + (should (equal (do-it "%:p") (expand-file-name "~/vim/src/version.c"))) + (should (equal (do-it "%:p:.") "src/version.c")) + (should (equal (do-it "%:p:~") "~/vim/src/version.c")) + (should (equal (do-it "%:h") "src")) + (should (equal (do-it "%:p:h") (expand-file-name "~/vim/src"))) + (should (equal (do-it "%:p:h:h") (expand-file-name "~/vim"))) + (should (equal (do-it "%:t") "version.c")) + (should (equal (do-it "%:p:t") "version.c")) + (should (equal (do-it "%:r") "src/version")) + (should (equal (do-it "%:p:r") (expand-file-name "~/vim/src/version"))) + (should (equal (do-it "%:t:r") "version")))) + + (ert-deftest custom-empty-file-modifiers () + (let (buffer-file-name default-directory) + (should (equal (do-it "%") "")) + (should (equal (do-it "%:r") "")) + (should (equal (do-it "%:e") "")) + (should (equal (do-it "%:h") "")) + (should (equal (do-it "%:t") "")) + (should (equal (do-it "%:.") "")) + (should (equal (do-it "%:~") "")) + (should (equal (do-it "%:s?e?x?") "")) + (should (equal (do-it "%:gs?e?x?") "")) + (should (equal (do-it "%:P") "")))))