Haskell-language-server: Coherence of command-line formatters versus formatter plugins

Created on 18 Sep 2020  路  9Comments  路  Source: haskell/haskell-language-server

As many people do, I have a gate in my CI that ensures that code passes a formatter before it is merged. That formatter (in this case stylish-haskell) is run as an executable.

If I also use HLS, I can format my code with stylish-haskell via the formatter plugin. But this will use the version of stylish-haskell that HLS was compiled with, not the one that I have (elsewhere) specified for use in CI.

This makes the in-editor formatting useless to me, as I will typically have to reformat with the executable version before my change will pass CI.

I can see two ways to keep things consistent.

  1. HLS follows environment

Ultimately I think this would mean that HLS would have to call the executable. I understand that this is undesirable since the current model allows us to use the nicer Haskell interfaces to tools and test them easily.

  1. Environment follows HLS

Perhaps we could have some way to format "as-if" using one of the HLS formatting plugins. For example:

  • Ship the formatter executables with HLS, so users can call those directly.
  • Expose the formatting capabilites through HLS itself as part of the CLI, e.g. haskell-language-server --format --formatter ormolu Foo.hs.
plugins enhancement

Most helpful comment

Okay, but I'd still need to recompile haskell-language-server using this plugin, wouldn't I? If I'm willing to do that I can just put constraint: stylish-haskell==whatever to get the version I want.

All 9 comments

Oh, another irritation of HLS directly picking a version of the formatter is that then the "canonical" formatting of my codebase changes with the version of HLS. I can't pin stylish-haskell independently of HLS, as I would be able to if it called it as an external command.

I wonder if it would be possible to take this one step further and make more functionality available via the command line. For example, if I want to apply some fix everywhere in my code, it is often more convenient to automate from the command line than to try to coerce a GUI into doing it (or doing it everywhere manually). But maybe this is a completely separate issue.

This could be ameliorated with the coming plugin architecture, which is specifically aimed at allowing a given user to configure exactly what plugins they want to use.

In time this should lead to a formatter publishing the appropriate plugin descriptor for use in HLS, then this becomes a simple matter of using the same version in both places.

How would the plugin API help here? Would the formatter provider have to provide N different plugins for N different versions? Or would they be loaded dynamically somehow so the user can choose which one? It seems difficult to make this work in the model where HLS statically depends on a particular formatter (at a particular version).

Given the plugin descriptor is just a set of callbacks, depending on the separately provided hls-plugin-api, and specified in a thin Main.hs (https://github.com/haskell/haskell-language-server/blob/b43a8cd78d10d77184a895293d766a0fbceb5573/exe/Main.hs#L38-L64), it should be perfectly feasible for a given formatter to provide it's own instance of the required descriptor. The only requirement is that it is compatible with the hls-plugin-api used in the underlying hls, and then allows you to precisely specify which version you want.

Okay, but I'd still need to recompile haskell-language-server using this plugin, wouldn't I? If I'm willing to do that I can just put constraint: stylish-haskell==whatever to get the version I want.

I also note that any solution which involves re-compiling a Haskell program rather defeats our attempt to distribute static binaries!

I think we will end up shipping the binary for the "all included" version (and maybe the ghcide-only version), and leave it up to each site to build their own versions with custom plugin sets.

It's more about providing the capability.

Was this page helpful?
0 / 5 - 0 ratings