jscheid / prettier.el

Prettier code formatting for Emacs.
GNU General Public License v3.0
163 stars 11 forks source link

Preserve global indent of a region #133

Closed mikpom1 closed 1 month ago

mikpom1 commented 4 months ago

When running prettier-prettify-region global indent of a chunk being fixed is always set to zero Consider example

function outer() {
  function inner() {console.log('inner')}

console.log('outer')  
}

Let's say I want to prettify only a line containing inner function. If I select the line and prettify I get

function outer() {
function inner() {
  console.log("inner");
}

console.log('outer')  
}

What I expect is

function outer() {
  function inner() {
    console.log("inner");
  }

console.log('outer')  
}

I.e. the block with inner function is indent as original definition.

Is it possible? I am new to prettier. This is something I enjoy in my Python+Black-mocchiato setup and I can't figure out for JS.

jscheid commented 4 months ago

The readme says:

M-x prettier-prettify-region to prettify region (but note that region should align with a complete block of code).

(Emphasis added . Maybe I didn't word this well, what I meant to say is a block of code that starts at indentation zero.)

The reason for this limitation is that at the most basic level, we're sending the block of code to Prettier without context, so Prettier couldn't know that it should be indented differently.

As far as I gather at this very moment (where I haven't spent much time thinking about this particular problem in years) the only way this could work is by sending everything from (beginning-of-buffer) to (region-end) but then only applying changes to region. This wouldn't be easy because currently changes are sent as a diff, and removing parts of the diff outside of region perhaps isn't impossible but also not entirely trivial.

Myself, I don't really see a use for this function. I always indent the whole buffer and happily so. So right now, I wouldn't be inclined to spend time on implementing such a feature. I might be missing something? What's the use case compared to prettifying the whole buffer, is it an optimization or something else?

mikpom1 commented 4 months ago

Ok the problem is I really don't understand what should align with a complete block of code means. Even now when you point me to it I doubt whether it is related to zero indentation. By zero indentation I mean that the region is properly indented within, but globally has no indent after prettifying. This what I tried to highlight in the example.

If this is intended and I recognized it from the README, then I wouldn't even create that issue. So sorry for bothering if it is the case.

As for the use cases: I am just an ascetic of coding aids and unless the code is to be shared with others, I prefer keeping my ugly formatting except maybe some regions I occasionally want to fix.

Also thanks for the hints on potential implementation: If I am not satisfied with prettifying the whole buffer I at least know where to look.

jscheid commented 4 months ago

@mikpom1 definitely no bother, the readme doesn't explain it very well in part because I didn't choose the words carefully and in part because I never spent much time on thinking how this feature ought to work. I guess what I was trying to say is "top-level block of code", i.e. where the first line has no indentation.

As for your use case, I think you could work around it pretty easily with a function of your own. Here's one that remembers the first line's indentation and prefixes region with it after prettification. There might be a few edge cases to handle (where region doesn't start/end on beginning of line perhaps) but should be a good start:

(defun my-prettify-region ()
  "Prettify region, then restore original indentation."
  (interactive)
  (atomic-change-group
    (let ((prefix
           (save-excursion
             (goto-char (region-beginning))
             (beginning-of-line)
             (buffer-substring
              (point)
              (re-search-forward "\\=[[:space:]]*" nil t))))
          (deactivate-mark nil))
      (prettier-prettify-region)
      (unless (zerop (length prefix))
        (save-excursion
          (goto-char (region-end))
          (let ((end (point-marker)))
            (goto-char (region-beginning))
            (beginning-of-line)
            (while (< (point) end)
              (save-excursion (insert prefix))
              (forward-line))))))))

There seems to be something wonky with prettier-prettify-region and undo though, it doesn't work properly while region is active. I'll try and take a look at fixing that soon.

mikpom1 commented 4 months ago

Thanks, @jscheid . This workaround will do. Will let you know if not )