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

Allow running multiple formatters in sequence #31

Closed
r-zip opened this issue Feb 10, 2021 · 15 comments · Fixed by #51
Closed

Allow running multiple formatters in sequence #31

r-zip opened this issue Feb 10, 2021 · 15 comments · Fixed by #51

Comments

@r-zip
Copy link

r-zip commented Feb 10, 2021

First, thank you for this great package.

Unless I'm mistaken, there is currently no way to configure apheleia-mode to run multiple formatters on save. When working with Python files, I often run isort followed by black. It would be nice to automate this workflow using this package. Maybe the apheleia-mode-alist could be changed to, for instance:

((python-mode . (isort black))
 (js-mode . prettier))

and the package would run the list entries sequentially.

@raxod502
Copy link
Member

Yeah, I agree, that would be a good feature. I like the proposed interface as well.

@shackra
Copy link

shackra commented Mar 20, 2021

I started a fork and read the source code, but I don't really know what I'm doing TBH. I was thinking on approaching this by passing the symbols as a list and looping through, having each ran on the buffer or something along the lines, but I'm not confident to modify any function to implement this feature

@shackra
Copy link

shackra commented Mar 22, 2021

I may try again if @raxod502 can orient me with the internals, the documentation in code is not good enough for my little brain, and maybe I'm too dense to figure how we should run each formatter in sequence.

@raxod502
Copy link
Member

raxod502 commented Apr 4, 2021

I think it would be sufficient to update the function apheleia--format-after-save. The tricky thing is that we want to take a flat list of formatters and turn them into nested callbacks since the Apheleia interface is asynchronous. The return value of apheleia--get-formatter-command would become a list of formatters to run, rather than just one. Then we would probably want to take advantage of lexical binding, and in the callback to apheleia-format-buffer, pop off the first formatter to run from that list, mutating it in place, and passing the same callback again recursively, until the list is empty. It's a bit hard to describe in detail without actually writing the code.

@shackra
Copy link

shackra commented Apr 5, 2021

It's a bit hard to describe in detail without actually writing the code.

I kind-of follow what you are describing, it sounds a bit complex tho

@codygman
Copy link

I just needed this to run the Haskell hlint formatter followed by brittany. I'm not sure if I'll become motivated enough to implement what @raxod502 described or become annoyed enough by still having to run hlint manually...

but I'll let the thought of implementing that bounce around a bit.

Awesome package though. Linting should be automated away and programmers shouldn't have to run commands from a shell to fix it up.

@lassik
Copy link

lassik commented Apr 20, 2021

Maybe the apheleia-mode-alist could be changed to, for instance:

((python-mode . (isort black))
 (js-mode . prettier))

and the package would run the list entries sequentially.

Format-all supports running more than one formatter in a chain; please check out how we configure it. If Apheleia uses the same alist format for configuration, people will have an easier time using the two packages.

We choose the formatters based on GitHub linguist language names instead of major modes. The language-id library maps Emacs buffers to language names.

@codygman
Copy link

Huh, integrating format-all-the-code and aphelia seems like an interesting idea. I'm guessing that format-all-the-code doesn't use the same RCS patch/dynamic programming algorithm to preserve point? If not, it seems integrating the two would be nice so that format-all-the-code users could get that improvement.

If it does then these packages are simply competing with one another 😄

@lassik
Copy link

lassik commented Apr 20, 2021

Yes, and reformatter is a third package :p

As far as I can remember, the maintainers are in agreement that it would be nice to build all of these packages on top of each other somehow, but none of us have had time to delve into it.

@lassik
Copy link

lassik commented Apr 20, 2021

The RCS patching is also useful outside of code formatters and a few MELPA packages roll their own. Someone (possibly Steve, reformatter's author?) was looking into how to extract the patching into a self-contained library that could be used by the other packages.

@raxod502
Copy link
Member

If you just want to special-case running hlint followed by brittany, you could probably do something like this (untested):

(defun my-apheleia-hlint-and-brittany ()
  (interactive)
  (apheleia-format-buffer
   (alist-get 'hlint apheleia-formatters)
   (lambda ()
     (apheleia-format-buffer
      (alist-get 'brittany apheleia-formatters)
      (lambda ()
        (message "Formatted!"))))))

and then put it on an appropriate hook, possibly with some additional cleanup.

Doing it "properly" is just a matter of automatically generating code like the above from an arbitrary list of formatters.

@mohkale

This comment has been minimized.

@mohkale
Copy link
Contributor

mohkale commented Oct 16, 2021

@raxod502

I see that for input we're creating a temporary file but I'm not seeing where we're deleting that file. None of the builtin formatters seem to use that option so I'm not sure whether that functionality has been tested but if you make a temp-file shouldn't apheleia delete it when the formatter is finished?

Edit:

For apheleia--run-formatter how should we handle when a we can't format one of the commands in a sequence. Say you want to run formatters 1, 2, 3 but 2 fails. Do we carry on and ignore 2, do we stop and only apply 1 or do we not apply any. The current approach is if the command can't be formatted we don't do anything but I'm not sure that'll work when we have multiple commands.

mohkale added a commit to mohkale/apheleia that referenced this issue Oct 16, 2021
Closes radian-software#31

This commit makes it so apheleia can run multiple formatters one after
the other and use the resultant output to format the current buffer.
This works somewhat like a pipeline. The output of one formatter becomes
the input to the next formatter until all formatters have run and then
an RCS patch is built from the resultant output and applied to the
current buffer.

Note: For convenience we internally represent the users configuration as
a list of formatters even when it may only be one. For example if the
user has configured `(python-mode . black)` in apheleia-mode-alist then
internally we interpret that as a formatter list of `(black)` instead of
`black` as we did previously.
mohkale added a commit to mohkale/apheleia that referenced this issue Oct 16, 2021
Closes radian-software#31

This commit makes it so apheleia can run multiple formatters one after
the other and use the resultant output to format the current buffer.
This works somewhat like a pipeline. The output of one formatter becomes
the input to the next formatter until all formatters have run and then
an RCS patch is built from the resultant output and applied to the
current buffer.

Note: For convenience we internally represent the users configuration as
a list of formatters even when it may only be one. For example if the
user has configured `(python-mode . black)` in apheleia-mode-alist then
internally we interpret that as a formatter list of `(black)` instead of
`black` as we did previously.
@mohkale
Copy link
Contributor

mohkale commented Oct 16, 2021

Alright I've added a prospective implementation. See #51 for more details.

I've tested and this change should be backwards compatible with the existing implementation. I've tried out using black and isort at the same time and I'd say its working. Please test it out and let me know if you have any issues.

mohkale added a commit to mohkale/apheleia that referenced this issue Oct 16, 2021
Closes radian-software#31

This commit makes it so apheleia can run multiple formatters one after
the other and use the resultant output to format the current buffer.
This works somewhat like a pipeline. The output of one formatter becomes
the input to the next formatter until all formatters have run and then
an RCS patch is built from the resultant output and applied to the
current buffer.

Note: For convenience we internally represent the users configuration as
a list of formatters even when it may only be one. For example if the
user has configured `(python-mode . black)` in apheleia-mode-alist then
internally we interpret that as a formatter list of `(black)` instead of
`black` as we did previously.
mohkale added a commit to mohkale/apheleia that referenced this issue Oct 16, 2021
Closes radian-software#31

This commit makes it so apheleia can run multiple formatters one after
the other and use the resultant output to format the current buffer.
This works somewhat like a pipeline. The output of one formatter becomes
the input to the next formatter until all formatters have run and then
an RCS patch is built from the resultant output and applied to the
current buffer.

Note: For convenience we internally represent the users configuration as
a list of formatters even when it may only be one. For example if the
user has configured `(python-mode . black)` in apheleia-mode-alist then
internally we interpret that as a formatter list of `(black)` instead of
`black` as we did previously.
@mohkale
Copy link
Contributor

mohkale commented Oct 16, 2021

@lassik

Format-all supports running more than one formatter in a chain

So from the looks of things lassik enumerates the entire configuration and if the same mode appears more than once it reads that as meaning use all of those formatters for the buffer. I'm not sure I'm a big fan of that implementation since my lazy self has a tendency to just push new entries onto configs instead of using setf and properly changing existing ones. Say for example I want to use something other than black in python-mode I'd just push a new entry onto the config, but since the default config already has python-mode associated with black I'd end up running both of them unintentionally. I say if that's the route we go down we should remove the default formatter associations to avoid confusion.

mohkale added a commit to mohkale/apheleia that referenced this issue Oct 17, 2021
Closes radian-software#31

This commit makes it so apheleia can run multiple formatters one after
the other and use the resultant output to format the current buffer.
This works somewhat like a pipeline. The output of one formatter becomes
the input to the next formatter until all formatters have run and then
an RCS patch is built from the resultant output and applied to the
current buffer.

Note: For convenience we internally represent the users configuration as
a list of formatters even when it may only be one. For example if the
user has configured `(python-mode . black)` in apheleia-mode-alist then
internally we interpret that as a formatter list of `(black)` instead of
`black` as we did previously.
mohkale added a commit to mohkale/apheleia that referenced this issue Oct 17, 2021
Closes radian-software#31

This commit makes it so apheleia can run multiple formatters one after
the other and use the resultant output to format the current buffer.
This works somewhat like a pipeline. The output of one formatter becomes
the input to the next formatter until all formatters have run and then
an RCS patch is built from the resultant output and applied to the
current buffer.

Note: For convenience we internally represent the users configuration as
a list of formatters even when it may only be one. For example if the
user has configured `(python-mode . black)` in apheleia-mode-alist then
internally we interpret that as a formatter list of `(black)` instead of
`black` as we did previously.
mohkale added a commit to mohkale/apheleia that referenced this issue Oct 17, 2021
Closes radian-software#31

This commit makes it so apheleia can run multiple formatters one after
the other and use the resultant output to format the current buffer.
This works somewhat like a pipeline. The output of one formatter becomes
the input to the next formatter until all formatters have run and then
an RCS patch is built from the resultant output and applied to the
current buffer.

Note: For convenience we internally represent the users configuration as
a list of formatters even when it may only be one. For example if the
user has configured `(python-mode . black)` in apheleia-mode-alist then
internally we interpret that as a formatter list of `(black)` instead of
`black` as we did previously.
mohkale added a commit to mohkale/apheleia that referenced this issue Oct 19, 2021
Closes radian-software#31

This commit makes it so apheleia can run multiple formatters one after
the other and use the resultant output to format the current buffer.
This works somewhat like a pipeline. The output of one formatter becomes
the input to the next formatter until all formatters have run and then
an RCS patch is built from the resultant output and applied to the
current buffer.

Note: For convenience we internally represent the users configuration as
a list of formatters even when it may only be one. For example if the
user has configured `(python-mode . black)` in apheleia-mode-alist then
internally we interpret that as a formatter list of `(black)` instead of
`black` as we did previously.
mohkale added a commit to mohkale/apheleia that referenced this issue Oct 19, 2021
Closes radian-software#31

This commit makes it so apheleia can run multiple formatters one after
the other and use the resultant output to format the current buffer.
This works somewhat like a pipeline. The output of one formatter becomes
the input to the next formatter until all formatters have run and then
an RCS patch is built from the resultant output and applied to the
current buffer.

Note: For convenience we internally represent the users configuration as
a list of formatters even when it may only be one. For example if the
user has configured `(python-mode . black)` in apheleia-mode-alist then
internally we interpret that as a formatter list of `(black)` instead of
`black` as we did previously.
mohkale added a commit to mohkale/apheleia that referenced this issue Oct 24, 2021
Closes radian-software#31

This commit makes it so apheleia can run multiple formatters one after
the other and use the resultant output to format the current buffer.
This works somewhat like a pipeline. The output of one formatter becomes
the input to the next formatter until all formatters have run and then
an RCS patch is built from the resultant output and applied to the
current buffer.

Note: For convenience we internally represent the users configuration as
a list of formatters even when it may only be one. For example if the
user has configured `(python-mode . black)` in apheleia-mode-alist then
internally we interpret that as a formatter list of `(black)` instead of
`black` as we did previously.
raxod502 added a commit that referenced this issue Oct 25, 2021
* Support multiple formatters (#31)

Closes #31

This commit makes it so apheleia can run multiple formatters one after
the other and use the resultant output to format the current buffer.
This works somewhat like a pipeline. The output of one formatter becomes
the input to the next formatter until all formatters have run and then
an RCS patch is built from the resultant output and applied to the
current buffer.

Note: For convenience we internally represent the users configuration as
a list of formatters even when it may only be one. For example if the
user has configured `(python-mode . black)` in apheleia-mode-alist then
internally we interpret that as a formatter list of `(black)` instead of
`black` as we did previously.

* Support multiple formatters (#31)

Closes #31

This commit makes it so apheleia can run multiple formatters one after
the other and use the resultant output to format the current buffer.
This works somewhat like a pipeline. The output of one formatter becomes
the input to the next formatter until all formatters have run and then
an RCS patch is built from the resultant output and applied to the
current buffer.

Note: For convenience we internally represent the users configuration as
a list of formatters even when it may only be one. For example if the
user has configured `(python-mode . black)` in apheleia-mode-alist then
internally we interpret that as a formatter list of `(black)` instead of
`black` as we did previously.

* Make some changes

* Make some changes

* Error when a (not-first) formatter uses file or filepath

* Prevent formatter recieving stdin when using `file'

Co-authored-by: Radon Rosborough <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

6 participants