puni icon indicating copy to clipboard operation
puni copied to clipboard

Option to auto re-indentation

Open Yevgnen opened this issue 3 years ago • 6 comments

Given

(bind-keys ("C-+" . change-number-at-point)
|           ("C-c C-o" . browse-url-at-point)
            ("C-|" . aj-toggle-fold))

can we add an option to get

(bind-keys ("C-+" . change-number-at-point)
           |("C-|" . aj-toggle-fold))

instead of

(bind-keys ("C-+" . change-number-at-point)
|("C-|" . aj-toggle-fold))

after two puni-kill-line calls like smartparens?

Not sure if this is a major mode specific issue, but the same 'issue' happens in python-mode. Sometimes I just want to really kill a line.

def test():
|   a = 1
    a += 1
    return a
def test():
|a += 1
    return a

Thanks.

Yevgnen avatar Jan 24 '22 00:01 Yevgnen

Well, to me this behavior brings more help than trouble. Think about this:

(progn |(a...)
       (b...)
       (c...))
;; Call `puni-kill-line` twice
(progn |(b...)
       (c...))

If (b...) is a multi-line form, you can then mark the sexp after point and re-indent it.

If we don't re-indent after killing a line, you'll have:

(progn |(a...)
       (b...)
       (c...))
;; Call `puni-kill-line` twice
(progn |      (b...)
       (c...))

Now you have to delete those spaces between the point and (b...).

I'm very used to this and if I do want to kill a whole line, from the beginning, I move my point to the indentation, then kill the line. I have a custom command bind to C-a:

(defun toki-beginning-of-line ()
  "A smarter version of `beginning-of-line'.
Jump to the beginning of current line, or if the point is already
there, move to the first non-whitespace character in current
line."
  (interactive)
  (if (and (bolp) (not (puni--line-empty-p)))
      (skip-chars-forward "[:blank:]")
    (beginning-of-line)))

In your example, I would do this:

(bind-keys ("C-+" . change-number-at-point)
|           ("C-c C-o" . browse-url-at-point)
           ("C-|" . aj-toggle-fold))
;; Call `toki-beginning-of-line`
(bind-keys ("C-+" . change-number-at-point)
           |("C-c C-o" . browse-url-at-point)
           ("C-|" . aj-toggle-fold))
;; Call `puni-kill-line` twice
(bind-keys ("C-+" . change-number-at-point)
           |("C-|" . aj-toggle-fold))

Does this make sense?

AmaiKinono avatar Jan 24 '22 16:01 AmaiKinono

Hi, thanks for your reply. I'm not sure if I fully understand your statement.

If we don't re-indent after killing a line

I do want re-indentation.

For these examples,

def test():
|a += 1
    return a

and

(bind-keys ("C-+" . change-number-at-point)
|("C-|" . aj-toggle-fold))

I need to re-indent manually by TAB or indent-region which is I'm trying to avoid. (I consider they are unindented because if they are not, a subsequent call to indent-region should not change them.)

if I do want to kill a whole line, from the beginning, I move my point to the indentation, then kill the line.

I also use mwim for moving between bol and indentation. But if without auto re-indentation, I just need to re-train my muscle memory :)

Yevgnen avatar Jan 25 '22 00:01 Yevgnen

@Yevgnen I'm not sure if auto-reindentation is in the scope of puni (or if it should be in its scope). But I'm happily using aggressive-indent-mode together with puni to have that feature, and not just in lisp but all prog-mode buffers where emacs knows indentation rules.

tsdh avatar Jan 25 '22 05:01 tsdh

@tsdh Hi, thanks for your suggestion. I just try aggressive-indent-mode. It does re-indent the line after double kills. But I see there's delay of the re-indentation even I've set aggressive-indent-sit-for-time to zero. Also, it seems too aggressive for me in other scenarios :)

I'm not sure if auto-reindentation is in the scope of puni (or if it should be in its scope).

Yes...Maybe I should just advise the commands to re-indent instead of adding an option.

Yevgnen avatar Jan 25 '22 06:01 Yevgnen

If we don't re-indent after killing a line

I do want re-indentation.

Sorry I didn't put it clearly. What Puni does is: when you call puni-kill-line, it calculates the current indentation, then do the kill. Then, if the next line is brought to the current line, it drags the first non-blank char of that line to the current indentation.

I call this "re-indentation" but actually it's not the same as what we may usually mean by "re-indentation" (say, pressing TAB).

Since you put the cursor at the beginning of the line, kill the line itself, then kill the newline character, the indentation is zero when you do the second kill, that causes your problem.

The remedy is:

  • set kill-whole-line to t (so you kill the line and the newline char at the same time).
  • or move to the indentation first, then do the kill.

The problem of not doing the "re-indentation" is shown in the example I gave:

(progn |(a...)
       (b...)
       (c...))
;; Call `puni-kill-line` twice
(progn |      (b...)
       (c...))

You could try this by calling the built-in kill-line twice.

I am not opposed to adding a user option, but we have to think about how to deal with the above situation.

AmaiKinono avatar Jan 25 '22 06:01 AmaiKinono

Hi, thanks for the clarification.

I didn't know about the kill-whole-line variable. It works pretty well when one do really want to kill the line without subsequent insertions. Otherwise, one will still have to bring the cursor back to indentation and open a new line.

In my option, puni-kill-line is more like kill-sexp than kill-line in some situations (although it could indeed kill both things). So I expected the following result like before I tried

(progn |(a...)
       (b...)
       (c...))

;; Call `puni-kill-line`, just like `kill-sexp` or `sp-kill-sexp`
(progn |     
       (b...)
       (c...))

;; One more `puni-kill-line`
(progn |     
       (c...))

But I agree that the current double puni-kill-line is convenient for the above example.

Yevgnen avatar Jan 25 '22 07:01 Yevgnen

I've fixed the initial example with C-k with the following advice:

(define-advice puni-kill-line (:before (&rest _))
  "Go back to indentation before killing the line if it makes sense to."
  (when (looking-back "^[[:space:]]*")
    (back-to-indentation)))

andreyorst avatar Dec 02 '22 21:12 andreyorst

@andreyorst I tried this advice and it feels really good ;) Can I use your code and make it a user option?

AmaiKinono avatar Dec 06 '22 10:12 AmaiKinono

@andreyorst I tried this advice and it feels really good ;) Can I use your code and make it a user option?

sure!

andreyorst avatar Dec 06 '22 10:12 andreyorst

@AmaiKinono actually, I've changed the advice a bit:

  (define-advice puni-kill-line (:before (&rest _))
    "Go back to indentation before killing the line if it makes sense to."
    (when (looking-back "^[[:space:]]*")
      (if (bound-and-true-p indent-line-function)
          (funcall indent-line-function)
        (back-to-indentation))))

andreyorst avatar Dec 09 '22 16:12 andreyorst