diff --git a/modules/feature/evil/autoload/evil.el b/modules/feature/evil/autoload/evil.el index 29a098750..55e1e9fd5 100644 --- a/modules/feature/evil/autoload/evil.el +++ b/modules/feature/evil/autoload/evil.el @@ -1,14 +1,30 @@ ;;; feature/evil/packages.el +;;;###autoload +(defun +evil/visual-indent () + "vnoremap < >gv" + (interactive) + (evil-shift-left (region-beginning) (region-end)) + (evil-normal-state) + (evil-visual-restore)) + ;;;###autoload (defun +evil*ex-replace-special-filenames (file-name) "Replace special symbols in FILE-NAME. Modified to include other substitution flags." - ;; TODO Generalize this so I can offer it upstream (let ((regexp (concat "\\(?:^\\|[^\\\\]\\)" "\\([#%@]\\)" "\\(\\(?::\\(?:[phtreS~.]\\|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 @@ -47,7 +63,7 @@ flags." ""))) (setq file-name (replace-regexp-in-string (format "\\(?:^\\|[^\\\\]\\)\\(%s\\)" - (s-trim-left (car match))) + (string-trim-left (car match))) path file-name t t 1))))) (setq file-name (replace-regexp-in-string regexp "\\1" file-name t)))) @@ -141,7 +157,25 @@ evil-window-move-* (e.g. `evil-window-move-far-left')" (switch-to-buffer this-buffer)) (select-window that-window)))) -;;;###autoload (autoload '+evil:macro-on-all-lines "feature/evil/autoload" nil t) +;;;###autoload +(defun +evil/window-move-left () "`+evil-window-move'" (interactive) (evil-window-move 'left)) +;;;###autoload +(defun +evil/window-move-right () "`+evil-window-move'" (interactive) (evil-window-move 'right)) +;;;###autoload +(defun +evil/window-move-up () "`+evil-window-move'" (interactive) (evil-window-move 'up)) +;;;###autoload +(defun +evil/window-move-down () "`+evil-window-move'" (interactive) (evil-window-move 'down)) + +;;;###autoload +(defun +evil/matchit-or-toggle-fold () + "If on a fold-able element, toggle the fold (`hs-toggle-hiding'). Otherwise, +if on a delimiter, jump to the matching one (`evilmi-jump-items')." + (interactive) + (if (ignore-errors (hs-already-hidden-p)) + (hs-toggle-hiding) + (call-interactively 'evilmi-jump-items))) + +;;;###autoload (autoload '+evil:macro-on-all-lines "feature/evil/autoload/evil" nil t) (evil-define-operator +evil:macro-on-all-lines (beg end &optional macro) "Apply macro to each line." :motion nil @@ -154,3 +188,33 @@ evil-window-move-* (e.g. `evil-window-move-far-left')" (concat "@" (single-key-description (or macro (read-char "@-")))))) + +;;;###autoload (autoload '+evil:open-folds-recursively "feature/evil/autoload/evil" nil t) +(evil-define-command +evil:open-folds-recursively (level) + "Opens all folds recursively, up to LEVEL." + (interactive "") + (unless (bound-and-true-p hs-minor-mode) + (hs-minor-mode 1)) + (if level (hs-hide-level level) (evil-open-folds))) + +;;;###autoload (autoload '+evil:close-folds-recursively "feature/evil/autoload/evil" nil t) +(evil-define-command +evil:close-folds-recursively (level) + "Closes all folds recursively, up to LEVEL." + (interactive "") + (unless (bound-and-true-p hs-minor-mode) + (hs-minor-mode 1)) + (if level (hs-hide-level level) (evil-close-folds))) + +;;;###autoload (autoload '+evil:retab "feature/evil/autoload/evil" nil t) +(evil-define-operator +evil:retab (&optional beg end) + "Wrapper around `doom/retab'." + :motion nil :move-point nil :type line + (interactive "") + (doom/retab beg end)) + +;;;###autoload (autoload '+evil:narrow-buffer "feature/evil/autoload/evil" nil t) +(evil-define-command +evil:narrow-buffer (beg end &optional bang) + "Wrapper around `doom-narrow-buffer'." + :move-point nil + (interactive "") + (doom-narrow-buffer beg end bang)) diff --git a/modules/feature/evil/autoload/files.el b/modules/feature/evil/autoload/files.el index 668951a3c..8e44f3e79 100644 --- a/modules/feature/evil/autoload/files.el +++ b/modules/feature/evil/autoload/files.el @@ -4,25 +4,27 @@ (evil-define-command +evil:file-delete (&optional bang filename) "Delete current buffer's file. If BANG, don't ask for confirmation." :repeat nil + ;; TODO Test me (interactive "") - (let ((filename (f-canonical (or filename (buffer-file-name)))) - (buf (current-buffer))) - (cond ((not (f-exists-p filename)) - (error "File doesn't exist: %s" filename)) + (let* ((fname (file-truename (or fname (buffer-file-name)))) + (fbase (file-name-sans-extension (file-name-nondirectory fname))) + (buf (current-buffer))) + (cond ((not (file-exists-p fname)) + (error "File doesn't exist: %s" fname)) - ((not (or bang (y-or-n-p (format "Delete %s?" (f-base filename))))) + ((not (or bang (y-or-n-p (format "Delete %s?" fbase)))) (message "Aborted")) (t (unwind-protect - (delete-file filename) - (if (f-exists-p filename) - (error "Failed to delete %s" (f-relative filename)) + (delete-file fname) + (if (file-exists-p fname) + (error "Failed to delete %s" (file-relative-name fname)) (doom/previous-real-buffer) (kill-buffer buf) (when (bound-and-true-p save-place-mode) (save-place-forget-unreadable-files)) - (message "Successfully deleted %s" (f-relative filename)))))))) + (message "Successfully deleted %s" (file-relative-name fname)))))))) (defun doom--save-exit() (save-buffer) (kill-buffer) (remove-hook 'yas-after-exit-snippet-hook '--save-exit)) ;;;###autoload (autoload '+evil:file-create "feature/evil/autoload/files" nil t) @@ -31,14 +33,15 @@ you to fill in each snippet field before buffer closes unless BANG is provided." :repeat nil + ;; TODO Test me (interactive "") - (let ((dir (f-dirname path)) - (fullpath (f-full path)) - (is-auto t)) - (when (and bang (not (f-exists? dir))) + (let* ((fullpath (expand-file-name path)) + (dir (file-name-directory fullpath)) + (is-auto t)) + (when (and bang (not (file-exists-p dir))) (mkdir dir)) - (if (f-exists? dir) - (if (f-exists? fullpath) + (if (file-exists-p dir) + (if (file-exists-p fullpath) (error "File already exists: %s" path) (find-file fullpath) (add-hook 'yas-after-exit-snippet-hook 'doom--save-exit) @@ -46,32 +49,32 @@ provided." (error "Directory doesn't exist: %s" dir)))) ;;;###autoload (autoload '+evil:file-move "feature/evil/autoload/files" nil t) -(evil-define-command +evil:file-move (path) +(evil-define-command +evil:file-move (bang dest-path) "Move current buffer's file to PATH. Replaces %, # and other variables (see `evil-ex-replace-special-filenames')" :repeat nil - (interactive "") - (let* ((old-path (buffer-file-name)) - (new-path (cond ((f-dir? path) - (f-expand (f-filename old-path) path)) - ((f-dir? (f-dirname path)) - (f-full path)) - (t (user-error "Not a valid destination: %s" path)))) - (project-root (doom-project-root))) - ;; Move all attachments if in org-mode - (when (eq major-mode 'org-mode) - (mapc (lambda (file) - (when (and (file-exists-p file) (not (f-same? old-path new-path))) - (rename-file file (f-expand (f-filename old-path) (f-dirname new-path)) t))) - (doom/org-attachments))) - (when (buffer-modified-p) - (save-buffer)) - (rename-file old-path new-path 1) - (rename-buffer (f-filename new-path)) - (set-visited-file-name new-path) - (set-buffer-modified-p nil) - (save-place-forget-unreadable-files) - (setq doom--spaceline-file-path nil) - (message "File '%s' successfully renamed to '%s'" - (f-relative old-path project-root) (f-relative new-path project-root)))) + ;; TODO Test me + (interactive "") + (let* ((dest-path (expand-file-name dest-path)) + (old-path (file-truename (buffer-file-name))) + (new-path (cond ((file-directory-p dest-path) + (expand-file-name (file-name-nondirectory old-path) dest-path)) + ((file-directory-p (file-name-directory dest-path)) + (expand-file-name dest-path)) + (t (user-error "Not a valid destination: %s" dest-path)))) + (project-root (doom-project-root)) + (short-old-path (file-relative-name old-path project-root)) + (short-new-path (file-relative-name new-path project-root))) + (when (y-or-n-p (format "Renaming %s to %s, proceed?" + short-old-path short-dest-path)) + (when (buffer-modified-p) + (save-buffer)) + (rename-file old-path new-path 1) + (rename-buffer (file-name-nondirectory new-path)) + (set-visited-file-name new-path) + (set-buffer-modified-p nil) + (save-place-forget-unreadable-files) + (setq doom--spaceline-file-path nil) + (message "File '%s' successfully renamed to '%s'" + short-old-path short-new-path)))) diff --git a/modules/feature/evil/autoload/neotree.el b/modules/feature/evil/autoload/neotree.el index a7039df4e..e16b3375c 100644 --- a/modules/feature/evil/autoload/neotree.el +++ b/modules/feature/evil/autoload/neotree.el @@ -12,7 +12,8 @@ (neotree-hide) (let ((project-root (doom-project-root))) (unless (and (neo-global--window-exists-p) - (f-same? (neo-global--with-buffer neo-buffer--start-node) project-root)) + (equal (file-truename (neo-global--with-buffer neo-buffer--start-node)) + (file-truename project-root))) (neotree-dir project-root)) (neotree-find path project-root))))) diff --git a/modules/feature/evil/config.el b/modules/feature/evil/config.el index 8defd9eba..905144c05 100644 --- a/modules/feature/evil/config.el +++ b/modules/feature/evil/config.el @@ -3,21 +3,15 @@ ;; I'm a vimmer at heart. Its modal philosophy suits me better, and this module ;; strives to make Emacs a much better vim than vim was. -(defvar +evil-leader "," - "The key, used by the `@map' macro for :leader bindings.") - -(defvar +evil-localleader "\\" - "The key, used by the `@map' macro for :localleader bindings.") - (@def-setting :evil-state (&rest mode-state-list) "Set the initialize STATE of MODE using `evil-set-initial-state'." - (if (-all-p 'listp mode-state-list) - (macroexp-progn - (--map (let ((argc (length it))) - (unless (= argc 2) - (error ":evil-state sub-lists expect 2 arguments, got %s" argc)) - `(evil-set-initial-state ',(car it) ',(cadr it))) - mode-state-list)) + (if (cl-every 'listp mode-state-list) + (let (forms) + (dolist (it mode-state-list) + (unless (consp it) + (error ":evil-state expected cons cells, got %s" it)) + (push `(evil-set-initial-state ',(car it) ',(cdr it)) forms)) + `(progn ,@(reverse forms))) (let ((argc (length mode-state-list))) (unless (= argc 2) (error ":evil-state expected 2 arguments, got %s" argc))) @@ -44,36 +38,51 @@ evil-insert-skip-empty-lines t) :config + (evil-mode +1) + (evil-select-search-module 'evil-search-module 'evil-search) + + ;; Set cursor colors later, presumably once theme is loaded + (@add-hook 'after-init-hook + (setq evil-default-cursor (face-attribute 'cursor :background nil t) + evil-normal-state-cursor 'box + evil-emacs-state-cursor `(,(face-attribute 'warning :foreground nil nil) box) + evil-insert-state-cursor 'bar + evil-visual-state-cursor 'hollow)) + + ;; highlight matching delimiters where it's important + (defun +evil|show-paren-mode-off () (show-paren-mode -1)) + (add-hook 'evil-insert-state-entry-hook 'show-paren-mode) + (add-hook 'evil-insert-state-exit-hook '+evil|show-paren-mode-off) + (add-hook 'evil-visual-state-entry-hook 'show-paren-mode) + (add-hook 'evil-visual-state-exit-hook '+evil|show-paren-mode-off) + (add-hook 'evil-operator-state-entry-hook 'show-paren-mode) + (add-hook 'evil-operator-state-exit-hook '+evil|show-paren-mode-off) + (add-hook 'evil-normal-state-entry-hook '+evil|show-paren-mode-off) + ;; Disable highlights on insert-mode + ;; (add-hook 'evil-insert-state-entry-hook 'evil-ex-nohighlight) + (@set :popup '("*evil-registers*" :size 0.3) '("*Command Line*" :size 8)) - (evil-mode +1) - (evil-select-search-module 'evil-search-module 'evil-search) - - ;; Don't move cursor on indent - (defun +evil*static-reindent (orig-fn &rest args) - (save-excursion (apply orig-fn args))) - (advice-add 'evil-indent :around '+evil*static-reindent) - - (mapc (lambda (r) (evil-set-initial-state (car r) (cdr r))) - '((compilation-mode . normal) - (help-mode . normal) - (message-mode . normal) - (debugger-mode . normal) - (image-mode . normal) - (doc-view-mode . normal) - (eww-mode . normal) - (tabulated-list-mode . emacs) - (profile-report-mode . emacs) - (Info-mode . emacs) - (view-mode . emacs) - (comint-mode . emacs) - (cider-repl-mode . emacs) - (term-mode . emacs) - (calendar-mode . emacs) - (Man-mode . emacs) - (grep-mode . emacs)))) + (@set :evil-state + '(compilation-mode . normal) + '(help-mode . normal) + '(message-mode . normal) + '(debugger-mode . normal) + '(image-mode . normal) + '(doc-view-mode . normal) + '(eww-mode . normal) + '(tabulated-list-mode . emacs) + '(profile-report-mode . emacs) + '(Info-mode . emacs) + '(view-mode . emacs) + '(comint-mode . emacs) + '(cider-repl-mode . emacs) + '(term-mode . emacs) + '(calendar-mode . emacs) + '(Man-mode . emacs) + '(grep-mode . emacs))) (defsubst +evil--textobj (key inner-fn &optional outer-fn) "Define a text object." @@ -93,18 +102,23 @@ (evil-ex-nohighlight))) (advice-add 'evil-force-normal-state :after '+evil*esc) +;; Don't move cursor on indent +(defun +evil*static-reindent (orig-fn &rest args) + (save-excursion (apply orig-fn args))) +(advice-add 'evil-indent :around '+evil*static-reindent) + ;; Move to new split (defun +evil*window-follow (&rest _) (evil-window-down 1)) (defun +evil*window-vfollow (&rest _) (evil-window-right 1)) (advice-add 'evil-window-split :after '+evil*window-follow) (advice-add 'evil-window-vsplit :after '+evil*window-vfollow) -;; Fix harmless (yet disruptive) error reporting w/ hidden buffers caused by -;; workgroups killing windows +;; Fix harmless (yet disruptive) error reporting w/ hidden buffers +;; caused by dead popup windows ;; TODO Delete timer on dead windows? -;; (defun +evil*ignore-hl-errors (orig-fn &rest args) -;; (ignore-errors (apply orig-fn args))) -;; (advice-add 'evil-ex-hl-do-update-highlight :around '+evil*ignore-hl-errors) +(defun +evil*ignore-hl-errors (orig-fn &rest args) + (ignore-errors (apply orig-fn args))) +(advice-add 'evil-ex-hl-do-update-highlight :around '+evil*ignore-hl-errors) ;; monkey patch `evil-ex-replace-special-filenames' to add more ex ;; substitution flags to evil-mode @@ -139,7 +153,7 @@ 1 1)) (evil-ex-define-cmd "g[lobal]" '+evil:global) -(evil-ex-define-cmd "al[ign]" '+evil:align) +(evil-ex-define-cmd "al[ign]" '+evil:align) ;; @@ -160,6 +174,7 @@ (@def-package evil-easymotion :defer 1 + :commands evilem-define :config (defvar +evil--snipe-repeat-fn) @@ -192,14 +207,14 @@ ;; Defuns (defun +evil--embrace-get-pair (char) - (acond ((cdr-safe (assoc (string-to-char char) evil-surround-pairs-alist)) - `(,(car it) . ,(cdr it))) - ((assoc-default char embrace--pairs-list) - (if (functionp (embrace-pair-struct-read-function it)) - (let ((pair (funcall (embrace-pair-struct-read-function it)))) - `(,(car pair) . ,(cdr pair))) - `(,(embrace-pair-struct-left it) . ,(embrace-pair-struct-right it)))) - (t `(,char . ,char)))) + (if-let (pair (cdr-safe (assoc (string-to-char char) evil-surround-pairs-alist))) + pair + (if-let (pair (assoc-default char embrace--pairs-list)) + (if-let (real-pair (and (functionp (embrace-pair-struct-read-function pair)) + (funcall (embrace-pair-struct-read-function pair)))) + real-pair + (cons (embrace-pair-struct-left pair) (embrace-pair-struct-right pair))) + (cons char char)))) (defun +evil--embrace-escaped () "Backslash-escaped surround character support for embrace." @@ -277,16 +292,7 @@ (@def-package evil-matchit :commands (evilmi-jump-items evilmi-text-object global-evil-matchit-mode) :config (global-evil-matchit-mode 1) - :init - (+evil--textobj "%" 'evilmi-text-object) - - (defun +evil/matchit-or-toggle-fold () - "If on a fold-able element, toggle the fold (`hs-toggle-hiding'). Otherwise, -if on a delimiter, jump to the matching one (`evilmi-jump-items')." - (interactive) - (if (ignore-errors (hs-already-hidden-p)) - (hs-toggle-hiding) - (call-interactively 'evilmi-jump-items)))) + :init (+evil--textobj "%" 'evilmi-text-object)) (@def-package evil-multiedit @@ -319,10 +325,10 @@ if on a delimiter, jump to the matching one (`evilmi-jump-items')." (@def-package evil-snipe :demand t :init (setq evil-snipe-smart-case t - evil-snipe-repeat-keys nil ; using space to repeat evil-snipe-scope 'line evil-snipe-repeat-scope 'visible - evil-snipe-override-evil-repeat-keys nil ; causes problems with remapped ; + evil-snipe-repeat-keys nil ; causes problems with remapped ; + evil-snipe-override-evil-repeat-keys nil evil-snipe-char-fold t evil-snipe-aliases '((?\[ "[[{(]") (?\] "[]})]") @@ -387,6 +393,8 @@ if on a delimiter, jump to the matching one (`evilmi-jump-items')." :config (@set :evil-state 'neotree-mode 'motion) + (push neo-buffer-name winner-boring-buffers) + ;; Adding keybindings to `neotree-mode-map' wouldn't work for me (they get ;; overridden when the neotree buffer is spawned). So we bind them in a hook. (add-hook 'neo-after-create-hook '+evil|neotree-init-keymap)