lpil / exfmt Goto Github PK
View Code? Open in Web Editor NEW๐ธ An opinionated Elixir source code formatter
License: Apache License 2.0
๐ธ An opinionated Elixir source code formatter
License: Apache License 2.0
When it finds a function declaration, the output format ensures there's white space around it and it expands ,do: ...
constructs into do ... end
. This is fine to ensure there is white space between functions. However, when a function has several bodies that use pattern matching to choose the correct implementation, it would be useful to ensure that those different bodies are grouped together to indicate that they are parts of the same function.
def etag(data) when is_bitstring(data) or is_binary(data) do
Crypto.hash(:sha256, data) |> Base.encode64
end
def etag(data) do
Crypto.hash(:sha256, Poison.encode!(data)) |> Base.encode64
end
In this example, I have no space between the two definitions because both define the etag
function with different conditions on the attributes. Those two blocks belong together. Separating them as if they implemented different functions renders the code less readable.
nil
case, main body below def key(nil), do: nil
def key(%Guide{key: key}) do
something more complicated
end
In this example, the first definition deals with a trivial case to catch a possible nil
while the meat of the function is in the second definition. Separating them would make it less obvious that the nil
case is dealt with.
def handle_result({:atomic, []}, _spec, {:one, _}), do: {:ok, nil}
def handle_result({:atomic, [r]}, spec, {:one, fmt}), do: {:ok, format_record(r, spec, fmt)}
def handle_result({:atomic, l}, _spec, {:one, _}), do: {:error, {:too_may, Enum.count(l)}}
def handle_result({:atomic, l}, spec, {:many, fmt}), do: {:ok, Enum.map(l, &(format_record(&1, spec, fmt)))}
def handle_result({:atomic, _}, _spec, {:static, v}), do: {:ok, v}
def handle_result({:atomic, r}, _spec, _mode), do: {:ok, r}
def handle_result({:aborted, error}, _spec, _mode), do: {:error, error}
def handle_result(error, _spec, _mode), do: {:error, error}
In this case, most of the work is done by the pattern matching, the bodies are (mostly) trivial so the benefit of this presentation is that it clearly shows the different patterns against each other. That said, I end up with individual lines that are slightly too long so maybe a better presentation for this code would be to expand the non-trivial bodies:
def handle_result({:atomic, []}, _spec, {:one, _}), do: {:ok, nil}
def handle_result({:atomic, [r]}, spec, {:one, fmt}) do
{:ok, format_record(r, spec, fmt)}
end
def handle_result({:atomic, l}, _spec, {:one, _}) do
{:error, {:too_may, Enum.count(l)}}
end
def handle_result({:atomic, l}, spec, {:many, fmt}) do
{:ok, Enum.map(l, &(format_record(&1, spec, fmt)))}
end
def handle_result({:atomic, _}, _spec, {:static, v}), do: {:ok, v}
def handle_result({:atomic, r}, _spec, _mode), do: {:ok, r}
def handle_result({:aborted, error}, _spec, _mode), do: {:error, error}
def handle_result(error, _spec, _mode), do: {:error, error}
,do: ...
construct into do ... end
if the full line is longer than 80 characters.Hi! Does this project aim to implement the Elixir Style Guide? https://github.com/lexmag/elixir-style-guide
If so, it'd be nice to mention / link to that in the Readme :)
Thanks!!
Followup issue to #47.
We are currently not adhering to the guidelines regarding parentheses around fn arguments, they are recommending against argument parentheses in anonymous functions.
The style guide is not explicit about this, but in most of the example snippets, arguments to function calls are always wrapped in parentheses. Currently, the formatter drops parentheses, even for multi-argument functions (example for this).
My personal stance on this would be pro-parentheses, we could also stir up a discussion on the style guide issues if we're not in agreement on what the idiomatic way would be!
Feature Proposal
I've got this implemented. I'll issue a pull request if this proposal is approved.
Per the style guide, when assigning the result of a multi-line expression, the expression should begin on a new line.
# Bad
{found, not_found} = files
|> Enum.map(&Path.expand(&1, path))
|> Enum.partition(&File.exists?/1)
# Good
{found, not_found} =
files
|> Enum.map(&Path.expand(&1, path))
|> Enum.partition(&File.exists?/1)
For example:
@doc "Retrieves an account by UUID, or return `nil` if not found."
def get_account_by_uuid(account_uuid) do
.....
end
<========= HERE
<========= HERE
@doc "Retrieves an account by handle name, or return `nil` if not found."
def get_account_by_handle_name(handle_name) when is_binary(handle_name) do
.....
end
<========= HERE
@doc "Retrieves a user account by primary_email, or return `nil` if not found."
def get_user_account_by_primary_email(primary_email)
when is_binary(primary_email) do
.....
end
Please notice <========= HERE
in the code snippet.
Right now function arguments are nested using :current
. I'd like to propose using the approach taken by Prettier. It feels more natural and less jarring to me (but I could be in the minitory here). The style guide doesn't address this point.
Please let me know your thoughts. Thanks!
Current format:
def some_function_with_mamy_args(argument1,
argument2,
argument3,
argument4,
argument5,
argument6,
argument7,
argument8,
argument9) do
true
end
Proposed format:
def some_function_with_mamy_args(
argument1,
argument2,
argument3,
argument4,
argument5,
argument6,
argument7,
argument8,
argument9) do
true
end
Function arrows seem somewhat randomly placed.
one with a missing space before
one with a whole line to itself
Spawned from #10
$ mix exfmt web/controllers/list_controller.ex
** (Protocol.UndefinedError) protocol String.Chars not implemented for {:doc_cons, "conn", ".assigns"}
(elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
(elixir) lib/string/chars.ex:17: String.Chars.to_string/1
lib/exfmt/ast/to_algebra.ex:277: Exfmt.Ast.ToAlgebra.to_algebra/2
lib/exfmt/ast/to_algebra.ex:413: Exfmt.Ast.ToAlgebra.infix_child_to_algebra/3
lib/exfmt/ast/to_algebra.ex:199: Exfmt.Ast.ToAlgebra.to_algebra/2
lib/exfmt/ast/to_algebra.ex:342: Exfmt.Ast.ToAlgebra.keyword_to_algebra/2
(elixir) lib/inspect/algebra.ex:576: Inspect.Algebra.do_surround_many/5
(elixir) lib/inspect/algebra.ex:559: Inspect.Algebra.do_surround_many/7
I managed to trim the controller down to this small snippet that causes the crash above to present itself:
defmodule Normal.ListController do
def show(conn, %{"slug" => slug} = params) do
posts = Normal.Data.Posts.by_list_slug(slug, safe_only: conn.assigns[:safe_mode_active])
end
end
Just got it working
Thanks an awesome feature for the language thanks a lot :)
I've been missing a few installation instruction
Would you like a PR with something like the following :
Add exfmt to your project dependencies:
# mix.exs (Elixir 1.5)
def deps do
[{:exfmt, "~> 0.4.0", only: :dev, runtime: false}]
end
update your dependencies with :
mix deps.get
all is ready to run :)
Since Elixir 1.6 will introduce a formatter, what will happen with this project?
Feature Proposal
Per the style guide's section on modules, multiple use
, import
, alias
, require
calls should be grouped together and separated with newlines.
Example:
defmodule App do
use GenServer
import Bitwise
import Kernel, except: [length: 1]
alias Mix.Utils
alias MapSet, as: Set
require Logger
end
should be formatted as:
defmodule App do
use GenServer
import Bitwise
import Kernel, except: [length: 1]
alias Mix.Utils
alias MapSet, as: Set
require Logger
end
I'm happy to work on this if accepted.
When this flag is given the CLI will check to see if formatting the file would result in any changes.
If it would result in changes exit non-zero.
If it would not result in changes exit zero.
While reading through the CoC I noticed the email for reporting misbehaviour was missing, it's now [INSERT EMAIL ADDRESS]
. I feel it should either mention a person/email to which people can report or change the description about enforcement.
Is anyone known to be working on Sublime support for exfmt?
Hi @lpil this is a very interesting project, definitely very useful once its finished.
Decided to try it out and found an issue with the first file I gave it, see below:
When I give exfmt
this:
defmodule Mssqlex.Mixfile do
use Mix.Project
def project do
[app: :mssqlex,
version: "0.7.0",
description: "Adapter to Microsoft SQL Server. Using DBConnection and ODBC.",
elixir: ">= 1.4.0",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps(),
package: package(),
aliases: aliases(),
# Testing
test_coverage: [tool: ExCoveralls],
preferred_cli_env: ["test.local": :test,
"coveralls": :test,
"coveralls.travis": :test],
# Docs
name: "Mssqlex",
source_url: "https://github.com/findmypast-oss/mssqlex",
docs: [main: "readme",
extras: ["README.md"]]]
end
def application do
[extra_applications: [:logger, :odbc]]
end
...
end
I get this:
defmodule Mssqlex.Mixfile do
use Mix.Project
def project do
[app: :mssqlex,
version: "0.7.0",
description: "Adapter to Microsoft SQL Server. Using DBConnection and ODBC.",
elixir: ">= 1.4.0",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps(),
package: package(),
aliases: aliases(),
test_coverage: [tool: # Testing
ExCoveralls],
preferred_cli_env: ["test.local": :test, coveralls: :test, "coveralls.travis": :test],
name: "Mssqlex",
source_url: "https://github.com/findmypast-oss/mssqlex",
docs: [main: "readme", extras: ["README.md"]]]
end
# Docs
def application do
[extra_applications: [:logger, :odbc]]
end
...
end
Notice where the comments # Testing
and # Docs
have been moved to.
Extend the current command line offering with a --stdin
flag.
When this flag is given read the input source code from STDIN, format it, and print the outputted style to STDOUT.
This is intended to be used with editor integrations such as https://github.com/sbdchd/neoformat :)
Code:
defmodule Executor do
alias Executor.Util
@moduledoc """
This module is responsible for routing code to the appropriate language module
"""
@doc """
Returns result map for given language and code
iex> run("[1,2,3].map {|i| 2 * i}", "ruby")
{:ok, %{return: "[2, 4, 6]", stdout: ""}}
"""
def run(code, language) do
with {:ok, language_module} <- module_for(language) do
code
|> language_module.run
end
end
@doc """
Returns module for language if one exists, error otherwise
iex> module_for("ruby")
{:ok, Elixir.Executor.Ruby}
iex> module_for("java")
{:error, "Can't run java code"}
"""
def module_for(language) do
module = [
"Elixir.Executor.",
language |> Util.String.titleize,
]
|> Enum.join
|> String.to_existing_atom
{:ok, module}
rescue
_ -> {:error, "Can't run #{language} code"}
end
end
Command:
ix exfmt lib/executor/executor.ex > lib/executor/executor.ex
Output:
Error: The semantic meaning of the source code differs
between the input and the formatted output! We are unable
to continue as formatting may break your code.
This is a bug in exfmt. ๐ข
Please report this problem, including the input source
code file if possible.
https://github.com/lpil/exfmt/issues/new
Elixir 1.5 and Erlang 20.0
There is sometimes 2 lines between functions, sometimes none. I don't see a pattern.
See input & output: https://gist.github.com/cschneid/5eb14df36143b6080db5bbb9fee697b5
I try exfmt code like this:
defmodule Example do
def get_tags() do
[%{name: 1}, %{name: 2}]
|> Enum.map(& &1.name)
end
end
got this error:
โ exfmt git:(master) โ mix exfmt my_ex.ex
** (Protocol.UndefinedError) protocol String.Chars not implemented for {:doc_cons, "&", "1"}
(elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
(elixir) lib/string/chars.ex:17: String.Chars.to_string/1
lib/exfmt/ast/to_algebra.ex:234: Exfmt.Ast.ToAlgebra.to_algebra/2
lib/exfmt/ast/to_algebra.ex:106: Exfmt.Ast.ToAlgebra.to_algebra/2
(elixir) lib/inspect/algebra.ex:559: Inspect.Algebra.do_surround_many/7
lib/exfmt/ast/to_algebra.ex:349: Exfmt.Ast.ToAlgebra.call_to_algebra/3
lib/exfmt/ast/to_algebra.ex:356: Exfmt.Ast.ToAlgebra.infix_child_to_algebra/3
lib/exfmt/ast/to_algebra.ex:160: Exfmt.Ast.ToAlgebra.to_algebra/2
I think is Enum.map(& &1.name)
the & &
make error happen.
Using exfmt
on neovim with neoformat
.
When formatting a file with a multi line @moduledoc
it gets reformatted to a huge single line, separated with \n
.
eg.
@moduledoc """
My module
documentation
"""
will become
@moduledoc " My module \ndocumentation"
Should this be the standard behaviour? Thank you.
See the moduledoc at the top of: https://gist.github.com/cschneid/5eb14df36143b6080db5bbb9fee697b5
The triple-quoted string is transformed into a single-quoted string with \n and escapes.
@moduledoc "Singleton that manages the state of the Agent's data. Mostly just\nroutes data to the correct per-minute data structure\n\nAlso is the core \"tick\" of the system, so each X seconds, the data\ncollected is checked to see if it's ready to be reported. If so, the\nreporting process is kicked off.\n"
(this issue is spawned from #10)
https://github.com/lexmag/elixir-style-guide#parentheses
# Bad
Mix.env
# Good
Mix.env()
Let's only target calls where the left hand side is either an atom or a module alias for now. Other calls will be tricky as they may be accessing a map property rather than a call.
I'm personally a big fan of parenthesis to identify function calls and make my code more readable.
exmt takes this:
persist_adjustments(disagree_rater_ids, [review_karma: -5])
and turns it into this:
persist_adjustments disagree_rater_ids, review_karma: -5
I think that the former is much more readable and I would like to keep the parenthesis around (as well as the brackets around the keyword list, but we can probably discuss that issue separately)
I realize not everyone may share this opinion, but perhaps exfmt could have a few configurable options around issues like this? Much like prettier lets you choose semicolons or not, etc.
Thanks!
defmodule NumberExample do
def large_number do
10_000
end
end
defmodule NumberExample do
def large_number do
10000
end
end
Hello ๐
I'm linking here a file that cannot be formatted by exfmt:
https://github.com/cloud8421/recipe/blob/b1b3e47c8861970824e976d01694144b89072d73/lib/recipe.ex
Thanks (and sorry for the vague issue title)
II think it would be great to have a way to install this globally as an executable or something like that. This way it doesn't have to be included in the project I'm working on.
Currently, we are aligning the starting ->
and the end
tokens of anonymous functions when passing them as arguments.
test "fn in long function calls" do
"""
Enum.find([1,2,3,4], fn num -> rem(num, 2) == 0 end)
""" ~> """
Enum.find [1, 2, 3, 4],
fn num ->
rem(num, 2) == 0
end
"""
"""
Logger.debug fn -> "Hey this is a long log message!" end
""" ~> """
Logger.debug fn ->
"Hey this is a long log message!"
end
"""
end
The idiomatic way usually employed (example in the style guide) aligns the end
token with the calling function.
I'm interested to implement your tool under my FlintCI project.
Do you have any ETA for the stable release? Is the output format stable enough or not at all?
Thanks.
This will help with automation and editor integration.
In order to make this more testable we can abstract the CLI into a function that takes in a list of strings (ARGV) and returns a struct with the properties %{exit_code: integer, stdout: string, stderr: string}
. This can be used to test the CLI without resorting to capturing IO or stubbing out exit calls.
The Neoformat project allows Vim used to set up formatters quickly and easily.
https://github.com/sbdchd/neoformat/
Let's have some docs on how to do this :)
`** (FunctionClauseError) no function clause matching in Access.get/3
The following arguments were given to Access.get/3:
# 1
{80, {5, 8}, nil}
# 2
:line
# 3
nil
Attempted function clauses (showing 5 out of 5):
def get(-%module{} = container-, +key+, +default+)
def get(+map+, +key+, +default+) when -is_map(map)-
def get(+list+, +key+, +default+) when -is_list(list)- and +is_atom(key)+
def get(+list+, +key+, +_default+) when -is_list(list)-
def get(-nil-, +_key+, +default+)
(elixir) lib/access.ex:306: Access.get/3
lib/exfmt/comment.ex:97: Exfmt.Comment.line/1
lib/exfmt/comment.ex:84: anonymous fn/2 in Exfmt.Comment.merge_node/2
(elixir) lib/enum.ex:3169: Enum.split_while_list/3
lib/exfmt/comment.ex:85: Exfmt.Comment.merge_node/2
(elixir) lib/macro.ex:238: Macro.traverse/4
lib/exfmt/comment.ex:68: Exfmt.Comment.merge/2
lib/exfmt.ex:131: Exfmt.do_format/3`
Erlang/OTP 20 [erts-9.1.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
IEx 1.6.0-dev (b6a5589) (compiled with OTP 20)
Similar issue to #12
The "module constant" is smushed down to the comment and function name below it.
# 60 seconds
# @tick_interval 60_000
@tick_interval 10_000
## Client API
def start_link do
GenServer.start_link(__MODULE__, :ok, [name: __MODULE__])
end
# 60 seconds
# @tick_interval 60_000
@tick_interval 10000
## Client API
def start_link do
GenServer.start_link __MODULE__, :ok, name: __MODULE__
end
The autogenerated context (aka model) files in Phoenix 1.3 use a multi-line @moduledoc for each function:
@doc """
Returns the list of roles.
## Examples
iex> list_roles()
[%Role{}, ...]
"""
def list_roles do
Repo.all(Role)
end
Which gets formatted like so:
@doc "Returns the list of roles.\n\n## Examples\n\n iex> list_roles()\n [%Role{}, ...]\n\n"
def list_roles do
Repo.all Role
end
These @moduledocs are all over my application, 20 or so in just the one file I'm working on. So this is preventing me from using exfmt
sadly.
Is there a plan to have a config file to disable certain formatting ala .credo.exs
?
I get this error trying to run exfmt (0.2.0
) on the following file:
==> exfmt
Compiling 11 files (.ex)
Generated exfmt app
Error: The semantic meaning of the source code differs
between the input and the formatted output! We are unable
to continue as formatting may break your code.
This is a bug in exfmt. ๐ข
Please report this problem, including the input source
code file if possible.
https://github.com/lpil/exfmt/issues/new
Here's the module I'm trying to format:
defmodule InjectDetect.CommandHandler do
def store_events([], multi), do: InjectDetect.Repo.transaction(multi)
def store_events([data = %type{} | events], multi) do
%InjectDetect.Model.Event{}
|> Map.put(:type, Atom.to_string(type))
|> Map.put(:data, Map.from_struct(data))
|> (&Ecto.Multi.insert(multi, data, &1)).()
|> (&store_events(events, &1)).()
end
def store_events(events), do: store_events(events, Ecto.Multi.new())
def notify_listeners(events, context, listeners) do
Enum.map(events, fn event -> Enum.map(listeners, &(&1.(event, context))) end)
end
def handle_command(command, context) do
case InjectDetect.Command.handle(command, context) do
{:ok, events, context} -> {:ok, events, context}
{:ok, events} -> {:ok, events, context}
error -> error
end
end
def handle(command, context, listeners) do
with {:ok, events, context} <- handle_command(command, context),
{:ok, _} <- store_events(events)
do
notify_listeners(events, context, listeners)
{:ok, context}
end
end
def handle(command, context \\ %{}) do
listeners = Application.fetch_env!(:inject_detect, :listeners)
handle(command, context, listeners)
end
end
current master brunch is https://github.com/elixir-lang/elixir/tree/fba7e5c240b114a5edcc52ec6f438f9d20dcd29f
~/exfmt# mix test
Compiling 14 files (.ex)
Generated exfmt app
.................
1) doctest Exfmt.Algebra.group/1 (8) (Exfmt.AlgebraTest)
test/exfmt/algebra_test.exs:3
Doctest failed
code: Inspect.Algebra.format(doc, 6) === ["Hello,", "\n", "A", " ", "B"]
left: ["Hello,", "\n", "A", "\n", "B"]
stacktrace:
lib/exfmt/algebra.ex:355: Exfmt.Algebra (module)
.
2) doctest Exfmt.Algebra.nest/2 (9) (Exfmt.AlgebraTest)
test/exfmt/algebra_test.exs:3
Doctest failed
code: doc = Inspect.Algebra.nest(Inspect.Algebra.glue("hello", "world"), 5)
Inspect.Algebra.format(doc, 5) === ["hello", "\n ", "world"]
left: ["hello", " ", "world"]
stacktrace:
lib/exfmt/algebra.ex:326: Exfmt.Algebra (module)
.
3) doctest Exfmt.Algebra.surround/3 (10) (Exfmt.AlgebraTest)
test/exfmt/algebra_test.exs:3
Doctest failed
code: doc = Inspect.Algebra.surround("[", Inspect.Algebra.glue("a", "b"), "]")
Inspect.Algebra.format(doc, 3) === ["[", "a", "\n ", "b", "]"]
left: ["[", "a", " ", "b", "]"]
stacktrace:
lib/exfmt/algebra.ex:392: Exfmt.Algebra (module)
.........
4) doctest Exfmt.Algebra.break/2 (2) (Exfmt.AlgebraTest)
test/exfmt/algebra_test.exs:3
Doctest failed
code: break = Inspect.Algebra.break("\t")
doc = Inspect.Algebra.concat([String.duplicate("a", 20), break, "b"])
Inspect.Algebra.format(doc, 10) === ["aaaaaaaaaaaaaaaaaaaa", "\n", "b"]
left: ["aaaaaaaaaaaaaaaaaaaa", "\t", "b"]
stacktrace:
lib/exfmt/algebra.ex:166: Exfmt.Algebra (module)
............................................................
5) test heredoc with preceeding spaces (Exfmt.Integration.BinaryTest)
test/exfmt/integration/binary_test.exs:108
Assertion with == failed
code: assert expected == output
left: "\"\"\"\none\n two\n\"\"\"\n"
right: "\"one\\n two\\n\"\n"
stacktrace:
test/exfmt/integration/binary_test.exs:109: (test)
........
6) test 2 line heredoc (Exfmt.Integration.BinaryTest)
test/exfmt/integration/binary_test.exs:104
Assertion with == failed
code: assert expected == output
left: "\"\"\"\none\ntwo\n\"\"\"\n"
right: "\"one\\ntwo\\n\"\n"
stacktrace:
test/exfmt/integration/binary_test.exs:105: (test)
7) test empty heredoc (Exfmt.Integration.BinaryTest)
test/exfmt/integration/binary_test.exs:96
Assertion with == failed
code: assert expected == output
left: "\"\"\"\n\"\"\"\n"
right: "\"\"\n"
stacktrace:
test/exfmt/integration/binary_test.exs:97: (test)
...
8) test 1 line heredoc (Exfmt.Integration.BinaryTest)
test/exfmt/integration/binary_test.exs:100
Assertion with == failed
code: assert expected == output
left: "\"\"\"\none\n\"\"\"\n"
right: "\"one\\n\"\n"
stacktrace:
test/exfmt/integration/binary_test.exs:101: (test)
..................................................................................................................................
9) test comments (Exfmt.Integration.CommentsTest)
test/exfmt/integration/comments_test.exs:5
Assertion with == failed
code: assert expected == output
left: "# Hello\n"
right: "__block__()\n# Hello\n"
stacktrace:
test/exfmt/integration/comments_test.exs:8: (test)
..................
10) test chars (Exfmt.Integration.BasicsTest)
test/exfmt/integration/basics_test.exs:80
Assertion with == failed
code: assert expected == output
left: "?a\n"
right: "97\n"
stacktrace:
test/exfmt/integration/basics_test.exs:81: (test)
.................
11) test comments within list (Exfmt.Integration.BasicsTest)
test/exfmt/integration/basics_test.exs:379
Assertion with == failed
code: assert expected == output
left: "[\n :one_one_one,\n # hi\n :two_two_two,\n]\n"
right: "[:one_one_one, :two_two_two]\n# hi\n"
stacktrace:
test/exfmt/integration/basics_test.exs:380: (test)
............
Finished in 1.7 seconds
33 doctests, 256 tests, 11 failures, 2 skipped
Randomized with seed 806669
Here is a comparison of input & output running exfmt on one of my more confusing files. (used exfmt's master as of a few minutes ago)
https://gist.github.com/cschneid/5eb14df36143b6080db5bbb9fee697b5
Currently all output from the mix task is printed to STDOUT. Any error messages should go to STDERR instead.
In order to make this more testable we can abstract the CLI into a function that takes in a list of strings (ARGV) and returns a struct with the properties %{exit_code: integer, stdout: string, stderr: string}
. This can be used to test the CLI without resorting to capturing IO or stubbing out exit calls.
Single-quoted strings formatted as list of ints. It's not compact and readable. It should be better to format lists with printable characters like single quoted strings or leave lists as they were initially.
Now 'test'
is converted to [116, 101, 115, 116]
.
With my Elixir version differing from some of the archives I have installed, I get warnings like:
warning: the archive nerves_bootstrap requires Elixir "~> 1.4.0" but you are running on v1.5.0
when compiling and such.
When using Neoformat
+ exfmt
and running :Neoformat
, that warning gets inserted into the file:
warning: the archive nerves_bootstrap requires Elixir "~> 1.4.0" but you are running on v1.5.0
defmodule Store.Map do
use GenServer
alias Store.ETS
I'm way too excited about this project, but it doesn't look like the mix task gets assigned upon install. I can see when I run iex -S mix
I can call ExFmt
with no issue but mix exfmt
is not found
The Elixir parser converts character literals into integers, so we have no way of knowing what was originally written. Some modification of the parser will be required to support this.
%{
foo: 1,
bar: 2
}
def even?(1), do: false
def even?(2), do: true
is reformatted to
def even?(1) do
false
end
def even?(2) do
true
end
So it does not seem like we're formatting to single-line function definitions yet, when it would probably make sense.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.