From 6cb5efc929585ee6d431547ab52d228b792f8b32 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Wed, 27 Sep 2017 01:21:48 +0200 Subject: [PATCH] core-lib: rename helper fns; move doom-resolve-vim-path + doom--resolve-paths => doom--resolve-path-forms + doom--resolve-hooks => doom--resolve-hook-forms + +evil*ex-replace-special-filenames => doom-resolve-vim-path --- core/core-lib.el | 87 +++++++++++++++++++++++++-- core/core-projects.el | 2 +- core/test/core-lib.el | 10 +-- modules/feature/evil/autoload/evil.el | 70 --------------------- modules/feature/evil/config.el | 3 +- 5 files changed, 88 insertions(+), 84 deletions(-) diff --git a/core/core-lib.el b/core/core-lib.el index 0c78f2faf..0ffbca94d 100644 --- a/core/core-lib.el +++ b/core/core-lib.el @@ -29,7 +29,7 @@ ;; Helpers ;; -(defun doom--resolve-paths (paths &optional root) +(defun doom--resolve-path-forms (paths &optional root) (cond ((stringp paths) `(file-exists-p (expand-file-name @@ -39,10 +39,10 @@ (or root `(doom-project-root)))))) ((listp paths) (cl-loop for i in paths - collect (doom--resolve-paths i root))) + collect (doom--resolve-path-forms i root))) (t paths))) -(defun doom--resolve-hooks (hooks) +(defun doom--resolve-hook-forms (hooks) (cl-loop with quoted-p = (eq (car-safe hooks) 'quote) for hook in (doom-enlist (doom-unquote hooks)) if (eq (car-safe hook) 'quote) @@ -61,6 +61,81 @@ "Return EXP wrapped in a list, or as-is if already a list." (if (listp exp) exp (list exp))) +(defun doom-resolve-vim-path (file-name) + "Take a path and resolve any vim-like filename modifiers in it. This adds +support for these special modifiers: + + %:P Resolves to `doom-project-root'. + +See http://vimdoc.sourceforge.net/htmldoc/cmdline.html#filename-modifiers." + (let* (case-fold-search + (regexp (concat "\\(?:^\\|[^\\\\]\\)" + "\\([#%]\\)" + "\\(\\(?::\\(?:[PphtreS~.]\\|g?s[^:\t\n ]+\\)\\)*\\)")) + (matches + (cl-loop with i = 0 + while (and (< i (length file-name)) + (string-match regexp file-name i)) + do (setq i (1+ (match-beginning 0))) + and collect + (cl-loop for j to (/ (length (match-data)) 2) + collect (match-string j file-name))))) + (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) + (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 (substring flag 1))) + (setq path + (or (pcase (substring flag 0 1) + ("p" (expand-file-name path)) + ("~" (concat "~/" (file-relative-name path "~"))) + ("." (file-relative-name path default-directory)) + ("t" (file-name-nondirectory (directory-file-name path))) + ("r" (file-name-sans-extension path)) + ("e" (file-name-extension path)) + ("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" + (if (featurep 'evil) + (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)))) + path)) + ("P" + (let ((default-directory (file-name-directory (expand-file-name path)))) + (abbreviate-file-name (doom-project-root)))) + (_ path)) + ""))) + ;; 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)))) + (replace-regexp-in-string regexp "\\1" file-name t))) + ;; ;; Library @@ -153,7 +228,7 @@ Body forms can access the hook's arguments through the let-bound variable (:append (setq append-p t)) (:local (setq local-p t)) (:remove (setq hook-fn 'remove-hook)))) - (let ((hooks (doom--resolve-hooks (pop args))) + (let ((hooks (doom--resolve-hook-forms (pop args))) (funcs (let ((val (car args))) (if (memq (car-safe val) '(quote function)) @@ -199,11 +274,11 @@ Body forms can access the hook's arguments through the let-bound variable (not ,mode) (and buffer-file-name (not (file-remote-p buffer-file-name))) ,(if match `(if buffer-file-name (string-match-p ,match buffer-file-name)) t) - ,(if files (doom--resolve-paths files) t) + ,(if files (doom--resolve-path-forms files) t) ,(or pred-form t)) (,mode 1))) ,@(if (and modes (listp modes)) - (cl-loop for hook in (doom--resolve-hooks modes) + (cl-loop for hook in (doom--resolve-hook-forms modes) collect `(add-hook ',hook ',hook-name)) `((add-hook 'after-change-major-mode-hook ',hook-name)))))) (match diff --git a/core/core-projects.el b/core/core-projects.el index bfc5fa59c..756df588c 100644 --- a/core/core-projects.el +++ b/core/core-projects.el @@ -72,7 +72,7 @@ If STRICT-P, return nil if no project was found, otherwise return Paths are relative to the project root, unless they start with ./ or ../ (in which case they're relative to `default-directory'). If they start with a slash, they are absolute." - (doom--resolve-paths files (doom-project-root))) + (doom--resolve-path-forms files (doom-project-root))) ;; diff --git a/core/test/core-lib.el b/core/test/core-lib.el index 6f2483985..42c413159 100644 --- a/core/test/core-lib.el +++ b/core/test/core-lib.el @@ -3,18 +3,18 @@ ;; --- Helpers ---------------------------- -;; `doom--resolve-paths' +;; `doom--resolve-path-forms' (def-test! resolve-paths (should - (equal (doom--resolve-paths '(and "fileA" "fileB")) + (equal (doom--resolve-path-forms '(and "fileA" "fileB")) '(and (file-exists-p (expand-file-name "fileA" (doom-project-root))) (file-exists-p (expand-file-name "fileB" (doom-project-root))))))) -;; `doom--resolve-hooks' +;; `doom--resolve-hook-forms' (def-test! resolve-hooks - (should (equal (doom--resolve-hooks '(js2-mode haskell-mode)) + (should (equal (doom--resolve-hook-forms '(js2-mode haskell-mode)) '(js2-mode-hook haskell-mode-hook))) - (should (equal (doom--resolve-hooks '(quote (js2-mode-hook haskell-mode-hook))) + (should (equal (doom--resolve-hook-forms '(quote (js2-mode-hook haskell-mode-hook))) '(js2-mode-hook haskell-mode-hook)))) ;; `doom-unquote' diff --git a/modules/feature/evil/autoload/evil.el b/modules/feature/evil/autoload/evil.el index 2bb850496..18c4d5147 100644 --- a/modules/feature/evil/autoload/evil.el +++ b/modules/feature/evil/autoload/evil.el @@ -26,76 +26,6 @@ (save-excursion (goto-char beg) (point-marker)) end))) -;;;###autoload -(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* (case-fold-search - (regexp (concat "\\(?:^\\|[^\\\\]\\)" - "\\([#%]\\)" - "\\(\\(?::\\(?:[PphtreS~.]\\|g?s[^:\t\n ]+\\)\\)*\\)")) - (matches - (cl-loop with i = 0 - while (and (< i (length file-name)) - (string-match regexp file-name i)) - do (setq i (1+ (match-beginning 0))) - and collect - (cl-loop for j to (/ (length (match-data)) 2) - collect (match-string j file-name))))) - (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) - (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 (substring flag 1))) - (setq path - (or (pcase (substring flag 0 1) - ("p" (expand-file-name path)) - ("~" (concat "~/" (file-relative-name path "~"))) - ("." (file-relative-name path default-directory)) - ("t" (file-name-nondirectory (directory-file-name path))) - ("r" (file-name-sans-extension path)) - ("e" (file-name-extension path)) - ("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)))) - (abbreviate-file-name (doom-project-root)))) - (_ path)) - ""))) - ;; 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)))) - (defun +evil--window-swap (direction) "Move current window to the next window in DIRECTION. If there are no windows there and there is only one window, split in that direction and place this diff --git a/modules/feature/evil/config.el b/modules/feature/evil/config.el index 32c74b853..66bd54cb2 100644 --- a/modules/feature/evil/config.el +++ b/modules/feature/evil/config.el @@ -124,8 +124,7 @@ across windows." ;; monkey patch `evil-ex-replace-special-filenames' to add more ex ;; substitution flags to evil-mode - (advice-add #'evil-ex-replace-special-filenames - :override #'+evil*ex-replace-special-filenames) + (advice-add #'evil-ex-replace-special-filenames :override #'doom-resolve-vim-path) ;; These arg types will highlight matches in the current buffer (evil-ex-define-argument-type buffer-match :runner +evil-ex-buffer-match)