emacs-evil / evil-collection

A set of keybindings for evil-mode
GNU General Public License v3.0
1.22k stars 272 forks source link

Lispy Key Theme #116

Open jojojames opened 6 years ago

jojojames commented 6 years ago

Following from https://github.com/noctuid/lispyville/issues/29

It might be nice to make the lispy-shortkey bindings for vim-y.

Here's just a quick copy-pasta of what I have so far (very rough and probably has some unnecessary changes).

  (lispy-define-key lispy-mode-map-special "y" 'lispy-new-copy) ;; `lispy-occur'
  (lispy-define-key lispy-mode-map-special "%" 'lispy-different)
  (lispy-define-key lispy-mode-map-special "d" (lispy-action-then-next-sexp 'lispy-delete)) ;; `lispy-different'
  (lispy-define-key lispy-mode-map-special "x" (lispy-action-then-next-sexp 'lispy-kill)) ;; `lispy-x'
  (lispy-define-key lispy-mode-map-special "p" 'lispy-yank) ;; `lispy-eval-other-window'
  (lispy-define-key lispy-mode-map-special "$" 'lispy-move-end-of-line)
  (lispy-define-key lispy-mode-map-special "^" 'lispy-move-beginning-of-line)

  (lispy-define-key lispy-mode-map-special "f" 'lispy-occur) ;; `lispy-flow'
  ;; (lispy-define-key lispy-mode-map-special "/" nil)
  ;; `lispy-splice'
  (lispy-define-key lispy-mode-map-special "J" 'lispy-eval-other-window) ;; `lispy-outline-next'
  (lispy-define-key lispy-mode-map-special "K" 'lispy-describe-inline) ;; `lispy-outline-prev'
  (lispy-define-key lispy-mode-map-special "i" 'lispy-x) ;; `lispy-tab'
  ;; (lispy-define-key lispy-mode-map-special (kbd "I") 'lispy-splice) ;; `lispy-shifttab'

  (lispy-define-key lispy-mode-map-special "M-j" 'lispy-move-down) ;; `lispy-split'
  (lispy-define-key lispy-mode-map-special "M-k" 'lispy-move-up) ;; `lispy-kill-sentence'

  (lispy-define-key lispy-mode-map-special "w" 'lispy-flow) ;; `lispy-move-up'
  (lispy-define-key lispy-mode-map-special "s" 'lispy-splice) ;; `lispy-move-down'

Instead of % for lispy-different, could probably just use 'o', but 'o' already has something attached.

Also / could probably be lispy-occur.

The lispy-action-then-next-sexp was just so I can rapidly type 'd' 3 times to delete 3 sexps quickly.

(defun lispy-action-then-next-sexp (lispy-action)
    "Return function that triggers LISPY-ACTION and then moves to next sexp."
    (defalias (intern (format "%S-then-next-sexp" lispy-action))
      (lambda ()
        (interactive)
        (call-interactively lispy-action)
        (unless (or (lispy-left-p)
                    (lispy-right-p)
                    (and (lispy-bolp)
                         (or (looking-at lispy-outline-header)
                             (looking-at lispy-outline))))
          (call-interactively #'sp-next-sexp)))))

@noctuid @Somelauw @Ambrevar

Would be good to see what you guys are doing and maybe we can synthesize something up and further cut down lispy's barrier to entry for vimmers.

Ambrevar commented 6 years ago

Timing couldn't be better! Let me drop my current config for now, I'll discuss this more in-depth later:

(with-eval-after-load 'lispyville
  ;; TODO: lispy-occur: helm-occur does not restrict to region.
  (lispyville-set-key-theme
   '(operators            ; Add equivalent for lispy-delete?
     c-w                  ; Bind M-backspace to lispyville-delete-backward-word?
     (escape insert)
     slurp/barf-cp
     ;; (mark insert)
     mark-toggle                        ; TODO: Check out readme.
     ))
  (lispyville--define-key '(motion normal visual)
    (kbd "M-h") #'lispyville-previous-opening
    (kbd "M-l") #'lispyville-next-opening
    (kbd "M-j") #'lispy-down
    (kbd "M-k") #'lispy-up
    (kbd "M-H") #'lispy-up-slurp        ; lispy-down-slurp?
    (kbd "M-J") #'lispyville-drag-forward
    (kbd "M-K") #'lispyville-drag-backward
    (kbd "M-L") #'lispy-move-right      ; lispy-up-slurp?
    (kbd "C-x C-e") #'lispy-eval
    (kbd "C-j") #'lispy-split
    (kbd "C-1") #'lispy-describe-inline
    (kbd "C-2") #'lispy-arglist-inline
    (kbd "C-4") #'lispy-x
    ;; (kbd "M-;") #'lispy-comment ; This conflicts with `iedit-toggle-selection' default binding.
    ;; TODO: lispy-eval-and-replace
    ")" #'lispy-right-nostring
    (kbd "=") #'lispyville-prettify)
  (lispyville--define-key 'insert
    ";" 'lispy-comment
    ":" 'lispy-colon
    "'" 'lispy-tick
    "`" 'lispy-backtick
    "\"" 'lispy-quotes
    "(" 'lispy-parens
    ")" 'lispy-right-nostring)
  (lispyville--define-key '(motion normal)
    "q" 'lispy-ace-paren              ; REVIEW: Conflicts with magit-blame's quit.  Fixed?
    ;; "Q" 'lispy-ace-symbol
    "Y" 'lispy-new-copy
    "C" 'lispy-clone
    "D" 'lispy-kill))
noctuid commented 6 years ago

Some comments:

Here is some of my configuration. Ideally as few commands as possible (or none) should be lost by using the key theme.

(general-def lispy-mode-map
  :definer 'lispy
  "y" #'lispy-new-copy                  ; lose lispy-occur -> /
  ;; swap p and P
  "p" #'lispy-paste
  "P" #'lispy-eval-other-window
  "d" #'noct:lispy-delete               ; lose lispy-different -> o
  ;; like in visual state
  "o" #'lispy-different                 ; lose lispy-other-mode (don't use)
  "/" #'lispy-occur                     ; lose lispy-splice -> x
  "x" #'lispy-splice                    ; lose lispy-x -> c
  ;; "change" works as a mnemonic for some of these actions (e.g. change to
  ;; cond, change to if, change to defun, and change to lambda; there's not
  ;; really a common theme for the other actions)
  "c" #'lispy-x                         ; lose lispy-clone -> q
  "f" #'lispy-ace-paren                 ; lose lispy-flow -> n
  ;; maybe
  "F" #'lispy-ace-char                  ; lose lispy-follow -> ???
  ;; or this
  "t" #'lispy-ace-char                  ; lose lispy-teleport -> ???
  ;; kind of like repeating a search
  "n" #'lispy-flow
  ;; I don't have a good mnemonic for this other than that q sounds vaguely
  ;; similar to c
  "q" #'lispy-clone
  ;; swap m and v
  "v" #'lispy-mark-list
  "m" #'lispy-view

  ;; extra not-vimmy personal configuration
  ;; swap H and A; makes more sense given default h and a
  "H" #'lispy-beginning-of-defun
  "A" #'lispy-ace-symbol-replace)

;; alternative that removes only convolute
"t" #'lispy-ace-char
"C" #'lispy-clone
"q" #'lispy-teleport
"Q" #'lispy-other-mode

I think most of the above is fairly reasonable, but feedback would be appreciated. For example, I think it makes sense to bind f to one of the ace commands. I bind it to lispy-special-ace-paren because I use it more often. lispy-special-ace-char makes more sense on t I think. Both of the lost commands (teleport and follow) are fairly useful, and Q (the only freed key that isn't rebound) isn't ideal for either. q is also weird for lispy-clone. Maybe C could be used instead like @Ambrevar has (but in special). lispy-convolute (default C) is probably the most situational binding lispy makes; I've never actually used it or noticed a situation where I could. q could then be used for teleport instead (I guess it kind of looks like a portal). I think the alternative listed at the bottom makes the most sense. Thoughts?

As for d, I'm using this:

(defun noct:lispy-delete (arg)
  "Copy and delete current sexp.
Passes ARG to `lispy-delete' or `lispy-delete-back'."
  (interactive "p")
  (cond ((or (lispy-left-p)
             (region-active-p))
         (lispy-new-copy)
         (lispy-delete arg))
        ((lispy-right-p)
         (lispy-new-copy)
         (lispy-delete-backward arg))))

@jojojames After using the above for a while, I feel like it should stay in special like your d. I could add that functionality and make a PR to lispy for the above command.

I'm splitting this comment because the rest of it isn't as relevant/important.

noctuid commented 6 years ago

Some reasons why losing lispy-other-mode (when binding o to lispy-other) may not be a huge deal:

That said, I think the movement commands may make it worth putting on one of the freed keys (e.g. Q like in the previous example) even though there's not a great mnemonic.

TAB can be used for hiding/showing outline sections, indenting (though usually unnecessary), and changing the nesting/indentation level of sexps (similar to hjkl in lispy-other-mode) all at once:

;; TODO PR
(defun noct:lispy-tab (arg)
  (interactive "p")
  (if (looking-at lispy-outline)
      (outline-toggle-children)
    (lispy-indent-adjust-parens arg)))

(general-def lispy-mode-map-lispy
  "TAB" #'noct:lispy-tab
  "<backtab>" #'lispy-dedent-adjust-parens)

The above functionality is basically the same as adjust-parens.el if you are familiar with that package (smartparens has equivalent commands as well). It's easier to explain with examples:

;; | is point
;; TAB still works for indentation if the current line needs to be indented
(foo
|bar)
;; after TAB
(foo
 |bar)

;; if current like is correctly indented
(let ((a 1)))
|(b 2)
;; after TAB (like k / `lispy-up-slurp' )
(let ((a 1))
  |(b 2))
;; another TAB (or using count originally)
(let ((a 1)
      |(b 2)))
;; reversible with S-TAB (like `lispy-move-right' for this specific case)
(let ((a 1))
  |(b 2))
;; then
(let ((a 1)))
|(b 2)

That's about all I have wrt a vim key theme. The rest of my lispy config is mostly commands that I've been meaning to add to lispy. If you're curious, here is some of it:

;; used for writing tests
(defun noct:lispy-stringify-oneline ()
  "Combination of `lispy-stingify' and `lispy-string-oneline'."
  (interactive)
  (lispy-stringify nil)
  (lispy-string-oneline))

(defun noct:flatten (arg)
  "`lispy-flatten' but use macrostep when possible."
  (interactive "P")
  (if (and (eq major-mode 'emacs-lisp-mode)
           (macrop (intern (lispy--current-function))))
      (macrostep-expand)
    (lispy-flatten arg)))

;; this is in the README, but it might be nice if it was directly in lispy
(defun abo-enable-lispy-for-M-: ()
  "Enable lispy in minibuffer for `eval-expression'."
  (when (eq this-command #'eval-expression)
    (lispy-mode)))
(add-hook 'minibuffer-setup-hook #'abo-enable-lispy-for-M-:)

;; this is also from the parinfer key theme; examples below
(general-def lispy-mode-map-lispy
  "(" #'lispy-parens-auto-wrap
  ;; "{" #'lispy-braces-auto-wrap
  ;; ...
  )

(defun noct:paren-advice (&rest _)
  (cond ((lispy--in-string-or-comment-p)
         ;; temporary workaround for lispy's weird paren behavior in strings
         (insert ")")
         (backward-char))
        (t
         (let ((map (make-sparse-keymap)))
           ;; easy way to undo unwanted wrap on reaction
           ;; instead of planning with a prefix arg
           (define-key map ")"
             (lambda ()
               (interactive)
               (call-interactively #'lispy-barf-to-point)
               (backward-char)))
           (set-transient-map map)))))

(general-add-advice 'lispy-parens-auto-wrap :after #'noct:paren-advice)

;; On line by itself, `lispy-parens-auto-wrap' acts like the default "(":
|
;; to
(|)

;; wraps when before sexp
|1 2 3
;; type (foo
(foo 1 2 3)

;; wrapping can be prevent with C--/negative argument or by pressing ")"
;; afterwards with above advice:
(|) 1 2 3
jojojames commented 6 years ago

(kbd "=") #'lispyville-prettify)

This function looks interesting though I've never used it. If there was something like that in lispy, = would be the best place for it for sure.

Revising after looking at above^:

  (lispy-define-key lispy-mode-map-special "y" 'lispy-new-copy) ;; `lispy-occur' -> /

  ;; Swap p and P
  (lispy-define-key lispy-mode-map-special "p" 'lispy-paste) ;; `lispy-eval-other-window' -> P
  (lispy-define-key lispy-mode-map-special "P" 'lispy-eval-other-window) ;; `lispy-paste' -> p

  ;; Like in visual state
  (lispy-define-key lispy-mode-map-special "o" 'lispy-different) ;; `lispy-other-mode'

  (lispy-define-key lispy-mode-map-special "d" (lispy-action-then-next-sexp 'lispy-delete)) ;; `lispy-different' -> o
  (lispy-define-key lispy-mode-map-special "x" 'lispy-splice) ;; `lispy-x'

  (lispy-define-key lispy-mode-map-special "/" 'lispy-occur) ;; `lispy-splice' -> x

  (lispy-define-key lispy-mode-map-special "f" 'lispy-ace-paren) ;; `lispy-flow' -> w

  (lispy-define-key lispy-mode-map-special "J" 'lispy-join) ; `lispy-outline-next'

  (lispy-define-key lispy-mode-map-special "K" 'lispy-describe) ;; `lispy-outline-prev'
  (lispy-define-key lispy-mode-map-special "i" 'lispy-x) ;; `lispy-tab'

  (lispy-define-key lispy-mode-map-special "M-j" 'lispy-move-down) ;; `lispy-split'
  (lispy-define-key lispy-mode-map-special "M-k" 'lispy-move-up) ;; `lispy-kill-sentence'

  (lispy-define-key lispy-mode-map-special "w" 'lispy-flow) ;; `lispy-move-up'
  (lispy-define-key lispy-mode-map-special "s" 'lispy-splice) ;; `lispy-move-down'

  ;; Swap m and v
  (lispy-define-key lispy-mode-map-special "v" 'lispy-mark-list) ;; `lispy-view' -> m
  (lispy-define-key lispy-mode-map-special "m" 'lispy-view) ;; `lispy-mark-list' -> v

We probably don't want this:

(lispy-define-key lispy-mode-map-special "i" 'lispy-x) ;; `lispy-tab'

Maybe T for lispy-teleport. That way we can have t and f for the ace-* functions while keeping F at follow.

Then Q can probably go to other-mode?

Somelauw commented 6 years ago

What I'm currently using is:


   ;; I base myself on paredit,because I don't like [ and ] to jump
   ;; Also, I like " and ' better in paredit-style
   (lispy-set-key-theme '(special paredit c-digits))

   ;; This makes the < and > behave similar to evil-cleverparens
   ;; Also I think it's more intuitive
   (lispy-define-key lispy-mode-map "<" #'lispy-slurp-or-barf-left)
   (lispy-define-key lispy-mode-map ">" #'lispy-slurp-or-barf-right)

   ;; Makes backspace splice sexp
   (define-key lispy-mode-map (kbd "DEL") 'lispy-delete-backward-or-splice-or-slurp)

I'll give my thoughts about @jojojames' bindings.

(lispy-define-key lispy-mode-map-special "d" (lispy-action-then-next-sexp 'lispy-delete)) ;; `lispy-different'

Is this the same as lispy-kill-at-point, which is mapped to C-, by default?

(lispy-define-key lispy-mode-map-special "%" 'lispy-different)

Although % is the default binding in evil, I think lispy-different is used a lot, so it might worth putting this on o or something easier to reach than %. o jumps to the other end in Vim in visual mode.

(lispy-define-key lispy-mode-map-special "i" 'lispy-x) ;; `lispy-tab'

I prefer keeping lispy-tab on i, because I use this command a lot in mark-mode to navigate to subexpressions.

lispy-kill seems like it's better left bound to a modifier and not in special (default C-k) same as above for ^ and $ (they make sense in normal state but not as much in special)

I agree. Personally I use readline keybinding (C-k, C-a and C-e). Vim's insert mode uses home and end for these actions. There is no C-k in Vim, but it has C-u, which someone may want to lispify.

I think it makes sense to keep w and s; these are useful enough that I think it makes sense to keep them on single letter keys, and I think n is potentially a decent alternative to use for flow

I think w and s are intuitive, because many games use wasd. On the other hand, I have the impression that most evil-collection's bindings (and lispyville) already use M-j and M-k and this would make the lispy keytheme inconsistent with everything else.

I'm pretty sure a PR directly to lispy will be accepted

I think so too, especially because there is already an evil keytheme in lispy. It's not well-designed in my opinion, so it would be nice if it were replaced by something better.

jojojames commented 6 years ago

Is this the same as lispy-kill-at-point, which is mapped to C-, by default?

Not sure, my intention was to do a deletion and then move point to the next sexp. That way you can press 'd' over and over again to delete.

Although % is the default binding in evil, I think lispy-different is used a lot, so it might worth putting this on o or something easier to reach than %. o jumps to the other end in Vim in visual mode.

Yup. I changed it to o in my more recent post.

I prefer keeping lispy-tab on i, because I use this command a lot in mark-mode to navigate to subexpressions.

Noted. The i was just a placeholder for now.

I agree. Personally I use readline keybinding (C-k, C-a and C-e). Vim's insert mode uses home and end for these actions. There is no C-k in Vim, but it has C-u, which someone may want to lispify.

Sounds good, removed.

I think w and s are intuitive, because many games use wasd. On the other hand, I have the impression that most evil-collection's bindings (and lispyville) already use M-j and M-k and this would make the lispy keytheme inconsistent with everything else.

I'm open to w/s or M-j/M-k either way. Maybe adding both will be fine.

;; This makes the < and > behave similar to evil-cleverparens ;; Also I think it's more intuitive (lispy-define-key lispy-mode-map "<" #'lispy-slurp-or-barf-left) (lispy-define-key lispy-mode-map ">" #'lispy-slurp-or-barf-right)

;; Makes backspace splice sexp

(define-key lispy-mode-map (kbd "DEL") 'lispy-delete-backward-or-splice-or-slurp)

These look really interesting. Any thoughts on using these as defaults instead (instead of the default slurp/barf)?

jojojames commented 6 years ago

I think so too, especially because there is already an evil keytheme in lispy. It's not well-designed in my opinion, so it would be nice if it were replaced by something better.

Yeah, I think it'd be good to eventually PR lispy directly though I want to avoid too much noise while we're still thinking of a nice set of defaults.

jojojames commented 6 years ago

Revised:

  (lispy-define-key lispy-mode-map-special "y" 'lispy-new-copy) ;; `lispy-occur' -> /

  ;; Swap p and P
  (lispy-define-key lispy-mode-map-special "p" 'lispy-paste) ;; `lispy-eval-other-window' -> P
  (lispy-define-key lispy-mode-map-special "P" 'lispy-eval-other-window) ;; `lispy-paste' -> p

  ;; Like in visual state
  (lispy-define-key lispy-mode-map-special "o" 'lispy-different) ;; `lispy-other-mode' -> Q

  (lispy-define-key lispy-mode-map-special "d" (lispy-action-then-next-sexp 'lispy-delete)) ;; `lispy-different' -> o
  (lispy-define-key lispy-mode-map-special "x" 'lispy-splice) ;; `lispy-x'

  (lispy-define-key lispy-mode-map-special "/" 'lispy-occur) ;; `lispy-splice' -> x

  (lispy-define-key lispy-mode-map-special "f" 'lispy-ace-paren) ;; `lispy-flow' -> w

  (lispy-define-key lispy-mode-map-special "J" 'lispy-join) ; `lispy-outline-next'

  (lispy-define-key lispy-mode-map-special "K" 'lispy-describe) ;; `lispy-outline-prev'

  (lispy-define-key lispy-mode-map-special "M-j" 'lispy-move-down) ;; `lispy-split'
  (lispy-define-key lispy-mode-map-special "M-k" 'lispy-move-up) ;; `lispy-kill-sentence'

  (lispy-define-key lispy-mode-map-special "w" 'lispy-flow) ;; `lispy-move-up'
  (lispy-define-key lispy-mode-map-special "s" 'lispy-splice) ;; `lispy-move-down'

  ;; Swap m and v
  (lispy-define-key lispy-mode-map-special "v" 'lispy-mark-list) ;; `lispy-view' -> m
  (lispy-define-key lispy-mode-map-special "m" 'lispy-view) ;; `lispy-mark-list' -> v

  (lispy-define-key lispy-mode-map-special "q" 'lispy-x) ;; `lispy-ace-paren' -> f

  (lispy-define-key lispy-mode-map-special "t" 'lispy-ace-char) ;; `lispy-teleport' -> T
  (lispy-define-key lispy-mode-map-special "T" 'lispy-teleport)
  (lispy-define-key lispy-mode-map-special "Q" 'lispy-other-mode) ;; `lispy-ace-char' -> t
noctuid commented 6 years ago

This function looks interesting though I've never used it. If there was something like that in lispy, = would be the best place for it for sure.

lispy-tab does this but also a bunch of other stuff.

I think the outline commands are worth keeping in some form; I guess it would be okay if they were bound with a modifer outside of special since they don't really depend on special, and once on an outline j and k can be used for navigation.

M-j and M-k

My main issue with this is that M-j and M-k now each have two different meanings. I think that this key theme should also alter lispy's non-special keybindings (e.g. M-j for lispy-splice isn't ideal).

Why is splice bound twice? While s is mnemonic, I think x makes more sense from a vim perspective.

c as lispy-clone is missing.

Maybe T for lispy-teleport. That way we can have t and f for the ace-* functions while keeping F at follow.

Since it's still unbound, this seems like a good idea. q and Q now are both hydras, which makes sense.

Ambrevar commented 6 years ago

M-j M-k

I don't think that w / s mean much in general, so I'd rather stick to the more conventional M-<hjkl>. What I did was using those bindings to navigate around while the shifted version moves expressions around. Pretty handy and intuitive in my opinion.

jojojames commented 6 years ago

Revised:

  (lispy-define-key lispy-mode-map-special "y" 'lispy-new-copy) ;; `lispy-occur' -> /

  ;; Swap p and P
  (lispy-define-key lispy-mode-map-special "p" 'lispy-paste) ;; `lispy-eval-other-window' -> P
  (lispy-define-key lispy-mode-map-special "P" 'lispy-eval-other-window) ;; `lispy-paste' -> p

  ;; Like in visual state
  (lispy-define-key lispy-mode-map-special "o" 'lispy-different) ;; `lispy-other-mode' -> Q

  (lispy-define-key lispy-mode-map-special
      "d" (lispy-action-then-next-sexp 'lispy-delete)) ;; `lispy-different' -> o

  (lispy-define-key lispy-mode-map-special "x" 'lispy-splice) ;; `lispy-x' -> q

  (lispy-define-key lispy-mode-map-special "/" 'lispy-occur) ;; `lispy-splice' -> x

  (lispy-define-key lispy-mode-map-special "f" 'lispy-ace-paren) ;; `lispy-flow' -> w

  (lispy-define-key lispy-mode-map-special "J" 'lispy-join) ; `lispy-outline-next'

  (lispy-define-key lispy-mode-map-special "K" 'lispy-describe) ;; `lispy-outline-prev'

  (lispy-define-key lispy-mode-map-special "n" 'lispy-flow) ;; `lispy-new-copy' -> y

  ;; Swap m and v
  (lispy-define-key lispy-mode-map-special "v" 'lispy-mark-list) ;; `lispy-view' -> m
  (lispy-define-key lispy-mode-map-special "m" 'lispy-view) ;; `lispy-mark-list' -> v

  (lispy-define-key lispy-mode-map-special "q" 'lispy-x) ;; `lispy-ace-paren' -> f

  (lispy-define-key lispy-mode-map-special "t" 'lispy-ace-char) ;; `lispy-teleport' -> T
  (lispy-define-key lispy-mode-map-special "T" 'lispy-teleport)
  (lispy-define-key lispy-mode-map-special "Q" 'lispy-other-mode) ;; `lispy-ace-char' -> t

  (lispy-define-key lispy-mode-map-special "M-j" 'lispy-move-down) ;; `lispy-split'
  (lispy-define-key lispy-mode-map-special "M-k" 'lispy-move-up) ;; `lispy-kill-sentence'

  ;; These don't change.
  (lispy-define-key lispy-mode-map-special "w" 'lispy-move-up) ;; `lispy-move-up'
  (lispy-define-key lispy-mode-map-special "s" 'lispy-move-down) ;; `lispy-move-down'
  (lispy-define-key lispy-mode-map-special "c" 'lispy-clone) ;; `lispy-clone'

w/s still up in the air as well as M-j/M-k

Tangential: Anyone know if lispy plays well with undo-tree?

I think the outline commands are worth keeping in some form;

I don't use them yet so I don't have a good idea where to place them.

I think that this key theme should also alter lispy's non-special keybindings

I was mainly focused on the single-key bindings but this would be a good idea as well.

jojojames commented 6 years ago

Still playing around with this every so often. The keybinds are pretty similar. Only w and s for moving s-exps seem to be a little awkward for me still.

I still need to look into the M-* type bindings.

jojojames commented 6 years ago

I've pushed a lot of my in progress lispy bindings to evil-collection-lispy. This is not in the default list of modes so if anyone wants to try it out, they'll have to add lispy to the init list themselves.

It'd be good if others tried it and gave feedback. I tried to structure the code to look like what it would look like if it were a PR to lispy itself.

The non-special mode map is a duplicate of evilcp for now. @Somelauw, it looks like you had opinions on that keymap judging from https://github.com/abo-abo/lispy/pull/327 so it'd be good to get your opinions here.

I'm planning (slowly) to revise both sets (the special one seems fairly solid at this point) of keymaps and then make a PR to lispy in the future (not sure when).

Somelauw commented 6 years ago

@jojojames The existing evil theme had some problems such as duplicated key bindings, special behaviour of y in insert mode and unnecessary dependencies on evil-cleverparens and smartparens. At first I tried to make a PR to attempt to fix these problems and mostly resolved conflicting keybindings in favour of evil-cleverparens (which itself is mostly based on the paredit). The original contributor seemed to prefer to have these resolved in favour of lispy. Then I realized it would be better if this theme could freely be combined with lispy, paredit or parinfer editing styles, but neither the original contribution nor my PR provides for this.

Now I think it would be best to:

Instead recommend new users to put something like:

(lispy-set-key-theme '(lispy evil-special))

in their config

Maybe this should be discussed further on https://github.com/abo-abo/lispy/pull/327

Ambrevar commented 6 years ago

I gave it a shot and it seems pretty neat, although I personally find the combination of Evil states and Lispy special keys quite confusing to my taste.

That said, I think this binding set deserves some extra explaination:

It seems that some bindings don't work in normal state but I'm not sure to get the logic there.

noctuid commented 6 years ago

I personally find the combination of Evil states and Lispy special keys quite confusing to my taste.

You may have seen the functionality already, but lispyville has some features to potentially mitigate this (e.g. keybindings to more get into insert state/special from both normal and insert state). If you elaborate, I could potentially offer suggestions.

Is Lispyville compatible? Recommended?

Lispyville doesn't touch any lispy keybindings, so there shouldn't be any compatibility issues.

Does the user need to enable lispy-mode?

Yes; I'd think it makes sense to keep it that way.

What's the intention with the different maps evil-collection-lispy-mode-map-special-evil and evil-collection-lispy-mode-map-evil?

Special is for lispy's "special" keybindings (at parens or active visual selection) and the other map is for bindings that should work globally regardless of the point location.

Ambrevar commented 6 years ago

Indeed, I use Lispyville without enabling Lispy: this enables me with nifty navigation / manipulation all round. I think it relieves some cognitive burden.

jojojames commented 6 years ago

Make a PR to add the evil-special theme

Yup, I plan to soon. The keymaps are a little rough at this moment for me so I don't want them to harden on @abo-abo's repo yet.

Remove the existing evil-theme to avoid confusion

I think we need two themes. One to evilify the special keymap and the other to evilify the global lispy keymap. Doing it this way, the user should be able to still mix and match the key themes they want.

Maybe this should be discussed further on abo-abo/lispy#327

I'd like to in a new PR at some point, but want to avoid too much noise in the lispy repo.

Indeed, I use Lispyville without enabling Lispy: this enables me with nifty navigation / manipulation all round. I think it relieves some cognitive burden.

I used to until @noctuid recommended the lispy keymap too. I'm still getting used to them but I think they're a net improvement to my workflow. These evil-collection lispy keymaps are used in combination with lispyville so I think they should be fairly compatible.

@Ambrevar I think you'd have good insight on the lispy keymaps when not in special state though. Those are more analogous to the lispyville keybinds.

jojojames commented 6 years ago

Commentary on lispy-special state for evil:

I think moving the knight keys (zj/zk) to gj/gk works more comfortably for an evil user.

    (lispy-define-key map "gj" 'lispy-knight-down)
    (lispy-define-key map "gk" 'lispy-knight-up)

Doing that kicks the 'g' key out (lispy-goto) but evil-collection already wants that at 'gd' anyways which is a good fit. Moving some other related keys along..

    (lispy-define-key map "gd" 'lispy-goto)
    (lispy-define-key map "C-t" 'pop-tag-mark)
    (lispy-define-key map "gl" 'lispy-goto-local)

gg and G are open for navigation bindings. Something analogous to going to top/bottom would fit very well.

I have something like this for that but it's not perfect.

    ;; Hmnn, not sure why there's no `lispy-end-of-defun'.
    (lispy-define-key map "G" 'end-of-defun) ;; `lispy-goto-local' -> gl
    (lispy-define-key map "gg" 'lispy-beginning-of-defun) ;; `lispy-goto' -> gd

Maybe there is another better function for g and gg that would make sense for an evil user. @noctuid ?

Because A is now free (was lispy-beginning-of-defun), we can make it similar to insertion in evil. I found no function in lispy that did something like (lispy-insert-at-end-of-sexp) / (lispy-insert-at-beginning-of-sexp).

I found something close in lispyville but still maybe not quite there in lispyville-insert-at-end-of-list and lispyville-insert-at-beginning-of-list. Maybe this is something that can be PR'd in lispy or there's already a function there that I missed. @noctuid ?

(defun evil-collection-lispy-insert-at-end-of-line ()
  (interactive)
  (call-interactively #'lispyville-insert-at-end-of-list))

(defun evil-collection-lispy-insert-at-beginning-of-line ()
  (interactive)
  (call-interactively #'lispyville-insert-at-beginning-of-list))

    (lispy-define-key map "A" 'evil-collection-lispy-insert-at-end-of-line) ;; `lispy-beginning-of-defun' -> gg
    (lispy-define-key map "I" 'evil-collection-lispy-insert-at-beginning-of-line) ;; `lispy-shifttab' -> zi?

Assuming these keys can be moved, I think the I (shifttab and maybe tab) keys can then go to z (which is similar to evil-collection's rationale of folding?).

I have these changes locally (in progress) for now since some functions just don't seem to exist in lispy.

jojojames commented 6 years ago

Hmnn.. I'm guessing prefix keys won't work with special state since it might interfere with normal typing.

noctuid commented 6 years ago

Maybe there is another better function for g and gg that would make sense for an evil user.

Your suggestion seems reasonable. I can't think of anything better.

Maybe this is something that can be PR'd in lispy or there's already a function there that I missed.

These commands don't exist in lispy, but it would be very easy to add them as a separate PR or with the new theme.

I'm guessing prefix keys won't work with special state since it might interfere with normal typing.

Prefix keys should work fine in special.

jojojames commented 6 years ago

Prefix keys should work fine in special.

I'm not too familiar with lispy but trying to bind something like 'gg' to lispy map will work but then typing 'g' in insert state pauses because it's a prefix?

Looking at lispy-define-key:

(defun lispy-define-key (keymap key def &rest plist)
  "Forward to (`define-key' KEYMAP KEY FUNC).
FUNC is obtained from (`lispy--insert-or-call' DEF PLIST)."
  (declare (indent 3))
  (require 'eldoc)
  (let ((func (defalias (intern (concat "special-" (symbol-name def)))
                (lispy--insert-or-call def plist))))
    (add-to-list 'ac-trigger-commands func)
    (unless (memq func mc/cmds-to-run-once)
      (add-to-list 'mc/cmds-to-run-for-all func))
    (eldoc-add-command func)
    (define-key keymap (kbd key) func)))

So it would just resolve to something like (define-key keymap (kbd "gg") 'some-func)) which will block 'g' from being pressed.

noctuid commented 6 years ago

My bad I thought you were talking about prefix arguments/counts. Binding prefix keys should work too. You just need to bind the base key to a prefix keymap/command, hydra, etc. instead of binding the entire sequence directly.

jojojames commented 6 years ago

Sounds good, I gave it a quick try and lispy-define-key doesn't seem to play nice with the prefix keymap.

I'll give hydra a shot .

noctuid commented 6 years ago

Since it expects def to be a symbol/command, you can use the prefix command created with define-prefix-command. Lispy uses hydras for its prefix keys (e.g. lispy-x), so that would probably be the way to go though (the hint is nice).

jojojames commented 6 years ago

I think lispy eventually calls (call-interactively 'def) somewhere in the pipeline and that didn't work with the prefix command for me.

noctuid commented 6 years ago

Right, my bad again; I didn't actually test it. It would be possible to support a prefix command/keymap, but having to use a hydra isn't a big deal.

jojojames commented 6 years ago

Right, my bad again; I didn't actually test it. It would be possible to support a prefix command/keymap, but having to use a hydra isn't a big deal.

np

I pushed some more in progress changes.

Dunno how to write a cleaner version of your lispyville functions @noctuid so I'm just forwarding to them for now. (Probably need to implement them for lispy though.)

(defun evil-collection-lispy-insert-at-end-of-list ()
  "Forward list and enter insert state."
  (interactive)
  (lispyville-insert-at-end-of-list 1))

(defun evil-collection-lispy-insert-at-beginning-of-list ()
  "Backward list and enter insert state."
  (interactive)
  (lispyville-insert-at-beginning-of-list 1))

There's a couple keys that don't seem to be getting bound right now. Looking at it... I have no idea..

Appreciate a look from anyone interested in lispy keybindings.

noctuid commented 6 years ago

There's a couple keys that don't seem to be getting bound right now. Looking at it... I have no idea..

Which keys? I can take a look when I get some free time.

Also, I can make a PR to add the insert functions to lispy before the evil theme PR.

jojojames commented 6 years ago

AFAIK,

    ;; FIXME: Looks like one of the evil keymaps is taking precedence.
    (lispy-define-key map "C-t" 'pop-tag-mark)

and

    ;; FIXME: This doesn't work for me for some reason.
    ;; `lispy-occur' doesn't show up in evil-collection-lispy-mode-map-special-evil.
    ;; Evaluating lispy-define-key again in *scratch* works...
    (lispy-define-key map "/" 'lispy-occur) ;; `lispy-x' -> q

But there might be others I didn't remember to mark/comment.

~

I think it might be cool to mark the lispy-splice-sexp-killing-forward/backward functions. I kind of like ^ and $ for those. Not sure what others think about that though.

jojojames commented 6 years ago

Also, I can make a PR to add the insert functions to lispy before the evil theme PR.

++

noctuid commented 6 years ago

lispy-define-key should only be used for special keybindings. It looks like you intentionally put it in the special keymaps, but I'd imagine C-t might make sense to have everywhere. I don't have C-t bound in insert mode by default, but keys that are also bound in insert mode by default should also be overridden in insert mode (it would probably make sense to do that in evil-collection and not in lispy, but it could potentially be done in lispy with an eval after load evil).

As for /, I'll have to look at that further. I have no idea why that would be an issue.

I think it might be cool to mark the lispy-splice-sexp-killing-forward/backward functions. I kind of like ^ and $ for those. Not sure what others think about that though.

Do you mean in special with an active region? That might be a little confusing since they are just motions in evil. On a related note, I've locally written lispyville versions of these that kill around the current atom (i.e. they well kill or splice to the end/beginning of the list without deleting part of the current atom if the point is in the middle of it). I plan to add these directly to lispy (along with other missing commands from smartparens) once they are more stable (the current atom selection function is not perfect; I may use a more dumb/simple approach here, but if anyone wants to provide cases where the atom text object fails, that would be awesome).

jojojames commented 6 years ago

Do you mean in special with an active region?

I was thinking just in 'special'. My rationale currently was that those keys might be confusing at first but users might adapt fairly quickly $ -> (kill forward to end)/etc but I can see it being confusing enough that it should just be a local customization.

On a related note, I've locally written lispyville versions of these that kill around the current atom (i.e. they well kill or splice to the end/beginning of the list without deleting part of the current atom if the point is in the middle of it). I plan to add these directly to lispy (along with other missing commands from smartparens) once they are more stable (the current atom selection function is not perfect; I may use a more dumb/simple approach here, but if anyone wants to provide cases where the atom text object fails, that would be awesome).

Do you have an example? I'm guessing it's something like:

(a (b) x|yz d e)
->
a (b) xyz
jojojames commented 5 years ago

I'm pretty sure a PR directly to lispy will be accepted

I've been using the lispy in evil-collection for a while (well more than a year since the last comment :D) and I've changed my mind on this. I think it's probably cleaner to have lispy's version (from a maintenance/fast interation point of view) in evil-collection and also pushed into its set of default modes.

It's still not super polished but works well enough of day to day (I haven't changed any of its bindings for a year). I think I'll look into lispy's delta with the current bindings, try to fix any previous issues and move it in as a default mode in evil-collection soon.

Ambrevar commented 5 years ago

Sorry for being a little late to the party :p

So why did you decide to make it part of the default modes? It seems quite disruptive in my opinion. Well, it's easy enough to disable it, but still, I wonder if it's a good default.

A few issues I've noticed so far:

jojojames commented 5 years ago

So why did you decide to make it part of the default modes? It seems quite disruptive in my opinion. Well, it's easy enough to disable it, but still, I wonder if it's a good default.

It's only there if you actually use lispy so I think it follows in the spirit of the rest of evil-collection. I don't think lispy even works that well without these types of bindings anyways. (Unless you memorize the somewhat-emacsy-somewhat-vimy bindings).

I think putting it as a default is also a good forcing function for getting user's feedback on specific keys. (Case in point, what we have here. :D)

] and [ should probably be [[ and ]] like in the rest of evil-collection.

Which functionality are you talking about? If it's the shortkey version, maybe not, since that special mode should be "short keys". As for going into special mode with ]. Yeah, that might be a good idea, not sure without playing with it though.

I miss my function navigation functions, usually bound to [[ and ]] (evil-forward-section-begin, etc.).

Without this integration, what does normal lispy do?

There is a defhydra: should hydra be a dependency then?

There is, but I think lispy requires it. I think doing no byte compile is probably a little better than explicitly requiring hydra.

Ambrevar commented 5 years ago

OK, makes sense to include Lispy by default then!

I miss my function navigation functions, usually bound to [[ and ]] (evil-forward-section-begin, etc.).

Without this integration, what does normal lispy do?

This is not a lispy binding, it's an Evil binding. [[ and ]] navigate top-level forms. I don't know how to do this once evil-collection-lispy is activated.

jojojames commented 5 years ago

Yeah I meant, without evil-collection-lispy, does lispy interfere with ] and [.

Ambrevar commented 5 years ago

In special mode, yes, but in normal mode.

Ambrevar commented 5 years ago

But not in normal mode.