:editor format refactor

Just a tracking post for refactor(format): replace with apheleia by elken · Pull Request #6369 · doomemacs/doomemacs · GitHub atm which intends to replace format-all with the much more up-to-date apheleia.

State of set-formatter!

Currently in the PR I mark it as obsolete and replace it with a no-op that advises users to use apheleia vars for formatter customisations as it handles more use cases and is already very capable than a wrapper macro.

There’s nothing we could really add that it doesn’t do already.

Per a comment from Henrik, set-formatter! is still intended to be included.

Planned summary of changes

Remove the tests included

apheleia already includes a comprehensive test suite and any new formatter is required to include tests to be considered. If it looks like we’re going to implement lots of our own rather than upstreaming, this can maybe be revisited.

Replace format-all with apheleia

format-all has been pinned and listed on do-not-pr for some time now while a replacement has been investigated. apheleia seems like a very viable candidate, formatting even very large buffers almost instantly.

Much like some of the complex advise included in the current module, apheleia uses an RCS patching system to only apply what changed to the buffer after it’s been saved (so as not to contest with other tools/hooks). Replacing it means the module becomes greatly simplified.

Refactor the autoloaded functions

apheleia does not yet support formatting regions, so this will either have to be implemented via refactoring the existing functions or upstream into apheleia.

Replace all in-doom invocations

As we have full control of the code, this should be reasonably simple. Defining a formatter per-mode or even per-buffer is extremely simple:

(setf (alist-get 'isort apheleia-formatters) '("isort" "--stdout" "-"))
(setf (alist-get 'python-mode apheleia-mode-alist) '(isort black))

The above example creates a new formatter called isort and sets it to run before black during python-mode only. Simpler and more complex configurations also exist.

Per a comment from Henrik, these will be incorporated into set-formatter! still.

Refactor docs

apheleia also has very good documentation (as is expected from raxod), so this should mostly just be stealing borrowing snippets to go here

Default formatters/cmdline

If applicable, the default formatter/cmdline for each module. Ideally filled out by either the module maintainer or Henrik.

:lang agda

:lang beancount

:lang cc

:lang clojure

:lang common-lisp

:lang coq

:lang crystal

:lang csharp

:lang dart

:lang data

:lang dhall

:lang elixir

:lang elm

:lang emacs-lisp

:lang erlang

:lang ess

:lang factor

:lang faust

:lang fortran

:lang fsharp

:lang fstar

:lang gdscript

:lang go

:lang graphql

:lang haskell

:lang hy

:lang idris

:lang java

:lang javascript

:lang json

:lang julia

:lang kotlin

Optionally include 'android-mode if it is bound and loaded, otherwise everything else should be up to the user

("ktlint" "--stdin" "-F"
               (when (and (boundp 'android-mode)
                          android-mode)
                 "-a"))

:lang latex

:lang lean

:lang ledger

:lang lua

:lang markdown

:lang nim

:lang nix

:lang ocaml

:lang org

:lang php

:lang plantuml

:lang purescript

:lang python

:lang qt

:lang racket

:lang raku

:lang rest

:lang rst

:lang ruby

:lang rust

:lang scala

:lang scheme

:lang sh

:lang sml

:lang solidity

:lang sql

:lang swift

:lang terra

:lang web

:lang yaml

:lang zig

Final

This is all WIP, and anything can/will change. Any other improvements/recommendations are welcome here.

Note that this is not the place to report issues unless during active development. After this is merged and live, issues should go to the relevant areas.

8 Likes

Created a discussion that may stall this out for a bit, but I’m still endeavouring to upstream formatters in the meantime.

Do you have any thoughts on https://unibeautify.com/ in that context? It tries doing similar things and might be worth factoring into the “one umbrella” thing.

You’re right that it does, unibeautify was mentioned in the issue thread linked in the discussion (a bit round the houses I know…)

At the end of the day, it doesn’t matter what that umbrella is; just as long as raxod, lassik and purcell can agree to one package :)

An unfortunate downside is the ub emacs package hasn’t been updated in 4y.

Oh, I only skimmed the discussion, so I missed it. We’ll see how it goes, I trust the guys to come up with a good solution.

It seems like UB is somewhat stalled in its development anyway. The reason I was interested in it is basically the same as LSP: keep the formatters definition external to Emacs and use an editor adapter. Wouldn’t be unthinkable to implement an LSP server which would only support textDocument/formatting or something like it.

Similar to null-ls on neovim yes.

It’s a good idea, and hopefully once the discussion starts that can also be proposed.

Maybe I’m misunderstanding, but it doesn’t seem like that discussion is all that related to a Doom PR. It didn’t even seem like purcell was interested in merging his package.

I don’t get why anyone needs to get consensus on merging them, either. Someone can merge them if they want, and people can opt into using it, or the maintainers can opt into aliasing their packages to the merged version, right? Either way, it should be easy enough to modify Doom’s hypothetical Apheleia-based format module to accept the new merged version, since it’ll have all the Apheleia functionality, no?

I’d like for the module to have a set-formatter!, whether or not it adds any value over apheleia-formatters. They exist to simplify cross-module configuration by removing the need for conditional after!'s containing implementation details spread across many :lang modules. They also no-op if their containing module is disabled.

I’m fine with dropping it altogether, frankly. Some formatters have project (or external) side-effects, others operate directly on files—fighting to standardize or isolate their effects is more work than I’m willing to maintain on a module I don’t use.

2 Likes

I’d like for the module to have a set-formatter! , whether or not it adds any value over apheleia-formatters . They exist to simplify cross-module configuration by removing the need for conditional after! 's containing implementation details spread across many :lang modules. They also no-op if their containing module is disabled.

Okay, I wasn’t sure if it was actually needed. I’ll have to spend some time coming up with a macro that handles all the cases apheleia-formatters handles.

I’m fine with dropping it altogether, frankly. Some formatters have project (or external) side-effects, others operate directly on files—fighting to standardize or isolate their effects is more work than I’m willing to maintain on a module I don’t use.

Yeah that’s fair, one idea I did have was creating a temp buffer with the region contents, apheleia-format-buffer it and replace the selection.

1 Like

There’s no point going all-in on adding apheleia if raxod is going to drop it to maintain the “new” project.

Makes sense. I guess I was thinking the point was that the new project is intended to support all the old’s functionality, so it wouldn’t be nearly as much work to move from Apheleia to the new project.

Almost certainly, I’m just poking around to see what the future is :)

1 Like

Also on :ok-statuses, I did attempt to upstream some way to handle that but raxod wasn’t keen so this will have to be advised.

Wait, do you not use the formatting module? How do you format your code then?

Raxod has clarified from the discussion that apheleia will be supported but “at some point” there will be a migration to “something”.

I’ll add myself as a maintainer of this module and keep an eye on that, but he said it’s ages off and not worth waiting for so I won’t be.

A couple more formatters have been upstreamed, but we might want to agree on good defaults for our formatters.

I’m aware not all :lang modules have maintainers, but those that do it would be good to get a list of sane defaults (which Henrik can obviously override), but I’l add a list to the original post.