Moving to Doom Emacs!

This commit is contained in:
Derek Taylor
2019-12-16 20:21:19 -06:00
parent d9f2f456f1
commit d4b4c33550
683 changed files with 51877 additions and 100 deletions

View File

@@ -0,0 +1,102 @@
;;; editor/evil/+commands.el -*- lexical-binding: t; -*-
;;
;;; Custom commands
;; Editing
(evil-ex-define-cmd "@" #'+evil:macro-on-all-lines) ; TODO Test me
(evil-ex-define-cmd "R[ead]" #'+evil:read)
(evil-ex-define-cmd "al[ign]" #'+evil:align)
(evil-ex-define-cmd "ral[ign]" #'+evil:align-right)
(evil-ex-define-cmd "enhtml" #'+web:encode-html-entities)
(evil-ex-define-cmd "dehtml" #'+web:decode-html-entities)
(evil-ex-define-cmd "mc" #'+multiple-cursors:evil-mc)
(evil-ex-define-cmd "iedit" #'evil-multiedit-ex-match)
(evil-ex-define-cmd "na[rrow]" #'+evil:narrow-buffer)
(evil-ex-define-cmd "retab" #'+evil:retab)
(evil-ex-define-cmd "rev[erse]" #'+evil:reverse-lines)
(evil-ex-define-cmd "l[ine]diff" #'evil-quick-diff)
;;; External resources
;; TODO (evil-ex-define-cmd "db" #'doom:db)
;; TODO (evil-ex-define-cmd "dbu[se]" #'doom:db-select)
;; TODO (evil-ex-define-cmd "go[ogle]" #'doom:google-search)
(evil-ex-define-cmd "lo[okup]" #'+lookup:online)
(evil-ex-define-cmd "dash" #'+lookup:dash)
(evil-ex-define-cmd "http" #'httpd-start) ; start http server
(evil-ex-define-cmd "repl" #'+eval:repl) ; invoke or send to repl
(evil-ex-define-cmd "h[elp]" #'+evil:help)
;; TODO (evil-ex-define-cmd "rx" 'doom:regex) ; open re-builder
(evil-ex-define-cmd "sh[ell]" #'+eshell:run)
(evil-ex-define-cmd "t[mux]" #'+tmux:run) ; send to tmux
(evil-ex-define-cmd "tcd" #'+tmux:cd-here) ; cd to default-directory in tmux
(evil-ex-define-cmd "pad" #'+evil:open-scratch-buffer)
;;; GIT
(evil-ex-define-cmd "gist" #'+gist:send) ; send current buffer/region to gist
(evil-ex-define-cmd "gistl" #'+gist:list) ; list gists by user
(evil-ex-define-cmd "gbrowse" #'+vc:git-browse) ; show file/region in github/gitlab
(evil-ex-define-cmd "gissues" #'forge-browse-issues) ; show github issues
(evil-ex-define-cmd "git" #'magit-status) ; open magit status window
(evil-ex-define-cmd "gstage" #'magit-stage)
(evil-ex-define-cmd "gunstage" #'magit-unstage)
(evil-ex-define-cmd "gblame" #'magit-blame)
(evil-ex-define-cmd "grevert" #'git-gutter:revert-hunk)
;;; Dealing with buffers
(evil-ex-define-cmd "k[ill]" #'doom/kill-current-buffer)
(evil-ex-define-cmd "k[ill]all" #'+evil:kill-all-buffers)
(evil-ex-define-cmd "k[ill]m" #'+evil:kill-matching-buffers)
(evil-ex-define-cmd "k[ill]o" #'doom/kill-other-buffers)
(evil-ex-define-cmd "k[ill]b" #'doom/kill-buried-buffers)
(evil-ex-define-cmd "l[ast]" #'doom/popup-restore)
(evil-ex-define-cmd "messages" #'view-echo-area-messages)
(evil-ex-define-cmd "pop[up]" #'doom/popup-this-buffer)
;;; Project navigation
(evil-ex-define-cmd "a" #'projectile-find-other-file)
(evil-ex-define-cmd "cd" #'+evil:cd)
(evil-ex-define-cmd "pwd" #'+evil:pwd)
(evil-define-command +evil:swiper (&optional search)
"Invoke `swiper' with SEARCH, otherwise with the symbol at point."
(interactive "<a>")
(swiper-isearch search))
(evil-ex-define-cmd "sw[iper]" #'+evil:swiper)
(cond ((featurep! :completion ivy)
(evil-ex-define-cmd "pg[rep]" #'+ivy:project-search)
(evil-ex-define-cmd "pg[grep]d" #'+ivy:project-search-from-cwd))
((featurep! :completion helm)
(evil-ex-define-cmd "pg[rep]" #'+helm:project-search)
(evil-ex-define-cmd "pg[grep]d" #'+helm:project-search-from-cwd)))
;;; Project tools
(evil-ex-define-cmd "compile" #'+evil:compile)
(evil-ex-define-cmd "mak[e]" #'+evil:make)
(evil-ex-define-cmd "debug" #'+debugger/start)
(evil-ex-define-cmd "er[rors]" #'flycheck-list-errors)
;;; File operations
(evil-ex-define-cmd "cp" #'+evil:copy-this-file)
(evil-ex-define-cmd "mv" #'+evil:move-this-file)
(evil-ex-define-cmd "rm" #'+evil:delete-this-file)
;;; Sessions/tabs
(evil-ex-define-cmd "sclear" #'+workspace/kill-session)
(evil-ex-define-cmd "sl[oad]" #'doom/quickload-session)
(evil-ex-define-cmd "ss[ave]" #'doom/quicksave-session)
(evil-ex-define-cmd "tabc[lose]" #'+workspace:delete)
(evil-ex-define-cmd "tabclear" #'doom/kill-all-buffers)
(evil-ex-define-cmd "tabl[ast]" #'+workspace/switch-to-last)
(evil-ex-define-cmd "tabload" #'+workspace:load)
(evil-ex-define-cmd "tabn[ew]" #'+workspace:new)
(evil-ex-define-cmd "tabn[ext]" #'+workspace:switch-next)
(evil-ex-define-cmd "tabp[rev]" #'+workspace:switch-previous)
(evil-ex-define-cmd "tabr[ename]" #'+workspace:rename)
(evil-ex-define-cmd "tabs" #'+workspace/display)
(evil-ex-define-cmd "tabsave" #'+workspace:save)
;;; Org-mode
(evil-ex-define-cmd "cap" #'org-capture)

View File

@@ -0,0 +1,238 @@
;;; editor/evil/+everywhere.el -*- lexical-binding: t; -*-
;; We load evil-collection ourselves for these reasons:
;;
;; 1. To truly lazy load it. Some of its modules, like
;; evil-collection-{elisp-mode,buff-menu} are loaded immediately, because
;; Emacs loads their packages immediately, which pulls in all of
;; evil-collection (and other packages with it, sometimes).
;; 2. This ensures a predictable load order, versus lazy loading using :defer or
;; :after-call. This means users can use (after! org ...) and be sure that
;; their changes will override evil-collection's.
;; 3. Ideally, we'd do away with evil-collection entirely. It changes too often,
;; introduces breaking bugs too frequently, and I don't agree with all their
;; design choices. Regardless, it does mork than it causes trouble, so it may
;; be here to stay.
;; 4. Adds `+evil-collection-disabled-list', to make it easier for users to
;; disable modules, and to reduce the effort required to maintain our copy of
;; `evil-collection-list' (now I can just copy it from time to time).
(defvar +evil-collection-disabled-list
'(anaconda-mode
buff-menu
comint
company
custom
eldoc
elisp-mode
ert
free-keys
help
helm
image
kotlin-mode
occur
package-menu
ruby-mode
simple
slime)
"A list of `evil-collection' modules to ignore. See the definition of this
variable for an explanation of the defaults (in comments). See
`evil-collection-mode-list' for a list of available options.")
(defvar evil-collection-setup-minibuffer nil)
;; We do this ourselves, and better.
(defvar evil-collection-want-unimpaired-p nil)
;; This has to be defined here since evil-collection doesn't autoload its own.
;; It must be updated whenever evil-collection updates theirs. Here's an easy
;; way to update it:
;;
;; (with-current-buffer
;; (url-retrieve-synchronously "https://raw.githubusercontent.com/emacs-evil/evil-collection/master/evil-collection.el" t t)
;; (goto-char (point-min))
;; (when (re-search-forward "^(defvar evil-collection--supported-modes\n[^(]+")
;; (let ((list (sexp-at-point)))
;; ;; Fixes
;; (when (assq 'pdf list)
;; (setf (alist-get 'pdf list) '(pdf-tools)))
;; (kill-new (prin1-to-string list)))))
(defvar evil-collection-mode-list
`(2048-game
ag
alchemist
anaconda-mode
apropos
arc-mode
bookmark
(buff-menu "buff-menu")
calc
calendar
cider
cmake-mode
comint
company
compile
(custom cus-edit)
cus-theme
daemons
deadgrep
debbugs
debug
diff-mode
dired
disk-usage
doc-view
docker
ebib
edbi
edebug
ediff
eglot
elfeed
elisp-mode
elisp-refs
elisp-slime-nav
emms
epa
ert
eshell
eval-sexp-fu
evil-mc
eww
flycheck
flymake
free-keys
geiser
ggtags
git-timemachine
go-mode
grep
guix
hackernews
helm
help
helpful
hg-histedit
hungry-delete
ibuffer
image
image-dired
image+
imenu-list
indium
info
ivy
js2-mode
leetcode
log-edit
log-view
lsp-ui-imenu
lua-mode
kotlin-mode
macrostep
man
magit
magit-todos
,@(if evil-collection-setup-minibuffer '(minibuffer))
monky
mu4e
mu4e-conversation
neotree
notmuch
nov
(occur replace)
omnisharp
outline
p4
(package-menu package)
pass
(pdf pdf-tools)
popup
proced
process-menu
prodigy
profiler
python
quickrun
racer
realgud
reftex
restclient
rjsx-mode
robe
rtags
ruby-mode
simple
slime
sly
tablist
(term term ansi-term multi-term)
tetris
tide
transmission
typescript-mode
vc-annotate
vc-dir
vc-git
vdiff
view
vlf
vterm
w3m
wdired
wgrep
which-key
woman
xref
youtube-dl
(ztree ztree-diff)))
(defun +evil-collection-init (module &optional disabled-list)
"Initialize evil-collection-MODULE.
Unlike `evil-collection-init', this respects `+evil-collection-disabled-list',
and complains if a module is loaded too early (during startup)."
(unless (memq (or (car-safe module) module) disabled-list)
(doom-log "Initialized evil-collection-%s %s"
(or (car-safe module) module)
(if doom-init-time "" "(too early!)"))
(with-demoted-errors "evil-collection error: %s"
(evil-collection-init (list module)))))
;;
;;; Bootstrap
;; These modes belong to packages that Emacs always loads at startup, causing
;; evil-collection to load immediately. We avoid this by loading them after
;; evil-collection has first loaded...
(with-eval-after-load 'evil-collection
(mapc #'+evil-collection-init '(comint custom help)))
;; ...or on first invokation of their associated major/minor modes.
(add-transient-hook! 'Buffer-menu-mode
(+evil-collection-init '(buff-menu "buff-menu")))
(add-transient-hook! 'image-mode
(+evil-collection-init 'image))
(add-transient-hook! 'emacs-lisp-mode
(+evil-collection-init 'elisp-mode))
(add-transient-hook! 'occur-mode
(+evil-collection-init 'replace))
(evil-define-key* 'normal process-menu-mode-map
"q" #'kill-current-buffer
"d" #'process-menu-delete-process)
;; Don't overwrite the leader keys
(setq evil-collection-key-blacklist
(list doom-leader-key doom-localleader-key
doom-leader-alt-key doom-localleader-alt-key))
;; Load the rest
(dolist (mode evil-collection-mode-list)
(dolist (req (or (cdr-safe mode) (list mode)))
(with-eval-after-load req
(+evil-collection-init mode +evil-collection-disabled-list))))

View File

@@ -0,0 +1,176 @@
#+TITLE: feature/evil
#+DATE: February 2, 2017
#+SINCE: v2.0
#+STARTUP: inlineimages
* Table of Contents :TOC_3:noexport:
- [[#description][Description]]
- [[#module-flags][Module Flags]]
- [[#plugins][Plugins]]
- [[#hacks][Hacks]]
- [[#prerequisites][Prerequisites]]
- [[#features][Features]]
- [[#ported-vim-plugins][Ported vim plugins]]
- [[#custom-text-objects][Custom Text Objects]]
- [[#custom-ex-commands][Custom Ex Commands]]
- [[#configuration][Configuration]]
- [[#removing-evil-mode][Removing evil-mode]]
- [[#restoring-old-substitution-behavior-on-ss][Restoring old substitution behavior on s/S]]
* Description
This holy module brings the vim experience to Emacs.
** Module Flags
+ =+everywhere= Enables evilified keybinds everywhere possible. Uses the
[[https://github.com/emacs-evil/evil-collection][evil-collection]] plugin as a foundation.
** Plugins
+ [[https://github.com/emacs-evil/evil][evil]]
+ [[https://github.com/wcsmith/evil-args][evil-args]]
+ [[https://github.com/PythonNut/evil-easymotion][evil-easymotion]]
+ [[https://github.com/cute-jumper/evil-embrace.el][evil-embrace]]
+ [[https://github.com/syl20bnr/evil-escape][evil-escape]]
+ [[https://github.com/Dewdrops/evil-exchange][evil-exchange]]
+ [[https://github.com/TheBB/evil-indent-plus][evil-indent-plus]]
+ [[https://github.com/redguardtoo/evil-nerd-commenter][evil-nerd-commentary]]
+ [[https://github.com/redguardtoo/evil-matchit][evil-matchit]]
+ [[https://github.com/cofi/evil-numbers][evil-numbers]]
+ [[https://github.com/noctuid/evil-textobj-anyblock][evil-textobj-anyblock]]
+ [[https://github.com/hlissner/evil-snipe][evil-snipe]]
+ [[https://github.com/emacs-evil/evil-surround][evil-surround]]
+ [[https://github.com/alexmurray/evil-vimish-fold][evil-vimish-fold]]
+ [[https://github.com/bling/evil-visualstar][evil-visualstar]]
+ [[https://github.com/ninrod/exato][exato]]
+ [[https://github.com/emacs-evil/evil-collection][evil-collection]]*
+ [[https://www.github.com/rgrinberg/evil-quick-diff][evil-quick-diff]]
** Hacks
+ The o/O keys will respect and continue commented lines (can be disabled by
setting ~+evil-want-o/O-to-continue-comments~ to ~nil~).
+ In visual mode, =*= and =#= will search for the current selection instead of
the word-at-point.
+ The ~:g[lobal]~ ex command has been modified to highlight matches.
+ More of vim's filename modifiers are supported in ex commands (like ~:p~,
~:p:h~ or ~:t~) than vanilla evil-mode offers.
+ A custom filename modifier is available in Doom: ~:P~, which expands to the
project root (throws an error if not in a project).
* Prerequisites
This module has no external prerequisites.
* Features
** Ported vim plugins
The following vim plugins have been ported to evil:
| Vim Plugin | Emacs Plugin | Keybind(s) |
|-----------------------+--------------------------------+--------------------------------------------|
| vim-commentary | evil-nerd-commenter | omap =gc= |
| vim-easymotion | evil-easymotion | omap =gs= |
| vim-seek or vim-sneak | evil-snipe | mmap =s= / =S=, omap =z= / =Z= & =x= / =X= |
| vim-surround | evil-embrace and evil-surround | vmap =S=, omap =ys= |
This module has also ported vim-unimpaired keybinds to Emacs.
In other modules:
+ The tools/neotree & tools/treemacs modules provide a =NERDTree= equivalent.
+ The editor/multiple-cursors module contains functionality equal to the
following vim plugins:
+ evil-multiedit => vim-multiedit
+ evil-mc => vim-multiple-cursors
** Custom Text Objects
This module provides a couple extra text objects, along with the built-in ones.
For posterity, here are the built-in ones:
+ =w W= words
+ =s= sentences
+ =p= paragraphs
+ =b= parenthesized blocks
+ =b ( ) { } [ ] < >= braces, parentheses and brackets
+ =' " `= quotes
+ =t= tags
+ =o= symbols
And these are text objects added by this module:
+ =a= C-style function arguments (provided by ~evil-args~)
+ =B= any block delimited by braces, parentheses or brackets (provided by
~evil-textobj-anyblock~)
+ =i j k= by indentation (=k= includes one line above; =j= includes one line
above and below) (provided by ~evil-indent-plus~)
+ =x= XML attributes (provided by ~exato~)
** Custom Ex Commands
| Ex Command | Description |
|-----------------------+--------------------------------------------------------------------------------------|
| ~:@~ | Apply macro on selected lines |
| ~:ag[!] REGEXP~ | Perform a project search with ag |
| ~:agcwd[!] REGEXP~ | Perform a project search with ag from the current directory |
| ~:al[ign][!] REGEXP~ | Align text to the first match of REGEXP. If BANG, align all matches on each line |
| ~:cp[!] NEWPATH~ | Copy the current file to NEWPATH |
| ~:dash QUERY~ | Look up QUERY (or the symbol at point) in dash docsets |
| ~:dehtml [INPUT]~ | HTML decode selected text / inserts result if INPUT is given |
| ~:enhtml [INPUT]~ | HTML encode selected text / inserts result if INPUT is given |
| ~:grep[!]~ | Perform a project search with git-grep |
| ~:grepcwd[!]~ | Perform a project search with git-grep from the current directory |
| ~:iedit REGEXP~ | Invoke iedit on all matches for REGEXP |
| ~:k[ill]all[!]~ | Kill all buffers (if BANG, affect buffer across workspaces) |
| ~:k[ill]b~ | Kill all buried buffers |
| ~:k[ill]m[!] REGEXP~ | Kill buffers whose name matches REGEXP (if BANG, affect buffers across workspaces) |
| ~:k[ill]o~ | Kill all other buffers besides the selected one |
| ~:k[ill]~ | Kill the current buffer |
| ~:lo[okup] QUERY~ | Look up QUERY on an online search engine |
| ~:mc REGEXP~ | Invoke multiple cursors on all matches for REGEXP |
| ~:mv[!] NEWPATH~ | Move the current file to NEWPATH |
| ~:na[rrow]~ | Narrow the buffer to the selection |
| ~:pad~ | Open a scratch pad for running code quickly |
| ~:ral[ign][!] REGEXP~ | Right-Align text that matches REGEXP. If BANG, align all matches on each line |
| ~:repl~ | Open a REPL and/or copy the current selection to it |
| ~:retab~ | Convert indentation to the default within the selection |
| ~:rev[erse]~ | Reverse the selected lines |
| ~:rg[!]~ | Perform a project search with ripgrep |
| ~:rgcwd[!]~ | Perform a project search with ripgrep from the current directory |
| ~:rm[!] [PATH]~ | Delete the current buffer's file and buffer |
| ~:tcd[!]~ | Send =cd X= to tmux. X = the project root if BANG, X = ~default-directory~ otherwise |
* Configuration
** Removing evil-mode
You must do two things to remove Evil:
1. Remove =:editor evil= from =~/.doom.d/init.el=,
2. Run ~doom refresh~ to clean up lingering dependencies and refresh your
autoloads files.
3. [OPTIONAL] You may want to assign new values to ~doom-leader-alt-key~ and
~doom-localleader-alt-key~. These are bound to =C-c= and =C-c l= by default.
#+begin_quote
Ignore ~doom-leader-key~ and ~doom-localleader-key~, they don't apply to
non-evil sessions.
#+end_quote
Evil-specific configuration and keybindings (defined with ~map!~) will be
ignored without =:editor evil= present (and omitted when byte-compiling).
Keep in mind that, at the time of this writing, Doom was designed by a vimmer,
for vimmers. Little consideration has been put into designing a keybind scheme
for vanilla Emacs users (though it's being worked on!).
That means that much of Doom's functionality will be orphaned in an evil-less
setup. You'll have to set your own keybinds.
I suggest studying [[file:../../config/default/+emacs-bindings.el][config/default/+emacs-bindings.el]] to see what keybinds are
available for non-evil users. Otherwise, you may find inspiration [[file:../../../docs/example_configs.org][on the example
Doom configurations page]].
** Restoring old substitution behavior on s/S
Doom replaces the =s= and =S= keys with the =evil-snipe= package (a port of
vim-seek/vim-sneak for 2-character versions of f/F/t/T).
To disable evil-snipe on s/S, you can either:
1. Disable ~evil-snipe-mode~ by adding ~(after! evil-snipe (evil-snipe-mode
-1))~ to =$DOOMDIR/config.el=,
2. Or disable =evil-snipe= completely with ~(package! evil-snipe :disable t)~
added to =$DOOMDIR/packages.el=, but this will also disable incremental
highlighting for the f/F/t/T motions keys.
3. Or use =cl= and =cc=, respectively; they do the same thing.

View File

@@ -0,0 +1,198 @@
;;; editor/evil/autoload/advice.el -*- lexical-binding: t; -*-
;;;###autoload
(defun +evil-escape-a (&rest _)
"Call `doom/escape' if `evil-force-normal-state' is called interactively."
(when (called-interactively-p 'any)
(call-interactively #'doom/escape)))
;;;###autoload
(defun +evil-resolve-vim-path-a (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 ((project-root (doom-project-root (file-name-directory (expand-file-name path)))))
(unless project-root
(user-error "Not in a project"))
(abbreviate-file-name 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)))
(defun +evil--insert-newline (&optional above _noextranewline)
(let ((pos (save-excursion (beginning-of-line-text) (point)))
comment-auto-fill-only-comments)
(require 'smartparens)
(evil-narrow-to-field
(if above
(if (save-excursion (nth 4 (sp--syntax-ppss pos)))
(evil-save-goal-column
(setq evil-auto-indent nil)
(goto-char pos)
(let ((ws (abs (skip-chars-backward " \t"))))
;; FIXME oh god why
(save-excursion
(if comment-line-break-function
(funcall comment-line-break-function nil)
(comment-indent-new-line))
(when (and (derived-mode-p 'c-mode 'c++-mode 'objc-mode 'java-mode 'js2-mode)
(eq (char-after) ?/))
(insert "*"))
(insert
(make-string (max 0 (+ ws (skip-chars-backward " \t")))
32)))
(insert (make-string (max 1 ws) 32))))
(evil-move-beginning-of-line)
(insert (if use-hard-newlines hard-newline "\n"))
(forward-line -1)
(back-to-indentation))
(evil-move-end-of-line)
(cond ((sp-point-in-comment pos)
(setq evil-auto-indent nil)
(if comment-line-break-function
(funcall comment-line-break-function)
(comment-indent-new-line)))
;; TODO Find a better way to do this
((and (eq major-mode 'haskell-mode)
(fboundp 'haskell-indentation-newline-and-indent))
(setq evil-auto-indent nil)
(haskell-indentation-newline-and-indent))
(t
(insert (if use-hard-newlines hard-newline "\n"))
(back-to-indentation)))))))
;;;###autoload
(defun +evil--insert-newline-below-and-respect-comments-a (orig-fn count)
(if (or (not +evil-want-o/O-to-continue-comments)
(not (eq this-command 'evil-open-below))
(evil-insert-state-p))
(funcall orig-fn count)
(cl-letf (((symbol-function 'evil-insert-newline-below)
(lambda () (+evil--insert-newline))))
(let ((evil-auto-indent evil-auto-indent))
(funcall orig-fn count)))))
;;;###autoload
(defun +evil--insert-newline-above-and-respect-comments-a (orig-fn count)
(if (or (not +evil-want-o/O-to-continue-comments)
(not (eq this-command 'evil-open-above))
(evil-insert-state-p))
(funcall orig-fn count)
(cl-letf (((symbol-function 'evil-insert-newline-above)
(lambda () (+evil--insert-newline 'above))))
(let ((evil-auto-indent evil-auto-indent))
(funcall orig-fn count)))))
;;;###autoload (autoload '+evil-window-split-a "editor/evil/autoload/advice" nil t)
(evil-define-command +evil-window-split-a (&optional count file)
"Same as `evil-window-split', but correctly updates the window history."
:repeat nil
(interactive "P<f>")
;; HACK This ping-ponging between the destination and source windows is to
;; update the window focus history, so that, if you close either split
;; afterwards you won't be sent to some random window.
(let ((doom-inhibit-switch-window-hooks t)
(origwin (selected-window)))
(select-window (split-window origwin count 'below))
(unless evil-split-window-below
(select-window origwin))
(run-hooks 'doom-switch-window-hook))
(recenter)
(when (and (not count) evil-auto-balance-windows)
(balance-windows (window-parent)))
(if file (evil-edit file)))
;;;###autoload (autoload '+evil-window-vsplit-a "editor/evil/autoload/advice" nil t)
(evil-define-command +evil-window-vsplit-a (&optional count file)
"Same as `evil-window-split', but correctly updates the window history."
:repeat nil
(interactive "P<f>")
;; HACK This ping-ponging between the destination and source windows is to
;; update the window focus history, so that, if you close either split
;; afterwards you won't be sent to some random window.
(let ((doom-inhibit-switch-window-hooks t)
(origwin (selected-window)))
(select-window (split-window origwin count 'right))
(unless evil-vsplit-window-right
(select-window origwin))
(run-hooks 'doom-switch-window-hook))
(run-hooks)
(recenter)
(when (and (not count) evil-auto-balance-windows)
(balance-windows (window-parent)))
(if file (evil-edit file)))
;;;###autoload
(defun +evil--fix-dabbrev-in-minibuffer-h ()
"Make `try-expand-dabbrev' from `hippie-expand' work in minibuffer. See
`he-dabbrev-beg', so we need to redefine syntax for '/'."
(set-syntax-table (let* ((table (make-syntax-table)))
(modify-syntax-entry ?/ "." table)
table)))

View File

@@ -0,0 +1,39 @@
;;; editor/evil/autoload/embrace.el -*- lexical-binding: t; -*-
;;;###autoload
(defun +evil--embrace-get-pair (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))))
;;;###autoload
(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)))))))
;;;###autoload
(defun +evil--embrace-latex ()
"LaTeX command support for embrace."
(cons (format "\\%s{" (read-string "\\")) "}"))
;;;###autoload
(defun +evil--embrace-elisp-fn ()
"Elisp function support for embrace."
(cons (format "(%s " (or (read-string "(") "")) ")"))
;;;###autoload
(defun +evil--embrace-angle-brackets ()
"Type/generic angle brackets."
(cons (format "%s<" (or (read-string "") ""))
">"))

View File

@@ -0,0 +1,180 @@
;; editor/evil/autoload/evil.el -*- lexical-binding: t; -*-
;;;###autodef
(defun set-evil-initial-state! (modes state)
"Set the initialize STATE of MODES using `evil-set-initial-state'."
(declare (indent defun))
(after! evil
(if (listp modes)
(dolist (mode (doom-enlist modes))
(evil-set-initial-state mode state))
(evil-set-initial-state modes state))))
;;
;;; Interactive commands
;;;###autoload
(defun +evil/visual-indent ()
"vnoremap < <gv"
(interactive)
(evil-shift-right (region-beginning) (region-end))
(evil-normal-state)
(evil-visual-restore))
;;;###autoload
(defun +evil/visual-dedent ()
"vnoremap > >gv"
(interactive)
(evil-shift-left (region-beginning) (region-end))
(evil-normal-state)
(evil-visual-restore))
;;;###autoload
(defun +evil/paste-preserve-register ()
"Call `evil-paste-after' without overwriting the clipboard (by writing to the
0 register instead). This allows you to paste the same text again afterwards."
(interactive)
(let ((evil-this-register ?0))
(call-interactively #'evil-paste-after)))
(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 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')."
(when (window-dedicated-p)
(user-error "Cannot swap a dedicated window"))
(let* ((this-window (selected-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)
(window-dedicated-p this-window))
(setq that-buffer nil that-window nil))
(if (not (or that-window (one-window-p t)))
(funcall (pcase 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
(pcase direction
('up 'above)
('down 'below)
(_ direction))))
(with-selected-window that-window
(switch-to-buffer (doom-fallback-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))))
;;;###autoload
(defun +evil/window-move-left () "See `+evil--window-swap'" (interactive) (+evil--window-swap 'left))
;;;###autoload
(defun +evil/window-move-right () "See `+evil--window-swap'" (interactive) (+evil--window-swap 'right))
;;;###autoload
(defun +evil/window-move-up () "See `+evil--window-swap'" (interactive) (+evil--window-swap 'up))
;;;###autoload
(defun +evil/window-move-down () "See `+evil--window-swap'" (interactive) (+evil--window-swap 'down))
;;;###autoload
(defun +evil/easymotion ()
"Invoke and lazy-load `evil-easymotion' without compromising which-key
integration."
(interactive)
(let ((prefix (this-command-keys)))
(evil-define-key* 'motion 'global prefix nil)
(evilem-default-keybindings (key-description prefix))
(setq prefix-arg current-prefix-arg
unread-command-events
(mapcar (lambda (e) (cons t e))
(vconcat (when evil-this-operator
(where-is-internal evil-this-operator
evil-normal-state-map
t))
prefix)))))
;;;###autoload (autoload '+evil:apply-macro "editor/evil/autoload/evil" nil t)
(evil-define-operator +evil:apply-macro (beg end)
"Apply macro to each line."
:move-point nil
(interactive "<r>")
(let ((register (or evil-this-register (read-char)))
macro)
(cond ((or (and (eq register ?@) (eq evil-last-register ?:))
(eq register ?:))
(setq macro (lambda () (evil-ex-repeat nil))
evil-last-register ?:))
((eq register ?@)
(unless evil-last-register
(user-error "No previously executed keyboard macro."))
(setq macro (evil-get-register evil-last-register t)))
((setq macro (evil-get-register register t)
evil-last-register register)))
(unless macro
(user-error "No macro recorded in %c register" register))
(evil-change-state 'normal)
(evil-with-single-undo
(let ((lines (count-lines beg end)))
(message "Applied macro in %c register %d times" register lines)
(apply-macro-to-region-lines beg end macro)
(message "Applied macro in %c register %d times...DONE" register lines)))))
;;;###autoload (autoload '+evil:retab "editor/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 "<r>")
(doom/retab beg end))
;;;###autoload (autoload '+evil:narrow-buffer "editor/evil/autoload/evil" nil t)
(evil-define-operator +evil:narrow-buffer (beg end &optional bang)
"Narrow the buffer to region between BEG and END.
Widens narrowed buffers first. If BANG, use indirect buffer clones instead."
:move-point nil
(interactive "<r><!>")
(if (not bang)
(if (buffer-narrowed-p)
(widen)
(narrow-to-region beg end))
(when (buffer-narrowed-p)
(doom/widen-indirectly-narrowed-buffer t))
(doom/narrow-buffer-indirectly beg end)))
;;;###autoload (autoload '+evil:yank-unindented "editor/evil/autoload/evil" nil t)
(evil-define-operator +evil:yank-unindented (beg end _type _register _yank-handler)
"Saves the (reindented) characters in motion into the kill-ring."
:move-point nil
:repeat nil
(interactive "<R><x><y>")
(let ((indent (save-excursion (goto-char beg) (current-indentation)))
(text (buffer-substring beg end)))
(with-temp-buffer
(insert text)
(indent-rigidly (point-min) (point-max) (- indent))
(evil-yank (point-min) (point-max)))))
;;
;;; wgrep
;;;###autoload (autoload '+evil-delete "editor/evil/autoload/evil" nil t)
(evil-define-operator +evil-delete (beg end type register yank-handler)
"A wrapper around `evil-delete' for `wgrep' buffers that will invoke
`wgrep-mark-deletion' on lines you try to delete."
(interactive "<R><x><y>")
(condition-case _ex
(evil-delete beg end type register yank-handler)
('text-read-only
(evil-apply-on-block
(lambda (beg _)
(goto-char beg)
(call-interactively #'wgrep-mark-deletion))
beg (1- end) nil))))

View File

@@ -0,0 +1,190 @@
;;; editor/evil/autoload/ex.el -*- lexical-binding: t; -*-
(defvar +evil--flag nil)
(defun +evil--ex-match-init (name &optional face update-hook)
(with-current-buffer evil-ex-current-buffer
(cond
((eq +evil--flag 'start)
(evil-ex-make-hl name
:face (or face 'evil-ex-lazy-highlight)
:update-hook (or update-hook #'evil-ex-pattern-update-ex-info))
(setq +evil--flag 'update))
((eq +evil--flag 'stop)
(evil-ex-delete-hl name)))))
(defun +evil--ex-buffer-match (arg &optional hl-name flags beg end)
(when (and (eq +evil--flag 'update)
evil-ex-substitute-highlight-all
(not (zerop (length arg))))
(condition-case lossage
(let* ((pattern (evil-ex-make-substitute-pattern
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-regexp-match (flag &optional arg invert)
(let ((hl-name 'evil-ex-buffer-match)
(+evil--flag flag))
(with-selected-window (minibuffer-selected-window)
(+evil--ex-match-init hl-name)
(cl-destructuring-bind (&optional arg flags)
(evil-delimited-arguments arg 2)
(let ((evil-ex-substitute-global
(if invert
(not evil-ex-substitute-global)
evil-ex-substitute-global)))
(+evil--ex-buffer-match
arg hl-name (string-to-list flags)))))))
;;
;;; Ex Commands
;;;###autoload (autoload '+evil:align "editor/evil/autoload/ex" nil t)
(evil-define-command +evil:align (beg end pattern &optional flags)
"Ex interface to `align-regexp'.
PATTERN is a vim-style regexp. FLAGS is an optional string of characters.
Supports the following flags:
g Repeat alignment on all matches in each line"
(interactive "<r><//>")
(align-regexp
beg end
(concat "\\(\\s-*\\)" (evil-transform-vim-style-regexp pattern))
1 1 (memq ?g flags)))
;;;###autoload (autoload '+evil:align-right "editor/evil/autoload/ex" nil t)
(evil-define-command +evil:align-right (beg end pattern &optional flags)
"Ex interface to `align-regexp' that right-aligns matches.
PATTERN is a vim-style regexp. FLAGS is an optional string of characters.
Supports the following flags:
g Repeat alignment on all matches in each line"
(interactive "<r><//>")
(align-regexp
beg end
(concat "\\(" (evil-transform-vim-style-regexp pattern) "\\)")
-1 1 (memq ?g flags)))
;; ;;;###autoload (autoload '+evil:sort "editor/evil/autoload/ex" nil nil)
;; (evil-define-command +evil:sort (beg end &optional pattern flags reverse)
;; (interactive "<r><//><!>"))
;;;###autoload (autoload '+evil:open-scratch-buffer "editor/evil/autoload/ex" nil t)
(evil-define-operator +evil:open-scratch-buffer (bang)
(interactive "<!>")
(doom/open-scratch-buffer bang))
;;;###autoload (autoload '+evil:pwd "editor/evil/autoload/ex" nil t)
(evil-define-command +evil:pwd (bang)
"Display the current working directory. If BANG, copy it to your clipboard."
(interactive "<!>")
(if (not bang)
(pwd)
(kill-new default-directory)
(message "Copied to clipboard")))
;;;###autoload (autoload '+evil:make "editor/evil/autoload/ex" nil t)
(evil-define-command +evil:make (arguments &optional bang)
"Run make with ARGUMENTS.
If BANG is non-nil, open compilation output in a comint buffer.
If BANG, then run ARGUMENTS as a full command. This command understands vim file
modifiers (like %:p:h). See `+evil-resolve-vim-path-a' for details."
(interactive "<sh><!>")
(+evil:compile (format "make %s"
(evil-ex-replace-special-filenames
arguments))
bang))
;;;###autoload (autoload '+evil:compile "editor/evil/autoload/ex" nil t)
(evil-define-command +evil:compile (arguments &optional bang)
"Run `compile-command' with ARGUMENTS.
If BANG is non-nil, open compilation output in a comint buffer.
This command understands vim file modifiers (like %:p:h). See
`+evil-resolve-vim-path-a' for details."
(interactive "<sh><!>")
(compile (evil-ex-replace-special-filenames
(format "%s %s"
(eval compile-command)
arguments))
bang))
;;;###autoload (autoload '+evil:reverse-lines "editor/evil/autoload/ex" nil t)
(evil-define-command +evil:reverse-lines (beg end)
"Reverse lines between BEG and END."
(interactive "<r>")
(reverse-region beg end))
;;;###autoload (autoload '+evil:cd "editor/evil/autoload/ex" nil t)
(evil-define-command +evil:cd (&optional path)
"Change `default-directory' with `cd'."
(interactive "<f>")
(let ((path (or path "~")))
(cd path)
(message "Changed directory to '%s'" (abbreviate-file-name (expand-file-name path)))))
;;;###autoload (autoload '+evil:kill-all-buffers "editor/evil/autoload/ex" nil t)
(evil-define-command +evil:kill-all-buffers (&optional bang)
"Kill all buffers. If BANG, kill current session too."
(interactive "<!>")
(if (and bang (fboundp '+workspace/kill-session))
(+workspace/kill-session)
(call-interactively #'doom/kill-all-buffers)))
;;;###autoload (autoload '+evil:kill-matching-buffers "editor/evil/autoload/ex" nil t)
(evil-define-command +evil:kill-matching-buffers (&optional bang pattern)
"Kill all buffers matching PATTERN regexp. If BANG, only match project
buffers."
(interactive "<a>")
(doom/kill-matching-buffers
pattern (if bang (doom-project-buffer-list))))
;;;###autoload (autoload '+evil:help "editor/evil/autoload/ex" nil t)
(evil-define-command +evil:help (&optional bang query)
"Look up documentation for QUERY.
If QUERY is in the format of an ex command, it will map it to the underlying
function and open its documentation with `helpful-function'. Otherwise, it will
search for it with `apropos'.
If QUERY is empty, this runs the equivalent of 'M-x apropos'. If BANG is
non-nil, a search is preformed against Doom's manual (with `doom/help-search')."
(interactive "<!><a>")
(if bang
(doom/help-search query)
(save-match-data
(cond ((or (null query) (string-empty-p (string-trim query)))
(call-interactively
(or (command-remapping #'apropos)
#'apropos)))
((string-match "^ *:\\([^ ]+\\)$" query)
(helpful-function
(evil-ex-completed-binding (match-string 1 query))))
((message "Searching for %S, this may take a while..." query)
(apropos query t))))))
;;;###autoload (autoload '+evil:read "editor/evil/autoload/ex" nil t)
(evil-define-command +evil:read (count file)
"Alternative version of `evil-read' that replaces filename modifiers in FILE."
(interactive "P<fsh>")
(evil-read count (evil-ex-replace-special-filenames file)))

View File

@@ -0,0 +1,33 @@
;;; editor/evil/autoload/files.el -*- lexical-binding: t; -*-
;;;###autoload (autoload '+evil:delete-this-file "editor/evil/autoload/files" nil t)
(evil-define-command +evil:delete-this-file (&optional filename force-p)
"Delete FILENAME (defaults to the file associated with current buffer) and
kills the buffer. If FORCE-P, force the deletion (don't ask for confirmation)."
:repeat nil
(interactive "<f><!>")
(doom/delete-this-file (or filename (file-truename buffer-file-name))
force-p))
;;;###autoload (autoload '+evil:move-this-file "editor/evil/autoload/files" nil t)
(evil-define-command +evil:move-this-file (new-path &optional force-p)
"Move current buffer's file to NEW-PATH. Replaces %, # and other vim-esque
filename modifiers (see `+evil*ex-replace-special-filenames'). If FORCE-P,
overwrite the destination file if it exists, without confirmation."
:repeat nil
(interactive "<f><!>")
(when (or (not new-path) (string-empty-p new-path))
(user-error "No new path was specified"))
(doom/move-this-file new-path force-p))
;;;###autoload (autoload '+evil:copy-this-file "editor/evil/autoload/files" nil nil)
(evil-define-command +evil:copy-this-file (new-path &optional force-p)
"Copy current buffer's file to NEW-PATH. Replaces %, # and other vim-esque
filename modifiers (see `+evil*ex-replace-special-filenames'). If FORCE-P,
overwrite the destination file if it exists, without confirmation."
:repeat nil
(interactive "<f><!>")
(when (or (not new-path) (string-empty-p new-path))
(user-error "No new path was specified"))
(doom/copy-this-file new-path force-p))

View File

@@ -0,0 +1,13 @@
;;; editor/evil/autoload/textobjects.el -*- lexical-binding: t; -*-
;;;###autoload (autoload '+evil:whole-buffer-txtobj "editor/evil/autoload/textobjects" nil nil)
(evil-define-text-object +evil:whole-buffer-txtobj (count &optional _beg _end type)
"Text object to select the whole buffer."
(evil-range (point-min) (point-max) type))
;;;###autoload (autoload '+evil:defun-txtobj "editor/evil/autoload/textobjects" nil nil)
(evil-define-text-object +evil:defun-txtobj (count &optional _beg _end type)
"Text object to select the whole buffer."
(cl-destructuring-bind (beg . end)
(bounds-of-thing-at-point 'defun)
(evil-range beg end type)))

View File

@@ -0,0 +1,200 @@
;;; editor/evil/autoload/unimpaired.el -*- lexical-binding: t; -*-
;; These are ported from vim-unimpaired https://github.com/tpope/vim-unimpaired
;; and bound in the :config default module (in +evil-bindings.el).
;;
;;; Next/Previous commands
;;;###autoload
(defun +evil/next-beginning-of-method (count)
"Jump to the beginning of the COUNT-th method/function after point."
(interactive "p")
(beginning-of-defun (- count)))
;;;###autoload
(defun +evil/previous-beginning-of-method (count)
"Jump to the beginning of the COUNT-th method/function before point."
(interactive "p")
(beginning-of-defun count))
;;;###autoload
(defalias #'+evil/next-end-of-method #'end-of-defun
"Jump to the end of the COUNT-th method/function after point.")
;;;###autoload
(defun +evil/previous-end-of-method (count)
"Jump to the end of the COUNT-th method/function before point."
(interactive "p")
(end-of-defun (- count)))
;;;###autoload
(defun +evil/next-preproc-directive (count)
"Jump to the COUNT-th preprocessor directive after point.
By default, this only recognizes C preproc directives. To change this see
`+evil-preprocessor-regexp'."
(interactive "p")
;; TODO More generalized search, to support directives in other languages?
(if (re-search-forward +evil-preprocessor-regexp nil t count)
(goto-char (match-beginning 0))
(user-error "No preprocessor directives %s point"
(if (> count 0) "after" "before"))))
;;;###autoload
(defun +evil/previous-preproc-directive (count)
"Jump to the COUNT-th preprocessor directive before point.
See `+evil/next-preproc-directive' for details."
(interactive "p")
(+evil/next-preproc-statement (- count)))
;;;###autoload
(defun +evil/next-comment (count)
"Jump to the beginning of the COUNT-th commented region after point."
(interactive "p")
(let ((orig-pt (point)))
(require 'newcomment)
(dotimes (_ (abs count))
(cond ((> count 0)
(while (and (not (eobp)) (sp-point-in-comment))
(forward-line 1))
(unless (comment-search-forward (point-max) 'noerror)
(goto-char orig-pt)
(user-error "No comment after point")))
(t
(while (and (not (bobp)) (sp-point-in-comment))
(forward-line -1))
(unless (comment-search-backward nil 'noerror)
(goto-char orig-pt)
(user-error "No comment before point")))))))
;;;###autoload
(defun +evil/previous-comment (count)
"Jump to the beginning of the COUNT-th commented region before point."
(interactive "p")
(+evil/next-comment (- count)))
;;; ] SPC / [ SPC
;;;###autoload
(defun +evil/insert-newline-below (count)
"Insert COUNT blank line(s) below current line. Does not change modes."
(interactive "p")
(dotimes (_ count)
(save-excursion (evil-insert-newline-below))))
;;;###autoload
(defun +evil/insert-newline-above (count)
"Insert COUNT blank line(s) above current line. Does not change modes."
(interactive "p")
(dotimes (_ count)
(save-excursion (evil-insert-newline-above))))
;;; ]t / [t
;;;###autoload
(defun +evil/next-frame (count)
"Focus next frame."
(interactive "p")
(dotimes (_ (abs count))
(let ((frame (if (> count 0) (next-frame) (previous-frame))))
(if (eq frame (selected-frame))
(user-error "No other frame")
(select-frame-set-input-focus frame)))))
;;;###autoload
(defun +evil/previous-frame (count)
"Focus previous frame."
(interactive "p")
(+evil/next-frame (- count)))
;;; ]f / [f
(defun +evil--next-file (n)
(unless buffer-file-name
(user-error "Must be called from a file-visiting buffer"))
(let* ((directory (file-name-directory buffer-file-name))
(filename (file-name-nondirectory buffer-file-name))
(files (doom-glob (file-name-directory buffer-file-name) "[!.]*"))
(index (cl-position filename files :test #'file-equal-p)))
(when (null index)
(user-error "Couldn't find this file in current directory"))
(let ((index (+ index n)))
(cond ((>= index (length files))
(user-error "No files after this one"))
((< index 0)
(user-error "No files before this one"))
((expand-file-name (nth index files) directory))))))
;;;###autoload
(defun +evil/next-file (count)
"Open file following this one, alphabetically, in the same directory."
(interactive "p")
(find-file (+evil--next-file count)))
;;;###autoload
(defun +evil/previous-file (count)
"Open file preceding this one, alphabetically, in the same directory."
(interactive "p")
(find-file (+evil--next-file (- count))))
;;
;;; Encoding/Decoding
;; NOTE For ]x / [x see :lang web
;; - `+web:encode-html-entities'
;; - `+web:decode-html-entities'
(defun +evil--encode (beg end fn)
(save-excursion
(goto-char beg)
(let* ((end (if (eq evil-this-type 'line) (1- end) end))
(text (buffer-substring-no-properties beg end)))
(delete-region beg end)
(insert (funcall fn text)))))
;;; ]u / [u
;;;###autoload (autoload '+evil:url-encode "editor/evil/autoload/unimpaired" nil t)
(evil-define-operator +evil:url-encode (_count &optional beg end)
"TODO"
(interactive "<c><r>")
(+evil--encode beg end #'url-encode-url))
;;;###autoload (autoload '+evil:url-decode "editor/evil/autoload/unimpaired" nil t)
(evil-define-operator +evil:url-decode (_count &optional beg end)
"TODO"
(interactive "<c><r>")
(+evil--encode beg end #'url-unhex-string))
;;; ]y / [y
;;;###autoload (autoload '+evil:c-string-encode "editor/evil/autoload/unimpaired" nil t)
(evil-define-operator +evil:c-string-encode (_count &optional beg end)
"TODO"
(interactive "<c><r>")
(+evil--encode
beg end
(lambda (text)
(replace-regexp-in-string "[\"\\]" (lambda (ch) (concat "\\" ch)) text))))
;;;###autoload (autoload '+evil:c-string-decode "editor/evil/autoload/unimpaired" nil t)
(evil-define-operator +evil:c-string-decode (_count &optional beg end)
"TODO"
(interactive "<c><r>")
(+evil--encode
beg end
(lambda (text)
(replace-regexp-in-string "\\\\[\"\\]" (lambda (str) (substring str 1)) text))))
;;
;;; Standalone
;;; gp
;;;###autoload
(defun +evil/reselect-paste ()
"Return to visual mode and reselect the last pasted region."
(interactive)
(cl-destructuring-bind (_ _ _ beg end &optional _)
evil-last-paste
(evil-visual-make-selection
(save-excursion (goto-char beg) (point-marker))
end)))

View File

@@ -0,0 +1,587 @@
;;; editor/evil/config.el -*- lexical-binding: t; -*-
;; 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-repeat-keys (cons ";" ",")
"The keys to use for universal repeating motions.
This is a cons cell whose CAR is the key for repeating a motion forward, and
whose CDR is for repeating backward. They should both be kbd-able strings.")
(defvar +evil-want-o/O-to-continue-comments t
"If non-nil, the o/O keys will continue comment lines if the point is on a
line with a linewise comment.")
(defvar +evil-preprocessor-regexp "^\\s-*#[a-zA-Z0-9_]"
"The regexp used by `+evil/next-preproc-directive' and
`+evil/previous-preproc-directive' on ]# and [#, to jump between preprocessor
directives. By default, this only recognizes C directives.")
;; Set these defaults before `evil'; use `defvar' so they can be changed prior
;; to loading.
(defvar evil-want-C-i-jump (or (daemonp) (display-graphic-p)))
(defvar evil-want-C-u-scroll t)
(defvar evil-want-C-w-scroll t)
(defvar evil-want-Y-yank-to-eol t)
(defvar evil-want-abbrev-expand-on-insert-exit nil)
(use-package! evil
:hook (doom-init-modules . evil-mode)
:demand t
:preface
(setq evil-want-visual-char-semi-exclusive t
evil-magic t
evil-echo-state t
evil-indent-convert-tabs t
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
evil-mode-line-format 'nil
evil-respect-visual-line-mode t
;; more vim-like behavior
evil-symbol-word-search t
;; cursor appearance
evil-default-cursor '+evil-default-cursor-fn
evil-normal-state-cursor 'box
evil-emacs-state-cursor '(box +evil-emacs-cursor-fn)
evil-insert-state-cursor 'bar
evil-visual-state-cursor 'hollow
;; must be set before evil/evil-collection is loaded
evil-want-keybinding (not (featurep! +everywhere))
;; Only do highlighting in selected window so that Emacs has less work
;; to do highlighting them all.
evil-ex-interactive-search-highlight 'selected-window)
:config
(evil-select-search-module 'evil-search-module 'evil-search)
(put 'evil-define-key* 'lisp-indent-function 'defun)
;; stop copying each visual state move to the clipboard:
;; https://bitbucket.org/lyro/evil/issue/336/osx-visual-state-copies-the-region-on
;; grokked from:
;; http://stackoverflow.com/questions/15873346/elisp-rename-macro
(advice-add #'evil-visual-update-x-selection :override #'ignore)
;; Start help-with-tutorial in emacs state
(advice-add #'help-with-tutorial :after (lambda (&rest _) (evil-emacs-state +1)))
;; Allows you to click buttons without initiating a selection
(define-key evil-motion-state-map [down-mouse-1] nil)
;; Done in a hook to ensure the popup rules load as late as possible
(add-hook! 'doom-init-modules-hook
(defun +evil--init-popup-rules-h ()
(set-popup-rules!
'(("^\\*evil-registers" :size 0.3)
("^\\*Command Line" :size 8)))))
;; Change the cursor color in emacs state. We do it this roundabout way
;; instead of changing `evil-default-cursor' (or `evil-emacs-state-cursor') so
;; it won't interfere with users who have changed these variables.
(defvar +evil--default-cursor-color "#ffffff")
(defvar +evil--emacs-cursor-color "#ff9999")
(add-hook! 'doom-load-theme-hook
(defun +evil-update-cursor-color-h ()
(setq +evil--default-cursor-color (face-background 'cursor)
+evil--emacs-cursor-color (face-foreground 'warning))))
(defun +evil-default-cursor-fn ()
(evil-set-cursor-color +evil--default-cursor-color))
(defun +evil-emacs-cursor-fn ()
(evil-set-cursor-color +evil--emacs-cursor-color))
(setq-hook! 'after-change-major-mode-hook evil-shift-width tab-width)
;; --- keybind fixes ----------------------
(after! wgrep
;; A wrapper that invokes `wgrep-mark-deletion' across lines you use
;; `evil-delete' in wgrep buffers.
(define-key wgrep-mode-map [remap evil-delete] #'+evil-delete))
(add-hook! 'doom-escape-hook
(defun +evil-disable-ex-highlights-h ()
"Disable ex search buffer highlights."
(when (evil-ex-hl-active-p 'evil-ex-search)
(evil-ex-nohighlight)
t)))
;; --- evil hacks -------------------------
(unless noninteractive
(setq save-silently t)
(add-hook! 'after-save-hook
(defun +evil-display-vimlike-save-message-h ()
"Shorter, vim-esque save messages."
(message "\"%s\" %dL, %dC written"
(if buffer-file-name
(file-relative-name (file-truename buffer-file-name) (doom-project-root))
(buffer-name))
(count-lines (point-min) (point-max))
(buffer-size)))))
;; 'gq' moves the cursor to the beginning of selection. Disable this, since
;; it's more disruptive than helpful.
(defadvice! +evil--dont-move-cursor-a (orig-fn &rest args)
:around #'evil-indent
(save-excursion (apply orig-fn args)))
;; In evil, registers 2-9 are buffer-local. In vim, they're global, so...
(defadvice! +evil--make-numbered-markers-global-a (_arg)
:after-until #'evil-global-marker-p
(and (>= char ?2) (<= char ?9)))
;; Make ESC (from normal mode) the universal escaper. See `doom-escape-hook'.
(advice-add #'evil-force-normal-state :after #'+evil-escape-a)
;; monkey patch `evil-ex-replace-special-filenames' to improve support for
;; file modifiers like %:p:h. This adds support for most of vim's modifiers,
;; and one custom one: %:P (expand to the project root).
(advice-add #'evil-ex-replace-special-filenames :override #'+evil-resolve-vim-path-a)
;; make `try-expand-dabbrev' (from `hippie-expand') work in minibuffer
(add-hook 'minibuffer-inactive-mode-hook #'+evil--fix-dabbrev-in-minibuffer-h)
;; Focus and recenter new splits
(advice-add #'evil-window-split :override #'+evil-window-split-a)
(advice-add #'evil-window-vsplit :override #'+evil-window-vsplit-a)
;; Make o/O continue comments (see `+evil-want-o/O-to-continue-comments')
(advice-add #'evil-open-above :around #'+evil--insert-newline-above-and-respect-comments-a)
(advice-add #'evil-open-below :around #'+evil--insert-newline-below-and-respect-comments-a)
;; Recenter screen after most searches
(dolist (fn '(evil-visualstar/begin-search-forward
evil-visualstar/begin-search-backward
evil-ex-search-word-backward
evil-ex-search-word-backward
evil-ex-search-forward
evil-ex-search-backward))
(advice-add fn :after #'doom-recenter-a))
;; --- custom interactive codes -----------
;; These arg types will highlight matches in the current buffer
(evil-ex-define-argument-type regexp-match
:runner (lambda (flag &optional arg) (+evil-ex-regexp-match flag arg 'inverted)))
(evil-ex-define-argument-type regexp-global-match
:runner +evil-ex-regexp-match)
(defun +evil--regexp-match-args (arg)
(when (evil-ex-p)
(cl-destructuring-bind (&optional arg flags)
(evil-delimited-arguments arg 2)
(list arg (string-to-list flags)))))
;; Other commands can make use of this
(evil-define-interactive-code "<//>"
:ex-arg regexp-match
(+evil--regexp-match-args evil-ex-argument))
(evil-define-interactive-code "<//!>"
:ex-arg regexp-global-match
(+evil--regexp-match-args evil-ex-argument))
;; Forward declare these so that ex completion works, even if the autoloaded
;; functions aren't loaded yet.
(evil-add-command-properties '+evil:align :ex-arg 'regexp-match)
(evil-add-command-properties '+evil:align-right :ex-arg 'regexp-match)
(evil-add-command-properties '+multiple-cursors:evil-mc :ex-arg 'regexp-global-match)
;; Lazy load evil ex commands
(delq! 'evil-ex features)
(add-transient-hook! 'evil-ex (provide 'evil-ex))
(after! evil-ex (load! "+commands")))
;;
;;; Packages
(use-package! evil-easymotion
:commands evilem-create evilem-default-keybindings
:config
;; Use evil-search backend, instead of isearch
(evilem-make-motion evilem-motion-search-next #'evil-ex-search-next
:bind ((evil-ex-search-highlight-all nil)))
(evilem-make-motion evilem-motion-search-previous #'evil-ex-search-previous
:bind ((evil-ex-search-highlight-all nil)))
(evilem-make-motion evilem-motion-search-word-forward #'evil-ex-search-word-forward
:bind ((evil-ex-search-highlight-all nil)))
(evilem-make-motion evilem-motion-search-word-backward #'evil-ex-search-word-backward
:bind ((evil-ex-search-highlight-all nil))))
(use-package! evil-embrace
:commands embrace-add-pair embrace-add-pair-regexp
:hook (LaTeX-mode . embrace-LaTeX-mode-hook)
:hook (org-mode . embrace-org-mode-hook)
:hook ((ruby-mode enh-ruby-mode) . embrace-ruby-mode-hook)
:hook (emacs-lisp-mode . embrace-emacs-lisp-mode-hook)
:hook ((lisp-mode emacs-lisp-mode clojure-mode racket-mode)
. +evil-embrace-lisp-mode-hook-h)
:hook ((org-mode LaTeX-mode) . +evil-embrace-latex-mode-hook-h)
:hook ((c++-mode rust-mode rustic-mode csharp-mode java-mode swift-mode typescript-mode)
. +evil-embrace-angle-bracket-modes-hook-h)
:init
(after! evil-surround
(evil-embrace-enable-evil-surround-integration))
:config
(setq evil-embrace-show-help-p nil)
(defun +evil-embrace-latex-mode-hook-h ()
(embrace-add-pair-regexp ?l "\\[a-z]+{" "}" #'+evil--embrace-latex))
(defun +evil-embrace-lisp-mode-hook-h ()
(push (cons ?f (make-embrace-pair-struct
:key ?f
:read-function #'+evil--embrace-elisp-fn
:left-regexp "([^ ]+ "
:right-regexp ")"))
embrace--pairs-list))
(defun +evil-embrace-angle-bracket-modes-hook-h ()
(set (make-local-variable 'evil-embrace-evil-surround-keys)
(delq ?< evil-embrace-evil-surround-keys))
(push (cons ?< (make-embrace-pair-struct
:key ?<
:read-function #'+evil--embrace-angle-brackets
:left-regexp "\\[a-z]+<"
:right-regexp ">"))
embrace--pairs-list))
;; Add escaped-sequence support to embrace
(setf (alist-get ?\\ (default-value 'embrace--pairs-list))
(make-embrace-pair-struct
:key ?\\
:read-function #'+evil--embrace-escaped
:left-regexp "\\[[{(]"
:right-regexp "\\[]})]")))
(use-package! evil-escape
:commands evil-escape
:after-call pre-command-hook
:init
(setq evil-escape-excluded-states '(normal visual multiedit emacs motion)
evil-escape-excluded-major-modes '(neotree-mode treemacs-mode vterm-mode)
evil-escape-key-sequence "jk"
evil-escape-delay 0.15)
(evil-define-key* '(insert replace visual operator) 'global "\C-g" #'evil-escape)
:config
;; no `evil-escape' in minibuffer
(add-hook 'evil-escape-inhibit-functions #'minibufferp)
;; so that evil-escape-mode-hook runs, and can be toggled by evil-mc
(evil-escape-mode +1))
(use-package! evil-exchange
:commands evil-exchange
:config
(add-hook! 'doom-escape-hook
(defun +evil--escape-exchange-h ()
(when evil-exchange--overlays
(evil-exchange-cancel)
t))))
(use-package! evil-quick-diff
:commands (evil-quick-diff evil-quick-diff-cancel))
(use-package! evil-nerd-commenter
:commands (evilnc-comment-operator
evilnc-inner-comment
evilnc-outer-commenter))
(use-package! evil-snipe
:commands (evil-snipe-mode
evil-snipe-override-mode
evil-snipe-local-mode
evil-snipe-override-local-mode)
:after-call pre-command-hook
:init
(setq evil-snipe-smart-case t
evil-snipe-scope 'line
evil-snipe-repeat-scope 'visible
evil-snipe-char-fold t)
:config
(pushnew! evil-snipe-disabled-modes 'Info-mode 'calc-mode)
(evil-snipe-mode +1)
(evil-snipe-override-mode +1))
(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-traces
:after evil-ex
:config
(pushnew! evil-traces-argument-type-alist
'(+evil:align . evil-traces-global)
'(+evil:align-right . evil-traces-global))
(evil-traces-mode))
;; Allows you to use the selection for * and #
(use-package! evil-visualstar
:commands (evil-visualstar/begin-search
evil-visualstar/begin-search-forward
evil-visualstar/begin-search-backward)
:init
(evil-define-key* 'visual 'global
"*" #'evil-visualstar/begin-search-forward
"#" #'evil-visualstar/begin-search-backward))
;;
;;; Text object plugins
(use-package! exato
:commands evil-outer-xml-attr evil-inner-xml-attr)
;;
;;; Keybinds
(defmacro set-repeater! (command next-func prev-func)
"Makes ; and , the universal repeat-keys in evil-mode.
To change these keys see `+evil-repeat-keys'."
(let ((fn-sym (intern (format "+evil/repeat-%s" (doom-unquote command)))))
`(progn
(defun ,fn-sym (&rest _)
(when +evil-repeat-keys
(evil-define-key* 'motion 'local
(kbd (car +evil-repeat-keys)) #',next-func
(kbd (cdr +evil-repeat-keys)) #',prev-func)))
(advice-add #',command :after-while #',fn-sym))))
;; n/N
(set-repeater! evil-ex-search-next evil-ex-search-next evil-ex-search-previous)
(set-repeater! evil-ex-search-previous evil-ex-search-next evil-ex-search-previous)
(set-repeater! evil-ex-search-forward evil-ex-search-next evil-ex-search-previous)
(set-repeater! evil-ex-search-backward evil-ex-search-next evil-ex-search-previous)
;; f/F/t/T/s/S
(after! evil-snipe
(setq evil-snipe-repeat-keys nil
evil-snipe-override-evil-repeat-keys nil) ; causes problems with remapped ;
(set-repeater! evil-snipe-f evil-snipe-repeat evil-snipe-repeat-reverse)
(set-repeater! evil-snipe-F evil-snipe-repeat evil-snipe-repeat-reverse)
(set-repeater! evil-snipe-t evil-snipe-repeat evil-snipe-repeat-reverse)
(set-repeater! evil-snipe-T evil-snipe-repeat evil-snipe-repeat-reverse)
(set-repeater! evil-snipe-s evil-snipe-repeat evil-snipe-repeat-reverse)
(set-repeater! evil-snipe-S evil-snipe-repeat evil-snipe-repeat-reverse)
(set-repeater! evil-snipe-x evil-snipe-repeat evil-snipe-repeat-reverse)
(set-repeater! evil-snipe-X evil-snipe-repeat evil-snipe-repeat-reverse))
;; */#
(set-repeater! evil-visualstar/begin-search-forward
evil-ex-search-next evil-ex-search-previous)
(set-repeater! evil-visualstar/begin-search-backward
evil-ex-search-previous evil-ex-search-next)
;; `evil-collection'
(when (featurep! +everywhere)
(unless doom-reloading-p
(load! "+everywhere"))
(setq evil-collection-company-use-tng (featurep! :completion company +tng))
;; Don't let evil-collection interfere with certain keys
(appendq! evil-collection-key-blacklist
(append (when (featurep! :tools lookup)
'("gd" "gf" "K"))
(when (featurep! :tools eval)
'("gr" "gR"))
'("[" "]" "gz" "<escape>")))
(defadvice! +evil-collection-disable-blacklist-a (orig-fn)
:around #'evil-collection-vterm-toggle-send-escape ; allow binding to ESC
(let (evil-collection-key-blacklist)
(apply orig-fn))))
;; Keybinds that have no Emacs+evil analogues (i.e. don't exist):
;; zq - mark word at point as good word
;; zw - mark word at point as bad
;; zu{q,w} - undo last marking
;; Keybinds that evil define:
;; z= - correct flyspell word at point
;; ]s - jump to previous spelling error
;; [s - jump to next spelling error
(map! :v "@" #'+evil:apply-macro
;; ported from vim-unimpaired
:n "] SPC" #'+evil/insert-newline-below
:n "[ SPC" #'+evil/insert-newline-above
:n "]b" #'next-buffer
:n "[b" #'previous-buffer
:n "]f" #'+evil/next-file
:n "[f" #'+evil/previous-file
:m "]u" #'+evil:url-encode
:m "[u" #'+evil:url-decode
:m "]y" #'+evil:c-string-encode
:m "[y" #'+evil:c-string-decode
(:when (featurep! :lang web)
:m "]x" #'+web:encode-html-entities
:m "[x" #'+web:decode-html-entities)
(:when (featurep! :ui vc-gutter)
:m "]d" #'git-gutter:next-hunk
:m "[d" #'git-gutter:previous-hunk)
(:when (featurep! :ui hl-todo)
:m "]t" #'hl-todo-next
:m "[t" #'hl-todo-previous)
(:when (featurep! :ui workspaces)
:n "gt" #'+workspace:switch-next
:n "gT" #'+workspace:switch-previous
:n "]w" #'+workspace/switch-right
:n "[w" #'+workspace/switch-left)
;; custom vim-unmpaired-esque keys
:m "]#" #'+evil/next-preproc-directive
:m "[#" #'+evil/previous-preproc-directive
:m "]a" #'evil-forward-arg
:m "[a" #'evil-backward-arg
:m "]c" #'+evil/next-comment
:m "[c" #'+evil/previous-comment
:m "]e" #'next-error
:m "[e" #'previous-error
:n "]F" #'+evil/next-frame
:n "[F" #'+evil/previous-frame
:m "]h" #'outline-next-visible-heading
:m "[h" #'outline-previous-visible-heading
:m "]m" #'+evil/next-beginning-of-method
:m "[m" #'+evil/previous-beginning-of-method
:m "]M" #'+evil/next-end-of-method
:m "[M" #'+evil/previous-end-of-method
:n "[o" #'+evil/insert-newline-above
:n "]o" #'+evil/insert-newline-below
:n "gp" #'+evil/reselect-paste
:v "gp" #'+evil/paste-preserve-register
:nv "g@" #'+evil:apply-macro
:nv "gc" #'evilnc-comment-operator
:nv "gx" #'evil-exchange
:nv "gy" #'+evil:yank-unindented
:n "g=" #'evil-numbers/inc-at-pt
:n "g-" #'evil-numbers/dec-at-pt
:v "g=" #'evil-numbers/inc-at-pt-incremental
:v "g-" #'evil-numbers/dec-at-pt-incremental
:v "g+" #'evil-numbers/inc-at-pt
(:when (featurep! :tools lookup)
:nv "K" #'+lookup/documentation
:nv "gd" #'+lookup/definition
:nv "gD" #'+lookup/references
:nv "gf" #'+lookup/file)
(:when (featurep! :tools eval)
:nv "gr" #'+eval:region
:n "gR" #'+eval/buffer
:v "gR" #'+eval:replace-region
;; Restore these keybinds, since the blacklisted/overwritten gr/gR will
;; undo them:
(:after dired
:map dired-mode-map
:n "gr" #'revert-buffer)
(:after notmuch
:map notmuch-common-keymap
:n "gr" #'notmuch-refresh-this-buffer
:n "gR" #'notmuch-poll-and-refresh-this-buffer)
(:after elfeed
:map elfeed-search-update--force
:n "gr" #'elfeed-search-update--force
:n "gR" #'elfeed-search-fetch))
:nv "z=" #'flyspell-correct-word-generic
;; custom evil keybinds
:nv "zn" #'+evil:narrow-buffer
:n "zN" #'doom/widen-indirectly-narrowed-buffer
:n "zx" #'kill-current-buffer
:n "ZX" #'bury-buffer
;; don't leave visual mode after shifting
:v "<" #'+evil/visual-dedent ; vnoremap < <gv
:v ">" #'+evil/visual-indent ; vnoremap > >gv
;; window management (prefix "C-w")
(:map evil-window-map
;; Navigation
"C-h" #'evil-window-left
"C-j" #'evil-window-down
"C-k" #'evil-window-up
"C-l" #'evil-window-right
"C-w" #'other-window
;; Swapping windows
"H" #'+evil/window-move-left
"J" #'+evil/window-move-down
"K" #'+evil/window-move-up
"L" #'+evil/window-move-right
"C-S-w" #'ace-swap-window
;; Window undo/redo
(:prefix "m"
"m" #'doom/window-maximize-buffer
"v" #'doom/window-maximize-vertically
"s" #'doom/window-maximize-horizontally)
"u" #'winner-undo
"C-u" #'winner-undo
"C-r" #'winner-redo
"o" #'doom/window-enlargen
;; Delete window
"d" #'evil-window-delete
"C-C" #'ace-delete-window)
;; text objects
:textobj "a" #'evil-inner-arg #'evil-outer-arg
:textobj "B" #'evil-textobj-anyblock-inner-block #'evil-textobj-anyblock-a-block
:textobj "c" #'evilnc-inner-comment #'evilnc-outer-commenter
:textobj "f" #'+evil:defun-txtobj #'+evil:defun-txtobj
:textobj "g" #'+evil:whole-buffer-txtobj #'+evil:whole-buffer-txtobj
:textobj "i" #'evil-indent-plus-i-indent #'evil-indent-plus-a-indent
:textobj "j" #'evil-indent-plus-i-indent-up-down #'evil-indent-plus-a-indent-up-down
:textobj "k" #'evil-indent-plus-i-indent-up #'evil-indent-plus-a-indent-up
:textobj "x" #'evil-inner-xml-attr #'evil-outer-xml-attr
;; evil-easymotion
(:after evil-easymotion
:map evilem-map
"a" (evilem-create #'evil-forward-arg)
"A" (evilem-create #'evil-backward-arg)
"s" #'evil-avy-goto-char-2
"SPC" (λ!! #'evil-avy-goto-char-timer t)
"/" #'evil-avy-goto-char-timer)
;; evil-snipe
(:after evil-snipe
:map evil-snipe-parent-transient-map
"C-;" (λ! (require 'evil-easymotion)
(call-interactively
(evilem-create #'evil-snipe-repeat
:bind ((evil-snipe-scope 'whole-buffer)
(evil-snipe-enable-highlight)
(evil-snipe-enable-incremental-highlight))))))
;; evil-surround
:v "S" #'evil-surround-region
:o "s" #'evil-surround-edit
:o "S" #'evil-Surround-edit
;; Omni-completion
(:when (featurep! :completion company)
(:prefix "C-x"
:i "C-l" #'+company/whole-lines
:i "C-k" #'+company/dict-or-keywords
:i "C-f" #'company-files
:i "C-]" #'company-etags
:i "s" #'company-ispell
:i "C-s" #'company-yasnippet
:i "C-o" #'company-capf
:i "C-n" #'+company/dabbrev
:i "C-p" #'+company/dabbrev-code-previous)))

View File

@@ -0,0 +1,29 @@
;; -*- no-byte-compile: t; -*-
;;; editor/evil/packages.el
(package! evil)
(package! evil-args)
(package! evil-easymotion)
(package! evil-embrace)
(package! evil-escape)
(package! evil-exchange)
(package! evil-indent-plus)
(package! evil-nerd-commenter)
(package! evil-numbers :recipe (:host github :repo "janpath/evil-numbers"))
(package! evil-snipe)
(package! evil-surround)
(package! evil-textobj-anyblock)
(package! evil-traces)
(package! evil-visualstar)
(package! exato)
(package! evil-quick-diff :recipe (:host github :repo "rgrinberg/evil-quick-diff"))
;;
(when (featurep! +everywhere)
;; `evil-collection-neotree' uses the `neotree-make-executor' macro, but this
;; requires neotree be available during byte-compilation (while installing).
(when (featurep! :ui neotree)
(package! neotree)
(autoload 'neotree-make-executor "neotree" nil nil 'macro))
(package! evil-collection))

View File

@@ -0,0 +1,69 @@
;; -*- no-byte-compile: t; -*-
;;; editor/evil/test/test-evil.el
(describe "feature/evil"
:var (resv project-root)
(require! :editor evil)
(require 'evil)
(load! "../autoload/evil")
(before-each
(fset 'resv #'+evil-resolve-vim-path-a)
(spy-on 'doom-project-root :and-call-fake (lambda () project-root)))
;; `evil-ex-replace-special-filenames' / `+evil-resolve-vim-path-a'
(describe "file modifiers"
(it "supports basic vim file modifiers"
(let ((buffer-file-name "~/.emacs.d/test/modules/feature/test-evil.el")
(default-directory "~/.emacs.d/test/modules/")
(project-root "~/.emacs.d/"))
(expect (resv "%") :to-equal "feature/test-evil.el")
(expect (resv "%:r") :to-equal "feature/test-evil")
(expect (resv "%:r.elc") :to-equal "feature/test-evil.elc")
(expect (resv "%:e") :to-equal "el")
(expect (resv "%:p") :to-equal (expand-file-name buffer-file-name))
(expect (resv "%:h") :to-equal "feature")
(expect (resv "%:t") :to-equal "test-evil.el")
(expect (resv "%:.") :to-equal "feature/test-evil.el")
(expect (resv "%:~") :to-equal "~/.emacs.d/test/modules/feature/test-evil.el")
(expect (file-truename (resv "%:p"))
:to-equal (file-truename buffer-file-name))))
(it "supports nested vim file modifiers"
(let ((buffer-file-name "~/vim/src/version.c")
(default-directory "~/vim/")
(project-root "~/vim/"))
(expect (resv "%:p") :to-equal (expand-file-name "~/vim/src/version.c"))
(expect (resv "%:p:.") :to-equal "src/version.c")
(expect (resv "%:p:~") :to-equal "~/vim/src/version.c")
(expect (resv "%:h") :to-equal "src")
(expect (resv "%:p:h") :to-equal (expand-file-name "~/vim/src"))
(expect (resv "%:p:h:h") :to-equal (expand-file-name "~/vim"))
(expect (resv "%:t") :to-equal "version.c")
(expect (resv "%:p:t") :to-equal "version.c")
(expect (resv "%:r") :to-equal "src/version")
(expect (resv "%:p:r") :to-equal (expand-file-name "~/vim/src/version"))
(expect (resv "%:t:r") :to-equal "version")))
(it "cleans up empty file modifiers"
(let (buffer-file-name default-directory)
(expect (resv "%") :to-equal "")
(expect (resv "%:r") :to-equal "")
(expect (resv "%:e") :to-equal "")
(expect (resv "%:h") :to-equal "")
(expect (resv "%:t") :to-equal "")
(expect (resv "%:.") :to-equal "")
(expect (resv "%:~") :to-equal "")
(expect (resv "%:P") :to-equal "")))
(it "supports substitution modifiers"
(let ((buffer-file-name "~/.emacs.d/test/modules/feature/test-evil.el")
(default-directory "~/.emacs.d/test/modules/"))
(expect (resv "%:s?e?x?") :to-equal "fxature/test-evil.el")
(expect (resv "%:gs?e?x?") :to-equal "fxaturx/txst-xvil.xl")))
(it "cleans up empty substitution modifiers"
(let (buffer-file-name default-directory)
(expect (resv "%:s?e?x?") :to-equal "")
(expect (resv "%:gs?e?x?") :to-equal "")))))