-
Notifications
You must be signed in to change notification settings - Fork 204
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
Fix formatting performance problems with .formatter.exs in subdirectories #609
Fix formatting performance problems with .formatter.exs in subdirectories #609
Conversation
This function is really an implementation detail, so let’s test the public API. The function also relies on interaction with the filesystem, so testing without fixtures is a bad idea because it will lead to incorrect results.
apps/language_server/lib/language_server/providers/formatting.ex
Outdated
Show resolved
Hide resolved
apps/language_server/lib/language_server/providers/formatting.ex
Outdated
Show resolved
Hide resolved
apps/language_server/lib/language_server/providers/formatting.ex
Outdated
Show resolved
Hide resolved
Thanks for the review @lukaszsamson 🙇 |
When dealing with `.formatter.exs` files that are in subdirectories of the main project, the behaviour was incorrect: elixir-lsp#361 And also caused performance problems: elixir-lsp#381 A good example is a project I currently work on. We have a top-level `.formatter.exs`, as well as a `test/.formatter.exs`. The one in `test/` has the `:inputs` option set to `["**/*.{ex,exs}"]`. Before this commit, should_format?/3 would take this pattern and concatenate it with the project directory. This would result in Path.wildcard/2 globbing for all source files in the entire project, rather than just under test/. This causes formatting to take about 5 seconds on our project. This commit fixes the problem by taking the file to be formatted, and finding the `.formatter.exs` file that exists in the closest parent directory (so long as that directory is still within the project root). We then perform the Path.wildcard/2 relative to that directory. I’ve removed the hack that was in place to support umbrella projects, because this is a more general solution to the problem. On our project, I am now able to format a file in 100ms or so, which isn’t a bad speed-up!
7580f18
to
053539d
Compare
I've updated the branch to address your comments @lukaszsamson |
The call to Path.wildcard/2 can cause use to traverse a large number of files, which can be very slow depending on the project and the globs used: elixir-lsp#381 I wrote a library that implements a glob parser and allows us to check whether a glob matches a path without touching the filesystem: https://github.com/jonleighton/path_glob This commit switches out the Path.wildcard/2 call to use this library.
Ok, I've added one more commit which avoids the filesystem traversal entirely:
If there is a concern about maintenance, I'd be happy for the library to be hosted under the |
Thank you for all the effort you already put into this proper solution, @jonleighton! Looking forward to having this released, and have migrations formatted on Save with the phoenix default |
Specifically will be used to not distribute nimble_parsec after merging elixir-lsp/elixir-ls#609
Thanks! This approach looks like a serious improvement over what we were doing previously 🎉 I haven't taken an in-depth look at the code yet (but I plan to soon!) but we will want to vendor |
@axelson Thanks for that, I've merged your PR and released v0.1.1 with it. I'll just note for completeness that I had a chat with José Valim about adding a I put that in the "maybe one day" basket. I don't think I'm going to make time to do that in the near future but it would be a good addition to the standard library I think and I understand why he doesn't want to just include/maintain a whole new parser in Elixir. In any case, |
Specifically will be used to not distribute nimble_parsec after merging elixir-lsp/elixir-ls#609
@jonleighton Thanks for that write-up and additional detail. I just opened #628 once we've tested and merged that I think this PR should be good to go! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great! Thanks! ❤️
Specifically will be used to not distribute nimble_parsec after merging elixir-lsp/elixir-ls#609 Update workflow
Specifically will be used to not distribute nimble_parsec after merging elixir-lsp/elixir-ls#609 Update workflow
When dealing with
.formatter.exs
files that are in subdirectories of themain project, the behaviour was incorrect:
#361
And also caused performance problems:
#381
A good example is a project I currently work on. We have a top-level
.formatter.exs
, as well as atest/.formatter.exs
.The one in
test/
has the:inputs
option set to["**/*.{ex,exs}"]
.Before this commit, should_format?/3 would take this pattern and
concatenate it with the project directory. This would result in
Path.wildcard/2 globbing for all source files in the entire project, rather
than just under test/. This causes formatting to take about 5 seconds on
our project.
This commit fixes the problem by taking the file to be formatted, and
finding the
.formatter.exs
file that exists in the closest parentdirectory (so long as that directory is still within the project root). We
then perform the Path.wildcard/2 relative to that directory.
I’ve removed the hack that was in place to support umbrella projects,
because this is a more general solution to the problem.
On our project, I am now able to format a file in 100ms or so, which isn’t
a bad speed-up!