Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hybrid line/sexp commands such as paredit-kill #95

Closed
monsanto opened this issue May 26, 2013 · 10 comments
Closed

Add hybrid line/sexp commands such as paredit-kill #95

monsanto opened this issue May 26, 2013 · 10 comments

Comments

@monsanto
Copy link

Hybrid line/sexp commands are very versatile, if hard to write. paredit-kill is probably my favorite feature of paredit, and it has no equivalent in smartparens. (In brief, it kills to the end of the line like kill-line, except will stop if the enclosing sexp ends, and will kill any hanging sexps at the end of the line. See http://www.emacswiki.org/emacs/PareditCheatsheet)

I have a clone sp-hybrid-kill-line written, but it needs #94 fixed before I can give you a pull request.

It would also be useful to have commands such as sp-hybrid-sort-lines, sp-hybrid-transpose-lines, etc. These are especially important when working in non-sexp-based languages such as Python, or languages like Clojure that don't always group related things in parens. For an example of the latter, it's very difficult to move variable definitions around when they are written like

(let [x1 v1
      x2 v2]
  ... body ...)
@monsanto
Copy link
Author

As an architectural aside, the way I am doing this is by adding sp-get-hybrid-line that returns a structure that can be manipulated with sp-get. The only oddity is that sometimes there is sometimes no beginning/ending delimiter (because the delimiter is the line begin/end).

@Fuco1
Copy link
Owner

Fuco1 commented May 26, 2013

In those cases I just put in empty strings. As those are used in some calculations of lengths and such, they just add nothing. You can check the sp--next-thing-selection in the latest patch, there's an example of such use.

@Fuco1
Copy link
Owner

Fuco1 commented May 26, 2013

Also, could you start an issue for each of sp-hybrid-sort-lines, sp-hybrid-transpose-lines with some specification/examples of what it should do? I'm not entirely sure what you mean.

Speaking about python, I was toying with an idea of creating some "python -> sexp" conversion system (based on depth most likely) that would enable the SP commands for python. But since I'm not a python programmer myself, I know quite little about the language.

@monsanto
Copy link
Author

I can make separate issues if you want, but let me explain what I mean here so the general idea doesn't get spread out over too many tickets. Maybe my explanation can be useful for the documentation.

"Hybrid sexp/line" is a term I just invented to refer to the general idea embodied within paredit-kill. I am of course open to suggestions for a different name.

The definition of a hybrid/sexp line is smallest balanced region containing the line (point) is on. Here are some examples of hybrid lines, where | is point, ( ) refer to sexps, and [ ] illustrate the bounds of the hybrid line:

[|(sexp1 sexp2 sexp3) sexp4]

sexp2 ([sexp3 | sexp4]) sexp5

[|sexp1 (sexp2
          (sexp3 sexp4)
          sexp5)]


([sexp1 | (sexp2 
            (sexp3 sexp4)
            sexp5)])

[(sexp1 sexp2
  (sexp3 sexp4)
  sexp5) | (sexp1 sexp2
             (sexp3 sexp4)
              sexp5)]

The importance of having hybrid line commands is that programmers tend to organize things in lines, but operating using rigid line commands often destroys any nested constructs. For example, kill-line on the 3rd example:

|sexp1 (sexp2
         (sexp3 sexp4)
         sexp5)
->
|
         (sexp3 sexp4)
         sexp5)

which is probably not what you wanted. sp-kill-hybrid-line and paredit-kill do this instead:

|sexp1 (sexp2
         (sexp3 sexp4)
         sexp5)
->
|   

Now, you said you weren't clear on sorting and transposing hybrid lines. These aren't in paredit; these features would set smartparens apart from the competition. Given the framework developed above, it's easy to understand them. Consider working with the following Python code:

x = big_function_call(a*x +
                      b*x**2 +
                      c)
|
(a,
 b,
 c) = read_user_input()

You say to yourself "oops, let me transpose those statements". Using transpose-lines:

x = big_function_call(a*x +
                      b*x**2 +
(a,
                      c)
 b,
 c) = read_user_input()

yeah, not very useful. Using a hybrid transpose, we identify the hybrid lines:

[x = big_function_call(a*x +
                      b*x**2 +
                      c)]
|
[(a,
 b,
 c) = read_user_input()]

and swap them:

(a,
 b,
 c) = read_user_input()
|
x = big_function_call(a*x +
                      b*x**2 +
                      c)

Now, let's use Clojure as an example to illustrate sorting. To avoid messing with my [] convention, i'll use regular parens where square brackets would be used in Clojure.

(let (c 3
      a 1
      b 2)
  ...)

You want to order your variables. Mark your variables, M-x sort-lines, and here is what you get:

(let       a 1
      b 2)
(c 3
  ...)

Just terrible. Lets mark the hybrid lines:

(let ([c 3]
[      a 1]
[      b 2])
  ...)

and sort them:

(let (      a 1
      b 2
c 3)
  ...)

still not totally what you want due to whitespace. So let's add a user setting for performing the operation modulo indentation:

(let ([c 3]
      [a 1]
      [b 2])
  ...)
->
(let (a 1
b 2
c 3)
  ...)

Now add a setting to run indent-sexp after a sort, and we get:

(let (a 1
      b 2
      c 3)
  ...)

It's worth noting that a hypothetical sort-sexps command could not do this on Clojure code because of the implicit groupings. And the non-hypothetical transpose-sexps fails miserably as well!

I hope that helps.

EDIT: you can find an implementation in #94

@monsanto
Copy link
Author

Speaking about python, I was toying with an idea of creating some "python -> sexp" conversion system (based on depth most likely) that would enable the SP commands for python. But since I'm not a python programmer myself, I know quite little about the language.

I know a lot about Python, although I spend my time coding in Clojure these days. Let me know if I can be of help.

Recognizing indentation depth + the line before indentation as a sexp would help Python. Recognizing pairs like "def" "end" would help Ruby. Unfortunately, outside Lisp languages, this sorta stuff gets complicated. I'm not sure what the right thing to do is with respect to such languages.

@swsnr
Copy link

swsnr commented Jun 11, 2013

@Fuco1 Any news on this issue?

@Fuco1
Copy link
Owner

Fuco1 commented Jun 11, 2013

I've exchanged an email with @monsanto, he has it sort of working in his branch and is polishing it out. It will definitely get added, sometime closer to the end of this month. Right now I can't afford spending too much time on anything, and this seems like pretty big change to incorporate properly.

@Jell
Copy link
Collaborator

Jell commented Jul 3, 2013

I think an sp-slurp-line would also be useful for non-lispy languages.

With slurp:

if te|st
end
object.method(bla) do
  yadiyada
end
someother.suff(cool)

if te|st
  object
end.method(bla) do
  yadiyada
end
someother.suff(cool)

With slurp-line:

if te|st
end
object.method(bla) do
  yadiyada
end
someother.suff(cool)

if te|st
  object.method(bla) do
    yadiyada
  end
end
someother.suff(cool)

@monsanto
Copy link
Author

Quick update: due to work obligations and a cross-country move I haven't had the time to address this issue. I should have some more free time soon to handle this. Sorry for the wait, everyone!

@Fuco1
Copy link
Owner

Fuco1 commented Sep 22, 2013

This has been implemented in #158.

@Fuco1 Fuco1 closed this as completed Sep 22, 2013
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants