apheleia icon indicating copy to clipboard operation
apheleia copied to clipboard

Format a region rather than the entire buffer

Open mnewt opened this issue 5 years ago • 9 comments
trafficstars

This is great, thanks for creating.

Do you have any tips on creating an apheleia-format-region command?

mnewt avatar Mar 12 '20 16:03 mnewt

Probably take the region, copy it to a temporary buffer, call apheleia-format-buffer as a subroutine, then in the callback copy the formatted text back and replace the region if it hasn't changed?

raxod502 avatar Mar 19 '20 14:03 raxod502

Thought of that, but wouldn't that method end up bypassing the buffer patching and point moving algorithm?

mnewt avatar Mar 19 '20 15:03 mnewt

Ok, but how are you going to get point in the middle of an active region in the first place? By definition, point is at either the beginning or end of the region, and so shouldn't need to be moved if you replace the region contents.

That said, it will indeed break the feature where marks inside the region are maintained, and also the feature where the scroll position of the buffer is maintained. I don't think these things are incredibly important so it would be sufficient for me if this limitation were documented. But, if somebody wants to make the formatting logic more modular so that it works with this use case, I certainly wouldn't object.

raxod502 avatar Mar 19 '20 16:03 raxod502

The use case I was thinking of is using apheleia-format-region to make an apheleia-format-defun command. So the latter would pass a region to the former. Perhaps there would be other uses if the flexibility was there.

A nice and straightforward implementation isn't obvious to me, but here is a not nice, straightforward way:

(defun apheleia-format-region (start end &optional callback)
    "Format from START to END with `apheleia'."
    (interactive "r")
    (when-let ((command (apheleia--get-formatter-command
                         (if current-prefix-arg
                             'prompt
                           'interactive)))
               (cur-buffer (current-buffer))
               (formatted-buffer (get-buffer-create " *apheleia-formatted*")))
      (with-current-buffer formatted-buffer
        (erase-buffer)
        (insert-buffer-substring-no-properties cur-buffer start end)
        (apheleia-format-buffer
         command
         (lambda ()
           (with-current-buffer cur-buffer
             (delete-region start end)
             (insert-buffer-substring-no-properties formatted-buffer)
             (when callback (funcall callback))))))))

  (defun apheleia-format-defun ()
    "Format the defun with `apheleia'."
    (interactive)
    (let ((pos (point))
          (start (progn (beginning-of-defun) (point)))
          (end (progn (end-of-defun) (point))))
      (apheleia-format-region start end
                              (lambda () (goto-char pos)))))

There are many problems with this but it actually seems good enough for my use case.

Should I close the issue or leave it open until there is a better solution?

mnewt avatar Mar 21 '20 02:03 mnewt

This seems like a reasonable feature to want implemented. I'll leave it open, although I don't anticipate working on it anytime soon.

raxod502 avatar Mar 24 '20 15:03 raxod502

This feature can be implemented in the following way: the format-region command sets two markers, one for the beginning of the region and one for the end of the region. It then formats the buffer (possibly in a temporary location), and by inspecting where the markers were moved to, it can identify what the region should be replaced with, and furthermore the location of point within the modified region. We should probably do it in a way that's internal to the formatting mechanism, though, so that it has explicit support for formatting only a specific set of regions using this idea.

If we do this, it suddenly becomes much more important that the alignment algorithm produces correct results, see #2.

raxod502 avatar Mar 31 '20 18:03 raxod502