Giter Site home page Giter Site logo

abo-abo / lispy Goto Github PK

View Code? Open in Web Editor NEW
1.2K 26.0 129.0 5.2 MB

Short and sweet LISP editing

Home Page: http://oremacs.com/lispy/

Emacs Lisp 91.38% Clojure 3.65% Makefile 0.09% Python 4.88%
navigation evaluation refactoring emacs-lisp clojure common-lisp scheme python

lispy's Introduction

License GPL 3 Build Status Coverage Status MELPA MELPA Stable

lispy logo

short and sweet LISP editing

Table of Contents

Introduction

This package reimagines Paredit - a popular method to navigate and edit LISP code in Emacs.

The killer-feature are the short bindings:

command binding binding command
paredit-forward C-M-f j lispy-down
paredit-backward C-M-b k lispy-up
paredit-backward-up C-M-u h lispy-left
paredit-forward-up C-M-n l lispy-right
paredit-raise-sexp M-r r lispy-raise
paredit-convolute-sexp M-? C lispy-convolute
paredit-forward-slurp-sexp C-) > lispy-slurp
paredit-forward-barf-sexp C-} < lispy-barf
paredit-backward-slurp-sexp C-( > lispy-slurp
paredit-backward-barf-sexp C-{ < lispy-barf

Most of more than 100 interactive commands that lispy provides are bound to a-z and A-Z in lispy-mode. You can see the full command reference with many examples here.

The price for these short bindings is that they are only active when:

  • the point is before an open paren: (, [ or {
  • the point is after a close paren: ), ] or }
  • the region is active

The advantage of short bindings is that you are more likely to use them. As you use them more, you learn how to combine them, increasing your editing efficiency.

To further facilitate building complex commands from smaller commands, lispy-mode binds digit-argument to 0-9. For example, you can mark the third element of the list with 3m. You can then mark third through fifth element (three total) with 2> or >>. You can then move the selection to the last three elements of the list with 99j.

If you are currently using Paredit, note that lispy-mode and paredit-mode can actually coexist with very few conflicts, although there would be some redundancy.

Relation to vi

The key binding method is influenced by vi, although this isn't modal editing per se.

Here's a quote from Wikipedia on how vi works, in case you don't know:

vi is a modal editor: it operates in either insert mode (where typed text becomes part of the document) or normal mode (where keystrokes are interpreted as commands that control the edit session). For example, typing i while in normal mode switches the editor to insert mode, but typing i again at this point places an "i" character in the document. From insert mode, pressing ESC switches the editor back to normal mode.

Here's an illustration of Emacs, vi and lispy bindings for inserting a char and calling a command:

insert "j" forward-list
Emacs j C-M-n
vi in insert mode j impossible
vi in normal mode impossible j
lispy j j

Advantages/disadvantages:

  • Emacs can both insert and call commands without switching modes (since it has none), but the command bindings are long
  • vi has short command bindings, but you have to switch modes between inserting and calling commands
  • lispy has short command bindings and doesn't need to switch modes

Of course it's not magic, lispy needs to have normal/insert mode to perform both functions with j. The difference from vi is that the mode is explicit instead of implicit - it's determined by the point position or the region state:

  • you are in normal mode when the point is before/after paren or the region is active
  • otherwise you are in insert mode

So people who generally like Emacs bindings (like me) can have the cake and eat it too (no dedicated insert mode + shorter key bindings). While people who like vi can still get an experience that's reasonably close to vi for LISP editing (since vi's line-based approach isn't very appropriate for LISP anyway).

But if you ask:

What if I want to insert when the point is before/after paren or the region is active?

The answer is that because of the LISP syntax you don't want to write this:

j(progn
   (forward-char 1))k

Also, Emacs does nothing special by default when the region is active and you press a normal key, so new commands can be called in that situation.

Features

  • Basic navigation by-list and by-region:

    • h moves left
    • j moves down
    • k moves up
    • l moves right
    • f steps inside the list
    • b moves back in history for all above commands
  • Paredit transformations, callable by plain letters:

    • > slurps
    • < barfs
    • r raises
    • C convolutes
    • s moves down
    • w moves up
  • IDE-like features for Elisp, Clojure, Scheme, Common Lisp, Hy, Python and Julia:

    • e evals
    • E evals and inserts
    • g jumps to any tag in the current directory with semantic
    • G jumps to any tag in the current file
    • M-. jumps to symbol, M-, jumps back
    • F jumps to symbol, D jumps back
    • C-1 shows documentation in an overlay
    • C-2 shows arguments in an overlay
    • Z breaks out of edebug, while storing current function's arguments

Some pictures here.

  • Code manipulation:
    • i prettifies code (remove extra space, hanging parens ...)
    • xi transforms cond expression to equivalent if expressions
    • xc transforms if expressions to an equivalent cond expression
    • x> transforms expressions from/to an equivalent thread-last expression
    • xf flattens function or macro call (extract body and substitute arguments)
    • xr evals and replaces
    • xl turns current defun into a lambda
    • xd turns current lambda into a defun
    • O formats the code into one line
    • M formats the code into multiple lines
  • Misc. bindings:
    • outlines navigation/folding (J, K, I, i)
    • narrow/widen (N, W)
    • ediff (b, B)
    • ert (T)
    • edebug (xe)

Function reference

Most functions are cataloged and described at http://abo-abo.github.io/lispy/.

Getting Started

Installation instructions

via MELPA

It's easiest/recommended to install from MELPA. Here's a minimal MELPA configuration for your ~/.emacs:

(package-initialize)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))

Afterwards, M-x package-install RET lispy RET (you might want to M-x package-refresh-contents RET beforehand if you haven't done so recently).

via el-get

el-get also features a lispy recipe. Use M-x el-get-install RET lispy RET to install.

Configuration instructions

Enable lispy automatically for certain modes

After installing, you can call M-x lispy-mode for any buffer with a LISP dialect source. To have lispy-mode activated automatically, use something like this:

(add-hook 'emacs-lisp-mode-hook (lambda () (lispy-mode 1)))

Enable lispy for eval-expression

Although I prefer to eval things in *scratch*, sometimes M-: - eval-expression is handy. Here's how to use lispy in the minibuffer during eval-expression:

(defun conditionally-enable-lispy ()
  (when (eq this-command 'eval-expression)
    (lispy-mode 1)))
(add-hook 'minibuffer-setup-hook 'conditionally-enable-lispy)

Customization instructions

If you want to replace some of the lispy-mode's bindings you can do it like this:

(eval-after-load "lispy"
  `(progn
     ;; replace a global binding with own function
     (define-key lispy-mode-map (kbd "C-e") 'my-custom-eol)
     ;; replace a global binding with major-mode's default
     (define-key lispy-mode-map (kbd "C-j") nil)
     ;; replace a local binding
     (lispy-define-key lispy-mode-map "s" 'lispy-down)))

Compatibility with other modes

Use the lispy-compat variable to enable compatibility with modes that could otherwise conflict. These currently include:

  • god-mode
  • magit-blame-mode
  • edebug
  • cider
  • macrostep

The default setting only enables compatibility with edebug.

Operating on lists

How to get into list-editing mode (special)

The plain keys will call commands when:

  • the point is positioned before paren
  • the point is positioned after paren
  • the region is active

When one of the first two conditions is true, I say that the point is special. When the point is special, it's very clear to which sexp the list-manipulating command will be applied to, what the result be and where the point should end up afterwards. You can enhance this effect with show-paren-mode or similar.

Here's an illustration to this effect, with lispy-clone (here, | represents the point):

before key after
(looking-at "(")| c (looking-at "(")
(looking-at "(")|
before key after
|(looking-at "(") c |(looking-at "(")
(looking-at "(")

You can use plain Emacs navigation commands to get into special, or you can use some of the dedicated commands:

Key Binding Description
] lispy-forward - move to the end of the closest list, analogous to C-M-n (forward-list)
[ lispy-backward - move to the start of the closest list, analogous to C-M-p (backward-list)
C-3 lispy-right - exit current list forwards, analogous to up-list
) lispy-right-nostring exit current list forwards, but self-insert in strings and comments

These are the few lispy commands that don't care whether the point is special or not. Other such bindings are DEL, C-d, C-k.

Special is useful for manipulating/navigating lists. If you want to manipulate symbols, use region selection instead.

Digit keys in special

When special, the digit keys call digit-argument which is very useful since most lispy commands accept a numeric argument. For instance, 3c is equivalent to ccc (clone sexp 3 times), and 4j is equivalent to jjjj (move point 4 sexps down).

Some useful applications are 9l and 9h - they exit list forwards and backwards respectively at most 9 times which makes them effectively equivalent to end-of-defun and beginning-of-defun. Or you can move to the last sexp of the file with 999j.

How to get out of special

To get out of the special position, you can use any of the good-old navigational commands such as C-f or C-n. Additionally SPC will break out of special to get around the situation when you have the point between the open parens like this

(|(

and want to start inserting; SPC will change the code to this:

(| (

List commands overview

Inserting pairs

Here's a list of commands for inserting pairs:

key command
( lispy-parens
{ lispy-braces
} lispy-brackets
" lispy-quotes

Reversible commands

A lot of Lispy commands come in pairs - one reverses the other:

key command key command
j lispy-down k lispy-up
s lispy-move-down w lispy-move-up
> lispy-slurp < lispy-barf
c lispy-clone C-d or DEL
C lispy-convolute C reverses itself
d lispy-different d reverses itself
M-j lispy-split + lispy-join
O lispy-oneline M lispy-multiline
S lispy-stringify C-u " lispy-quotes
; lispy-comment C-u ; lispy-comment
xi lispy-to-ifs xc lispy-to-cond
x> lispy-toggle-thread-last x> reverses itself

Keys that modify whitespace

These commands handle whitespace in addition to inserting the expected thing.

key command
SPC lispy-space
: lispy-colon
^ lispy-hat
C-m lispy-newline-and-indent

Command chaining

Most special commands will leave the point special after they're done. This allows to chain them as well as apply them continuously by holding the key. Some useful hold-able keys are jkf<>cws;. Not so useful, but fun is /: start it from |( position and hold until all your Lisp code is turned into Python :).

Navigating with avy-related commands

key command
q lispy-ace-paren
Q lispy-ace-char
a lispy-ace-symbol
H lispy-ace-symbol-replace
- lispy-ace-subword

q - lispy-ace-paren jumps to a "(" character within current top-level form (e.g. defun). It's much faster than typing in the avy binding + selecting "(", and there's less candidates, since they're limited to the current top-level form.

a - lispy-ace-symbol will let you select which symbol to mark within current form. This can be followed up with e.g. eval, describe, follow, raise etc. Or you can simply m to deactivate the mark and edit from there.

- - lispy-ace-subword is a niche command for a neat combo. Start with:

(buffer-substring-no-properties
 (region-beginning)|)

Type c, -, b and C-d to get:

(buffer-substring-no-properties
 (region-beginning)
 (region-|))

Fill end to finish the statement.

Operating on regions

Sometimes the expression that you want to operate on isn't bounded by parens. In that case you can mark it with a region and operate on that.

Ways to activate region

While in special:

  • Mark a sexp with m - lispy-mark-list
  • Mark a symbol within sexp a - lispy-ace-symbol.

While not in special:

  • C-SPC - set-mark-command
  • mark a symbol at point with M-m - lispy-mark-symbol
  • mark containing expression (list or string or comment) with C-M-, - lispy-mark

Move region around

The arrow keys j/k will move the region up/down within the current list. The actual code will not be changed.

Switch to the other side of the region

Use d - lispy-different to switch between different sides of the region. The side is important since the grow/shrink operations apply to current side of the region.

Grow/shrink region

Use a combination of:

  • > - lispy-slurp - extend by one sexp from the current side. Use digit argument to extend by several sexps.
  • < - lispy-barf - shrink by one sexp from the current side. Use digit argument to shrink by several sexps.

The other two arrow keys will mark the parent list of the current region:

  • h - lispy-left - mark the parent list with the point on the left
  • l - lispy-right - mark the parent list with the point on the right

To do the reverse of the previous operation, i.e. to mark the first child of marked list, use i - lispy-tab.

Commands that operate on region

  • m - lispy-mark-list - deactivate region
  • c - lispy-clone - clone region and keep it active
  • s - lispy-move-down - move region one sexp down
  • w - lispy-move-up - move region one sexp up
  • u - lispy-undo - deactivate region and undo
  • t - lispy-teleport - move region inside the sexp you select with lispy-ace-paren
  • C - lispy-convolute - exchange the order of application of two sexps that contain region
  • n - lispy-new-copy - copy region as kill without deactivating the mark
  • P - lispy-paste - replace region with current kill

IDE-like features

These features are specific to the Lisp dialect used. Currently Elisp and Clojure (via cider) are supported. There's also basic evaluation support for:

  • Scheme (via geiser)
  • Common lisp (via slime or sly).
  • Hy (via comint).
  • Python (via comint and jedi).
  • Julia (via julia-shell).

lispy-describe-inline

Bound to C-1. Show the doc for the current function inline.

C-h f is fine, but the extra buffer, and having to navigate to a symbol is tiresome. C-1 toggles on/off the inline doc for current function. No extra buffer necessary:

screenshot

Here's how it looks for Clojure:

screenshot

lispy-arglist-inline

Bound to C-2. Show arguments for current function inline.

eldoc-mode is cool, but it shows you arguments over there and you're writing over here!. No problem, C-2 fixes that:

screenshot

As you see, normal, &optional and &rest arguments have each a different face. Here's how it looks for Clojure:

screenshot

lispy-goto

Bound to g.

Use completion to select a symbol to jump to from all top-level symbols in the in current directory.

Works out of the box for Elisp, Scheme and Common Lisp. clojure-semantic is required for Clojure.

lispy-eval

There's a feature similar to ipython-notebook. Evaluating an Emacs outline will evaluate all of the outline's code and echo the result of the last expression. When an outline ends with a colon (:), the result will instead be inserted into the buffer. If the evaluation result changes for whatever reason, it will be replaced after each subsequent e.

Python, Clojure, and Julia currently have a slightly better notebook support, pressing e on the parent outline will evaluate all the children outlines sequentially. This allows to arrange scripts hierarchically, with relatively few top-level outlines and relatively many total outlines. Each outline's output can be examined by adding a : to the title of the outline.

The following example shows a buffer before and after pressing e.

lispy-python-notebook.png

There is one top-level outline, with one level-2 child, which in turn has a four level-3 children. Three of these children end in :, so their output will be updated after the eval.

Demos

Screencasts

lispy's People

Contributors

aaronjensen avatar abo-abo avatar ahungry avatar akirak avatar alphapapa avatar anwyn avatar bcarrell avatar blak3mill3r avatar daanturo avatar et2010 avatar felipeochoa avatar gmichokostas avatar gopiandcode avatar mikavilpas avatar mookid avatar nbfalcon avatar nnoodle avatar noctuid avatar odanoburu avatar peeja avatar rcj avatar ruijieyu avatar ruricolist avatar seanirby avatar vindarel avatar wilfred avatar yangsheng6810 avatar yuhan0 avatar zaeph avatar zilti 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lispy's Issues

Eval isn't supported in `cider-scratch` buffers

Cider uses cider-eval-print-last-sexp (C-j by default) in cider-scratch buffers.

lispy-eval doesn't work here -- it throws cider-clojure-interaction-mode is not supported currently.

Is it possible to make lispy-eval work here as well?

conflict with eldoc-mode

For example, when the point ("*" represents the point) is on (defun *), the echo area would automatically display defun: (NAME ARGLIST &optional DOCSTRING DECL &rest BODY), and highlight the parameter I need to fill in (in this case, NAME).

When lispy-mode on, the echo area doesn't display anything in the above example. Strangely, if the point is on (defun * ) (notice a space after point), it works fine. Position before ")" isn't special after all, right?

Though I can get the same information by entering C-2, what eldoc-mode provides is more intuitive and convenient.

Is there an easier way to append to a list?

I'm in this situation all the time:

|(foo bar)

My goal is to turn it into (foo bar baz) with the fewest keystrokes. To accomplish this, I would basically do d -> C-b -> SPC -> baz. Is there a faster way? When I'm doing this sort of operation all the time, three keystrokes tends to feel less optimal.

make `lispy-cursor-down` work properly

The first time running lispy-cursor-down, multiple-cursors would ask "Do lispy-cursor-down for all cursors? (y or n)". It should be set to no so that 5C-7 does add 5 cursors below. Otherwise, it looks messy seeing many many cursors down there.

Any way to ensure that automatically, or we should warn users about that in README?

lispy does not return to the original point correctly after exiting document buffer

When the docstring is large, a proper buffer is used. The problem is, point does not returns to the same position but at beginning of line. This happens when there's a dedicated window in other window, and lispy opens docstring buffer in current window instead. For an example of dedicated window, you could install sr-speedbar package, and run sr-speedbar-toggle to open it, then try C-1 on a symbo.

`lispy-teleport` not quitting

Somewhat of an obscure one:

If you issue t for lispy-teleport, then change your mind and press C-g, then q for lispy-ace-paren, the teleport command will actually stick around, so when you select the paren after q, the teleport will still happen.

Documentation suggestion: mention `lispy-define-key' in the customization section

I use a non-qwerty keyboard layout, and I'm used to using the ijkl inverted triangle to move around (which correspond to the dtsr keys here).

I thus tweaked lispy's configuration to satisfy this, but I had to look at the code to figure out how to introduce a new special binding. It may be useful to mention it in the documentation.

Here is what I did:

(eval-after-load "lispy"
  `(progn
     ;; motion
     (lispy-define-key lispy-mode-map "s" 'lispy-down)
     (lispy-define-key lispy-mode-map "d" 'lispy-up)
     (lispy-define-key lispy-mode-map "t" 'lispy-left)
     (lispy-define-key lispy-mode-map "r" 'lispy-right)))

lispy will disable delete-selection-mode

Normally I set (delete-selection-mode t) in my .emacs, when I mark a region, using C-y will overwrite that part with the content in my clipper, but if I enable lispy-mode, it will disable delete-selection-mode, and C-y won't overwrite the marked region.

This is even happening in comments.

I know there was a discussion at #8, but I didn't see the solution.

I use rebox2, and there a lot of default shortkeys are replaced by rebox2, but I can just use something like (define-key rebox-mode-map [(control y)] nil) to disable the C-y bound to rebox-yank, then I can still use C-y to yank, but this won't work for lispy, neither will (put 'lispy-yank 'disabled nil)refer or (fmakunbound 'lispy-yank)refer.

Please DO NOT require hydra

When I update my packages from melpa, I noticed that the hydra package of yours has been downloaded and you require it inside lispy package, but only a few lines of code is related, I don't know if you are going to add more or not, but I rarely use hydra, and honestly, the functions lispy provides is too much, I already disable several lispy-mode-map bindings,

lispy is great, it provides a lot of commands I won't even imagine, the bad thing is, it will use a lot of bindings to replace the default ones related to Ctrl and Alt which I already defined and maybe will use in my own init.el.

Have you ever thought about making it cleaner and cutting off some features/functions that are not that important, you really don't have to take every possibility into consideration and put it into lispy.

Clojure hashes inside macros add a space

Here's a fun one for you for a rainy day. It's a sufficiently rare scenario so as not to be an immediate concern, but symbols like foo# are valid (syntactical sugar) symbols (they're gensyms) within Clojure macros.

But you can't actually type in foo#, since Lispy will automatically insert a space after foo. So you have to C-bDEL to bump the hash over.

This is probably fairly challenging to fix without breaking other functionality, but you be the judge.

A few ways to break a buffer

Here's an interesting situation I just ran into:

(foo |"string )" "bar")

kill-word does this:

(foo )" "bar") -- which, of course, leaves the buffer broken.

kill-sentence is also similar, which will do:

(foo

Do we need Lispy versions of M-d and M-k?

Installation instructions?

Im a complete emacs newbie. Having some installation instructions in the readme would be really helpful. Any plans to add these?

I've also noticed lispy is not packaged in marmalade. Having it in marmalade could be a nice way to get up and running since a lot of tutorials use it to install packages.

Slurp from nested sexps

It would be convenient to be able to slurp past the boundary of a surrounding sexp:
((a)|) b c >>> ((a b)|) c

lispy-move-up/down not consistent with non-form elements

Example (-|- is point):

(make-string -|-(string-to-char "asdf") 4)

I'd expect if I used lispy-move-down (s) here, it'd shift the string-to-char form behind the 4. But since 4 isn't a list, it doesn't move anything. However, if I mark the string-to-char form with m and activate the region, s works as desired by moving the form after the 4. This affects both lispy-move-up and lispy-move-down.

Sliding args around is a fairly frequent operation so it would be nice if this worked without having to activate a region first.

Add an option to toggle region behaviour

Thanks for this excellent extension! This is the most useful edit tools for lisp programming I've ever tried since Paredit. But these days I find sometimes its default behaviour for current active region is kind of annoying.

I think the commands' behaviour for active region are not really closely related to lisp program's structure (S-exp) and may conflict with other widely used extension (delete-selection-mode which is shipped with Emacs itself, for example), so this feature should be optional.

Conflicts with god-mode

I can't think of a good way around this, but you should know that lispy doesn't coexist well with god-mode.

I frequently jump in and out of god-mode for plain navigation with fbnp (among plenty of other things), etc., which doesn't work at all when lispy is on, since both packages have a similar idea of using keys without modifiers.

Here's what I do to make both packages work:

(defun update-lispy ()
  (if (bound-and-true-p lispy-mode)
      (lispy-mode -1)
    (lispy-mode 1)))

(add-hook 'god-mode-enabled-hook 'update-lispy)
(add-hook 'god-mode-disabled-hook 'update-lispy)

Which basically just turns off Lispy while I'm in God-Mode and turns it back on when I jump out. I frequently jump into God-mode for small things.

Optionally, as a user, you can also turn on Paredit when Lispy is off to ensure parens stay balanced.

Anyway, I'm not sure if you have a better solution, but I thought you should be aware.

`lispy-kill-word` jumps out when killing last word

(foo "|bar")

Here's what lispy-kill-word does:

(foo "")|

Here's what I wish lispy-kill-word did:

(foo "|")

Because in most cases I want to replace the word with another. Even if I didn't, I probably don't want an empty string left in there.

I can probably fix this and send a PR if you want to do other things.

conflict with company-mode

Once lispy-mode enabled, company-mode cannot automatically completes. Would you investigate?

Thanks for your wonderful package!

M-m is a common keybinding and should not get redefined

This is easy to fix but it would be better to not redefine any keys while not in a special position. Same for "[" and "]". It's easy enough to remove those bindings but please be very considerate when overriding standard keybindings as some people might rely on those bindings a lot.

Extend region not working

According to README, press j after m should extend region one list forward. Below is the test case not passed:

(ert-deftest lispy-mark-list ()
  (should (string= (lispy-with "|(a) (b) (c)" "m")
                   "~(a)| (b) (c)"))
  (should (string= (lispy-with "|(a) (b) (c)" "mj")
                   "~(a) (b)| (c)")))

Active region corrupts undo (C-/)

  1. Activate a sexp region (m)
  2. Run sequence of revertible commands (w s ...)
  3. Don't deactivate the region
  4. Run undo (C-/) until 'No further undo information' in the minibuffer

What I get very often is either complete mess or even Emacs' freeze.

"Unbalanced parens" error

When the point is on

;; (foo bar
;;      tanf)*

Entering [ echoes "Unbalanced parens", and the point doesn't move.

Also

;; (foo bar)*

Entering [ would result in

;; *(foo bar)

Well, I expected to just skip the comment, and move to whatever valid parens above.

syntax table entry for { breaks cursor movements

lispy-mode has this line in it:

(modify-syntax-entry ?{ "(}")

which modifies the syntax table entry for { character so that it is treated
similar to ( as far as cursor movement is concerned. This is problematic,
because built-in cursor movement functions no longer work as expected, e.g.,
C-M-f and C-M-b bound to forward-sexp and backward-sexp respectively. To see
the problem evalute the following in the scratch buffer to set the syntax table
entry to the default value:

(modify-syntax-entry ?{ "_")

Now you should be able to execute C-M-f and C-M-b to move the cursor
between beginning and end of this line:

(char-syntax ?{)

So far so good. Now evalute this which appears in lispy-mode:

(modify-syntax-entry ?{ "(}")

Try hitting C-M-f and C-M-b as before. C-M-f fails becase ?{ is treated same as
( for the purpose of the cursor movement. Thus it sees two opening paired delimiters,
but only one closing delimiter. If we add a backslash like this

(char-syntax ?{)

then C-M-f and C-M-b works again. So adding a backslash is a work-around.
Unfortunately backslash is not required to escape { character. Lots of built-in
emacs lisp code use ?{ instead of ?{ as well as add-on pacakges, so that it is
not feasible to replace all ?{ with ?{ as a work around to
(modify-syntax-entry ?{ "(}")
done in lispy-mode.

Would it be possible for lispy-mode not to modify the syntax entries?

Helm not found

Hi, Oleh,

I tried to install lispy from melpa, and got an error about helm not found. I don't use Helm (and don't want to - I'm an Icicles guy). Any suggestions?

Deletion oddities

Here's a few things to look into!

  • lispy-delete-backwards isn't working properly with braces {}. To reproduce, try lispy-delete-backwards from inside {-|-}. Throws Variable binding depth exceeds max-specpdl-size. If the braces are inside a list, e.g. (1 2 3 {-|-}), lispy-delete-backwards will actually blow away the entire containing list, too. It seems to be working properly for brackets and parens.
  • Quoted lists are a little bit off in some situations. Here's an example --
(defun test () '(-|-1 2 3))

lispy-delete-backward here will do this...

(defun test ()-|- ' )

In this situation, when I try to delete backward with a list full of things, I expect it to either do nothing (this is probably my preferred behavior because if I accidentally delete one too many times trying to modify the first element, I lost the entire list, but Im fine with blowing it all away too) because the list still contains elements, or I expect it to do either of these things...

(defun test ()-|-)
(defun test () -|-)

But either way, if it blows away the list and jumps back to the previous paren, the quote should probably not be left there with a space after it.

mysterious behavior of key "a" and "q"

I've no idea how to reproduce, but I've encountered it several times. When entering q, which is supposed to call special-lispy-ace-paren, the mini-buffer echoes "Nothing to complete". And a also behaves strangely, when entering, it behaves like calling l, echoing "Nothing to complete" in the meantime.

Is there any change I can disable the lispy key bindings

lispy-newline-and-indent-plain basically do nothing but newline-and-indent, the thing is I made a defun in my .emacs to make RET more powerful, so the (define-key map (kbd "RET") 'lispy-newline-and-indent-plain) in lispy.el is in conflict with my RET.

I use (define-key lispy-mode-map "RET" nil) to disable the RET bound to lispy-newline-and-indent-plain, but if I restart Emacs, it says

Debugger entered--Lisp error: (void-variable lispy-mode-map)

Why it says lispy-mode-map is void variable??

And there is another function called lispy-move-end-of-line, but I got a more powerful customized C-e, I really need to disable lispy-move-end-of-line.

I believe there are more examples like these two.

So as I said in #30, since you use a lot of lispy-xx to replace the default bindings, I hope you can give us the abilities to disable them if we want, but I tries many ways, I cannot disable a single key binding defined in lispy.

Conflict with org-mode

Enabling lispy-mode inside orgmode buffers causes some minor issues because both org and lispy implementations redefine many common keys from self-insert to their own versions to hook into it.

This causes org-use-speed-commands to not work anymore:

  • Enable org-use-speed-commands using customize
  • Open .org file
  • Enable lispy-mode
  • Go to beginning of headline "* foo"
  • Press n/p to move to next/prev headline - nothing happens

Also editing in tables is broken:

  • In org mode buffer create a small table
  • Enable lispy-mode
  • Move to prev/next field using tab
  • Start editing. Without lispy-mode the table cell gets overwritten by the first key stroke. When lispy-mode is enabled this does not happen anymore.

A possible fix might be to look up the binding of the given key w/o lispy-mode-map in lispy--insert-or-call like this (not tested, this is a sketch)

(let ((lispy-mode nil))
    (key-binding (this-single-command-keys t))

Multiline works unpredictably

Here's an example in Clojure:

-|-(= 1 1)

Multiline transforms this into:

(=
 1
 1)

But in this case...

-|-(= {:a 1} {:a 1})

Multiline transforms this into:

(= {:a 1}
   {:a 1})

This can be quite confusing on more complicated structures. Vectors use the same behavior as the integer example above, whereas lists seem to follow the map structure in the second one.

Slurping into an empty list yields extra space

So, sometimes I have this situation:

()| foo and I slurp in the foo and it ends up being ( foo). This is almost never what I want -- if I'm slurping into an empty list I don't want there to be any spaces. This is easy to fix via i but it's somewhat common enough to be a hint of friction with the extra keypress.

A corner case of lispy-parens

For example (notice a single quote before the open paren)

'-|-(foo bar)

Press 2(, I would expect the point to be

'(-|- (foo bar))

so that I can start typing right away.

In fact, the result is

'-|-( (foo bar))

I need to press another C-f to starting typing.

Killing forms doesn't consistently save to kill-ring

Killing some forms won't save to the kill ring.

When I briefly looked into why this was happening, it seems to be because lispy-kill eventually calls lispy--delete, which calls delete-region. Since delete-region doesn't save to the kill ring, I suspect it's just being lost.

Examples (-|- is point):

C-k will save this to kill ring:

-|-(defun single-line-form () (message "Testing"))

C-k will not save this to kill ring:

-|-(defun multi-line-form ()
  (message "Testing"))

As an aside on this, when you kill the second example, it'll raise the next top-level form up to the current line (which, personally, I don't like, since if I want to yank another top-level form into its place without screwing up the line, I basically have to do: C-k RET RET (to move the function back down) C-p C-p C-y. This is just personal opinion though.

Killing the first example keeps the lines where they are without moving them, which is my preferred behavior. Perhaps a user-land setting for controlling this would be good?

lispy-ace-symbol* operations working incorrectly

a and H can't target the first element or symbol of some structures: square brackets (vectors), curly braces (maps), maybe more?

It targets the entire vector instead of the first symbol. Every element after the first works correctly.

|(let [x 1] x) --> a --> a(let b[x c1] dx)

b will select the entire vector instead of the x. acd will correctly select the symbol.

Quoted lists give you an extra target for the quote, which then selects the entire list. Not sure if that's necessarily a problem, but since the operation is advertised as symbols, it should probably be impossible to get an entire list region activated from lispy-ace-symbol.

press `i` changes original code

According to README, i is indent-sexp. The following testcase fails:

(ert-deftest lispy-index-sexp ()
  (should (string= (lispy-with "|(defun test?  (x) x)" "i")
                   "(defun test? (x) x)")))

As you can see, pressing i adds superfluous \ before ?

special-lispy-eval broken under newest cider

  • Emacs version: 24.4.50
  • Cider version: 0.8.0-SNAPSHOT

Below is the error message:

Debugger entered--Lisp error: (void-function nrepl-send-string-sync)
(nrepl-send-string-sync str)
(plist-get (nrepl-send-string-sync str) :value)
lispy--eval-clojure("(defn not-teen? [age]\n (not (teen? age)))")
funcall(lispy--eval-clojure "(defn not-teen? [age]\n (not (teen? age)))")
lispy--eval("(defn not-teen? [age]\n (not (teen? age)))")
(replace-regexp-in-string "%" "%%" (lispy--eval (lispy--string-dwim)))
(message (replace-regexp-in-string "%" "%%" (lispy--eval (lispy--string-dwim))))
(save-excursion (if (or (looking-back lispy-right) (region-active-p)) nil (lispy-forward 1)) (message (replace-regexp-in-string "%" "%%" (lispy--eval (lispy--string-dwim)))))
lispy-eval()
funcall-interactively(lispy-eval)
#(lispy-eval nil nil)
ad-Advice-call-interactively(# lispy-eval)
apply(ad-Advice-call-interactively # lispy-eval)
call-interactively(lispy-eval)
(cond ((and (bound-and-true-p edebug-active) (= 1 (length (this-command-keys))) (let ((char (aref (this-command-keys) 0))) (setq cmd (or (assq char edebug-mode-map) (assq char global-edebug-map))))) (call-interactively (cdr cmd))) ((region-active-p) (call-interactively (quote lispy-eval))) ((lispy--in-string-or-comment-p) (call-interactively (quote self-insert-command))) ((or (looking-at lispy-left) (looking-back lispy-right)) (call-interactively (quote lispy-eval))) ((and (looking-back "^ *") (looking-at ";")) (call-interactively (quote lispy-eval))) (t (call-interactively (quote self-insert-command))))
(let (cmd) (cond ((and (bound-and-true-p edebug-active) (= 1 (length (this-command-keys))) (let ((char (aref ... 0))) (setq cmd (or (assq char edebug-mode-map) (assq char global-edebug-map))))) (call-interactively (cdr cmd))) ((region-active-p) (call-interactively (quote lispy-eval))) ((lispy--in-string-or-comment-p) (call-interactively (quote self-insert-command))) ((or (looking-at lispy-left) (looking-back lispy-right)) (call-interactively (quote lispy-eval))) ((and (looking-back "^ *") (looking-at ";")) (call-interactively (quote lispy-eval))) (t (call-interactively (quote self-insert-command)))))
special-lispy-eval()
funcall-interactively(special-lispy-eval)
#(special-lispy-eval nil nil)
ad-Advice-call-interactively(# special-lispy-eval nil nil)
apply(ad-Advice-call-interactively # (special-lispy-eval nil nil))
call-interactively(special-lispy-eval nil nil)
command-execute(special-lispy-eval)

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.