fceruti
How to programatically format code using plugins
I’m building a library that’s constantly re generating certain modules, building them from string templates. The generated modules include certain sigils that are nicely formatted using a custom plugin.
I need to format the string before it touches disk, because there are also file watchers, who would receive the event twice if I format as a second separate step. That’s why I can’t just call System.cmd("mix format") after creating the file.
Is there something equivalent to using Code.format_string!/2 but with the ability of passing a plugin list?
Thanks!
Marked As Solved
Marcus
If I understand right you want to format in dev mode and therefore you can access Mix. In this case you can use this:
> iex -S mix
iex(1)> {formatter, _opts} = Mix.Tasks.Format.formatter_for_file("source.ex")
{#Function<19.89531025/1 in Mix.Tasks.Format.find_formatter_for_file/2>,
[
sigils: [],
plugins: [FreedomFormatter],
trailing_comma: true,
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
locals_without_parens: [noop: 1]
]}
iex(2)> formatter.("""
...(2)> defmodule Foo do
...(2)> def foo do
...(2)> noop 5
...(2)> [
...(2)> 1,
...(2)> 2,
...(2)> ]
...(2)> end
...(2)> end
...(2)> """) |> IO.puts()
defmodule Foo do
def foo do
noop 5
[
1,
2,
]
end
end
:ok
The "source.ex" given to formatter_for_file does not have to be an existing file.
Also Liked
krasenyp
You can go even simpler:
def write_and_format_module(file_path, content) do
command = """
mix format - <<STDIN > #{file_path}
#{content}
STDIN
"""
{"", 0} = System.shell(command)
Logger.info("Re generated: #{file_path}")
end
fceruti
Thanks! this was it ![]()
def write_and_format_module(file_path, content) do
:ok = file_path |> Path.dirname() |> File.mkdir_p()
{formatter, _} = Mix.Tasks.Format.formatter_for_file("source.ex")
File.write(file_path, formatter.(content))
IO.puts(IO.ANSI.green() <> "* creating " <> IO.ANSI.reset() <> file_path)
end
fceruti
awesome, thanks!








