Refactor feature/evil hacks & advice; fix tests

This commit is contained in:
Henrik Lissner 2018-01-06 18:50:49 -05:00
parent 0c3484414c
commit 5cd29479f4
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
6 changed files with 166 additions and 163 deletions

View file

@ -25,7 +25,6 @@ PROMPT (a string) and COMMAND (a list of command plists; see `def-menu!').")
(t default-directory))))
(cond ((stringp command)
(with-current-buffer (get-buffer-create "*compilation*")
(setq command (doom-resolve-vim-path command))
(save-window-excursion
(compile command))
(setq header-line-format

View file

@ -48,81 +48,6 @@
"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. On top of the
classical vim modifiers, this adds support for:
%: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

View file

@ -32,48 +32,6 @@
(should (equal (doom-enlist 'a) '(a)))
(should (equal (doom-enlist '(a)) '(a))))
;; `doom-resolve-vim-path'
(def-test! resolve-vim-path
(cl-flet ((do-it #'doom-resolve-vim-path))
;; file modifiers
(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 (file-truename (do-it "%:p"))
(file-truename buffer-file-name))))
;; 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")))
;; 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 "%:P") "")))))
;; --- Macros -----------------------------

View file

@ -193,3 +193,99 @@ evil-window-move-* (e.g. `evil-window-move-far-left')"
(goto-char beg)
(call-interactively #'wgrep-mark-deletion))
beg (1- end) nil))))
;;
;; Advice
;;
;;;###autoload
(defun +evil*static-reindent (orig-fn &rest args)
"Don't move cursor on indent."
(save-excursion (apply orig-fn args)))
;;;###autoload
(defun +evil*restore-normal-state-on-windmove (orig-fn &rest args)
"If in anything but normal or motion mode when moving to another window,
restore normal mode. This prevents insert state from bleeding into other modes
across windows."
(unless (memq evil-state '(normal motion emacs))
(evil-normal-state +1))
(apply orig-fn args))
;;;###autoload
(defun +evil*resolve-vim-path (file-name)
"Take a path and resolve any vim-like filename modifiers in it. This adds
support for most vim file modifiers, as well as:
%:P Resolves to `doom-project-root'.
See http://vimdoc.sourceforge.net/htmldoc/cmdline.html#filename-modifiers for
more information on 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)))

View file

@ -45,9 +45,6 @@
(set! :popup "^\\*evil-registers" '((size . 0.3)))
(set! :popup "^\\*Command Line" '((size . 8)))
;; Don't interfere with localleader key
(define-key evil-motion-state-map "\\" nil)
;; Set cursor colors later, once theme is loaded
(defun +evil*init-cursors (&rest _)
(setq evil-default-cursor (face-background 'cursor nil t)
@ -63,19 +60,11 @@
(dolist (mode '(help-mode debugger-mode))
(evil-set-initial-state mode 'normal))
;; make `try-expand-dabbrev' from `hippie-expand' work in minibuffer
;; @see `he-dabbrev-beg', so we need re-define syntax for '/'
(defun minibuffer-inactive-mode-hook-setup ()
(set-syntax-table (let* ((table (make-syntax-table)))
(modify-syntax-entry ?/ "." table)
table)))
(add-hook 'minibuffer-inactive-mode-hook #'minibuffer-inactive-mode-hook-setup)
;; --- keybind fixes ----------------------
(map! (:after wgrep
;; a wrapper that invokes `wgrep-mark-deletion' across lines
;; you use `evil-delete' on.
;; A wrapper that invokes `wgrep-mark-deletion' across lines you use
;; `evil-delete' in wgrep buffers.
:map wgrep-mode-map [remap evil-delete] #'+evil-delete)
;; replace native folding commands
@ -86,10 +75,6 @@
[remap evil-close-folds] #'+evil:fold-close-all
[remap evil-open-folds] #'+evil:fold-open-all)
;; --- evil hacks -------------------------
(advice-add #'evil-force-normal-state :after #'doom/escape)
(defun +evil|disable-highlights ()
"Disable ex search buffer highlights."
(when (evil-ex-hl-active-p 'evil-ex-search)
@ -97,28 +82,40 @@
t))
(add-hook 'doom-escape-hook #'+evil|disable-highlights)
(defun +evil*restore-normal-state-on-windmove (orig-fn &rest args)
"If in anything but normal or motion mode when moving to another window,
restore normal mode. This prevents insert state from bleeding into other modes
across windows."
(unless (memq evil-state '(normal motion emacs))
(evil-normal-state +1))
(apply orig-fn args))
;; --- evil hacks -------------------------
;; Make ESC (from normal mode) the universal escaper. See `doom-escape-hook'.
(advice-add #'evil-force-normal-state :after #'doom/escape)
;; Ensure buffer is in normal mode when we leave it and return to it.
(advice-add #'windmove-do-window-select :around #'+evil*restore-normal-state-on-windmove)
(defun +evil*static-reindent (orig-fn &rest args)
"Don't move cursor on indent."
(save-excursion (apply orig-fn args)))
;; Don't move cursor when indenting
(advice-add #'evil-indent :around #'+evil*static-reindent)
;; monkey patch `evil-ex-replace-special-filenames' to add more ex
;; substitution flags to evil-mode
(advice-add #'evil-ex-replace-special-filenames :override #'doom-resolve-vim-path)
(advice-add #'evil-ex-replace-special-filenames :override #'+evil*resolve-vim-path)
;; make `try-expand-dabbrev' from `hippie-expand' work in minibuffer
;; @see `he-dabbrev-beg', so we need re-define syntax for '/'
(defun +evil*fix-dabbrev-in-minibuffer ()
(set-syntax-table (let* ((table (make-syntax-table)))
(modify-syntax-entry ?/ "." table)
table)))
(add-hook 'minibuffer-inactive-mode-hook #'+evil*fix-dabbrev-in-minibuffer)
;; Move to new split -- setting `evil-split-window-below' &
;; `evil-vsplit-window-right' to non-nil mimics this, but that doesn't update
;; window history. That means when you delete a new split, Emacs leaves you on
;; the 2nd to last window on the history stack, which is jarring.
;;
;; Also recenters window on cursor in new split
(defun +evil*window-follow (&rest _) (evil-window-down 1) (recenter))
(advice-add #'evil-window-split :after #'+evil*window-follow)
(defun +evil*window-vfollow (&rest _) (evil-window-right 1) (recenter))
(advice-add #'evil-window-vsplit :after #'+evil*window-vfollow)
;; These arg types will highlight matches in the current buffer
(evil-ex-define-argument-type buffer-match :runner +evil-ex-buffer-match)
(evil-ex-define-argument-type global-match :runner +evil-ex-global-match)
;; By default :g[lobal] doesn't highlight matches in the current buffer. I've
;; got to write my own argument type and interactive code to get it to do so.
(evil-ex-define-argument-type global-delim-match :runner +evil-ex-global-delim-match)
@ -137,18 +134,7 @@ across windows."
(evil-set-command-properties
'+evil:align :move-point t :ex-arg 'buffer-match :ex-bang t :evil-mc t :keep-visual t :suppress-operator t)
(evil-set-command-properties
'+evil:mc :move-point nil :ex-arg 'global-match :ex-bang t :evil-mc t)
;; Move to new split -- setting `evil-split-window-below' &
;; `evil-vsplit-window-right' to non-nil mimics this, but that doesn't update
;; window history. That means when you delete a new split, Emacs leaves you on
;; the 2nd to last window on the history stack, which is jarring.
;;
;; Also recenters window on cursor in new split
(defun +evil*window-follow (&rest _) (evil-window-down 1) (recenter))
(defun +evil*window-vfollow (&rest _) (evil-window-right 1) (recenter))
(advice-add #'evil-window-split :after #'+evil*window-follow)
(advice-add #'evil-window-vsplit :after #'+evil*window-vfollow))
'+evil:mc :move-point nil :ex-arg 'global-match :ex-bang t :evil-mc t))
;;

View file

@ -5,8 +5,47 @@
;;
;; `evil-ex-replace-special-filenames'
;; NOTE The majority of this function is tested in core/test/core-lib.el, this
;; only tests the evil-mode-specific functionality.
(def-test! resolve-vim-path
(cl-flet ((do-it #'evil-ex-replace-special-filenames))
;; file modifiers
(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 (file-truename (do-it "%:p"))
(file-truename buffer-file-name))))
;; 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")))
;; 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 "%:P") "")))))
(def-test! file-modifiers
(cl-flet ((do-it #'evil-ex-replace-special-filenames))
(let ((buffer-file-name "~/.emacs.d/test/modules/feature/test-evil.el")