noctuid / lispyville

lispy + evil = lispyville
GNU General Public License v3.0
313 stars 23 forks source link

Support for lispy-tab #33

Open Ambrevar opened 6 years ago

Ambrevar commented 6 years ago

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 commented 6 years ago

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 commented 6 years ago

lispy--normalize-1 does mainly 2 things (as far as I know) beside indentation:

It might not sound so useful if you've got good lisp-y habits and always write your code with good style, but the point is that it allows you to completely leave aside the good manners and let you go berserk writing code faster than you ever did. (In my opinion.)

What would the expected behavior be? Act on top-level forms only or forms at the earliest level in the region only?

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

My suggestion is as follows:

noctuid commented 6 years ago

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 commented 6 years ago

What are ~ and |? Mark and point?

Agreed, I think it's better to lean towards more precision. For now, I'm not sure which one I'd like better, I guess I need some time and practice to figure it out.

noctuid commented 6 years ago

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 commented 6 years ago

Looks great so far!

NightMachinery commented 6 years ago

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

noctuid commented 6 years ago

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 commented 6 years ago

@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 commented 6 years ago

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 commented 6 years ago

👍🏻

Does =a solve the preservation problem? If so, that’s better. If not, go with the marker solution? If someone improves the marker system this will automatically improve.

On Wed, Mar 28, 2018 at 1:28 AM Fox Kiester notifications@github.com wrote:

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.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/noctuid/lispyville/issues/33#issuecomment-376673057, or mute the thread https://github.com/notifications/unsubscribe-auth/Aii--nrGTf-1NCUnzAAEI-iS_xaTgTkgks5tiqfdgaJpZM4SHnjU .

noctuid commented 6 years ago

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 commented 6 years ago

lispy-tab now aligns to the margin: https://github.com/abo-abo/lispy/issues/422#issuecomment-382846874

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