noctuid / lispyville

lispy + evil = lispyville
GNU General Public License v3.0
313 stars 23 forks source link

lispy-raise-some not working due to lispy-right-p #68

Open some-mthfka opened 4 years ago

some-mthfka commented 4 years ago

Hi! Awesome package! With lispy and lispyville I just found everything I was missing in lisp editing, things I didn't even know I should have had.

Now, as I understand, it's either a bug or lispy-raise-some has simply not been ported to lispyville yet (in which case this is a feature request), but here's what happens:

(progn
  (message "one")
  (message "two"[)] ;; <- cursor here
  (message "three"))

"Unexpected" is messaged here w/ lispy-raise-some, because (lispy-right-p) returns nil here. This works though:

(progn
  (message "one")
  (message "two")[ ] ;; <- cursor here
  (message "three"))

I tried redefining lispy-right-p as

(defsubst lispy-right-p ()
  "Return t if after variable `lispy-right'."
  (or (member (following-char) '(?\[ ?\] ?\) ?\} ?\]))
      (looking-back lispy-right
                    (line-beginning-position))))

but this yields:

(progn
  (message "one")
  message "two" 
  (message "three"))

(oh well, at least I tried)

PS The opening paren case works just fine:

(progn
  (message "one")
  [(]message "two") ;; <- cursor in the beginning
  (message "three"))

Checked with: emacs -Q, emacs 27.1, evil 1.14.0.

noctuid commented 3 years ago

Probably the best way to handle this is to provide a generic way to wrap lispy commands so that they are run at the correct location. I think lispyville-wrap-command can be changed slightly to support this:

(defmacro lispyville-wrap-command (command state)
  "Return a function that executes COMMAND and then enters STATE.
The main purpose of this macro is to wrap lispy commands, so that they can be
used in normal state and end up in lispy special or in visual state with a
correct selection. STATE can be any evil state, but it is mainly meant to be
'special' or 'visual', which it will handle differently. When STATE is special,
the resulting command will enter `lispyville-preferred-lispy-state' and move the
point (if necessary) to get to lispy special (like motions will when
`lispyville-motions-put-into-special' is non-nil). When STATE is visual, the
resulting command will ensure that the selection resulting from COMMAND is
correct."
  (let ((name (intern (concat "lispyville-wrap-" (symbol-name command)
                              "-" (symbol-name state)))))
    `(progn
       (defun ,name ()
         ,(concat "Call `" (symbol-name command) "' and then enter "
                  (cl-case state
                    (special "lispy special.")
                    (visual "visual state with a corrected selection.")
                    (t
                     (concat (symbol-name state) " state."))))
         (interactive)
         (lispyville--state-transition t)
         (call-interactively #',command)
         ,(cl-case state
            (special
             '(lispyville--state-transition t))
            (visual
             '(lispyville--state-transition))
            (t
             `(evil-change-state ',state))))
       #',name)))

(evil-define-key 'normal lispyville-mode-map
  "R" (lispyville-wrap-command lispy-raise-some normal))

I'll need to check that this doesn't break existing commands, but if that works for you, I can make it a little nicer by allowing choosing the name of the created function and by maybe having it move the cursor one character back afterwards if it had to move one character forward.

some-mthfka commented 3 years ago

@noctuid Yeah, this works!

I'll need to check that this doesn't break existing commands

just push to master and quickly revert back in case of angry roars muhaha

Anyway, it seems to not have broken anything with the few of the commands I am using. Will report back in case something turns up, though.

Thank you.