forked from nyobe/evil-lispy
-
Notifications
You must be signed in to change notification settings - Fork 1
/
evil-lispy.el
352 lines (312 loc) · 11.4 KB
/
evil-lispy.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
;;; evil-lispy.el --- Description -*- lexical-binding: t; -*-
;;
;; Copyright (C) 2021 Ethan Leba
;;
;; Author: Ethan Leba <https://github.com/ethanleba>
;; Maintainer: Ethan Leba <ethanleba5@gmail.com>
;; Version: 0.0.1
;; Homepage: https://github.com/ethanleba/evil-lispy
;; Package-Requires: ((emacs "24.3"))
;;
;; This file is not part of GNU Emacs.
;;
;;; Commentary:
;;
;; YAE-Lispy is Yet-Another-Evil Lispy variant that aims to make lispy familiar
;; and intuitive to the Vim user while retaining the full power of Lispy's
;; structural editing capabilities.
;;
;; YAE-Lispy differs from it's peers in the following goals:
;; 1. Vimify Lispy as much as possible, leveraging the Vimmer's muscle memory and minimizing
;; the need to learn/use Emacs binding with lispy.
;; 2. Make Lispy the primary state, while allowing for transient forays into Normal state.
;;
;; Description
;;
;;; Code:
(require 'evil)
(require 'lispy)
;;* Variables
(defvar evil-lispy-normal-exit-commands
'(lispyville-delete
lispyville-delete-line
lispyville-delete-char-or-splice
evil-delete-char
evil-replace)
"The list of Normal state commands that should trigger re-entry into Lispy state.")
(add-to-list 'evil-lispy-normal-exit-commands 'lispyville-discard)
(defvar evil-lispy--command-wrappers
'((:insert . evil-lispy--insert-after)
(:jump . evil-lispy--jump-back)
(:hybrid . evil-lispy--insert-nonspecial)))
;;* Mode/State definition
;;;###autoload
(define-minor-mode evil-lispy-mode
"Context sensitive paredit"
:lighter " eLY"
:keymap (make-sparse-keymap)
:init-value nil
:after-hook (evil-normal-state))
;; Evil sexp editing state
(evil-define-state
lispy "lispy operation state"
:tag " <S> "
:cursor ("orange" box)
:suppress-keymap t
;; disable automatically going into visual mode...
(if (evil-lispy-state-p)
(remove-hook 'activate-mark-hook 'evil-visual-activate-hook t)
(add-hook 'activate-mark-hook 'evil-visual-activate-hook nil t)))
;;* Utilities
(defmacro evil-lispy--bind (&rest code)
"Helper to make an bindable command. "
`(lambda ()
(interactive)
,@code))
(defun evil-lispy--insert-after (fun)
"Helper to make an bindable command"
`(lambda ()
,(format "Call `%s', and then enter insert mode.\n\n%s"
(symbol-name fun) (documentation fun))
(interactive) (call-interactively #',fun) (evil-lispy-enter-transient-insert-state)))
(defun evil-lispy--jump-back (fun)
"Helper to make an bindable command"
`(lambda ()
,(format "Call `%s', and then jump to the left-most paren.\n\n%s"
(symbol-name fun) (documentation fun))
(interactive) (call-interactively #',fun) (lispy-backward 1)))
(defun evil-lispy--insert-nonspecial (fun)
"Helper to make an bindable command"
`(lambda ()
,(format "Call `%s', and then jump to the left-most paren.\n\n%s"
(symbol-name fun) (documentation fun))
(interactive)
(call-interactively #',fun)
(unless (or (region-active-p)
(lispy-left-p)
(lispy-right-p)
(and (lispy-bolp)
(or (looking-at lispy-outline-header)
(looking-at lispy-outline))))
(evil-insert-state))))
(defun evil-lispy-define-key (keymap key def type)
"Forward to (`define-key' KEYMAP KEY FUNC).
FUNC is obtained from (`lispy--insert-or-call' DEF PLIST)."
(declare (indent 3))
(require 'eldoc)
(let* ((lamb (alist-get type evil-lispy--command-wrappers))
(func (defalias (intern (concat "yae-" (symbol-name def) "-and-" (substring (symbol-name type) 1)))
(funcall lamb def))))
(eldoc-add-command func)
(define-key keymap (kbd key) func)))
;;* Evil-Lispy commands
(defun evil-lispy-enter-transient-normal-state ()
"Execute the next command in Normal state."
(interactive)
(let ((marker (point-marker)))
(lispy--remember)
(evil-normal-state)
(evil-delay `(or (memq this-command `,evil-lispy-normal-exit-commands)
(evil-lispy-state-p)
(evil-insert-state-p))
`(unless (evil-lispy-state-p)
(with-current-buffer ,(current-buffer)
(if (evil-insert-state-p)
(evil-delay `(or (not (evil-insert-state-p))
(evil-lispy-state-p))
`(with-current-buffer ,(current-buffer)
(unless (evil-lispy-state-p)
(evil-lispy-state)
(if (symbol-at-point)
(lispy-mark-symbol)
(lispy-backward 1)))
;; FIXME: This marking behavior is not sane.
)
'post-command-hook)
(progn (when (evil-normal-state-p)
;; TODO: disable inc. search highlighting (hope this doesn't affect anything else!)
;; (evil-force-normal-state)
))
(evil-lispy-state)
;; FIXME empty string
(if (symbol-at-point)
(lispy-mark)
(lispy-backward 1)))))
'post-command-hook)
(deactivate-mark t)))
(defun evil-lispy-enter-transient-insert-state ()
"Execute the next command in Normal state."
(interactive)
(lispy--remember)
(evil-insert-state)
(evil-delay `(or (not (evil-insert-state-p))
(evil-lispy-state-p))
`(with-current-buffer ,(current-buffer)
(unless (evil-lispy-state-p)
(evil-lispy-state)
(if (symbol-at-point)
(lispy-mark-symbol)
(lispy-backward 1))
)
;; FIXME: This marking behavior is not sane.
)
'post-command-hook)
(deactivate-mark t))
(defun evil-lispy-alter-sexp-right ()
"Move bound of sexp right"
(interactive)
(if (looking-at lispy-left)
(lispy-barf 1)
(lispy-slurp 1)))
(defun evil-lispy-alter-sexp-left ()
"Move bound of sexp left"
(interactive)
(if (looking-at lispy-left)
(lispy-slurp 1)
(lispy-barf 1)))
(defun evil-lispy-bounds ()
(interactive)
(if (not mark-active)
;; TODO: Do this with just lispy commands?
(save-excursion
(when (lispy-right-p)
(lispy-different))
(cons (1+ (point))
(progn (lispy-different)
(1- (point)))))
(car (region-bounds))))
(defun evil-lispy-open-below ()
(interactive)
(lispy-newline-and-indent)
(previous-line)
(evil-lispy-enter-transient-insert-state))
(defun evil-lispy-open-above ()
(interactive)
(lispy-different)
(lispy-newline-and-indent)
(evil-lispy-enter-transient-insert-state))
(defun evil-lispy-insert ()
"Move bound of sexp left"
(interactive)
(goto-char (car (evil-lispy-bounds)))
(evil-lispy-enter-transient-insert-state))
(defun evil-lispy-append ()
"Move bound of sexp left"
(interactive)
(goto-char (cdr (evil-lispy-bounds)))
(evil-lispy-enter-transient-insert-state))
(defun evil-lispy-mark-nth (arg)
"Move bound of sexp left"
(interactive "p")
(call-interactively (if (> arg 1) #'lispy-mark-list #'lispy-mark-car)))
(defun evil-lispy-repeat ()
"Repeat last command with last prefix arg."
(interactive)
(unless (equal last-command 'evil-lispy-repeat)
(setq lispy-repeat--command last-command)
(setq lispy-repeat--prefix-arg
(or last-prefix-arg 1)))
(setq current-prefix-arg lispy-repeat--prefix-arg)
(call-interactively lispy-repeat--command))
(defun evil-lispy-yank-delete (arg)
(interactive "p")
(lispy-new-copy)
(lispy-delete arg))
;;* Evil standard keymap overrides
(let ((map evil-lispy-mode-map))
;; Entering lispy state
(evil-define-key 'normal evil-lispy-mode-map (kbd "(")
(evil-lispy--bind
(evil-lispy-state)
(evil-forward-char 1 nil t)
(lispy-backward 1)))
(evil-define-key 'normal evil-lispy-mode-map (kbd ")")
(evil-lispy--bind
(evil-lispy-state)
(evil-backward-char 1 nil t)
(lispy-forward 1)))
(evil-define-key 'insert evil-lispy-mode-map (kbd "SPC") #'lispy-space))
;;* Lispy state binds
(let ((map evil-lispy-state-map))
;; Exiting state
(define-key map (kbd "C-g")
(evil-lispy--bind
(deactivate-mark)
(evil-normal-state)))
(define-key map [escape] (kbd "C-g"))
(define-key map "i" #'evil-lispy-insert)
(define-key map "A" #'lispy-beginning-of-defun)
(evil-lispy-define-key map "RET" #'lispy-newline-and-indent :hybrid)
(define-key map "o" #'evil-lispy-open-above)
(define-key map "O" #'evil-lispy-open-below)
;; Navigation
(evil-lispy-define-key map "(" #'lispy-parens :hybrid)
(define-key map (kbd ")") #'lispy-out-forward)
(define-key map "h" #'lispy-backward)
(define-key map "j" #'lispy-down)
(define-key map "k" #'lispy-up)
(define-key map "l" #'lispy-forward)
(define-key map "f" #'lispy-flow)
(define-key map "D" #'lispy-different)
(define-key map "a" #'evil-lispy-append)
(define-key map "gd" #'lispy-follow)
;; (define-key map "G" #'lispy-goto)
(define-key map "G" #'counsel-imenu)
(define-key map "q" #'lispy-ace-paren)
(define-key map "b" #'lispy-back)
(define-key map "Q" (lambda () (interactive) (lispy-ace-paren 2)))
;; FIXME: real func
;; Paredit transformations
(define-key map ">" #'evil-lispy-alter-sexp-right)
(define-key map "<" #'evil-lispy-alter-sexp-left)
(define-key map "/" #'lispy-splice)
(define-key map "r" #'lispy-raise)
(define-key map "R" #'lispy-raise-some)
(define-key map (kbd "gJ") #'lispy-split)
;; (define-key map "J" #'lispy-join)
(define-key map "J" #'lispy-oneline)
(define-key map "C" #'lispy-convolute)
(define-key map (kbd "C-k") #'lispy-move-up)
(define-key map (kbd "C-j") #'lispy-move-down)
;; (define-key map "O" #'lispy-oneline)
(define-key map "M" #'lispy-multiline)
(evil-lispy-define-key map ";" #'lispy-comment :hybrid)
(define-key map "C" #'lispy-clone)
(define-key map "t" #'lispy-teleport)
;; Kill related
(evil-lispy-define-key map "DEL" #'lispy-delete-backward :hybrid)
(evil-lispy-define-key map "d" #'lispy-delete :jump)
(evil-lispy-define-key map "m" #'evil-lispy-yank-delete :jump)
(evil-lispy-define-key map "c" #'lispy-delete :insert)
(evil-lispy-define-key map "p" #'lispy-paste :jump)
(define-key map "P" #'yank-pop)
(define-key map "n" #'evil-lispy-enter-transient-normal-state)
(define-key map "y" #'lispy-new-copy)
;; Marking
(define-key map "s" #'lispy-ace-symbol)
(evil-lispy-define-key map "gs" #'lispy-ace-symbol-replace :insert)
(define-key map "v" #'evil-lispy-mark-nth)
(define-key map "V" #'lispy-mark-list)
(define-key map "-" #'lispy-ace-subword)
;; Misc
(define-key map "u" #'lispy-undo)
(define-key map "e" #'lispy-eval)
(define-key map "E" #'lispy-eval-and-insert)
(define-key map "K" #'lispy-describe)
(define-key map "F" #'lispy-follow)
(define-key map "." #'evil-lispy-repeat)
(define-key map "," #'self-insert-command)
(define-key map "x" #'lispy-x)
;; (define-key map "" #'lispy-describe)
(define-key map "gq" #'lispy-normalize)
(define-key map (kbd "TAB") #'lispy-tab)
(define-key map (kbd "<backtab>") #'lispy-shifttab)
(define-key map "zz" #'evil-scroll-line-to-center)
;; Outline
(define-key map "gj" #'lispy-outline-next)
(define-key map "gk" #'lispy-outline-prev)
;; Digit argument
(mapc (lambda (x) (define-key map (format "%d" x) #'digit-argument))
(number-sequence 0 9)))
(provide 'evil-lispy)
;;; evil-lispy.el ends here