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

Support for lispy-tab #33

Open
Ambrevar opened this issue Feb 15, 2018 · 13 comments
Open

Support for lispy-tab #33

Ambrevar opened this issue Feb 15, 2018 · 13 comments

Comments

@Ambrevar
Copy link

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... :)

@noctuid
Copy link
Owner

noctuid commented Feb 15, 2018

I personally just use aggressive-indent-mode and never manually indent. I know that lispy-tab does extra prettification as well, but I don't often use it. Could you give an example where it does something that evil-indent/= doesn't do?

lispy--normalize-1 works on a single sexp as opposed to a region, but an new equivalent of = could potentially loop through each sexp in the region. What would the expected behavior be? Act on top-level forms only or forms at the earliest level in the region only?

@Ambrevar
Copy link
Author

Ambrevar commented Feb 15, 2018 via email

@noctuid
Copy link
Owner

noctuid commented Feb 16, 2018

I'm not sure I understand. lispy--normalize is recursive, if that was a misunderstanding.

Right, so it would be redundant to use it on a sexp within an already normalized sexp (which is why I mentioned same level options). I ask this because I'm not sure what behavior would be most preferable and some behaviors would be easier to implement.

Consider this example:

(foo
 (bar
  ~(baz)
   (qux)))
|

Basic summary of some possiblities:

  1. behavior: always act only on top-level sexps (easy to implement)
    result: (foo) sexp is normalized
  2. behavior: act on sexps that start within region (easy)
    result: (baz) and (quix) sexps are normalized
  3. behavior: act on the current sexp and all following sexps that start within the region (easy)
    result: (bar) sexp is normalized
  4. behavior: act on all sexps where at least one side is in region (harder because can't just check beginning of region)
    result: (foo) sexp is normalized
  5. behavior: to 4 what 3 is to 2; include sexps that contain either the region beginning or end

1 is less precise, and since you could just use a text object for the top level form if you wanted, it's probably preferable to be more precise. I'm leaning towards 3 or 5. What would your preference be?

@Ambrevar
Copy link
Author

Ambrevar commented Feb 16, 2018 via email

@noctuid
Copy link
Owner

noctuid commented Feb 18, 2018

What are ~ and |? Mark and point?

Yes. I went ahead and added an experimental lispyville-prettify operator for testing that has the third behavior.

@Ambrevar
Copy link
Author

Looks great so far!

@NightMachinery
Copy link

@noctuid After calling lispyville-prettify with gg, I can't go back to my position using C-o.

@noctuid
Copy link
Owner

noctuid commented Mar 26, 2018

I still haven't exactly decided how point restoration should work. Would you care to post some before/after examples with | as the point to show where the point starts and where you think it should end up? I can have lispyville-prettify add to the jumplist if necessary, but I'd prefer to just have the point end up in a sane location afterwards if possible.

@NightMachinery
Copy link

@noctuid Sure!

(defmacro lcomp (expression for var in list conditional conditional-test)
  ;; create a unique variable name for the result
  (let ((result (gensym)))
    ;; the arguments are really code so we can substitute them
    ;; store nil in the unique variable name generated above
    `(let ((,result nil))
       ;; var is a variable name
       ;; list is the list literal we are suppose to iterate over
       (loop for ,var in ,list
             ;; conditional is if or unless
             ;; conditional-test is (= (mod x 2) 0) in our examples
             ,conditional ,condi|tional-test
             ;; and this is the action from the earlier lisp example
             ;; result = result + [x] in python
             do (setq ,result (append ,result (list ,expression))))
       ;; return the result
       ,result)))
;; the following two functions just make equivalent of Python's range function
;; you can safely ignore them unless you are running this code
(defun range-helper (x)
  (if (= x 0)
      (list x)
    (cons x (range-helper (- x 1)))))

(defun range (x)
  (reverse (range-helper (- x 1))))
(lcomp x for x in (range 50) if (= (mod x 2) 0))

It jumps to the beginning of the buffer after prettifying. I want it to stay where it was.

noctuid added a commit that referenced this issue Mar 27, 2018
- Add key theme
- Add basic test
- Add section to readme

Addresses #33.
@noctuid
Copy link
Owner

noctuid commented Mar 27, 2018

I've made some minor changes and added a key theme that remaps evil-indent. The main issue is whether the point should be preserved by buffer position or a marker. For example, by position:

(foo
 |a
 )
;; with == becomes 
(foo
 |a)

This seems like the expected behavior for this case as using a marker will move the point to the previous line. However, when characters before the buffer position are deleted, this doesn't work the same:

(foo

 |)

(bar)
;; with == becomes
(foo)

|(bar)
;; or using a marker becomes
|(foo)

(bar)

Neither stays with the closing paren for this case, but the marker behavior makes a little more sense.

This issue could be avoided by using the 2nd behavior and only normalizing lists that start within the region. This would prevent characters from before the point from being deleted, but it seems a little less convenient (e.g. for the above examples you would need to use =a( instead of ==).

The choice doesn't seem obvious to me, so any feedback would be appreciated.

@NightMachinery
Copy link

NightMachinery commented Mar 27, 2018 via email

@noctuid
Copy link
Owner

noctuid commented Apr 7, 2018

It looks like the reason markers don't work as expected is because lispy actually deletes the entire sexp and then re-inserts it, so the marker just ends up before the newly inserted sexp. I can't think of any sane workaround at the moment.

@Ambrevar
Copy link
Author

lispy-tab now aligns to the margin:
abo-abo/lispy#422 (comment)

@noctuid: Could you update lispyville-prettify to support that too?

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

3 participants