From df0d1f6ab67fcd9333b0749c773260817672e0b7 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 31 Jan 2017 19:50:02 -0500 Subject: [PATCH] Add modules/core/evil --- modules/core/evil/autoload.el | 293 ++++++++++++++++++++++++ modules/core/evil/config.el | 413 ++++++++++++++++++++++++++++++++++ modules/core/evil/packages.el | 20 ++ 3 files changed, 726 insertions(+) create mode 100644 modules/core/evil/autoload.el create mode 100644 modules/core/evil/config.el create mode 100644 modules/core/evil/packages.el diff --git a/modules/core/evil/autoload.el b/modules/core/evil/autoload.el new file mode 100644 index 000000000..f45bef4dc --- /dev/null +++ b/modules/core/evil/autoload.el @@ -0,0 +1,293 @@ + +;;;###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) + (dolist (match (s-match-strings-all regexp file-name)) + (let ((flags (split-string (caddr match) ":" t)) + (path (file-relative-name + (pcase (cadr match) + ("@" (doom-project-root)) + ("%" (buffer-file-name)) + ("#" (and (other-buffer) (buffer-file-name (other-buffer))))) + default-directory)) + flag global) + (when 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))) + (setq path + (or (pcase (substring flag 0 1) + ("p" (expand-file-name path)) + ("~" (file-relative-name path "~")) + ("." (file-relative-name path default-directory)) + ("h" (directory-file-name path)) + ("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)) + (_ path)) + ""))) + (setq file-name + (replace-regexp-in-string (format "\\(?:^\\|[^\\\\]\\)\\(%s\\)" + (s-trim-left (car match))) + path file-name t t 1))))) + (setq file-name (replace-regexp-in-string regexp "\\1" file-name t)))) + + +;; +;; Custom argument handlers +;; + +(defvar +evil--buffer-match-global evil-ex-substitute-global "") + +(defun +evil--ex-match-init (name &optional face update-hook) + (with-current-buffer evil-ex-current-buffer + (cond + ((eq flag 'start) + (evil-ex-make-hl name + :face (or face 'evil-ex-substitute-matches) + :update-hook (or update-hook #'evil-ex-pattern-update-ex-info)) + (setq flag 'update)) + + ((eq flag 'stop) + (evil-ex-delete-hl name))))) + +(defun +evil--ex-buffer-match (arg &optional hl-name flags beg end) + (when (and (eq flag 'update) + evil-ex-substitute-highlight-all + (not (zerop (length arg)))) + (condition-case lossage + (let ((pattern (evil-ex-make-substitute-pattern + (if evil-ex-bang (regexp-quote arg) arg) + (or flags (list)))) + (range (or (evil-copy-range evil-ex-range) + (evil-range (or beg (line-beginning-position)) + (or end (line-end-position)) + 'line + :expanded t)))) + (evil-expand-range range) + (evil-ex-hl-set-region hl-name + (max (evil-range-beginning range) (window-start)) + (min (evil-range-end range) (window-end))) + (evil-ex-hl-change hl-name pattern)) + (end-of-file + (evil-ex-pattern-update-ex-info nil "incomplete replacement")) + (user-error + (evil-ex-pattern-update-ex-info nil (format "?%s" lossage)))))) + +;;;###autoload +(defun +evil-ex-buffer-match (flag &optional arg) + (let ((hl-name 'evil-ex-buffer-match)) + (with-selected-window (minibuffer-selected-window) + (+evil--ex-match-init hl-name) + (+evil--ex-buffer-match arg hl-name (list (if +evil--buffer-match-global ?g)))))) + +;;;###autoload +(defun +evil-ex-global-match (flag &optional arg) + (let ((hl-name 'evil-ex-global-match)) + (with-selected-window (minibuffer-selected-window) + (+evil--ex-match-init hl-name) + (let ((result (car-safe (evil-ex-parse-global arg)))) + (+evil--ex-buffer-match result hl-name nil (point-min) (point-max)))))) + +;;;###autoload +(defun +evil-window-move (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 +window there. If there are no windows and this isn't the only window, use +evil-window-move-* (e.g. `evil-window-move-far-left')" + (let* ((this-window (get-buffer-window)) + (this-buffer (current-buffer)) + (that-window (windmove-find-other-window direction nil this-window)) + (that-buffer (window-buffer that-window))) + (when (or (minibufferp that-buffer) + (doom-popup-p that-window)) + (setq that-buffer nil that-window nil)) + (if (not (or that-window (one-window-p t))) + (funcall (case direction + ('left 'evil-window-move-far-left) + ('right 'evil-window-move-far-right) + ('up 'evil-window-move-very-top) + ('down 'evil-window-move-very-bottom))) + (unless that-window + (setq that-window + (split-window this-window nil (cond ((eq direction 'up) 'above) + ((eq direction 'down) 'below) + (t direction)))) + (with-selected-window that-window + (switch-to-buffer doom-buffer)) + (setq that-buffer (window-buffer that-window))) + (with-selected-window this-window + (switch-to-buffer that-buffer)) + (with-selected-window that-window + (switch-to-buffer this-buffer)) + (select-window that-window)))) + +;; Register keywords for proper indentation (see `map!') +(put ':prefix 'lisp-indent-function 'defun) +(put ':map 'lisp-indent-function 'defun) +(put ':map* 'lisp-indent-function 'defun) +(put ':after 'lisp-indent-function 'defun) +(put ':when 'lisp-indent-function 'defun) +(put ':unless 'lisp-indent-function 'defun) +(put ':leader 'lisp-indent-function 'defun) +(put ':localleader 'lisp-indent-function 'defun) + +;;;###autoload +(defmacro map! (&rest rest) + "A nightmare of a key-binding macro that will use `evil-define-key', +`evil-define-key*', `define-key' and `global-set-key' depending on context and +plist key flags. It was designed to make binding multiple keys more concise, +like in vim. + +Yes, it tries to do too much. Yes, I only did it to make the \"frontend\" config +that little bit more concise. Yes, I could simply have used the above functions. +But it takes a little insanity to custom write your own emacs.d, so what else +were you expecting? + +States + :n normal + :v visual + :i insert + :e emacs + :o operator + :m motion + :r replace + :L local + + These can be combined (order doesn't matter), e.g. :nvi will apply to + normal, visual and insert mode. The state resets after the following + key=>def pair. + + Capitalize the state flag to make it a local binding. + + If omitted, the keybind will be defined globally. + +Flags + :unset [KEY] ; unset key + (:map [KEYMAP] [...]) ; apply inner keybinds to KEYMAP + (:map* [KEYMAP] [...]) ; apply inner keybinds to KEYMAP (deferred) + (:prefix [PREFIX] [...]) ; assign prefix to all inner keybindings + (:after [FEATURE] [...]) ; apply keybinds when [FEATURE] loads + +Conditional keybinds + (:when [CONDITION] [...]) + (:unless [CONDITION] [...]) + +Example + (map! :map magit-mode-map + :m \"C-r\" 'do-something ; assign C-r in motion state + :nv \"q\" 'magit-mode-quit-window ; assign to 'q' in normal and visual states + \"C-x C-r\" 'a-global-keybind + + (:when IS-MAC + :n \"M-s\" 'some-fn + :i \"M-o\" (lambda (interactive) (message \"Hi\"))))" + (let ((keymaps (if (boundp 'keymaps) keymaps)) + (defer (if (boundp 'defer) defer)) + (prefix (if (boundp 'prefix) prefix)) + (state-map '(("n" . normal) + ("v" . visual) + ("i" . insert) + ("e" . emacs) + ("o" . operator) + ("m" . motion) + ("r" . replace))) + local key def states forms) + (while rest + (setq key (pop rest)) + (cond + ;; it's a sub expr + ((listp key) + (push (macroexpand `(map! ,@key)) forms)) + + ;; it's a flag + ((keywordp key) + (when (cond ((eq key :leader) + (push +evil-leader rest)) + ((eq key :localleader) + (push +evil-localleader rest))) + (setq key :prefix)) + (pcase key + (:prefix (setq prefix (concat prefix (kbd (pop rest))))) + (:map (setq keymaps (-list (pop rest)))) + (:map* (setq defer t keymaps (-list (pop rest)))) + (:unset `(,(macroexpand `(map! ,(kbd (pop rest)))))) + (:after (prog1 `((after! ,(pop rest) ,(macroexpand `(map! ,@rest)))) (setq rest '()))) + (:when (prog1 `((if ,(pop rest) ,(macroexpand `(map! ,@rest)))) (setq rest '()))) + (:unless (prog1 `((if (not ,(pop rest)) ,(macroexpand `(map! ,@rest)))) (setq rest '()))) + (otherwise ; might be a state prefix + (mapc (lambda (letter) + (cond ((assoc letter state-map) + (push (cdr (assoc letter state-map)) states)) + ((string= letter "L") + (setq local t)) + (t (user-error "Invalid mode prefix %s in key %s" letter key)))) + (split-string (substring (symbol-name key) 1) "" t)) + (unless states + (user-error "Unrecognized keyword %s" key)) + (when (assoc "L" states) + (cond ((= (length states) 1) + (user-error "local keybinding for %s must accompany another state" key)) + ((> (length keymaps) 0) + (user-error "local keybinding for %s cannot accompany a keymap" key))))))) + + ;; It's a key-def pair + ((or (stringp key) + (characterp key) + (vectorp key)) + (when (stringp key) + (setq key (kbd key))) + (when prefix + (setq key (cond ((vectorp key) (vconcat prefix key)) + (t (concat prefix key))))) + (unless (> (length rest) 0) + (user-error "Map has no definition for %s" key)) + (setq def (pop rest)) + (push + (cond ((and keymaps states) + (macroexp-progn + (mapcar (lambda (keymap) + `(,(if defer 'evil-define-key 'evil-define-key*) + ',states ,keymap ,key ,def)) + keymaps))) + (keymaps + (macroexp-progn + (mapcar (lambda (keymap) + `(define-key ,keymap ,key ,def)) + keymaps))) + (states + (macroexp-progn + (mapcar (lambda (state) + `(define-key + (evil-state-property ',state ,(if local :local-keymap :keymap) t) + ,key ,def)) + states))) + (t `(,(if local 'local-set-key 'global-set-key) + ,key ,def))) + forms) + (setq states '() + local nil)) + + (t (user-error "Invalid key %s" key)))) + (macroexp-progn (reverse forms)))) + +(when noninteractive + (defmacro map! (&rest rest))) diff --git a/modules/core/evil/config.el b/modules/core/evil/config.el new file mode 100644 index 000000000..2453928d0 --- /dev/null +++ b/modules/core/evil/config.el @@ -0,0 +1,413 @@ +;;; core-evil.el --- come to the dark side, we have cookies + +;; 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.") + + +;; +;; evil-mode +;; + +(use-package evil + :demand t + :init + (setq evil-want-C-u-scroll t + evil-want-visual-char-semi-exclusive t + evil-want-fine-undo nil + evil-want-Y-yank-to-eol t + evil-magic t + evil-echo-state t + evil-ex-interactive-search-highlight 'selected-window + evil-ex-search-vim-style-regexp t + evil-ex-substitute-global t + evil-ex-visual-char-range t ; column range for ex commands + evil-insert-skip-empty-lines t) + + :config + (defpopup! + ("*evil-registers*" :size 0.3) + ("*Command Line*" :size 8)) + + (evil-mode +1) + (evil-select-search-module 'evil-search-module 'evil-search) + + (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)))) + +;;; Private macros +(defsubst +evil--textobj! (key inner-fn &optional outer-fn) + "Define a text object." + (define-key evil-inner-text-objects-map key inner-fn) + (define-key evil-outer-text-objects-map key (or outer-fn inner-fn))) + +;; Shortcuts for the evil expression register +(defmacro $= (str &rest args) `(calc-eval (format ,str ,@args))) +(defmacro $r (char) `(evil-get-register ,char)) +(defmacro $expand (path) `(evil-ex-replace-special-filenames ,path)) + + +;; +;; evil hacks +;; + +(defun +evil*esc () + "Disable search highlights and quit the minibuffer if open." + (when (minibuffer-window-active-p (minibuffer-window)) + (abort-recursive-edit)) + (when (evil-ex-hl-active-p 'evil-ex-search) + (evil-ex-nohighlight))) +(advice-add 'evil-force-normal-state :after '+evil*esc) + +;; 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 +;; TODO Delete timer on dead windows? +;; (defun doom*ignore-errors (orig-fn &rest args) +;; (ignore-errors (apply orig-fn args))) +;; (advice-add 'evil-ex-hl-do-update-highlight :around 'doom*ignore-errors) + +;; 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) + +;; Add extra argument types that highlight matches in the current buffer. +(evil-ex-define-argument-type buffer-match :runner doom-evil-ex-buffer-match) +(evil-ex-define-argument-type global-match :runner doom-evil-ex-global-match) + +(evil-define-interactive-code "" + :ex-arg buffer-match (list (when (evil-ex-p) evil-ex-argument))) +(evil-define-interactive-code "" + :ex-arg global-match (when (evil-ex-p) (evil-ex-parse-global evil-ex-argument))) + +(evil-define-operator +evil:global (beg end pattern command &optional invert) + "Rewritten :g[lobal] that will highlight buffer matches. Takes the same arguments." + :motion mark-whole-buffer :move-point nil + (interactive "") + (evil-ex-global beg end pattern command invert)) + +(evil-define-operator +evil:align (&optional beg end bang pattern) + "Ex interface to `align-regexp'. Accepts vim-style regexps." + (interactive "") + (align-regexp + beg end + (concat "\\(\\s-*\\)" + (if bang + (regexp-quote pattern) + (evil-transform-vim-style-regexp pattern))) + 1 1)) + + +;; +;; Plugins +;; + +(use-package evil-anzu + :init + ;; evil-anzu is strangely slow on startup. Byte compiling doesn't help. We use + ;; this to lazy load it instead. + ;; (defun doom*evil-search (&rest _) + ;; (require 'evil-anzu) + ;; (advice-remove 'evil-ex-start-search 'doom*evil-search)) + ;; (advice-add 'evil-ex-start-search :before 'doom*evil-search) + + :config + (setq anzu-cons-mode-line-p nil + anzu-minimum-input-length 1 + anzu-search-threshold 250)) + + +(use-package evil-args + :commands (evil-inner-arg evil-outer-arg evil-forward-arg evil-backward-arg + evil-jump-out-args) + :init + (+evil--textobj! "a" 'evil-inner-arg 'evil-outer-arg)) + + +(use-package evil-commentary + :commands (evil-commentary evil-commentary-yank evil-commentary-line) + :config! (evil-commentary-mode 1)) + + +(use-package evil-easymotion + :defer 1 + :config + (defvar doom--evil-snipe-repeat-fn) + + (evilem-default-keybindings "g SPC") + (evilem-define (kbd "g SPC n") 'evil-ex-search-next) + (evilem-define (kbd "g SPC N") 'evil-ex-search-previous) + (evilem-define "gs" 'evil-snipe-repeat + :pre-hook (save-excursion (call-interactively #'evil-snipe-s)) + :bind ((evil-snipe-scope 'buffer) + (evil-snipe-enable-highlight) + (evil-snipe-enable-incremental-highlight))) + (evilem-define "gS" 'evil-snipe-repeat-reverse + :pre-hook (save-excursion (call-interactively #'evil-snipe-s)) + :bind ((evil-snipe-scope 'buffer) + (evil-snipe-enable-highlight) + (evil-snipe-enable-incremental-highlight))) + + (setq doom--evil-snipe-repeat-fn + (evilem-create 'evil-snipe-repeat + :bind ((evil-snipe-scope 'whole-buffer) + (evil-snipe-enable-highlight) + (evil-snipe-enable-incremental-highlight))))) + + +(use-package evil-embrace + :after evil-surround + :config + (setq evil-embrace-show-help-p nil) + (evil-embrace-enable-evil-surround-integration) + + ;; 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)))) + + (defun +evil--embrace-escaped () + "Backslash-escaped surround character support for embrace." + (let ((char (read-char "\\"))) + (if (eq char 27) + (cons "" "") + (let ((pair (+evil--embrace-get-pair (string char))) + (text (if (sp-point-in-string) "\\\\%s" "\\%s"))) + (cons (format text (car pair)) + (format text (cdr pair))))))) + + (defun +evil--embrace-latex () + "LaTeX command support for embrace." + (cons (format "\\%s{" (read-string "\\")) "}")) + + (defun +evil--embrace-elisp-fn () + "Elisp function support for embrace." + (cons (format "(%s " (or (read-string "(") "")) ")")) + + ;; Add escaped-sequence support to embrace + (push (cons ?\\ (make-embrace-pair-struct + :key ?\\ + :read-function '+evil--embrace-escaped + :left-regexp "\\[[{(]" + :right-regexp "\\[]})]")) + (default-value 'embrace--pairs-list)) + + ;; Add extra pairs + (add-hook 'LaTeX-mode-hook 'embrace-LaTeX-mode-hook) + (add-hook 'org-mode-hook 'embrace-org-mode-hook) + (add-hook! emacs-lisp-mode + (embrace-add-pair ?\` "`" "'")) + (add-hook! (emacs-lisp-mode lisp-mode) + (embrace-add-pair-regexp ?f "([^ ]+ " ")" '+evil--embrace-elisp-fn)) + (add-hook! (org-mode latex-mode) + (embrace-add-pair-regexp ?l "\\[a-z]+{" "}" '+evil--embrace-latex))) + + +(use-package evil-escape + :commands evil-escape-mode + :init + (defun +evil|escape-disable () (evil-escape-mode -1)) + (defun +evil|escape-enable () (evil-escape-mode +1)) + ;; I only need evil-escape in insert and replace modes. + (add-hook 'evil-insert-state-entry-hook '+evil|escape-enable) + (add-hook 'evil-insert-state-exit-hook '+evil|escape-disable) + (add-hook 'evil-replace-state-entry-hook '+evil|escape-enable) + (add-hook 'evil-replace-state-exit-hook '+evil|escape-disable) + :config + (setq evil-escape-key-sequence "jk" + evil-escape-delay 0.25)) + + +(use-package evil-exchange + :commands evil-exchange + :config + (defun +evil*exchange-off () + (if evil-exchange--overlays (evil-exchange-cancel))) + (advice-add 'evil-force-normal-state :after '+evil*exchange-off)) + + +(use-package evil-indent-plus + :commands (evil-indent-plus-i-indent + evil-indent-plus-a-indent + evil-indent-plus-i-indent-up + evil-indent-plus-a-indent-up + evil-indent-plus-i-indent-up-down + evil-indent-plus-a-indent-up-down) + :init + (+evil--textobj! "i" 'evil-indent-plus-i-indent 'evil-indent-plus-a-indent) + (+evil--textobj! "I" 'evil-indent-plus-i-indent-up 'evil-indent-plus-a-indent-up) + (+evil--textobj! "J" 'evil-indent-plus-i-indent-up-down 'evil-indent-plus-a-indent-up-down)) + + +(use-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)))) + + +(use-package evil-multiedit + :commands (evil-multiedit-match-all + evil-multiedit-match-and-next + evil-multiedit-match-and-prev + evil-multiedit-match-symbol-and-next + evil-multiedit-match-symbol-and-prev + evil-multiedit-toggle-or-restrict-region + evil-multiedit-next + evil-multiedit-prev + evil-multiedit-abort + evil-multiedit-ex-match) + :config + (evil-multiedit-default-keybinds)) + + +(use-package evil-textobj-anyblock + :commands (evil-numbers/inc-at-pt evil-numbers/dec-at-pt) + :init + (+evil--textobj! "B" 'evil-textobj-anyblock-inner-block 'evil-textobj-anyblock-a-block)) + + +(use-package evil-search-highlight-persist + :demand t + :commands (evil-textobj-anyblock-inner-block evil-textobj-anyblock-a-block) + :config + (global-evil-search-highlight-persist t) + (advice-add 'evil-force-normal-state :after 'evil-search-highlight-persist-remove-all)) + + +(use-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-char-fold t + evil-snipe-aliases '((?\[ "[[{(]") + (?\] "[]})]") + (?\; "[;:]"))) + + :config + (evil-snipe-mode 1) + (evil-snipe-override-mode 1) + ;; Switch to evil-easymotion/avy after first snipe + (define-key evil-snipe-parent-transient-map "\C-;" + (λ! (require 'evil-easymotion) + (call-interactively doom--evil-snipe-repeat-fn)))) + + +(use-package evil-surround + :commands (global-evil-surround-mode + evil-surround-edit + evil-Surround-edit + evil-surround-region) + :config (global-evil-surround-mode 1)) + + +(use-package evil-visualstar + :commands (global-evil-visualstar-mode + evil-visualstar/begin-search + evil-visualstar/begin-search-forward + evil-visualstar/begin-search-backward) + :config (global-evil-visualstar-mode 1)) + + +;; A side-panel for browsing my project files. Inspired by vim's NERDTree. +(use-package neotree + :commands (neotree-show + neotree-hide + neotree-toggle + neotree-dir + neotree-find + neo-global--with-buffer + neo-global--window-exists-p) + :init + (setq neo-create-file-auto-open t + neo-auto-indent-point nil + neo-mode-line-type 'none + neo-persist-show nil + neo-window-width 25 + neo-show-updir-line nil + neo-theme 'nerd ; fallback + neo-banner-message nil + neo-show-hidden-files nil + neo-hidden-regexp-list + '(;; vcs folders + "^\\.\\(git\\|hg\\|svn\\)$" + ;; compiled files + "\\.\\(pyc\\|o\\|elc\\|lock\\|css.map\\)$" + ;; generated files, caches or local pkgs + "^\\(node_modules\\|vendor\\|.\\(project\\|cask\\|yardoc\\|sass-cache\\)\\)$" + ;; org-mode folders + "^\\.\\(sync\\|export\\|attach\\)$" + "~$" + "^#.*#$")) + + :config + (evil-set-initial-state 'neotree-mode 'motion) + + ;; 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 'doom|neotree-init-keymap) + (defun doom|neotree-init-keymap (&rest _) + (let ((map evil-motion-state-local-map)) + (define-key map (kbd "\\\\") 'evil-window-prev) + (define-key map (kbd "RET") 'neotree-enter) + (define-key map (kbd "") 'neotree-enter) + (define-key map (kbd "ESC ESC") 'neotree-hide) + (define-key map [return] 'neotree-enter) + (define-key map "q" 'neotree-hide) + (define-key map "J" 'neotree-select-next-sibling-node) + (define-key map "K" 'neotree-select-previous-sibling-node) + (define-key map "H" 'neotree-select-up-node) + (define-key map "L" 'neotree-select-down-node) + (define-key map "v" 'neotree-enter-vertical-split) + (define-key map "s" 'neotree-enter-horizontal-split) + (define-key map "c" 'neotree-create-node) + (define-key map "d" 'neotree-delete-node) + (define-key map "\C-r" 'neotree-refresh) + (define-key map "r" 'neotree-rename-node) + (define-key map "R" 'neotree-change-root)))) + diff --git a/modules/core/evil/packages.el b/modules/core/evil/packages.el new file mode 100644 index 000000000..5b2d3ab54 --- /dev/null +++ b/modules/core/evil/packages.el @@ -0,0 +1,20 @@ +;;; packages.el + +(package! evil) +(package! evil-anzu) +(package! evil-args) +(package! evil-commentary) +(package! evil-easymotion) +(package! evil-embrace) +(package! evil-escape) +(package! evil-exchange) +(package! evil-indent-plus) +(package! evil-matchit) +(package! evil-multiedit) +(package! evil-numbers) +(package! evil-textobj-anyblock) +(package! evil-search-highlight-persist) +(package! evil-snipe) +(package! evil-surround) +(package! evil-visualstar) +(package! neotree)