abo-abo / lispy

Short and sweet LISP editing
http://oremacs.com/lispy/
1.21k stars 132 forks source link

Non-intuitive string termination #332

Open bpstahlman opened 7 years ago

bpstahlman commented 7 years ago

When I hit " to enter a quoted string, Lispy inserts a pair of quotes and leaves point between them to allow me to enter the text of the string. This feels natural and is pretty much the way lisp editors generally work (and the way paren pairs work even in Lispy). When I've finished entering the string, however, there doesn't appear to be a natural way to terminate the string and move on to insert the next sexp. Initially, I would hit ", expecting point to move past the closing quote, but this resulted in a pair of escaped quotes being inserted in the string. I can imagine that there are situations in which you'd want a pair of escaped quotes inside the string, but this is a much less common use case than the one in which you simply want to hit " to terminate the string. I see that I can hit C-f in this situation. Is that the recommended way? Is the reason for not making the "move past terminating quote" functionality the default that this would force the "insert pair of escaped quotes" functionality to be moved to something like lispy-quotes with a negative arg (since positive arg is already used to unquote a string)?

Somelauw commented 7 years ago

@bpstahlman C-f seems to be the only way, but you can bind (kbd "\"") to lispy-doublequote if you want " to terminate.

I have a slightly related question: How to delete an empty string?

I would expect DEL and C-d, but they go in and out of quotes without deleting anything. Even M-x lispy-splice removes parentheses instead of quotes. Maybe I'm lacking creativity, but tell me how to delete an empty string?

bpstahlman commented 7 years ago

@Somelauw With point inside the empty string, DEL moves point just to the right of the empty string (just past the closing "). From that position, a subsequent DEL will delete the entire string.

Somelauw commented 7 years ago

@bpstahlman thanks, there is a difference between lispy-delete-backward and lispy-backward-delete and I was using the latter instead of the former.

abo-abo commented 7 years ago

I see that I can hit C-f in this situation. Is that the recommended way?

Yes, that's what I do. Using lispy-alt-line, which I bind to RET in my own config also works to escape strings.

abo-abo commented 7 years ago

@Somelauw When inside a string, delete it:

bpstahlman commented 7 years ago

@abo-abo Ok. Thanks. I wasn't aware of lispy-alt-line as it doesn't appear in the documentation. Just found it in lispy.el. Fwiw, I would say that in the case of strings (unlike lists), needing to "escape from" the sexp is much more common than needing to nest a pair of delimiters within it, to the point that most users would probably be fine with doing something extra to get the nesting behavior if it meant they could simply hit " in the common case to escape from the string. Even more significant than the extra keystroke is the violation of the "principle of least surprise," which can be especially problematic for new users. (My son was thrown by the same thing when he started using lispy a few days ago... ;-) But perhaps backwards-compatibility is the issue...

sooheon commented 7 years ago

@bpstahlman I've just gone to binding \" to nil in lispy-mode-map, and letting smartparens take care of that pair.

@abo-abo What is the rationale for having to call lispy-delete or lispy-delete-backward twice to delete empty strings? You can already lispy-alt-line or C-f/b to move out of the empty string.

The way deleting empty lists works from within is inconsistent as well. From (|), lispy-delete-backward will kill the pair, but lispy-delete will first move point out of pair and require another keypress.

abo-abo commented 7 years ago

What is the rationale for having to call lispy-delete or lispy-delete-backward twice to delete empty strings?

I'll give an example. In this case, it's correct to delete at once with C-d, since the cursor is before an atom (it's like you press C-d on one char):

|"test"

But what about C-d in this case:

"test|"

It feels unnatural for C-d to delete backwards. So the first C-d will move out backwards, to reduce the deletion to the natural case. Works exactly the same way with lists.

sooheon commented 7 years ago

I think the consistency could be improved.

You are correct that C-d from the following states are equivalent:

(test|)
"test|"

But here:

(|test)

lispy-delete-backward will remove the entire sexp immediately, and I find this intuitive, because in the following, backspace will kill the pair (but, another inconsistency, C-d won't).

(|)

If it feels unnatural to kill a whole atom when deleting from within, that's ok, but I think the behavior when backspacing or C-d from within an empty list or string should always be just to kill the pair.

I think this could be accounted for with a variable `lispy-delete-atom-from-within'. When true, C-d from any of the following:

(|)
(foo|)
"|"
"foo|"

or backspace from one of the following:

(|)
(|fooo)
"|"
"|bar"

Will always just kill the whole thing.

Thoughts? If you think this is ok but don't want to spend time on it, I could make a PR

abo-abo commented 7 years ago

Thoughts? If you think this is ok but don't want to spend time on it, I could make a PR

sounds good.

noctuid commented 7 years ago

I think it may make more sense to rename the variable to something like lispy-delete-string-or-sexp-from-within or lispy-delete-entire-from-within since "atom" only describes the string part.

sooheon commented 7 years ago

I guess string-or-sexp is most descriptive

abo-abo commented 7 years ago

@noctuid I agree. @sooheon Either string-or-sexp or plain sexp is fine with me.

sondr3 commented 7 years ago

Stupid question, I'm not all too well versed in Emacs, how do I bind " to nil in the lispy-mode-map? Doing (define-key lispy-mode-map (kbd "\"") nil) doesn't do anything.

abo-abo commented 7 years ago

@sondr3

(define-key lispy-mode-map-lispy (kbd "\"") nil)
sondr3 commented 7 years ago

Thanks you @abo-abo, that worked 😄

zzantares commented 4 years ago

I believe this issue could be closed unless there's an intention to make lispy-doublequote the default behavior for " which I think fits more naturally according to what the main point of the issue was. It would be even better if this function would actually insert a \"\" if we are not at the end of the string instead of a single \" but that's a minor nitpick, (define-key lispy-mode-map-lispy (kbd "\"") 'lispy-doublequote) is enough for me.