Giter Club home page Giter Club logo

exfmt's Introduction

Circle CI Hex version API Docs Licence

exfmt ๐ŸŒธ

This tool has been deprecated

With the new formatter in Elixir v1.6 there is no need for this formatter any more. Thanks for checking out exfmt, it's been fun. :)

Intro

exfmt is in alpha. If you run into any problems, please report them.

The format produced by exfmt will change significantly before the 1.0.0 release. If this will cause problems for you, please refrain from using exfmt during the alpha- and beta-test periods.

exfmt is inspired by Aaron VonderHaar's elm-format, and aims to format Elixir source code largely according to the standards defined in Aleksei Magusev's Elixir Style Guide.

# exfmt takes any Elixir code...

defmodule MyApp, do: (
    use( SomeLib )
    def run( data ), do: {
      :ok,
      data
   }
)

# and rewrites it in a clean and idiomatic style:

defmodule MyApp do
  use SomeLib

  def run(data) do
    {:ok, data}
  end
end

The benefits of exfmt:

  • It makes code easier to write, because you never have to worry about minor formatting concerns while powering out new code.
  • It makes code easier to read, because there are no longer distracting minor stylistic differences between different code bases. As such, your brain can map more efficiently from source to mental model.
  • It makes code easier to maintain, because you can no longer have diffs related only to formatting; every diff necessarily involves a material change.
  • It saves your team time debating how to format things, because there is a standard tool that formats everything the same way.
  • It saves you time because you don't have to nitpick over formatting details of your code.

Contents

Usage

mix exfmt path/to/file.ex

Command line options

  • --check - Check if file is formatted, sets exit status to 1 if false.
  • --stdin - Read from STDIN instead of a file.
  • --unsafe - Disable the semantics check that verifies that exmft has not altered the semantic meaning of the input file.

Installation

exfmt makes use of Elixir compiler features coming in Elixir v1.6.0 and as a result can only be run with Elixir v1.6-dev off the Elixir master branch, which you will need to download and compile yourself. Use with earlier versions may work without crashing, but the output format will be incorrect.

An easier method of installation will be available when Elixir v1.6.0 is released. Or sooner, perhaps!

Editor integration

Atom

Atom users can install Ron Green's exfmt-atom package.

Vim

Vim users can use exfmt with Steve Dignam's Neoformat.

Once installed the following config will enable formatting of the current Elixir buffer using :Neoformat. For further instructions, please reference the Neoformat documentation.

let g:neoformat_elixir_exfmt = {
  \ 'exe': 'mix',
  \ 'args': ['exfmt', '--stdin'],
  \ 'stdin': 1
  \ }

let g:neoformat_enabled_elixir = ['exfmt']

Visual Studio Code

VSCode users can use exfmt with James Hrisho's vscode-exfmt package.

exfmt's People

Contributors

binaryseed avatar jason-cooke avatar jfornoff avatar lpil avatar matsubara0507 avatar mikestok avatar rgreenjr avatar securingsincity avatar thetamind avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

exfmt's Issues

Preserve heredocs

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)

[Discussion] Parentheses style

Followup issue to #47.

Parentheses around arguments in anonymous functions

We are currently not adhering to the guidelines regarding parentheses around fn arguments, they are recommending against argument parentheses in anonymous functions.

Parentheses in function calls

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!

The semantic meaning of the source code differs

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

@moduledoc multi-line formatting

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?

(FunctionClauseError) no function clause matching in Access.get/3

`** (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)

Any Elixir warnings will be inserted into the file via Neoformat

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

`--check` or `--validate` flag for CLI

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.

CLI should return a non-zero exit code when it fails

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.

(Protocol.UndefinedError) protocol String.Chars not implemented for {:doc_cons, "conn", ".assigns"}

$ 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

Indent assignment expressions with multi-line pipelines

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)

Allow for single line function definitions

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.

CLI should print error messages to STDERR

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.

Code grouping on functions with multiple bodies

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.

Examples from code

Several bodies of equivalent complexity

  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.

First body for trivial or 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.

Implementations where most of the work is done in pattern matching

  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}

Suggestions

  • Have different spacing between genuinely different functions than between different bodies of the same function so that the grouping is clear.
  • Only expand the ,do: ... construct into do ... end if the full line is longer than 80 characters.

CoC is missing a contact to report stuff

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.

Module Attributes are condensed with next comment & function

Similar issue to #12

The "module constant" is smushed down to the comment and function name below it.

raw

  # 60 seconds
  # @tick_interval 60_000
  @tick_interval 10_000

  ## Client API

  def start_link do
    GenServer.start_link(__MODULE__, :ok, [name: __MODULE__])
  end

formatted

  # 60 seconds
  # @tick_interval 60_000
  @tick_interval 10000
  ## Client API
  def start_link do
    GenServer.start_link __MODULE__, :ok, name: __MODULE__
  end

[Discussion] Function argument nesting

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

Stable release ETA

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.

Align closing 'end' of anonymous functions in argument list with top-level call

Currently, we are aligning the starting -> and the end tokens of anonymous functions when passing them as arguments.

Current formatting:

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

Desired formatting

The idiomatic way usually employed (example in the style guide) aligns the end token with the calling function.

Preserve character literals

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.

Installation documentation

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 :

Installation :

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 :)

mix test failed using the current master branch of elixir

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

Preserve char lists

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].

Comments put in wrong position

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.

** (Protocol.UndefinedError) protocol String.Chars not implemented for {:doc_cons, "&", "1"}

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.

Semantic violation

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

Mix task not found on install

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

[Discussion] Group multiple use, import, alias, require calls

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.

Render large numbers with underscores

Before formatting:

defmodule NumberExample do
  def large_number do
    10_000
  end
end

After formatting

defmodule NumberExample do
  def large_number do
    10000
  end
end

Desired:

  • Do not drop underscores from numbers
  • Add underscores to large numbers where not present (?)

Request: Configuration option for parentheses in function calls

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!

Global install via an escript

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.

`--stdin` flag for CLI

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 :)

Inconsistent spacing

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.

Multi line `@moduledoc` formatted to one line

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.