Skip to content
This repository has been archived by the owner on Jan 25, 2024. It is now read-only.

feat: format via alejandra when feature is set #89

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

rvolosatovs
Copy link

@rvolosatovs rvolosatovs commented Jun 23, 2022

Refs #80
Refs #81

Summary & Motivation

It's nice to give users freedom of choosing the formatter to use. I think that the correct approach would be to parse this from flake.nix formatter and/or expose this via LSP config.
For now, just added a simple solution, which is fully backwards-compatible and does not introduce additional dependencies.

When --features alejandra is specified, https://github.com/kamadorueda/alejandra is used as the formatter.
https://github.com/nix-community/nixpkgs-fmt is still the default, but can be disabled at compile-time via --no-default-features

Until merged this can be consumed via NixOS/nixpkgs@5ddf7f7 in nixpkgs

@Ma27
Copy link
Member

Ma27 commented Jun 23, 2022

Why did you close this?

@rvolosatovs
Copy link
Author

Why did you close this?

I tried it and it didn't work, pushing a fix!

@rvolosatovs rvolosatovs reopened this Jun 23, 2022
@rvolosatovs rvolosatovs force-pushed the feat/alejandra branch 2 times, most recently from cb71399 to f99332e Compare June 23, 2022 18:57
@rvolosatovs rvolosatovs marked this pull request as ready for review June 23, 2022 18:57
@rvolosatovs
Copy link
Author

alejandra crate was just published kamadorueda/alejandra#306, updated the PR

@rvolosatovs
Copy link
Author

rvolosatovs commented Jun 23, 2022

Flipped the order, now --features alejandra would make alejandra the preferred backend without having to --no-default-features as well moved to OP

Copy link
Member

@Ma27 Ma27 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

Added a few minor remarks, also a test would be nice :)

Cargo.toml Outdated
@@ -24,15 +24,16 @@ rnix = "0.10.2"
rowan = "0.12.6"
serde = "1.0.104"
serde_json = "1.0.44"
nixpkgs-fmt-rnix = "1.2.0"
nixpkgs-fmt-rnix = { version = "1.2.0", optional = true }
alejandra = { version = "1.5.0", optional = true }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you checked how much it would increase the closure size if we just added both by default and make it configurable via e.g. the environment which formatter to use. I'm asking for two reasons:

  • I think it's realistic that it depends on a project which formatter is used (i.e. nixpkgs uses de-facto nixpkgs-fmt and a hypothetical other project may use alejandra).
  • I don't want users to recompile rnix-lsp just because they prefer alejandra (and I also don't want to expose multiple attributes for rnix-lsp in nixpkgs depending on the feature-set).

Also cc @kamadorueda for opinions.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I laid out in the description, I think that and ideal solution would infer the formatter to use from the flake.nix file and maybe even directly rely on nix fmt to do that. In that case neither of these two dependencies would be necessary and nix would be the only dependency.
Until then, this would need to be configurable by the user via LSP config, such that it could be overriden on a per-project basis and for that we'd need to compile these in.

So until (and if) nix is integrated here directly, I think the project should absolutely include both by default. Of course, if more formatters emerge (https://xkcd.com/927/) that could become an issue. For just two I'd say it should be fine.

I did not implement handling this via LSP config to minimize the diff, but I am happy to do so if you prefer, although it does seem that the PR would be quite a bit bigger, since I'd have to implement LSP config handling from scratch, there's no existing functionality for that in the project yet, correct?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with the LSP, but I can throw here that there is a third approach: using an environment variable pointing to the formatter binary, and just feeding the code via stdin and getting the output via stdout. That way the closure would decrease substantially as we would be reusing the binaries already present in the user system (instead of compiling and linking them here), we would not need to implement a configuration handler for this LSP, and we would unify the interface and allow for more formatters to be chosen in the future (including nix fmt)

Just an idea, you both are more familiar with this project than me

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course, if more formatters emerge (https://xkcd.com/927/) that could become an issue

Well, the primary reason why I even consider this is because Alejandra seems to be used by quite a bunch of people and is IIRC even a candidate for the nixpkgs formatting RFC, so integrating it is justifiable IMHO.

That way the closure would decrease substantially as we would be reusing the binaries already present in the user system

Admittedly I haven't checked the actual impact on closure size of both alejandra and nixpkgs-fmt, but an LSP server is something that's only running on developer machines anyways, so I wouldn't consider a slight (i.e. non-optimal, but not "bad") increase of closure-size much of an issue.

I'm not familiar with the LSP, but I can throw here that there is a third approach: using an environment variable pointing to the formatter binary, and just feeding the code via stdin and getting the output via stdout.

As described in #87 partial reformats are desirable and something I'd like to see here as well. If we get somewhat reasonable APIs from formatters for that, I'd prefer that solution over having to parse stdout from a formatter. I could imagine that we'd also run into funny quoting or encoding issues when reformatting files with weird stuff in it and parse the (structured) output of the (incremental) edits from stdout.

code.to_string(),
) {
(alejandra::format::Status::Changed(true), new_text) => {
self.reply(Response::new_ok(id, vec![TextEdit { range, new_text }]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're implementing "partial" edits in #87 for nixpkgs-fmt, i.e. only the actual diffs, but not the reformatted file is returned.
AFAIU this also returns the entire file.

If we add a new formatter we should do it properly from the beginning, i.e. only return diffs. Otherwise it might be painful in the editor if e.g. all-packages.nix is attempted to be reformatted.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that would require changes in alejandra to support this in the first place.
@kamadorueda https://github.com/kamadorueda/alejandra/blob/c685879cda91934f29f42574b8eb367496b09df1/src/alejandra/src/format.rs#L13-L40 is the only functionality I found for formatting text in-memory, is there something else I am not aware of?
If such functionality does not exist:

  1. would you be open to having that feature implemented in the first place?
  2. how difficult do you think it would be to implement?
  3. do you have the capacity and willing to do this and if not, would you be open to a PR?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rvolosatovs alejandra::format::in_memory is all we have

I understand why sending small TextEdits instead of a TextEdit with the whole file can be a good thing. The most simple approach I can think of is:

  • Use alejandra::format::in_memory to get a string with the formatted text
  • Use a diffing library to see which lines are equal in the input text and in the formatted text
  • Emit TextEdits for the ranges that are different

I'm not sure if that is good enough for this use case, I'm not familiar with LSP

would you be open to having that feature implemented in the first place?

That feature is more related to this repository than to Alejandra, so it would be better to have it here and let Alejandra just focus on being good at formatting

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much of a change would that be to Alejandra actually? The change in nixpkgs-fmt seemed rather trivial: nix-community/nixpkgs-fmt#296.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ma27 Would it be possible to land this even without partial reformatting? I'm working on projects that use alejandra, and my workflow ends up: edit, :w, :!nix run nixpkgs\#alejandra -- %, test, edit, start again. Even without partial reformatting this change will make my workflow much better. Could it be done separately later?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a maintainer of this anymore, so I don't have a strong opinion. Please discuss that with the current maintainers :)

YorikSar added a commit to YorikSar/dotfiles that referenced this pull request Dec 6, 2022
Applying patch from nix-community/rnix-lsp#89
directly on top of current rnix-lsp and providing UseAlejandra command
to switch to in when Vim is already running.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants