-
Notifications
You must be signed in to change notification settings - Fork 3.3k
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
Formatter plugins #11246
Formatter plugins #11246
Conversation
Mix.Tasks.Format.formatter_opts_for_file/2 is soft deprecated.
Mix already doesn't load unused deps, so there is no need to run such checks and slow down the feedback of commands such as `mix format`.
Elixir does not perform a blank loading of lib/*/ebin, so there is actually no need to prune deps.
On behalf of the entire Surface community, a big thank you! This is going to scratch a common itch. 🙂 |
234dc10
to
76313df
Compare
76313df
to
8c0a918
Compare
8c0a918
to
ea6d12d
Compare
@doc """ | ||
Returns which features this plugin should plug into. | ||
""" | ||
@callback features(Keyword.t()) :: [sigils: [atom()], extensions: [binary()]] |
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.
What do you think about inputs
, sources
, locations
, or identifiers
as a replacement for features
? For some reason, the term "feature" does not feel right to me 🙂
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.
I am not a big fan of features
but I don't think any of the other ones make it justice. I thought maybe call it plugin
, but I don't think it is better either. :(
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.
Oh! It could be plug_in
.
Another alternative is to split features
in two, sigils
and extensions
, but not a big fan of that option either :(
ea6d12d
to
95be391
Compare
95be391
to
152eada
Compare
plugins = Keyword.get(formatter_opts, :plugins, []) | ||
|
||
Enum.find(plugins, fn plugin -> | ||
Code.ensure_loaded?(plugin) and function_exported?(plugin, :features, 1) and |
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.
Is this required? It seems the list of plugins is already validated in eval_deps_and_subdirectories
, and not checking that here could be a perf improvement 🚀
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.
They are validated BUT they are still kept because we only validate it when .formatter.exs is loaded but then the result of .formatter.exs is cached. So we need to look at the cached result and see if they have been loaded post caching or not.
end | ||
|
||
formatter_opts = Keyword.put(formatter_opts, :plugins, plugins) | ||
|
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.
What do you think about moving all this logic to a new function, like validate_formatter_opts
or similar?
path | ||
end | ||
|
||
prune_deps(config, load_paths, "--no-deps-check" in args) |
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.
Why are dep builds no longer pruned? Performance?
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.
The commit message has some context but this is not necessary anymore. before we loaded everything in _build/ENV/lib/*/ebin
but now we only load your deps, so we never load something we should not.
💚 💙 💜 💛 ❤️ |
This PR adds formatter plugins which customizes how the formatter behaves.
Plugins must implement the
Mix.Tasks.Format
behaviour. For example, imaginethat your project uses Markdown in two distinct ways: via a custom
~M
sigil andvia files with the
.md
and.markdown
extensions. A custom plugin would looklike this:
Now any application can use your formatter as follows:
Remember that, when running the formatter with plugins, you must make
sure that your dependencies and your application has been been compiled,
so the relevant plugin code can be loaded. Otherwise a warning is logged.
This can be extremely useful for projects like Phoenix LiveView and Surface,
as they can now directly hook into Elixir's formatter.