Giter Site home page Giter Site logo

lispyville's Introduction

Lispyville User Manual

https://melpa.org/packages/lispyville-badge.svg https://github.com/noctuid/lispyville/workflows/test/badge.svg https://codecov.io/gh/noctuid/lispyville/branch/master/graph/badge.svg

Recent Changes

2018-01-20: s-operators key theme has been removed

The operators key theme now remaps commands instead of directly binding keys, so there is no longer a need for a separate s-operators key theme.

About LispyVille

Welcome to LispyVille!

lispyville.el’s main purpose is to provide a lisp-editing environment suited towards evil users. It can serve as a minimal layer on top of lispy-mode for better integration with evil, but it does not require use of lispy’s keybinding style. The provided commands allow for editing lisp in normal state and will work even without lispy-mode enabled. If you are just looking for a way to prevent evil’s operators from unbalancing parentheses, you can enable lispyville-mode in your configuration and just ignore the rest of its features.

Here are the main features of lispyville:

  • Provides “safe” versions of vim’s yank, delete, and change related operators that won’t unbalance parentheses
  • Provides lisp-related evil operators, commands, motions, and text objects
  • Integrates evil with lispy by providing commands to more easily switch between normal state and lispy’s “special” context/mode and by providing options for integrating visual state with lispy’s special region mode

Note that this package does not create any new evil states; it assumes that the user will be using lispy in insert or emacs state.

Comparison with Evil Cleverparens

lispyville.el has a similar intent to evil-cleverparens and related packages. It creates “safe” versions of standard evil editing commands. For example, it ensures that dd will not unbalance parenthesis but instead only delete the “safe” portion of the line. This allows the evil’s line-oriented commands to become far more useful for lisps.

The primary difference between lispyville and other similar packages is that it uses lispy instead of smartparens or paredit. This matters because lispyville is primarily intended to be used in conjunction with lispy. Lispy already has a lot in common with evil. Unlike smartparens and paredit, lispy’s primary keybindings are just letters. It may help to think of lispy as just an additional evil state. The main difference from an evil state is that lispy’s “special” is contextually based on the point (special is when the point is before an opening delimiter, after a closing delimiter, or when there is an active region).

However, if you’d rather stick to mainly vim keybindings, lispyville will also eventually provide “key themes” to replicate all of evil-cleverparens’ keybindings as well as the keybindings of some other popular evil/lisp editing packages. I also plan to add key themes that are more similar to lispy’s keybindings.

The other main reason I created this package is that I found the implementation of safe operators in other packages to be confusing and buggy. Lispyville uses a simple/clear method of acting on regions with unmatched delimiters and attempts to intelligently handle all edge cases. Please make an issue if you find any abnormalities or have any suggestions for improvement.

Finally, note that although smartparens is generic and lispy is meant for lisps, this package’s safe operators should work correctly in non-lisp modes as well. If you encounter any problems or would like a separate mode dedicated for non-lisp modes, feel free to make an issue.

Relationship with Lispy

While lispyville can be used without lispy-mode, some additional setup may be required to make editing lisp comfortable. For example, ( would need to be explicitly bound to lispy-parens for auto-pairing behavior (and the other functionality provided by lispy-pair). If your gripe with lispy is its style of having “special” locations where letter keys act as commands, you can still use lispy-mode for the “normal” keybindings it provides by not using special in your lispy “key theme”:

(lispy-set-key-theme '(lispy c-digits))

You can always override these keybindings later.

Basic Configuration

Lispyville is a minor mode. To enable it wherever lispy is enabled, you can add the following to your configuration:

(add-hook 'lispy-mode-hook #'lispyville-mode)

Lispyville can also be used without lispy:

(add-hook 'emacs-lisp-mode-hook #'lispyville-mode)
(add-hook 'lisp-mode-hook #'lispyville-mode)
;; ...

Here is an example configuration with use-package and general that adds additional keybindings.

(use-package lispyville
  :init
  (general-add-hook '(emacs-lisp-mode-hook lisp-mode-hook) #'lispyville-mode)
  :config
  (lispyville-set-key-theme '(operators c-w additional)))

Safe Operators

The operators behave similarly to evil-cleverparens’ operators with a few exceptions. The delete operator will always act safely by ignoring unmatched delimiters, whereas cleverparens will sometimes splice. While cleverparens’ yank operators will attempt to add unmatched delimiters, lispyville’s yank operators will simply exclude the unmatched delimiters, which is consistent with how the delete operator works. The operators will also work in visual block mode, unlike with cleverparens. The user can also choose whether or not they want to act safely on delimiters in strings and comments (see Lispy Settings).

Y acts like a safe y$ unlike in evil and cleverparens. If anyone takes issue with this change, I can add a command for its regular functionality, but I think most people dislike the default inconsistency between Y and D in vim.

Additionally, I think that the function used for safe behavior is a lot more sanely implemented in lispyville than in other related packages (it intelligently analyzes a region once instead of repeatedly calling check-parens).

I’ve added this functionality directly to lispy, and if you want lispy’s copy, delete, and/or paste commands to keep parentheses balanced, you can set the relevant options for lispy (see Lispy Settings).

Key Themes

By default, the only keys that lispyville remaps are the operators and C-w. To allow for the user to choose between various sets of keybindings without making them manually remap every command, lispyville provides “key themes” similarly to how lispy does.

The user can still define commands in lispyville-mode using evil-define-key or something like general, but lispyville-set-key-theme can also be used to define keys. It takes one argument which is a list of symbols corresponding to the different themes. By default, most commands will be mapped in the normal (and visual) state. The default states are listed below. To change them, a list of the key theme symbol and the states to map the keys in can be specified instead.

As an example, the following command will map the “operators” theme in the normal and visual states, the “escape” theme in just the insert state, and the “additional-movement” theme in the normal, visual, and motion states:

(with-eval-after-load 'lispyville
  (lispyville-set-key-theme
   '(operators
     c-w
     (escape insert)
     (additional-movement normal visual motion))))

Note that you will generally not need to change the states. It is usually unnecessary to bind keys in both normal and visual state because keys bound in normal state are inherited in visual state. Similarly, keys bound in motion state are inherited in the normal and visual (and operator) states. As an example, the ) motion is bound only in evil-motion-state-map. The lispyville ) motion is also only bound in motion state. However, if you were to bind a custom ) motion in evil-visual-state-map, for example, it would override lispyville’s ). Most users will not have to worry about this issue as this customization is unlikely to be useful (more likely it would come as the result of the user or some package unnecessarily binding a motion in visual state), but this is a case where you would need to alter the states if you wanted lispyville’s motion to have precedence:

(evil-define-key 'visual 'global
  ")" #'my-custom-motion)

;; if `lispyville-up-list' should be used instead of `my-custom-motion' in
;; visual state when `lispyville-mode' is active:
(lispyville-set-key-theme '((additional-movement motion visual)))
;; or just
(evil-define-key 'visual lispyville-mode-map
  ")" #'lispyville-up-list)

If you find yourself having to do this because a package explicitly binds motions in normal or visual state, you should probably make an issue for that package. Finally, note that this is not applicable when [remap] is used. For key themes that use [remap], the states do not matter.

lispyville-set-key-theme will not reset lispyville’s keymap, so it will not remove user-defined keybindings (unless they are overwritten by a key in one of the themes). The keybindings will be added in the order of the list, so if there is overlap between the listed themes, the one listed last will take precedence.

Operators Key Theme

The corresponding symbol is operators. There are no default states; any state where these operators are bound will be affected. These are safe versions of the corresponding evil operators that won’t unbalance parentheses.

Like with cleverparens, dd will bring closing delimiters that are on a line by themselves to the previous line while cc won’t. On lines with unmatched opening or closing delimiters, cc will put the point after the opening delimiters or before the closing delimiters.

keycommand
[remap evil-yank]lispyville-yank
[remap evil-delete]lispyville-delete
[remap evil-change]lispyville-change
[remap evil-yank-line]lispyville-yank-line
[remap evil-delete-line]lispyville-delete-line
[remap evil-change-line]lispyville-change-line
[remap evil-delete-char]lispyville-delete-char-or-splice
[remap evil-delete-backward-char]lispyville-delete-char-or-splice-backwards
[remap evil-substitute]lispyville-substitute
[remap evil-change-whole-line]lispyville-change-whole-line
[remap evil-join]lispyville-join

In particular, J implements a safe version of the evil-join operator, which preserves structure by always placing uncommented regions to the left of line comments, avoiding the scenario of an unbalanced line being joined to the inline comment above it.

;; before (cursor at |)
|(foo  ; bar
  baz)

;; after "J":
(foo| baz) ; bar

To “slurp” following line(s) into the commented region in the usual manner, first explicitly comment them out with lispyville-comment-or-uncomment, which moves unbalanced delimiters out of the way (refer to the commentary theme). lispyville-join will then splice the comments together, removing any intermediate whitespace and comment syntax.

;; initial state (cursor at |)
(foo  ; bar
 |quux)

;; "gcc"
(foo  ; bar
 |;; quux
 )

;; "kJ"
(foo  ; bar| quux
 )

;; "J"
(foo)|  ; bar quux

C-w Key Theme

The corresponding symbol is c-w. There are no default states; any state where evil-delete-backward-word is bound will be affected. This is the safe version of evil-delete-backward-word. It will act as lispy-delete-backward after delimiters (and delete everything within the delimiters).

The reason no safe version of evil-delete-backward-char-and-join is provided is because lispy already maps DEL to lispy-delete-backward.

keycommand
[remap evil-delete-backward-word]lispyville-delete-backward-word

C-u Key Theme

The corresponding symbol is c-u. There are no default states; any state where evil-delete-back-to-indentation is bound will be affected. This is the safe version of evil-delete-back-to-indentation. It will act as lispy-delete-backward after delimiters (and delete everything within the delimiters).

The reason no safe version of evil-delete-backward-char-and-join is provided is because lispy already maps DEL to lispy-delete-backward.

keycommand
[remap evil-delete-back-to-indentation]lispyville-delete-back-to-indentation

Prettify Key Theme

The corresponding symbol is prettify. There are no default states; any state where evil-indent is bound will be affected. This key theme replaces evil-indent with an operator equivalent of lispy-tab. In addition to correcting indentation, lispy-tab will also, for example, remove empty newlines and pull trailing closing delimiters all onto the same line. This operator works by normalizing the current list and all subsequent same-level lists that start within the region.

keycommand
[remap evil-indent]lispyville-prettify

Text Objects Key Theme

Note that these commands are considered experimental (e.g. there are still no written tests and they need to be polished).

The corresponding symbol is text-objects. There are no default states; the text objects are bound globally in evil-inner-text-objects-map and evil-outer-text-objects-map by default. Alternatively, you could bind the full key sequences in the visual and operator states in lispyville-mode-map (this will likely be the default in the future).

inner and a versions exist for all of these:

keycommand
alispyville-inner-atom
llispyville-inner-list
xlispyville-inner-sexp
flispyville-inner-function
clispyville-inner-comment
Slispyville-inner-string

An atom is comparable to an evil symbol, except it will select entire strings and comments. The string, comment, and top-level function/form text objects are fairly generic and will likely work in other programming languages. Multiple adjacent line comments are considered to be one comment.

All text objects have corresponding forward-begin, forward-end, backward-begin, and backward-end evil motions.

All text objects are designed to work with targets.el, and it is highly recommended that you use it if only for these text objects as they will work much better:

  • Seeking and region expansion will work
  • Next, previous, and remote (i.e. selected with avy overlays) text objects are provided

Once targets is more stable, I will likely depend on it for this package. For now, if you want to try these out with targets, you can create and bind them with targets-define-to. Here’s example setup that will only create corresponding versions of the lispyville text objects:

(setq targets-text-objects nil)

(targets-setup)

(targets-define-to lispyville-comment 'lispyville-comment nil object
                   :bind t :keys "c")

(targets-define-to lispyville-atom 'lispyville-atom nil object
                   :bind t :keys "a")

(targets-define-to lispyville-list 'lispyville-list nil object
                   :bind t :keys "l")

(targets-define-to lispyville-sexp 'lispyville-sexp nil object
                   :bind t :keys "x")

(targets-define-to lispyville-function 'lispyville-function nil object
                   :bind t :keys "f")

(targets-define-to lispyville-comment 'lispyville-comment nil object
                   :bind t :keys "c")

(targets-define-to lispyville-string 'lispyville-string nil object
                   :bind t :keys "S")

Atom Movement Key Theme

The corresponding symbol is atom-motions or atom-movement. There are no default states as remaps are used. The states argument is repurposed to determine whether to override the WORD motions instead. These motions are comparable to cleverparen’s “symbol” motions and vim-sexp’s “element” motions.

Normally (e.g. (theme1 ... atom-movement)):

keycommand
[remap evil-forward-word-begin]lispyville-forward-atom-begin
[remap evil-forward-word-end]lispyville-forward-atom-end
[remap evil-backward-word-begin]lispyville-backward-atom-begin
[remap evil-backward-word-end]lispyville-backward-atom-end

With a states argument (e.g. (theme1 ... (atom-movement t)))

keycommand
[remap evil-forward-WORD-begin]lispyville-forward-atom-begin
[remap evil-forward-WORD-end]lispyville-forward-atom-end
[remap evil-backward-WORD-begin]lispyville-backward-atom-begin
[remap evil-backward-WORD-end]lispyville-backward-atom-end

Additional Movement Key Theme

The corresponding symbol is additional-motions or additional-movement. The default state is motion (inherited in the normal, visual, and operator states). This key theme is the equivalent of cleverparen’s additional movement keys. [ and ] are like the reverse of lispy-flow. { and } are like lispy-flow. ( and ) are like lispy-left and lispy-right. Also see here for some extra information on automatically enter special after executing these motions.

keycommand
Hlispyville-backward-sexp
Llispyville-forward-sexp
M-hlispyville-beginning-of-defun
M-llispyville-end-of-defun
[lispyville-previous-opening
]lispyville-next-closing
{lispyville-next-opening
}lispyville-previous-closing
(lispyville-backward-up-list
)lispyville-up-list

lispyville-left is an alias for lispyville-backward-up-list, and lispyville-right is an alias for lispyville-up-list.

There is also the unbound lispyville-beginning-of-next-defun.

Commentary Key Theme

The corresponding symbol is commentary. The default state is normal state (inherited in visual state). The bindings follow vim/evil-commentary defaults as shown below:

keycommand
gclispyville-comment-or-uncomment
gylispyville-comment-and-clone-dwim
s-/lispyville-comment-or-uncomment-line

If you prefer evil-nerd-commenter style bindings, add the following to your configuration, where =,= is the evil leader key:

(evil-define-key 'normal lispyville-mode-map
  ",,"  #'lispyville-comment-or-uncomment
  ",."  #'lispyville-comment-and-clone-dwim
  ",ci" #'lispyville-comment-or-uncomment-line)

The safe comment-and-clone operator operates only on the rightmost balanced region by default. If the region is selected visually, it operates separately on all balanced subregions.

;; initial state (cursor on first line)
(foo (bar) (baz|
            (quux)))

;; "gyy"
(foo (bar) (;; baz
            baz
            (quux)))

;; with visual line selection: "Vgy"
(;; foo (bar)
 foo (bar) (;; baz
            baz
            (quux)))

Slurp/Barf Key Themes

Two key themes are provided for slurping and barfing keybindings. The default state for both is normal. Note that the commands in both key themes work with digit arguments. A positive argument will barf or slurp that many times like in cleverparens. Additionally, for the slurp commands, an argument of -1 will slurp to the end of the line where the sexp after the closing paren ends, and an argument of 0 will slurp as far as possible. See the documentation for lispy-slurp for more information. Also see here for some extra information on automatically entering special after executing these commands.

Note that the commands for both key themes will act on the paren after the point, meaning that the point should be before a closing paren to be considered “on” it.

The slurp/barf-cp key theme provides commands that act the same as cleverparens’ slurp and barf keys or lispy’s lispy-slurp-or-barf-right and lispy-slurp-or-barf-left. > and < can be thought of arrows that will move the paren at point in the corresponding direction. If there is no paren at the point, the keys will take the action they would on a right paren but will not move the point.

keycommand
>lispyville->
<lispyville-<

The slurp/barf-lispy key theme provides commands that act the same as the default lispy-slurp and lispy-barf. In this case, > and < can be thought to correspond to “grow” and “shrink” respectively. > will always slurp, and < will always barf. If there is no paren at the point, the keys will take the action they would on a right paren but will not move the point.

keycommand
>lispyville-slurp
<lispyville-barf

For both < bindings, if lispyville-barf-stay-with-closing is non-nil and barfing would move the closing delimiter behind the point, the point will instead be put on the closing delimiter.

Wrap Key Theme

The corresponding symbol is wrap. The default state is normal state. Note binding M-[ in terminal is the same as binding the scroll wheel. If you use the terminal Emacs and use the scroll wheel, you should not use this key theme.

keycommand
M-(lispyville-wrap-with-round
M-[lispyville-wrap-with-brackets
M-{lispyville-wrap-with-braces

These are operators that will wrap the specified region with the corresponding delimiter. These are potentially fewer keypresses than using evil-surround since you do not have to specify the delimiter to use afterwards. If you use these often, you might want to bind them to something more convenient (e.g. M-b or ( for lispyville-wrap-with-round: (evil-define-key 'normal lispyville-mode-map "(" 'lispyville-wrap-with-round) if you are not using the additional movement key theme). Also note that you can wrap in lispy special (e.g. in insert state with region selected) just by pressing the delimiter. If you don’t use the movement key theme in visual state (e.g. you only use it to enter lispy special), you can bind ( to wrap only in visual state (e.g. (evil-define-key 'visual lispyville-mode-map "(" 'lispy-parens); lispy-parens (which is what lispyville-wrap-with-round calls) can be used directly in this case; v$( would then wrap to the end of the line).

See the additional wrap key theme for an alternative.

Additional Key Theme

The corresponding symbol is additional. The default state is normal state. This key theme is the equivalent of cleverparens’ “additional bindings” keys. It is currently incomplete. M-j is comparable to evil-cp-drag-forward and lispy-move-down. M-k is comparable to evil-cp-drag-backward and lispy-move-up.

keycommand
M-jlispyville-drag-forward
M-klispyville-drag-backward
M-Jlispy-join
M-slispy-splice
M-Slispy-split
M-rlispy-raise-sexp
M-Rlispyville-raise-list
M-ttranspose-sexps
M-vlispy-convolute-sexp

lispyville-move-down is an alias for lispyville-drag-forward, and lispyville-move-up is an alias for lispyville-drag-backward.

Additional Insert Key Theme

The corresponding symbol is additional-insert. The default state is normal state. This key theme also corresponds to keybindings from cleverparens additional keybindings.

keycommand
M-ilispyville-insert-at-beginning-of-list
M-alispyville-insert-at-end-of-list
M-olispyville-open-below-list
M-Olispyville-open-above-list

Unlike cleverparens, these commands work only with lists. evil-cp-insert-at-beginning-of-form, for example, will insert at the beginning of strings as well. To me, it is simpler and more consistent to only consider lists instead of specially handling string atoms. If you would prefer the original behavior, feel free to make an issue, and I can add alternative commands.

Additional Wrap Key Theme

The corresponding symbol is additional-wrap. The default state is normal state (to mimic cleverparens; you may want to also bind these in insert state).

keycommand
M-(lispyville-wrap-round
M-[lispyville-wrap-brackets
M-{lispyville-wrap-braces

These are equivalents of lispy-wrap-round, lispy-wrap-brackets, and lispy-wrap-braces. By default, they will wrap the sexp at the point. With a positive count, they will wrap that number of sexps. With a count of 0, they will wrap as far as possible. With a negative count, they will wrap to the sexp at the end of the line (e.g. |foo bar to (|foo bar)). If you would prefer this behavior by default, you can bind ( to lispy-parens-auto-wrap in insert state (e.g. (define-key lispy-mode-map-lispy "(" 'lispy-parens-auto-wrap)). Also, if you would prefer to use something more generic, you can try the wrap key theme which provides corresponding operators instead.

Normally, lispy will insert a space after the opening delimiter when wrapping. The lispyville versions will never insert a space in normal state. When in a state in lispyville-insert-states, these commands will insert a space when lispy-insert-space-after-wrap is non-nil (the default).

Unlike cleverparens, no commands to wrap previous sexps are provided. If you would like this functionality, feel free to make an issue.

Arrows Key Theme

The corresponding symbol is arrows. The default state is normal state. This key theme provides similar keybindings to those from vim-sexp-mappings-for-regular-people. It is currently incomplete.

keycommand
<ilispyville-insert-at-beginning-of-list
>ilispyville-insert-at-end-of-list

Note that the original plugin uses >I and <I in order not to override the default < and > used with inner text objects. Since manual indentation is never necessary with lisp (e.g. use aggressive-indent-mode or lispyville-prettify / lispy-tab instead), this key theme does not attempt to leave the original keybindings intact.

Escape Key Theme

The corresponding symbol is escape. The default states are insert and emacs. See here for more information.

keycommand
ESClispyville-normal-state

Mark Key Themes

The corresponding symbols are mark and mark-special. The default states are normal and visual. While the commands from mark will enter visual state, the commands from mark-special will enter lispyville-preferred-lispy-state. See here for more information.

keycommand
vwrapped lispy-mark-symbol
Vwrapped lispy-mark
C-vwrapped lispy-mark

Mark Toggle Key Theme

The corresponding symbol is mark-toggle. The default states are insert and emacs. Note that v will be bound in visual state (not changeable).

keycommand
vlispyville-toggle-mark-type
ESClispyville-escape

The idea of this theme is to use the same key you used to get into visual state or special to toggle between them and to use ESC to get rid of the region. For example, after entering visual state, you can press v to enter lispy special or ESC to return to normal state and cancel the region. After marking something with lispy, you can press the key for lispy-mark-list (I use v, but it is m by default) to enter visual state or ESC to return to insert or emacs state and cancel the region.

Note that this requires also binding lispyville-toggle-mark-type in lispy after it loads:

(lispy-define-key lispy-mode-map "m" #'lispyville-toggle-mark-type)
;; or v for better consistency (I swap m and v)
(lispy-define-key lispy-mode-map "v" #'lispyville-toggle-mark-type)

By re-purposing v in visual state (which normally enters visual line mode) to enter lispy special and re-purposing m (or v) in lispy special with an active region to enter visual state (while moving m’s normal functionality to ESC), this functionality is achieved without requiring any complicated keybindings. Note that the toggle key will still act as lispy-mark-list in lispy special if you use a prefix arg (other than 1).

When using this theme with the mark theme, the mark theme should be specified first. If you would prefer that ESC always enters normal state (instead of returning you to lispy special with no region if you are in lispy special with a region), you can specify the escape theme after the mark-toggle theme.

Integration with Lispy

Mode Line Indicator for Lispy Special

If you would like an additional visual indicator that lispy keybindings are active (i.e. when in special and in a state in lispyville-insert-states), lispyville also provides lispyville-mode-line-string. It optionally takes two arguments: the text to display when lispy keybindings are active (“🍰-special ” by default) and the default text (nothing by default). You can change the color/style of the text by customizing lispyville-special-face.

(setq-default mode-line-format
              ;; ...
               '(:eval (when (featurep 'lispyville)
                         (lispyville-mode-line-string)))
              ;; ...
              )

Alternatively, you can use lispyville’s lighter for this purpose (it will change to the color/style of lispyville-special-face when lispy keybindings are active):

(diminish 'lispyville-mode (lispyville-mode-line-string " 🍰" " 🍰"))

More Fluid Transitioning Between Normal State and Special

Getting to special when in insert or emacs state is already pretty easy. You can use ) or [ and ] (if you like those keybindings) to jump to a special location at any time. If you want to get there from normal state, it’s a bit more tedious, since you need to first navigate to a special location and then enter insert or emacs state.

Lispyville provides an option that will automatically enter insert or emacs state for lispyville navigation commands that would put you at a paren. To enable this behavior, lispyville-motions-put-into-special can be set to a non-nil value. If you prefer to edit in emacs-state, you can set lispyville-preferred-lispy-state to emacs.

Note that this behavior will not affect the use of motions with an operator or in visual state (which wouldn’t make sense).

There is also an option for commands called lispyville-commands-put-into-special that can be customized in the same way. The currently applicable commands are the slurp and barf commands.

Visual State and Special Integration

Lispyville tries to be unobtrusive by default, only rebinding the major operator keys. Since there are many potential ways to better integrate evil’s visual state with lispy’s special (with the region active), lispyville doesn’t make a default choice for the user.

Using Both Separately

This is probably the simplest method of improving things. By default, pressing escape after using something like lispy-mark from special will enter normal state but won’t cancel the region. Lispyville provides lispyville-normal-state to deactivate the region and enter normal state in one step. You can map it manually or use the escape key theme (e.g. (lispyville-set-key-theme '(... (escape insert emacs)))).

On the other hand, if you want to map a key in normal state to mark something with a lispy command like lispy-mark, normally evil’s visual state will be entered, and the selection will be off by a character. lispyville-wrap-command can be used to create commands that will enter a specific evil state and ensure that the resulting selection is correct. It is mainly meant to be used with visual and special:

;; enter visual state after `lispy-mark-symbol' with correct selection
(evil-define-key 'normal lispyville-mode-map
  "v" (lispyville-wrap-command lispy-mark-symbol visual))
;; enter lispy special after `lispy-mark-symbol' with correct selection
(evil-define-key 'normal lispyville-mode-map
  "v" (lispyville-wrap-command lispy-mark-symbol special))

To toggle between special and visual state at any time, you can use the mark-toggle key theme.

Using Only Lispy’s Mark Commands

Lispy’s special mark state won’t always work correctly when entered with an active region it wouldn’t normally mark (e.g. half of a symbol is marked). Because of this, you’ll probably want to rebind v, V, and C-v. Lispyville provides a key theme to remap v to a wrapped version of lispy-mark-symbol and V and C-v to a wrapped version of lispy-mark (e.g. (lispyville-set-key-theme '(... mark-special))).

The old way of automatically switching to insert or emacs state was found to have serious bugs, so I do not currently recommend using it. Instead, you need to wrap all selection-related functions that you use with lispyville-wrap-command.

Using Only Evil’s Mark Commands

One can have all lispy mark commands enter evil’s visual state instead:

(lispyville-enter-visual-when-marking)

The behavior can be removed by running lispyville-remove-marking-hooks.

Final Notes

If you prefer evil or lispy for working with regions but don’t want to use either all of the time, it’s probably best to pick the one you find the most useful and bind some keys from the other in the relevant keymap.

I may add a key theme for this, but I personally prefer to mainly using lispy’s keys, as they are generally more useful than the default evil motions and will keep the region balanced. Evil’s commands can be more useful for editing comments, so I’m personally using the first solution (Using Both Separately) to choose which to use.

Note that you can still use the mark-toggle keybinding to switch between visual and special even if you run (lispyville-enter-special-when-marking) (use not recommended) or (lispyville-enter-visual-when-marking).

Lispy Settings

I’ve added the main functions behind safe deletion and copying directly to lispy. To have lispy’s commands always act safely on a region, lispy-safe-delete, lispy-safe-copy, and lispy-safe-paste can be set to non-nil values. Lispyville’s commands keep delimiters balanced regardless of these settings. Lispyville does not yet have a safe paste operator though.

The options that will affect lispyville’s behavior are lispy-safe-threshold, lispy-safe-actions-ignore-strings, lispy-safe-actions-ignore-comments, and lispy-safe-actions-no-pull-delimiters-into-comments.

lispy-safe-threshold is the maximum size a region can be before operators will no longer attempt to keep delimiters balanced. If you ever have an issue with the limit, you can try increasing it and see if there are any performance issues. I haven’t tested performance on larger regions, so any feedback would be appreciated.

The “ignore” options will determine whether commands will ignore unbalanced delimiters in comments and strings. It is recommended to keep these options at their default value (true).

When lispy-safe-actions-no-pull-delimiters-into-comments is non-nil, lispy/lispyville commands will avoid pulling unmatched delimiters into comments (e.g. dd on a line after a comment will keep unmatched closing delimiters on the same line instead of commenting them out).

By default, lispyville-mode will automatically make the following changes when turned on for maximum safety levels:

(setq lispy-safe-delete t
      lispy-safe-copy t
      lispy-safe-paste t
      lispy-safe-actions-no-pull-delimiters-into-comments t)

To prevent lispyville from changing lispy variables, you can set lispyville-no-alter-lispy-options to a non-nil value.

lispyville's People

Contributors

br1ght0ne avatar danskarda avatar fbialek avatar fredericgiquel avatar jgkamat avatar justbur avatar noctuid avatar sorawee avatar syohex avatar tannart avatar wpcarro avatar yuhan0 avatar yunhao94 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lispyville's Issues

How to create a function like vim's ^u, but keeping parens balanced

So I quite like ^u in vim, which is like C-a C-k in emacs but without deleting the stuff after your cursor. Essentially CMD-backspace in your general editors. But we're dealing with parens so we need to keep pairs intact. When outside of parens, I want it to behave exactly like ^u does in vim. Within parens, I want it to behave like cc using lispyville, but only deleting sexps which are before point.

  1. ((foo) (bar|) (baz)) should turn into ((|) (baz))
  2. ((foo) (b|ar) (baz)) should turn into ((|ar) (baz))
  3. ((foo) |(bar) (baz)) should turn into (|(bar) (baz))

Any ideas on how to achieve this?

text-objects theme breaks `w` operator

When text-objects enabled w operator doesnt work inside string or comments:

When disabled:
(fn "|some |long |string" |atom)

When enabled:
(fn "|some long string" |atom)

Default configuration not working at all under evil mode

I have the following in my .emacs:

(require 'lispy)
(require 'lispyville)
(add-hook 'scheme-mode-hook #'lispy-mode)
(add-hook 'lispy-mode-hook #'lispyville-mode)
(require 'evil)
(evil-mode t)

When I open a .scm file, I have access to a few lispyville operators, but very little lispy functionality: e.g., stuff like [, ], and lispy-flow are missing, and most of the lispy bindings I see involve Control or Meta keys. Positioning point on a paren doesn't put me in lispy special. Disabling evil mode makes all the lispy bindings available, and allows me to get into lispy special. As I understand it, lispyville is meant to work under evil mode, so how do I make evil, lispyville and lispy work together?

Thanks,
Brett S.

broken link in README

line 145 of the README has a broken link to targets.el because it is missing the last l.

Using lispyville operators as drop-in replacements

Hi,

Can the lispyville operators be used as drop-in replacements for evil operators using defalias like

(defalias 'evil-delete 'lispyville-delete)

Do you foresee any issues with doing that?

Thanks for the wonderful package.

PS: I'm trying to use lispyville in non-lispy modes same as #54

Keep custom operators up to date with evil changes

The better long term solution would be to not redefine operators but wrap or advise them somehow. When I tried this before, I couldn't come up with anything that wasn't extremely convoluted. The other solution is to just watch the evil repository and pull in any new changes (e.g. emacs-evil/evil#1049; I believe that there are one or two other relevant issues or pull requests).

When are safe operators enabled by default?

Unless I am somehow misunderstanding a major part of lispyville and how it can be used, I am having some issues getting the operators such as safe delete to work properly.

If we have, line-by-line C-M-x'ed:

(require 'evil) ; => evil
(require 'lispyville) ; => lispyville
(evil-mode) ; => t
(lispyville-mode) ; => t

(defun inc (n)
 | (+ 1 n))

With | being the cursor/point in normal mode, pressing D deletes the whole line (including )), leaving us in an inconsistent state:

(defun inc (n)
|

Am I somehow misconfiguring lispyville, or am I misunderstanding what should be happening? Calling e.g. lispyville-delete-whole-line via M-x does lead to a consistent state, where the last ) is kept.

[Request] evil-cleverparens insert functionality

Just thought I'd request this from evil-cleverparens.

Insert

When entering insert-state via i in a situation where the point is between a round opening parentheses and a symbol, evil-cleverparens will automatically insert a space and then move the cursor back for you. The rationale is that when you are in this situation, it's much more likely that you are inserting a new word at the beginning of the list rather than modifying the beginning of the current head of the list, and therefore it would be nice if the two words were already separated so that your auto-completion mode can do its thing. This behavior can be disabled by setting evil-cleverparens-use-regular-insert to t.

May or may not be appropriate for this package but that was one thing I missed transitioning over from evil-cleverparens to lispyville.

Support for lispy-tab

I find lispy-tab super useful, but it's mapping to Evil is not obvious since it serves both for folding (usually TAB) and for indenting (usually =).

The indentation part is done by lispy--normalize. This function is so good it'd be fantastic to be able to run it over the whole buffer. Don't know If gg=G can do that... :)

Deleting Lines with Strings

Hey, really enjoying lispyville so far :).

I notice some weird behavior whenever I dd on a single line of strings.

For instance, deleting the line containing "1234" "5678" gets transformed to " which is unexpected. I would expect the whole line to get deleted or maybe have it result in "" which would match the behavior for parens. The code actually does the latter until it reaches this section
https://github.com/noctuid/lispyville/blob/master/lispyville.el#L373-L376

Just curious what the reasoning is for that. Thanks

lispyville-change word is too greedy

lispyville-change when used with w will delete any spaces after the atom being changed. Worse, it will delete ' on a following list. For example:

;; initially (| is point)
(my-|word '(a list))

;; cw (actual)
(my-|(a list))

;; cw (expected)
(my-| '(a list))

edit another way to say this, is it would be great if evil-want-change-word-to-end was respected.

Does not rebind anything

lispyville does not seem to rebind any keys after I enable it with
M-x lispyville-mode. If I call lispyville commands directly with
M-x such as M-x lispyville-delete they work fine.

These are the versions of related packages that I have installed:

Package Version
evil 1.2.13
lispy 20181004.1959
lispyville 20180704.1158

This is my config:

(use-package lispyville
  :after lispy
  :init
(setq lispyville-key-theme
        '(operators
        c-w
        prettify)))

;; Required for lispyville
(use-package lispy
  :pin melpa)

Oh my god this is AWESOME!

@noctuid, this is just hands down awesome.

you wrote targets.el and now I discover that you wrote this. Are you a lisp hacker or something? How did you learn all this?

I'm a fan. 👍 💯 ❤️

How to transpose in normal mode?

Consider the following lisp snippet:

(if (eq branch nil)
        (format " [%s]" branch)
      "")

Where I want to transpose the if branches. If I put my cursor on the 2nd line on the ), and try to transpose the sexps I get this error:

forward-sexp: Scan error: "Containing expression ends prematurely", 473, 474

Does lispyville have a solution for this?

dd bug

This seems to have been introduced recently. Here's a test case

(let ((a 1)
      (b 3))
  (setq c a))
(func 1)

dd on the second line gets me

(let ((a 1))  (setq c a))


(func 1)

There's an extra join line in there, pulling the setq expression up a line. dd on the original third line gets me

(let ((a 1)
      (b 3)))(func 1)

I couldn't think of anything in my settings that would cause this, and I don't remember this behavior happening until recently.

"atom-end" motions are offset to the right

;;    Aa  B  Cc
(peanut butter sandwich)

With the cursor on A and the atom-movement theme enabled, pressing "E" and "gE" should bring the cursor to C and A respectively, but it ends up being offset one character to the right (c and a).
Also the backward-atom-end function is missing a let-block for orig-pos variable.

It might not be the most elegant solution, but I fixed the behaviour by simply adding (forward-char -1) at the end of the functions and :type inclusive to keep dE, cE working as expected.

 (evil-define-motion lispyville-forward-atom-end (count)
   "Go to the next atom or comment end COUNT times."
+  :type inclusive
   (or count (setq count 1))
+  (forward-char 1)
   (if (< count 0))
...
-          (cl-return))))))
+          (cl-return))))
+    (forward-char -1)))

If this is alright (still haven't figured out how to run the tests properly) I could submit a simple PR :)
Thanks!

lispyville-up-list goes up one level too much

Current config:

(setq lispyville-key-theme '(operators
                                 (additional-movement normal)
                                 (escape insert hybrid emacs))
          lispyville-motions-put-into-special t)

backward up list works as intended.

e: Ah this only happens when two closing parens are stacked.

Can lispyville-< drag cursor back if closing paren passes over it?

Say you have the following, with the cursor before :init

(defun soo-parens/init-lispyville ()
  (use-package lispyville
    :diminish lispyville-mode
    |:init
    (add-hook 'lispy-mode-hook #'lispyville-mode)
    (setq lispyville-key-theme '(operators
                                 (additional-movement normal)
                                 (escape insert hybrid emacs)
                                 slurp/barf-cp)
          lispyville-motions-put-into-special t)))

< three times puts closing paren at end of line 3, and cursor gets pushed to bol 4. I can't directly reverse the last < motion with >, I need to now move cursor up a line. If by muscle memory you press >, you will slurp the next top level sexp. Since calling < from the middle of a sexp is clearly acting on the closing paren, when the < operation takes the closing paren past the cursor, instead of leaving cursor point outside of sexp it was operating on, can it follow the closing paren? Then all such calls should be nicely reversible, and make more sense imo. This can be a customisation, of course.

Splicing operator 'x' deletes wrong set of parens from end

;;            AB
(defn foo [bar])

With the cursor at position B (over the round closing paren) pressing 'x' for lispyville-delete-char-or-splice removes the square brackets instead. This issue occurs with all types of brackets (round, square or curly), but not string quotes.

lispy-pair with positive numeric arg doesn't wrap in evil-normal state

Consider the following sexp:
'(a b c d e f)
In evil-insert state, with point before symbol d, I can do C-u 3 ( to wrap 3 sexps like so:
'(a b c (d e f))

But it doesn't work in evil-normal state. Is that by design? Is there a configuration that would permit this sort of wrapping in evil-normal state as well?

Thanks,
Brett S.

vim-sexp-mappings-for-regular-people additional binds

You mentioned that you were going to be implementing vim-sexp-mappings-for-regular-people binds at some point, which is awesome. I ended up having to do it myself manually with the smartparens api because I didn't find existing packages that did it.

I'm gonna take a look at this package because I had seen other similar packages and also lispy, but lispy seemed like its own separate thing and the other packages I found for interfacing with it didn't seem all that enticing. I want to write down my ideas first though before I forget to, so it's likely that some of these things you already have covered in your package.

By the way, I found that I had to make use of the on-parens package for certain functions I created so that they would work well in evil normal mode, which is where I have these binds setup. See its readme for the rationale, which may or may not affect you. Here's an excerpt:

In vi/vim/evil-mode in normal-state, the cursor is viewed as being ON a character rather than BETWEEN characters. This breaks smartparens particularly when it tries to put you past the last character of the line, since normal-state will push point back one character so it can be viewed as on the last character rather than past it. Additionally, most vim/evil movements go to the beginning of a text object, with an alternative version that goes to the end. Emacs tends to go past the end of things when it goes forward, but to the beginning when it goes back.

I added a few additional bindings for operations that I think are useful, though you can be the judge on which keys to actually bind them on. You can find the full source for this here.

I have something like:

    "< u" 'sp-splice-sexp-killing-backward
    "> u" 'sp-splice-sexp-killing-forward

I actually use these a lot. Say I have (func |param thing other), then < u would make it param thing other and > u makes it func.

There's also sp-splice-sexp-killing-around which is useful so that it becomes just param. I wasn't sure what to bind this variant too though, and I do use it a lot, so I was just using the default smartparens bind for it which is C-S-Backspace. Perhaps something like ^u but I guess ^ is already taken.

I bound them to the letter "u" because it feels like I'm "unwrapping" the sexp in a certain direction. Then again smartparens already has a sp-unwrap-sexp which literally just removes the sexp delimiters, so perhaps it may be better to go with < s and > s for "splice".

I also have ones for deleting in a certain direction up to the sexp delimiter:

    "< d" 'blaenk/delete-sexp-backward
    "> d" 'blaenk/delete-sexp-forward

The definition of the -backward one for example is just:

      (defun blaenk/delete-sexp-backward ()
        (interactive)
        (sp-kill-sexp '(-4)))

It's very useful. So if I have (func |param thing other), < d makes it (param thing other) and > d makes it (func).

One other minor thing is that my binds for inserting at the beginning or end are < i and > i, i.e. not < I and > I. It's not a big deal and I'm fine with either, just a difference I noticed.

Also for < i I made it so that it auto-creates a space, so like if you have (one two |three) and you do < i, it'll leave you at (| one two three) instead of just (|one two three). I'm not sure that's all that useful though or even something that one would desire all the time, but it's an idea you might want to pursue for other bindings perhaps. Then again it's not that difficult to just type out a space manually.

I also created bindings for splitting sexps and joining them, M-S and M-J respectively, but I guess that doesn't really go well with the whole > and < binds anymore so that's perhaps not all that relevant.

Doesn't play well with general.el and general-key-dispatch

I ran into this problem when I was trying to use both lispyville and general.el. All the keybindings defined work except for the general-key-dispatch ones. For some reason, co would just put me in insert mode, like what s would normally do. It took me a while to isolate the package that caused the trouble.

Here's my actual configuration for lispyville and general.el -

(use-package lispyville
  :ensure t           ; ensure the package is present
  :demand t           ; load the package immediately
  :diminish lispyville-mode   ; clean the modeline of this mode
  :diminish lispy-mode        ; disable lispy mode also
  :config             ; configuration to be loaded after package loading
  (with-eval-after-load 'lispyville ; once lispyville loads
    (lispyville-set-key-theme '(operators ; set the operators theme in normal and visual states
                (escape insert)
                (slurp/barf-lispy)))) ; set the escape theme in the insert state
  (add-hook 'emacs-lisp-mode-hook #'lispyville-mode)
  (add-hook 'lispyville-mode-hook #'lispy-mode))

(use-package evil               ; extensible vi layer - evil
:ensure t                       ; ensure the package is present
:demand t
:general                    ; `general.el' maps
(general-nmap :prefix sk--evil-global-leader  ; create a global leader map
        "" '(nil :which-key "user prefix")
        "q" '(evil-quit :which-key "quit window/emacs")
        "k" '(evil-delete-buffer :which-key "kill buffer")
        "a" (general-simulate-keys "C-x b" t "switch buffers")
        "f" (general-simulate-keys "C-x C-f" t "find files")
        "j" (general-simulate-keys "M-x" t "command")
        "y" (general-simulate-keys "M-y" t "yank ring")
        "x" (general-simulate-keys "C-h" t "help")
        "w" 'save-buffer)
(general-nmap "c" (general-key-dispatch 'evil-change
        "ow" 'toggle-word-wrap
        "c" 'evil-change-whole-line))
(general-vmap "c" 'evil-change)
(general-nmap "go" (general-simulate-keys "C-x C-e" t "eval"))
(general-nmap "w" (general-simulate-keys "C-x o" t "other window"))
(general-nmap "gz" (general-simulate-keys "C-x 1" t "only window"))
(general-nmap "W" 'winner-undo)
(general-nmap "gW" 'winner-redo)
(general-nmap "gS" (general-simulate-keys "C-j" t "split line"))
(general-nmap "gJ" 'evil-join)
:init                           ; settings to be loaded before the package is loaded
(setq evil-want-C-u-scroll t)               ; scroll like Vim when in normal mode
(setq evil-want-Y-yank-to-eol t)            ; yank 'Y' like 'D' and 'C'
:config                     ; settings after the package is loaded
(setq evil-normal-state-modes (append evil-emacs-state-modes evil-normal-state-modes))
(setq evil-emacs-state-modes nil)           ; let there be no emacs state modes. add them all to normal state
(evil-mode 1))                  ; turn on the Vi emulation

A couple of questions

Thanks for this. I like it so far

I'm wondering if you were planning on putting this on melpa.

I'm also not clear on the lispy dependence. Does lispy-mode have to be active for the evil key bindings to work properly?

lispy freeze when deleting text

I have a buffer like this (scratch),

;; This buffer is for text that is not saved, and for Lisp evaluation.
;; To create a file, visit it with C-x C-f and enter text in its buffer.

(use-package lispy
  :ensure t
  :init
  (dolist (hook (+lisp-hooks))
    (add-hook hook #'lispy-mode))
  :config
  (lispy-define-key lispy-mode-map-special (kbd "]") 'lispy-forward)
  (lispy-define-key lispy-mode-map-special (kbd "[") 'lispy-backward)
  (lispy-define-key lispy-mode-map-special (kbd "}") 'lispy-brackets)

  (define-key lispy-mode-map-lispy (kbd "M-q") nil)
  (define-key lispy-mode-map-lispy (kbd "[") nil)
  (define-key lispy-mode-map-lispy (kbd "]") nil)
  (define-key lispy-mode-map-lispy (kbd "}") nil)
  (define-key lispy-mode-map-lispy (kbd "\"") nil)
  (define-key lispy-mode-map-lispy (kbd "/") nil)

  (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-delete) ;; `lispy-different'
  (lispy-define-key lispy-mode-map-special "x" '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 "/" 'lispy-occur) ;; `lispy-flow'
  ;; (lispy-define-key lispy-mode-map-special "/" nil)
  ;; `lispy-splice'[PLACE CURSOR HERE]
  (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'
  )

If I press 'x' on [PLACE CURSOR HERE] (bound to (lispyville-delete-char-or-splice), the editor stalls and I have to c-g out of it.

Emacs 26


Debugger entered--Lisp error: (quit)
  (#f(compiled-function () #<bytecode 0x46d10fd1>))
  (syntax-ppss)
  (lispy--in-comment-p)
  (lispy--bounds-comment)
  (lispy--find-unmatched-delimiters 1393 1468)
  (lispyville--safe-delete-by-splice 1393 1468 t nil nil)
  (lispyville-delete-char-or-splice 1393 1468 line nil)
  (funcall-interactively lispyville-delete-char-or-splice 1393 1468 line nil)
  (call-interactively lispyville-delete-char-or-splice nil nil)
  (command-execute lispyville-delete-char-or-splice)

Emacs 25

Debugger entered--Lisp error: (quit)
  syntax-ppss()
  comment-beginning()
  lispy--beginning-of-comment()
  lispy--bounds-comment()
  lispy--find-unmatched-delimiters(1392 1467)
  lispyville--safe-delete-by-splice(1392 1467 t nil nil)
  lispyville-delete-char-or-splice(1392 1467 line nil)
  funcall-interactively(lispyville-delete-char-or-splice 1392 1467 line nil)
  call-interactively(lispyville-delete-char-or-splice nil nil)
  command-execute(lispyville-delete-char-or-splice)

Emacs -Q

(add-to-list 'load-path "/Users/jnguyen/.emacs.d/elpa/25/avy-20171230.220")
(add-to-list 'load-path "/Users/jnguyen/.emacs.d/elpa/25/iedit-20170916.1024")
(add-to-list 'load-path "/Users/jnguyen/.emacs.d/elpa/25/lispy-20180123.1255")
(add-to-list 'load-path "/Users/jnguyen/.emacs.d/elpa/25/ivy-20180124.1127")
(add-to-list 'load-path "/Users/jnguyen/.emacs.d/elpa/25/swiper-20180124.1142")
(add-to-list 'load-path "/Users/jnguyen/.emacs.d/elpa/25/hydra-20171120.1042")

(add-to-list 'load-path "/Users/jnguyen/.emacs.d/elpa/25/lispyville-20180120.1206")

(add-to-list 'load-path "/Users/jnguyen/.emacs.d/elpa/25/evil-20180126.1159")

(require 'evil)

(require 'iedit)
(require 'avy)
(require 'ivy)
(require 'swiper)
(require 'hydra)
(require 'lispy)

(require 'lispyville)

(evil-mode)
(lispy-mode)
(lispyville-mode)

(use-package lispy
  :ensure t
  :init
  (dolist (hook (+lisp-hooks))
    (add-hook hook #'lispy-mode))
  :config
  (lispy-define-key lispy-mode-map-special (kbd "]") 'lispy-forward)
  (lispy-define-key lispy-mode-map-special (kbd "[") 'lispy-backward)
  (lispy-define-key lispy-mode-map-special (kbd "}") 'lispy-brackets)

  (define-key lispy-mode-map-lispy (kbd "M-q") nil)
  (define-key lispy-mode-map-lispy (kbd "[") nil)
  (define-key lispy-mode-map-lispy (kbd "]") nil)
  (define-key lispy-mode-map-lispy (kbd "}") nil)
  (define-key lispy-mode-map-lispy (kbd "\"") nil)
  (define-key lispy-mode-map-lispy (kbd "/") nil)

  (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-delete) ;; `lispy-different'
  (lispy-define-key lispy-mode-map-special "x" '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 "/" 'lispy-occur) ;; `lispy-flow'
  ;; (lispy-define-key lispy-mode-map-special "/" nil)
  ;; `lispy-splice'[PLACE CURSOR HERE]
  (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'
  )

This might be more appropriate for lispy, but I thought I'd leave it here first.

Using C-[ in lieu of <escape>

I've been wanting to switch to having "emacs" as my "preferred" state, but for this to be practical, I need a quick and easy way to get from "emacs" state to "normal" state. The "escape theme" seemed to provide the solution; however, it seems to work only with the actual <escape> key, which I intentionally never use. (I've used C-[ for years, to the point that it feels almost like a regular key to me at this point.)

So I've been poring over keymaps, keymap alists, etc..., trying to understand why C-[ works in insert state, but not in emacs state. My hunch is that, when it works, it works because there's no meta prefix sub-map that would cause the ASCII 27 to be treated as a prefix key: e.g., nothing like this in the active maps...

(27 keymap
		    (108 . lispyville-end-of-defun)
		    (104 . lispyville-beginning-of-defun))

Is this the case? What's confusing me though is that in both cases (insert and emacs states) I can see such submaps in the output of (current-active-maps), and yet, there's definitely something different going on in the insert state case. In emacs state, C-[ is being treated as <escape>, which is mapped to lispyville-escape, but in insert state, it's treated as ESC-prefix: i.e., I'm prompted with ESC- in the minibuffer, and emacs simply waits for the next key.

Is the problem that emacs state has meta mappings and evil insert state doesn't? And if that's the case, am I correct in assuming what I'm trying to do can't be done? If you have any advice on how to search through active keymaps to see exactly which bindings emacs would find in a give minor mode, I'd greatly appreciate it. I must be missing something, because I'm seeing what look like active meta key submaps even in the evil insert state case, and yet C-[ gets mapped to <escape> somehow in insert state.

lispyville-delete called with `dG` acts strangely

In a buffer with multiple lines, calling dG will leave the cursor in the middle of the screen in a position it cannot be in (where no lines exist). Then pressing any other key moves the cursor back to last real line, but there are trailing whitespace in the line above.

Additional movement breaks Clojure

With Lispyville and Lispy I can't insert a pair of square brackets [] because when I type [ my point jumps to the precious closing paren rather than inserting the pair.

I'm 90% sure enabling the additional movement keys with normal, visual and motion states is the cause of the problem.

I think the solution might be to simply remove the motion state but I haven't had a chance to test this theory yet.

Lispy has a function to insert the relevant square bracket pair, if that helps.

BTW, thanks for an awesome library!

Scheme (R6RS) and square brackets mapping

Is it possible to remap [ to lispy-brackets, and ] and also } to maybe lispy-right-nostring?

Sorry if this is a stupid question, I just started using Lispyville today (which by the way it's fantastic!), but in Scheme and Racket square brackets are used all the time and it's a bit difficult to me to remember to use } instead.

Another thing that I think could make this a bit more intuitive for Vim/Evil users would be to map % when the point is before or after a paren to have the same behavior than in normal mode, although I am not sure if such feature exists in Lispy.

Thanks a lot!

lispy-mark-symbol in normal state marks extra char past symbol unless lispyville-enter-special-when-marking is used

When I'm not using lispyville-enter-special-when-marking and lispy-mark-symbol is executed from normal state, an extra char past the end of the symbol is pulled in to the region. I've verified that lispy-mark-symbol is calculating the correct bounds to pass to lispy--mark, which in turn passes the correct bounds to set-mark, so I'm assuming the problem lies in some sort of region activation hook.

Installation documentation?

Thanks for making this package! I'm excited to get it up-and-running, but I'm currently having trouble.

Here is my configuration:

(defconst wpc/lisp-mode-hooks
  '(lisp-mode-hook
    emacs-lisp-mode-hook
    clojure-mode-hook
    clojurescript-mode-hook))

(use-package lispy
  :config
  (general-add-hook wpc/lisp-mode-hooks (enable lispy-mode))) ;; enable is just a macro that expands into `(lambda () (lispy-mode 1)`

(use-package lispyville
  :after (lispy)
  :config
  (add-hook 'lispy-mode-hook #'lispyville-mode))

I have confirmed that lispyville-mode is activated when I'm in .el files using describe-mode. What's troubling me, is that some of the keybindings I'm expecting to be mapped don't appear to be mapped.

For example, I'm primarily interested in using the lispyville-drag-{forward,backward} commands. Currently when I run M-j or M-k, I get lispy-split and lispy-kill-sentence.

While I'm on the subject of lispy bindings, I know lispyville cooperates with lispy, but I wasn't expecting to have any lispy KBDs in my keymaps; I was only expecting to have lispyville bindings. Is this an unreasonable expectation? In general I'm leery of overpopulating my keymaps because I find keybinding in Emacs to be somewhat messy. (P.s. I use general to help me with this, so thank you for that package.)

Lastly, would you be willing to accept a PR that includes a basic "Installation" section. The Readme is quite informative and as such it's long and takes time to read. While I'm at work, sometimes I only have time to quickly install and test out packages. For these moments, I look for quickstart installation instructions. I'm having trouble finding those in the Readme. It's possible that I'm missing something obvious.

Confusion regarding visual state transitions with escape key theme disabled

I've been using (escape insert) in my key themes, but as I began to learn more about the non-Lispy functionality provided by Lispyville, I found myself wanting to use operators like y and d in visual state. But then it occurred to me that I was never really in evil visual state. Because I also have (mark normal) in my key themes, v, V and M-m all put me into evil insert state, and the aforementioned escape theme ensures that I transition directly back to evil normal state when I cancel the region with escape. With the escape key theme disabled, if I mark a symbol in insert state, a subsequent escape transitions me back to normal state (according to the <N> in the status line), but leaves the region active and allows me to use commands like y to operate on the region. Also, subsequent motions move the edge of the region as it would in evil. This would seem to indicate I'm in visual state, though the status area gives no indication of it (e.g., no -- VISUAL --). But what really confused me was the fact that if I activate the mark in normal state (e.g., with M-m), the status lines look the same as just described (transition to <I> on mark, then back to <N> on escape), but the region disappears after the escape. Clearly, there's something different going on here, which isn't reflected in the modes displayed in the status area. Another thing that surprises me about this (apparent) visual state (and makes me wonder whether it's really true visual state) is that I can't get out of it with escape, but have to hit C-g to deactivate the region. Interestingly, hitting C-g instead of escape will take me from lispy region special all the way back to normal state, skipping right over the visual state normally entered on escape.

Any light you can shed on what's going on here would be greatly appreciated. I'm trying to settle on a set of key themes that best fits my workflow, and would like to understand a bit more about the actual modes and transitions in play here before making a decision. My original motivation for looking at this was the following use case: I've marked a sexp and want to operate on it somehow: e.g., perhaps I want to copy it (and don't like having to use M-w), or perhaps I want to change it (and don't really like having to reach for something like M-DEL). Being able to hit y or c in this scenario would be nice. Of course, I'm thinking I'd have to weigh this convenience against the inconvenience of having to use C-g instead of the more convenient C-[ to cancel the region when I don't want to operate on it...

[enhancement] issues using lispyville in non-lispy modes

Continuing discussion in #53

  • problem: lispyville-delete lispyville-up-list automatically prettifies code, pulling things after { to the same line, which is unwanted. (Not using prettify key theme but binding lispyville-prettify or evil-indent depending on major mode fixes evil-indent.)
    idea: Maybe provide option to not prettify anything after delete etc.? Or, option to override the internal prettify mechanism with normal indent?
  • problem: y y on console.log(`hello, ${name}`), when pasting, the second ` will be missing.

Fix Local Links

Github doesn't work for org links that link to a heading in the current file. I guess I need to add absolute links unfortunately.

[question] How to evil-define-motion something like lispyville-backward-up-list that's paren-safe?

I want to move to any [] () {} in normal state in a single key((), in any programming language, so I defined 2 functions using evil-define-motion. The forward one works well, but the backward one, say, moves to(right before) the closest ( correctly, but also deletes it when I type d(, even if I use :type exclusive. How to make it behave like lispyville-backward-up-list? Here's the code:

(evil-define-motion backward-bracket (count)
  "Move backward to a (, [ or {."
  :type exclusive
  (setq count (or count 1))
  (search-backward-regexp "[([{]" nil t count))

Stupid question(s)

Hey, so I'm just starting to get into some Lisp and Scheme programming (going through The Little Schemer) and finally wanted to try out Lispy (and Lispyville). However since I haven't really seriously programmed in either I have some questions about them.

First off, I use Evil and was wondering how it works with Insert. I haven't used Paredit so I'm not sure how it would work with it, but when you press i into insert mode, it goes into Lispy-mode whenever I'm over a ( and so on, and regular insert mode whenever I'm not. However, I'm curious as to what power users would use when they are in either mode to jump to its other insert mode.

So say I've been writing some code normally but want to change it with Lispy, is there a way to directly jump from insert mode in Evil to Lispy-mode, or do I exit to normal mode, find a paranthesis and then go into Lispy-mode? And for the other way, when I'm done manipulating things with Lispy, is there a way to jump directly into regular insert mode?

Sorry if I'm asking in the wrong place, but since this package caters directly to people who use Evil and Lispy I figured I'd ask here.

[suggestion] Add TOC to README

I came from terget.el and read the README. And, for me, it is a bit hard to grasp the structure of it. I think it is because 1) GitHub renders headings without numbers 2) Paragraphs are long.

Adding a TOC or manually number the heading might help a bit.

Odd behavior when yanking/pasting with block selection

For example, with lispyville-mode on, if I have:

"
123 456 789
123 456 789
"

And I highlight the 456 column and delete it (with point at top 4, C-v 3l j d), then try to paste them after the 789 column (with point at top 9, p) I get:

"
123 789456 
456 
123 789
"

If repeat all these steps with lispyville-mode off, I get the expected:

"
123 789456
123 789456
"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.