Sort alphabetically args in "mix new" task (#8763)

defmodule Mix.Tasks.New do
  use Mix.Task

  import Mix.Generator

  @shortdoc "Creates a new Elixir project"

  @moduledoc """
  Creates a new Elixir project.
  It expects the path of the project as argument.

      mix new PATH [--app APP] [--module MODULE] [--sup] [--umbrella]

  A project at the given PATH will be created. The
  application name and module name will be retrieved
  from the path, unless `--module` or `--app` is given.

  An `--app` option can be given in order to
  name the OTP application for the project.

  A `--module` option can be given in order
  to name the modules in the generated code skeleton.

  A `--sup` option can be given to generate an OTP application
  skeleton including a supervision tree. Normally an app is
  generated without a supervisor and without the app callback.

  An `--umbrella` option can be given to generate an
  umbrella project.

  ## Examples

      mix new hello_world

  Is equivalent to:

      mix new hello_world --module HelloWorld

  To generate an app with a supervision tree and an application callback:

      mix new hello_world --sup

  To generate an umbrella application with sub applications:

      mix new hello_world --umbrella
      cd hello_world/apps
      mix new child_app

  """

  @switches [
    app: :string,
    module: :string,
    sup: :boolean,
    umbrella: :boolean
  ]

  @impl true
  def run(argv) do
    {opts, argv} = OptionParser.parse!(argv, strict: @switches)

    case argv do
      [] ->
        Mix.raise("Expected PATH to be given, please use \"mix new PATH\"")

      [path | _] ->
        app = opts[:app] || Path.basename(Path.expand(path))
        check_application_name!(app, !opts[:app])
        mod = opts[:module] || Macro.camelize(app)
        check_mod_name_validity!(mod)
        check_mod_name_availability!(mod)

        unless path == "." do
          check_directory_existence!(path)
          File.mkdir_p!(path)
        end

        File.cd!(path, fn ->
          if opts[:umbrella] do
            generate_umbrella(app, mod, path, opts)
          else
            generate(app, mod, path, opts)
          end
        end)
    end
  end

  defp generate(app, mod, path, opts) do
    assigns = [
      app: app,
      mod: mod,
      sup_app: sup_app(mod, !!opts[:sup]),
      version: get_version(System.version())
    ]

    mod_filename = Macro.underscore(mod)

    create_file("README.md", readme_template(assigns))
    create_file(".formatter.exs", formatter_template(assigns))
    create_file(".gitignore", gitignore_template(assigns))

    if in_umbrella?() do
      create_file("mix.exs", mix_exs_apps_template(assigns))
    else
      create_file("mix.exs", mix_exs_template(assigns))
    end

    create_directory("config")
    create_file("config/config.exs", config_template(assigns))

    create_directory("lib")
    create_file("lib/#{mod_filename}.ex", lib_template(assigns))

    if opts[:sup] do
      create_file("lib/#{mod_filename}/application.ex", lib_app_template(assigns))
    end

    create_directory("test")
    create_file("test/test_helper.exs", test_helper_template(assigns))
    create_file("test/#{mod_filename}_test.exs", test_template(assigns))

    """

    Your Mix project was created successfully.
    You can use "mix" to compile it, test it, and more:

        #{cd_path(path)}mix test

    Run "mix help" for more commands.
    """
    |> String.trim_trailing()
    |> Mix.shell().info()
  end

  defp sup_app(_mod, false), do: ""
  defp sup_app(mod, true), do: ",\n      mod: {#{mod}.Application, []}"

  defp cd_path("."), do: ""
  defp cd_path(path), do: "cd #{path}\n    "

  defp generate_umbrella(_app, mod, path, _opts) do
    assigns = [app: nil, mod: mod]

    create_file("README.md", readme_template(assigns))
    create_file(".formatter.exs", formatter_umbrella_template(assigns))
    create_file(".gitignore", gitignore_template(assigns))
    create_file("mix.exs", mix_exs_umbrella_template(assigns))

    create_directory("apps")

    create_directory("config")
    create_file("config/config.exs", config_umbrella_template(assigns))

    """

    Your umbrella project was created successfully.
    Inside your project, you will find an apps/ directory
    where you can create and host many apps:

        #{cd_path(path)}cd apps
        mix new my_app

    Commands like "mix compile" and "mix test" when executed
    in the umbrella project root will automatically run
    for each application in the apps/ directory.
    """
    |> String.trim_trailing()
    |> Mix.shell().info()
  end

  defp check_application_name!(name, inferred?) do
    unless name =~ Regex.recompile!(~r/^[a-z][a-z0-9_]*$/) do
      Mix.raise(
        "Application name must start with a lowercase ASCII letter, followed by " <>
          "lowercase ASCII letters, numbers, or underscores, got: #{inspect(name)}" <>
          if inferred? do
            ". The application name is inferred from the path, if you'd like to " <>
              "explicitly name the application then use the \"--app APP\" option"
          else
            ""
          end
      )
    end
  end

  defp check_mod_name_validity!(name) do
    unless name =~ Regex.recompile!(~r/^[A-Z]\w*(\.[A-Z]\w*)*$/) do
      Mix.raise(
        "Module name must be a valid Elixir alias (for example: Foo.Bar), got: #{inspect(name)}"
      )
    end
  end

  defp check_mod_name_availability!(name) do
    name = Module.concat(Elixir, name)

    if Code.ensure_loaded?(name) do
      Mix.raise("Module name #{inspect(name)} is already taken, please choose another name")
    end
  end

  defp check_directory_existence!(path) do
    msg = "The directory #{inspect(path)} already exists. Are you sure you want to continue?"

    if File.dir?(path) and not Mix.shell().yes?(msg) do
      Mix.raise("Please select another directory for installation")
    end
  end

  defp get_version(version) do
    {:ok, version} = Version.parse(version)

    "#{version.major}.#{version.minor}" <>
      case version.pre do
        [h | _] -> "-#{h}"
        [] -> ""
      end
  end

  defp in_umbrella? do
    apps = Path.dirname(File.cwd!())

    try do
      Mix.Project.in_project(:umbrella_check, "../..", fn _ ->
        path = Mix.Project.config()[:apps_path]
        path && Path.expand(path) == apps
      end)
    catch
      _, _ -> false
    end
  end

  embed_template(:readme, """
  # <%= @mod %>

  **TODO: Add description**
  <%= if @app do %>
  ## Installation

  If [available in Hex](https://hex.pm/docs/publish), the package can be installed
  by adding `<%= @app %>` to your list of dependencies in `mix.exs`:

  ```elixir
  def deps do
    [
      {:<%= @app %>, "~> 0.1.0"}
    ]
  end
  ```

  Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
  and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
  be found at [https://hexdocs.pm/<%= @app %>](https://hexdocs.pm/<%= @app %>).
  <% end %>
  """)

  embed_template(:formatter, """
  # Used by "mix format"
  [
    inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
  ]
  """)

  embed_template(:formatter_umbrella, """
  # Used by "mix format"
  [
    inputs: ["mix.exs", "config/*.exs"],
    subdirectories: ["apps/*"]
  ]
  """)

  embed_template(:gitignore, """
  # The directory Mix will write compiled artifacts to.
  /_build/

  # If you run "mix test --cover", coverage assets end up here.
  /cover/

  # The directory Mix downloads your dependencies sources to.
  /deps/

  # Where third-party dependencies like ExDoc output generated docs.
  /doc/

  # Ignore .fetch files in case you like to edit your project deps locally.
  /.fetch

  # If the VM crashes, it generates a dump, let's ignore it too.
  erl_crash.dump

  # Also ignore archive artifacts (built via "mix archive.build").
  *.ez
  <%= if @app do %>
  # Ignore package tarball (built via "mix hex.build").
  <%= @app %>-*.tar
  <% end %>
  """)

  embed_template(:mix_exs, """
  defmodule <%= @mod %>.MixProject do
    use Mix.Project

    def project do
      [
        app: :<%= @app %>,
        version: "0.1.0",
        elixir: "~> <%= @version %>",
        start_permanent: Mix.env() == :prod,
        deps: deps()
      ]
    end

    # Run "mix help compile.app" to learn about applications.
    def application do
      [
        extra_applications: [:logger]<%= @sup_app %>
      ]
    end

    # Run "mix help deps" to learn about dependencies.
    defp deps do
      [
        # {:dep_from_hexpm, "~> 0.3.0"},
        # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
      ]
    end
  end
  """)

  embed_template(:mix_exs_apps, """
  defmodule <%= @mod %>.MixProject do
    use Mix.Project

    def project do
      [
        app: :<%= @app %>,
        version: "0.1.0",
        build_path: "../../_build",
        config_path: "../../config/config.exs",
        deps_path: "../../deps",
        lockfile: "../../mix.lock",
        elixir: "~> <%= @version %>",
        start_permanent: Mix.env() == :prod,
        deps: deps()
      ]
    end

    # Run "mix help compile.app" to learn about applications.
    def application do
      [
        extra_applications: [:logger]<%= @sup_app %>
      ]
    end

    # Run "mix help deps" to learn about dependencies.
    defp deps do
      [
        # {:dep_from_hexpm, "~> 0.3.0"},
        # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
        # {:sibling_app_in_umbrella, in_umbrella: true}
      ]
    end
  end
  """)

  embed_template(:mix_exs_umbrella, """
  defmodule <%= @mod %>.MixProject do
    use Mix.Project

    def project do
      [
        apps_path: "apps",
        version: "0.1.0",
        start_permanent: Mix.env() == :prod,
        deps: deps()
      ]
    end

    # Dependencies listed here are available only for this
    # project and cannot be accessed from applications inside
    # the apps folder.
    #
    # Run "mix help deps" for examples and options.
    defp deps do
      []
    end
  end
  """)

  embed_template(:config, ~S"""
  # This file is responsible for configuring your application
  # and its dependencies with the aid of the Mix.Config module.
  use Mix.Config

  # This configuration is loaded before any dependency and is restricted
  # to this project. If another project depends on this project, this
  # file won't be loaded nor affect the parent project. For this reason,
  # if you want to provide default values for your application for
  # third-party users, it should be done in your "mix.exs" file.

  # You can configure your application as:
  #
  #     config :<%= @app %>, key: :value
  #
  # and access this configuration in your application as:
  #
  #     Application.get_env(:<%= @app %>, :key)
  #
  # You can also configure a third-party app:
  #
  #     config :logger, level: :info
  #

  # It is also possible to import configuration files, relative to this
  # directory. For example, you can emulate configuration per environment
  # by uncommenting the line below and defining dev.exs, test.exs and such.
  # Configuration from the imported file will override the ones defined
  # here (which is why it is important to import them last).
  #
  #     import_config "#{Mix.env()}.exs"
  """)

  embed_template(:config_umbrella, ~S"""
  # This file is responsible for configuring your application
  # and its dependencies with the aid of the Mix.Config module.
  use Mix.Config

  # By default, the umbrella project as well as each child
  # application will require this configuration file, as
  # configuration and dependencies are shared in an umbrella
  # project. While one could configure all applications here,
  # we prefer to keep the configuration of each individual
  # child application in their own app, but all other
  # dependencies, regardless if they belong to one or multiple
  # apps, should be configured in the umbrella to avoid confusion.
  import_config "../apps/*/config/config.exs"

  # Sample configuration (overrides the imported configuration above):
  #
  #     config :logger, :console,
  #       level: :info,
  #       format: "$date $time [$level] $metadata$message\n",
  #       metadata: [:user_id]
  """)

  embed_template(:lib, """
  defmodule <%= @mod %> do
    @moduledoc \"""
    Documentation for <%= @mod %>.
    \"""

    @doc \"""
    Hello world.

    ## Examples

        iex> <%= @mod %>.hello()
        :world

    \"""
    def hello do
      :world
    end
  end
  """)

  embed_template(:lib_app, """
  defmodule <%= @mod %>.Application do
    # See https://hexdocs.pm/elixir/Application.html
    # for more information on OTP Applications
    @moduledoc false

    use Application

    def start(_type, _args) do
      children = [
        # Starts a worker by calling: <%= @mod %>.Worker.start_link(arg)
        # {<%= @mod %>.Worker, arg}
      ]

      # See https://hexdocs.pm/elixir/Supervisor.html
      # for other strategies and supported options
      opts = [strategy: :one_for_one, name: <%= @mod %>.Supervisor]
      Supervisor.start_link(children, opts)
    end
  end
  """)

  embed_template(:test, """
  defmodule <%= @mod %>Test do
    use ExUnit.Case
    doctest <%= @mod %>

    test "greets the world" do
      assert <%= @mod %>.hello() == :world
    end
  end
  """)

  embed_template(:test_helper, """
  ExUnit.start()
  """)
end

defmodule Mix.Tasks.New do
  use Mix.Task

  import Mix.Generator

  @shortdoc "Creates a new Elixir project"

  @moduledoc """
  Creates a new Elixir project.
  It expects the path of the project as argument.

      mix new PATH [--sup] [--module MODULE] [--app APP] [--umbrella]

  A project at the given PATH will be created. The
  application name and module name will be retrieved
  from the path, unless `--module` or `--app` is given.

  A `--sup` option can be given to generate an OTP application
  skeleton including a supervision tree. Normally an app is
  generated without a supervisor and without the app callback.

  An `--umbrella` option can be given to generate an
  umbrella project.

  An `--app` option can be given in order to
  name the OTP application for the project.

  A `--module` option can be given in order
  to name the modules in the generated code skeleton.

  ## Examples

      mix new hello_world

  Is equivalent to:

      mix new hello_world --module HelloWorld

  To generate an app with a supervision tree and an application callback:

      mix new hello_world --sup

  To generate an umbrella application with sub applications:

      mix new hello_world --umbrella
      cd hello_world/apps
      mix new child_app

  """

  @switches [
    app: :string,
    module: :string,
    sup: :boolean,
    umbrella: :boolean
  ]

  @impl true
  def run(argv) do
    {opts, argv} = OptionParser.parse!(argv, strict: @switches)

    case argv do
      [] ->
        Mix.raise("Expected PATH to be given, please use \"mix new PATH\"")

      [path | _] ->
        app = opts[:app] || Path.basename(Path.expand(path))
        check_application_name!(app, !opts[:app])
        mod = opts[:module] || Macro.camelize(app)
        check_mod_name_validity!(mod)
        check_mod_name_availability!(mod)

        unless path == "." do
          check_directory_existence!(path)
          File.mkdir_p!(path)
        end

        File.cd!(path, fn ->
          if opts[:umbrella] do
            generate_umbrella(app, mod, path, opts)
          else
            generate(app, mod, path, opts)
          end
        end)
    end
  end

  defp generate(app, mod, path, opts) do
    assigns = [
      app: app,
      mod: mod,
      sup_app: sup_app(mod, !!opts[:sup]),
      version: get_version(System.version())
    ]

    mod_filename = Macro.underscore(mod)

    create_file("README.md", readme_template(assigns))
    create_file(".formatter.exs", formatter_template(assigns))
    create_file(".gitignore", gitignore_template(assigns))

    if in_umbrella?() do
      create_file("mix.exs", mix_exs_apps_template(assigns))
    else
      create_file("mix.exs", mix_exs_template(assigns))
    end

    create_directory("config")
    create_file("config/config.exs", config_template(assigns))

    create_directory("lib")
    create_file("lib/#{mod_filename}.ex", lib_template(assigns))

    if opts[:sup] do
      create_file("lib/#{mod_filename}/application.ex", lib_app_template(assigns))
    end

    create_directory("test")
    create_file("test/test_helper.exs", test_helper_template(assigns))
    create_file("test/#{mod_filename}_test.exs", test_template(assigns))

    """

    Your Mix project was created successfully.
    You can use "mix" to compile it, test it, and more:

        #{cd_path(path)}mix test

    Run "mix help" for more commands.
    """
    |> String.trim_trailing()
    |> Mix.shell().info()
  end

  defp sup_app(_mod, false), do: ""
  defp sup_app(mod, true), do: ",\n      mod: {#{mod}.Application, []}"

  defp cd_path("."), do: ""
  defp cd_path(path), do: "cd #{path}\n    "

  defp generate_umbrella(_app, mod, path, _opts) do
    assigns = [app: nil, mod: mod]

    create_file("README.md", readme_template(assigns))
    create_file(".formatter.exs", formatter_umbrella_template(assigns))
    create_file(".gitignore", gitignore_template(assigns))
    create_file("mix.exs", mix_exs_umbrella_template(assigns))

    create_directory("apps")

    create_directory("config")
    create_file("config/config.exs", config_umbrella_template(assigns))

    """

    Your umbrella project was created successfully.
    Inside your project, you will find an apps/ directory
    where you can create and host many apps:

        #{cd_path(path)}cd apps
        mix new my_app

    Commands like "mix compile" and "mix test" when executed
    in the umbrella project root will automatically run
    for each application in the apps/ directory.
    """
    |> String.trim_trailing()
    |> Mix.shell().info()
  end

  defp check_application_name!(name, inferred?) do
    unless name =~ Regex.recompile!(~r/^[a-z][a-z0-9_]*$/) do
      Mix.raise(
        "Application name must start with a lowercase ASCII letter, followed by " <>
          "lowercase ASCII letters, numbers, or underscores, got: #{inspect(name)}" <>
          if inferred? do
            ". The application name is inferred from the path, if you'd like to " <>
              "explicitly name the application then use the \"--app APP\" option"
          else
            ""
          end
      )
    end
  end

  defp check_mod_name_validity!(name) do
    unless name =~ Regex.recompile!(~r/^[A-Z]\w*(\.[A-Z]\w*)*$/) do
      Mix.raise(
        "Module name must be a valid Elixir alias (for example: Foo.Bar), got: #{inspect(name)}"
      )
    end
  end

  defp check_mod_name_availability!(name) do
    name = Module.concat(Elixir, name)

    if Code.ensure_loaded?(name) do
      Mix.raise("Module name #{inspect(name)} is already taken, please choose another name")
    end
  end

  defp check_directory_existence!(path) do
    msg = "The directory #{inspect(path)} already exists. Are you sure you want to continue?"

    if File.dir?(path) and not Mix.shell().yes?(msg) do
      Mix.raise("Please select another directory for installation")
    end
  end

  defp get_version(version) do
    {:ok, version} = Version.parse(version)

    "#{version.major}.#{version.minor}" <>
      case version.pre do
        [h | _] -> "-#{h}"
        [] -> ""
      end
  end

  defp in_umbrella? do
    apps = Path.dirname(File.cwd!())

    try do
      Mix.Project.in_project(:umbrella_check, "../..", fn _ ->
        path = Mix.Project.config()[:apps_path]
        path && Path.expand(path) == apps
      end)
    catch
      _, _ -> false
    end
  end

  embed_template(:readme, """
  # <%= @mod %>

  **TODO: Add description**
  <%= if @app do %>
  ## Installation

  If [available in Hex](https://hex.pm/docs/publish), the package can be installed
  by adding `<%= @app %>` to your list of dependencies in `mix.exs`:

  ```elixir
  def deps do
    [
      {:<%= @app %>, "~> 0.1.0"}
    ]
  end
  ```

  Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
  and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
  be found at [https://hexdocs.pm/<%= @app %>](https://hexdocs.pm/<%= @app %>).
  <% end %>
  """)

  embed_template(:formatter, """
  # Used by "mix format"
  [
    inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
  ]
  """)

  embed_template(:formatter_umbrella, """
  # Used by "mix format"
  [
    inputs: ["mix.exs", "config/*.exs"],
    subdirectories: ["apps/*"]
  ]
  """)

  embed_template(:gitignore, """
  # The directory Mix will write compiled artifacts to.
  /_build/

  # If you run "mix test --cover", coverage assets end up here.
  /cover/

  # The directory Mix downloads your dependencies sources to.
  /deps/

  # Where third-party dependencies like ExDoc output generated docs.
  /doc/

  # Ignore .fetch files in case you like to edit your project deps locally.
  /.fetch

  # If the VM crashes, it generates a dump, let's ignore it too.
  erl_crash.dump

  # Also ignore archive artifacts (built via "mix archive.build").
  *.ez
  <%= if @app do %>
  # Ignore package tarball (built via "mix hex.build").
  <%= @app %>-*.tar
  <% end %>
  """)

  embed_template(:mix_exs, """
  defmodule <%= @mod %>.MixProject do
    use Mix.Project

    def project do
      [
        app: :<%= @app %>,
        version: "0.1.0",
        elixir: "~> <%= @version %>",
        start_permanent: Mix.env() == :prod,
        deps: deps()
      ]
    end

    # Run "mix help compile.app" to learn about applications.
    def application do
      [
        extra_applications: [:logger]<%= @sup_app %>
      ]
    end

    # Run "mix help deps" to learn about dependencies.
    defp deps do
      [
        # {:dep_from_hexpm, "~> 0.3.0"},
        # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
      ]
    end
  end
  """)

  embed_template(:mix_exs_apps, """
  defmodule <%= @mod %>.MixProject do
    use Mix.Project

    def project do
      [
        app: :<%= @app %>,
        version: "0.1.0",
        build_path: "../../_build",
        config_path: "../../config/config.exs",
        deps_path: "../../deps",
        lockfile: "../../mix.lock",
        elixir: "~> <%= @version %>",
        start_permanent: Mix.env() == :prod,
        deps: deps()
      ]
    end

    # Run "mix help compile.app" to learn about applications.
    def application do
      [
        extra_applications: [:logger]<%= @sup_app %>
      ]
    end

    # Run "mix help deps" to learn about dependencies.
    defp deps do
      [
        # {:dep_from_hexpm, "~> 0.3.0"},
        # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
        # {:sibling_app_in_umbrella, in_umbrella: true}
      ]
    end
  end
  """)

  embed_template(:mix_exs_umbrella, """
  defmodule <%= @mod %>.MixProject do
    use Mix.Project

    def project do
      [
        apps_path: "apps",
        version: "0.1.0",
        start_permanent: Mix.env() == :prod,
        deps: deps()
      ]
    end

    # Dependencies listed here are available only for this
    # project and cannot be accessed from applications inside
    # the apps folder.
    #
    # Run "mix help deps" for examples and options.
    defp deps do
      []
    end
  end
  """)

  embed_template(:config, ~S"""
  # This file is responsible for configuring your application
  # and its dependencies with the aid of the Mix.Config module.
  use Mix.Config

  # This configuration is loaded before any dependency and is restricted
  # to this project. If another project depends on this project, this
  # file won't be loaded nor affect the parent project. For this reason,
  # if you want to provide default values for your application for
  # third-party users, it should be done in your "mix.exs" file.

  # You can configure your application as:
  #
  #     config :<%= @app %>, key: :value
  #
  # and access this configuration in your application as:
  #
  #     Application.get_env(:<%= @app %>, :key)
  #
  # You can also configure a third-party app:
  #
  #     config :logger, level: :info
  #

  # It is also possible to import configuration files, relative to this
  # directory. For example, you can emulate configuration per environment
  # by uncommenting the line below and defining dev.exs, test.exs and such.
  # Configuration from the imported file will override the ones defined
  # here (which is why it is important to import them last).
  #
  #     import_config "#{Mix.env()}.exs"
  """)

  embed_template(:config_umbrella, ~S"""
  # This file is responsible for configuring your application
  # and its dependencies with the aid of the Mix.Config module.
  use Mix.Config

  # By default, the umbrella project as well as each child
  # application will require this configuration file, as
  # configuration and dependencies are shared in an umbrella
  # project. While one could configure all applications here,
  # we prefer to keep the configuration of each individual
  # child application in their own app, but all other
  # dependencies, regardless if they belong to one or multiple
  # apps, should be configured in the umbrella to avoid confusion.
  import_config "../apps/*/config/config.exs"

  # Sample configuration (overrides the imported configuration above):
  #
  #     config :logger, :console,
  #       level: :info,
  #       format: "$date $time [$level] $metadata$message\n",
  #       metadata: [:user_id]
  """)

  embed_template(:lib, """
  defmodule <%= @mod %> do
    @moduledoc \"""
    Documentation for <%= @mod %>.
    \"""

    @doc \"""
    Hello world.

    ## Examples

        iex> <%= @mod %>.hello()
        :world

    \"""
    def hello do
      :world
    end
  end
  """)

  embed_template(:lib_app, """
  defmodule <%= @mod %>.Application do
    # See https://hexdocs.pm/elixir/Application.html
    # for more information on OTP Applications
    @moduledoc false

    use Application

    def start(_type, _args) do
      children = [
        # Starts a worker by calling: <%= @mod %>.Worker.start_link(arg)
        # {<%= @mod %>.Worker, arg}
      ]

      # See https://hexdocs.pm/elixir/Supervisor.html
      # for other strategies and supported options
      opts = [strategy: :one_for_one, name: <%= @mod %>.Supervisor]
      Supervisor.start_link(children, opts)
    end
  end
  """)

  embed_template(:test, """
  defmodule <%= @mod %>Test do
    use ExUnit.Case
    doctest <%= @mod %>

    test "greets the world" do
      assert <%= @mod %>.hello() == :world
    end
  end
  """)

  embed_template(:test_helper, """
  ExUnit.start()
  """)
end


Fix type of Inspect.Opts limit

Fix type of Inspect.Opts limit

defmodule Inspect.Opts do
  @moduledoc """
  Defines the Inspect.Opts used by the Inspect protocol.

  The following fields are available:

    * `:structs` - when false, structs are not formatted by the inspect
      protocol, they are instead printed as maps, defaults to true.

    * `:binaries` - when `:as_strings` all binaries will be printed as strings,
      non-printable bytes will be escaped.

      When `:as_binaries` all binaries will be printed in bit syntax.

      When the default `:infer`, the binary will be printed as a string if it
      is printable, otherwise in bit syntax.

    * `:char_lists` - when `:as_char_lists` all lists will be printed as char
      lists, non-printable elements will be escaped.

      When `:as_lists` all lists will be printed as lists.

      When the default `:infer`, the list will be printed as a char list if it
      is printable, otherwise as list.

    * `:limit` - limits the number of items that are printed for tuples,
      bitstrings, and lists, does not apply to strings nor char lists, defaults
      to 50.

    * `:pretty` - if set to true enables pretty printing, defaults to false.

    * `:width` - defaults to the 80 characters, used when pretty is true or
      when printing to IO devices.

    * `:base` - print integers as :binary, :octal, :decimal, or :hex, defaults
      to :decimal

  """

  defstruct structs: true,
            binaries: :infer,
            char_lists: :infer,
            limit: 50,
            width: 80,
            base: :decimal,
            pretty: false

  @type t :: %__MODULE__{
               structs: boolean,
               binaries: :infer | :as_binaries | :as_strings,
               char_lists: :infer | :as_lists | :as_char_lists,
               limit: pos_integer | :infinity,
               width: pos_integer | :infinity,
               base: :decimal | :binary | :hex | :octal,
               pretty: boolean}
end

defmodule Inspect.Algebra do
  @moduledoc ~S"""
  A set of functions for creating and manipulating algebra
  documents, as described in ["Strictly Pretty" (2000) by Christian Lindig][0].

  An algebra document is represented by an `Inspect.Algebra` node
  or a regular string.

      iex> Inspect.Algebra.empty
      :doc_nil

      iex> "foo"
      "foo"

  With the functions in this module, we can concatenate different
  elements together and render them:

      iex> doc = Inspect.Algebra.concat(Inspect.Algebra.empty, "foo")
      iex> Inspect.Algebra.format(doc, 80)
      ["foo"]

  The functions `nest/2`, `space/2` and `line/2` help you put the
  document together into a rigid structure. However, the document
  algebra gets interesting when using functions like `break/2`, which
  converts the given string into a line break depending on how much space
  there is to print. Let's glue two docs together with a break and then
  render it:

      iex> doc = Inspect.Algebra.glue("a", " ", "b")
      iex> Inspect.Algebra.format(doc, 80)
      ["a", " ", "b"]

  Notice the break was represented as is, because we haven't reached
  a line limit. Once we do, it is replaced by a newline:

      iex> doc = Inspect.Algebra.glue(String.duplicate("a", 20), " ", "b")
      iex> Inspect.Algebra.format(doc, 10)
      ["aaaaaaaaaaaaaaaaaaaa", "\n", "b"]

  Finally, this module also contains Elixir related functions, a bit
  tied to Elixir formatting, namely `surround/3` and `surround_many/5`.

  ## Implementation details

  The original Haskell implementation of the algorithm by [Wadler][1]
  relies on lazy evaluation to unfold document groups on two alternatives:
  `:flat` (breaks as spaces) and `:break` (breaks as newlines).
  Implementing the same logic in a strict language such as Elixir leads
  to an exponential growth of possible documents, unless document groups
  are encoded explictly as `:flat` or `:break`. Those groups are then reduced
  to a simple document, where the layout is already decided, per [Lindig][0].

  This implementation slightly changes the semantic of Lindig's algorithm
  to allow elements that belong to the same group to be printed together
  in the same line, even if they do not fit the line fully. This was achieved
  by changing `:break` to mean a possible break and `:flat` to force a flat
  structure. Then deciding if a break works as a newline is just a matter
  of checking if we have enough space until the next break that is not
  inside a group (which is still flat).

  Custom pretty printers can be implemented using the documents returned
  by this module and by providing their own rendering functions.

    [0]: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.34.2200
    [1]: http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf

  """

  @surround_separator ","
  @tail_separator " |"
  @newline "\n"
  @nesting 1
  @break " "

  # Functional interface to `doc` records

  @type t :: :doc_nil | :doc_line | doc_cons | doc_nest | doc_break | doc_group | binary

  @typep doc_cons :: {:doc_cons, t, t}
  defmacrop doc_cons(left, right) do
    quote do: {:doc_cons, unquote(left), unquote(right)}
  end

  @typep doc_nest :: {:doc_nest, t, non_neg_integer}
  defmacrop doc_nest(doc, indent) do
    quote do: {:doc_nest, unquote(doc), unquote(indent) }
  end

  @typep doc_break :: {:doc_break, binary}
  defmacrop doc_break(break) do
    quote do: {:doc_break, unquote(break)}
  end

  @typep doc_group :: {:doc_group, t}
  defmacrop doc_group(group) do
    quote do: {:doc_group, unquote(group)}
  end

  defmacrop is_doc(doc) do
    if Macro.Env.in_guard?(__CALLER__) do
      do_is_doc(doc)
    else
      var = quote do: doc
      quote do
        unquote(var) = unquote(doc)
        unquote(do_is_doc(var))
      end
    end
  end

  defp do_is_doc(doc) do
    quote do
      is_binary(unquote(doc)) or
      unquote(doc) in [:doc_nil, :doc_line] or
      (is_tuple(unquote(doc)) and
       elem(unquote(doc), 0) in [:doc_cons, :doc_nest, :doc_break, :doc_group])
    end
  end

  @doc """
  Converts an Elixir structure to an algebra document
  according to the inspect protocol.
  """
  @spec to_doc(any, Inspect.Opts.t) :: t
  def to_doc(%{__struct__: struct} = map, %Inspect.Opts{} = opts) when is_atom(struct) do
    if opts.structs do
      try do
        Inspect.inspect(map, opts)
      rescue
        e ->
          # Because we try to raise a nice error message in case
          # we can't inspect a struct, there is a chance the error
          # message itself relies on the struct being printed, so
          # we need to trap the inspected messages to guarantee
          # we won't try to render any failed instruct when building
          # the error message.
          if Process.get(:inspect_trap) do
            Inspect.Map.inspect(map, opts)
          else
            try do
              Process.put(:inspect_trap, true)
              res = Inspect.Map.inspect(map, opts)
              formatted = IO.iodata_to_binary(format(res, :infinity))
              raise ArgumentError,
                "Got #{inspect e.__struct__} with message " <>
                "\"#{Exception.message(e)}\" while inspecting #{formatted}"
            after
              Process.delete(:inspect_trap)
            end
          end
      end
    else
      Inspect.Map.inspect(map, opts)
    end
  end

  def to_doc(arg, %Inspect.Opts{} = opts) do
    Inspect.inspect(arg, opts)
  end

  @doc """
  Returns a document entity used to represent nothingness.

  ## Examples

      iex> Inspect.Algebra.empty
      :doc_nil

  """
  @spec empty() :: :doc_nil
  def empty, do: :doc_nil

  @doc """
  Concatenates two document entities.

  ## Examples

      iex> doc = Inspect.Algebra.concat "hello", "world"
      iex> Inspect.Algebra.format(doc, 80)
      ["hello", "world"]

  """
  @spec concat(t, t) :: doc_cons
  def concat(x, y) when is_doc(x) and is_doc(y) do
    doc_cons(x, y)
  end

  @doc """
  Concatenates a list of documents.
  """
  @spec concat([t]) :: doc_cons
  def concat(docs) do
    folddoc(docs, &concat(&1, &2))
  end

  @doc ~S"""
  Nests document entity `x` positions deep.

  Nesting will be appended to the line breaks.

  ## Examples

      iex> doc = Inspect.Algebra.nest(Inspect.Algebra.glue("hello", "world"), 5)
      iex> Inspect.Algebra.format(doc, 5)
      ["hello", "\n     ", "world"]

  """
  @spec nest(t, non_neg_integer) :: doc_nest
  def nest(x, 0) when is_doc(x) do
    x
  end

  def nest(x, i) when is_doc(x) and is_integer(i) do
    doc_nest(x, i)
  end

  @doc ~S"""
  Document entity representing a break.

  This break can be rendered as a linebreak or as spaces,
  depending on the `mode` of the chosen layout or the provided
  separator.

  ## Examples

  Let's glue two docs together with a break and then render it:

      iex> doc = Inspect.Algebra.glue("a", " ", "b")
      iex> Inspect.Algebra.format(doc, 80)
      ["a", " ", "b"]

  Notice the break was represented as is, because we haven't reached
  a line limit. Once we do, it is replaced by a newline:

      iex> doc = Inspect.Algebra.glue(String.duplicate("a", 20), " ", "b")
      iex> Inspect.Algebra.format(doc, 10)
      ["aaaaaaaaaaaaaaaaaaaa", "\n", "b"]

  """
  @spec break(binary) :: doc_break
  def break(s) when is_binary(s), do: doc_break(s)

  @spec break() :: doc_break
  def break(), do: doc_break(@break)

  @doc """
  Inserts a break between two docs. See `break/1` for more info.
  """
  @spec glue(t, t) :: doc_cons
  def glue(x, y), do: concat(x, concat(break, y))

  @doc """
  Inserts a break, passed as the second argument, between two docs,
  the first and the third arguments.
  """
  @spec glue(t, binary, t) :: doc_cons
  def glue(x, g, y) when is_binary(g), do: concat(x, concat(break(g), y))

  @doc ~S"""
  Returns a group containing the specified document.

  ## Examples

      iex> doc = Inspect.Algebra.group(
      ...>   Inspect.Algebra.concat(
      ...>     Inspect.Algebra.group(
      ...>       Inspect.Algebra.concat(
      ...>         "Hello,",
      ...>         Inspect.Algebra.concat(
      ...>           Inspect.Algebra.break,
      ...>           "A"
      ...>         )
      ...>       )
      ...>     ),
      ...>     Inspect.Algebra.concat(
      ...>       Inspect.Algebra.break,
      ...>       "B"
      ...>     )
      ...> ))
      iex> Inspect.Algebra.format(doc, 80)
      ["Hello,", " ", "A", " ", "B"]
      iex> Inspect.Algebra.format(doc, 6)
      ["Hello,", "\n", "A", " ", "B"]

  """
  @spec group(t) :: doc_group
  def group(d) when is_doc(d) do
    doc_group(d)
  end

  @doc """
  Inserts a mandatory single space between two document entities.

  ## Examples

      iex> doc = Inspect.Algebra.space "Hughes", "Wadler"
      iex> Inspect.Algebra.format(doc, 80)
      ["Hughes", " ", "Wadler"]

  """
  @spec space(t, t) :: doc_cons
  def space(x, y), do: concat(x, concat(" ", y))

  @doc ~S"""
  Inserts a mandatory linebreak between two document entities.

  ## Examples

      iex> doc = Inspect.Algebra.line "Hughes", "Wadler"
      iex> Inspect.Algebra.format(doc, 80)
      ["Hughes", "\n", "Wadler"]

  """
  @spec line(t, t) :: doc_cons
  def line(x, y), do: concat(x, concat(:doc_line, y))

  @doc """
  Folds a list of document entities into a document entity
  using a function that is passed as the first argument.

  ## Examples

      iex> doc = ["A", "B"]
      iex> doc = Inspect.Algebra.folddoc(doc, fn(x,y) ->
      ...>   Inspect.Algebra.concat [x, "!", y]
      ...> end)
      iex> Inspect.Algebra.format(doc, 80)
      ["A", "!", "B"]

  """
  @spec folddoc([t], ((t, t) -> t)) :: t
  def folddoc([], _), do: empty
  def folddoc([doc], _), do: doc
  def folddoc([d|ds], f), do: f.(d, folddoc(ds, f))

  # Elixir conveniences

  @doc ~S"""
  Surrounds a document with characters.

  Puts the document between left and right enclosing and nesting it.
  The document is marked as a group, to show the maximum as possible
  concisely together.

  ## Examples

      iex> doc = Inspect.Algebra.surround "[", Inspect.Algebra.glue("a", "b"), "]"
      iex> Inspect.Algebra.format(doc, 3)
      ["[", "a", "\n ", "b", "]"]

  """
  @spec surround(binary, t, binary) :: t
  def surround(left, doc, right) do
    group concat left, concat(nest(doc, @nesting), right)
  end

  @doc ~S"""
  Maps and glues a collection of items.

  It uses the given left and right as surrounding and a separator for
  each item. A limit can be passed which, once reached, stops gluing
  and outputs "..." instead.

  ## Examples

      iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]",
      ...>         %Inspect.Opts{limit: :infinity}, fn i, _opts -> to_string(i) end)
      iex> Inspect.Algebra.format(doc, 5) |> IO.iodata_to_binary
      "[1,\n 2,\n 3,\n 4,\n 5]"

      iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]",
      ...>         %Inspect.Opts{limit: 3}, fn i, _opts -> to_string(i) end)
      iex> Inspect.Algebra.format(doc, 20) |> IO.iodata_to_binary
      "[1, 2, 3, ...]"

      iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]",
      ...>         %Inspect.Opts{limit: 3}, fn i, _opts -> to_string(i) end, "!")
      iex> Inspect.Algebra.format(doc, 20) |> IO.iodata_to_binary
      "[1! 2! 3! ...]"
  """
  @spec surround_many(binary, [any], binary, Inspect.Opts.t, (term, Inspect.Opts.t -> t), binary) :: t
  def surround_many(left, docs, right, opts, fun, separator \\ @surround_separator) do
    do_surround_many(left, docs, right, opts.limit, opts, fun, separator)
  end

  defp do_surround_many(left, [], right, _, _opts, _fun, _) do
    concat(left, right)
  end

  defp do_surround_many(left, docs, right, limit, _opts, fun, sep) do
    surround(left, do_surround_many(docs, limit, _opts, fun, sep), right)
  end

  defp do_surround_many(_, 0, _opts, _fun, _sep) do
    "..."
  end

  defp do_surround_many([h], limit, opts, fun, _sep) do
    fun.(h, %{opts | limit: limit})
  end

  defp do_surround_many([h|t], limit, opts, fun, sep) when is_list(t) do
    limit = decrement(limit)
    glue(
      concat(fun.(h, %{opts | limit: limit}), sep),
      do_surround_many(t, limit, opts, fun, sep)
    )
  end

  defp do_surround_many([h|t], limit, opts, fun, _sep) do
    limit = decrement(limit)
    glue(
      concat(fun.(h, %{opts | limit: limit}), @tail_separator),
      fun.(t, %{opts | limit: limit})
    )
  end

  defp decrement(:infinity), do: :infinity
  defp decrement(counter),   do: counter - 1

  @doc """
  The formatting function.

  Takes the maximum width and a document to print as its arguments
  and returns an IO data representation of the best layout for the
  document to fit in the given width.
  """
  @spec format(t, non_neg_integer | :infinity) :: iodata
  def format(d, w) do
    format(w, 0, [{0, default_mode(w), doc_group(d)}])
  end

  defp default_mode(:infinity), do: :flat
  defp default_mode(_),         do: :break

  # Record representing the document mode to be rendered: flat or broken
  @typep mode :: :flat | :break

  @spec fits?(integer, [{integer, mode, t}]) :: boolean
  defp fits?(w, _) when w < 0,                      do: false
  defp fits?(_, []),                                do: true
  defp fits?(_, [{_, _, :doc_line} | _]),           do: true
  defp fits?(w, [{_, _, :doc_nil} | t]),            do: fits?(w, t)
  defp fits?(w, [{i, m, doc_cons(x, y)} | t]),      do: fits?(w, [{i, m, x} | [{i, m, y} | t]])
  defp fits?(w, [{i, m, doc_nest(x, j)} | t]),      do: fits?(w, [{i + j, m, x} | t])
  defp fits?(w, [{i, _, doc_group(x)} | t]),        do: fits?(w, [{i, :flat, x} | t])
  defp fits?(w, [{_, _, s} | t]) when is_binary(s), do: fits?((w - byte_size s), t)
  defp fits?(w, [{_, :flat, doc_break(s)} | t]),    do: fits?((w - byte_size s), t)
  defp fits?(_, [{_, :break, doc_break(_)} | _]),   do: true

  @spec format(integer | :infinity, integer, [{integer, mode, t}]) :: [binary]
  defp format(_, _, []),                                do: []
  defp format(w, _, [{i, _, :doc_line} | t]),           do: [indent(i) | format(w, i, t)]
  defp format(w, k, [{_, _, :doc_nil} | t]),            do: format(w, k, t)
  defp format(w, k, [{i, m, doc_cons(x, y)} | t]),      do: format(w, k, [{i, m, x} | [{i, m, y} | t]])
  defp format(w, k, [{i, m, doc_nest(x, j)} | t]),      do: format(w, k, [{i + j, m, x} | t])
  defp format(w, k, [{i, m, doc_group(x)} | t]),        do: format(w, k, [{i, m, x} | t])
  defp format(w, k, [{_, _, s} | t]) when is_binary(s), do: [s | format(w, (k + byte_size s), t)]
  defp format(w, k, [{_, :flat, doc_break(s)} | t]),    do: [s | format(w, (k + byte_size s), t)]
  defp format(w, k, [{i, :break, doc_break(s)} | t]) do
    k = k + byte_size(s)

    if w == :infinity or fits?(w - k, t) do
      [s | format(w, k, t)]
    else
      [indent(i) | format(w, i, t)]
    end
  end

  defp indent(0), do: @newline
  defp indent(i), do: @newline <> :binary.copy(" ", i)
end

defmodule Inspect.Opts do
  @moduledoc """
  Defines the Inspect.Opts used by the Inspect protocol.

  The following fields are available:

    * `:structs` - when false, structs are not formatted by the inspect
      protocol, they are instead printed as maps, defaults to true.

    * `:binaries` - when `:as_strings` all binaries will be printed as strings,
      non-printable bytes will be escaped.

      When `:as_binaries` all binaries will be printed in bit syntax.

      When the default `:infer`, the binary will be printed as a string if it
      is printable, otherwise in bit syntax.

    * `:char_lists` - when `:as_char_lists` all lists will be printed as char
      lists, non-printable elements will be escaped.

      When `:as_lists` all lists will be printed as lists.

      When the default `:infer`, the list will be printed as a char list if it
      is printable, otherwise as list.

    * `:limit` - limits the number of items that are printed for tuples,
      bitstrings, and lists, does not apply to strings nor char lists, defaults
      to 50.

    * `:pretty` - if set to true enables pretty printing, defaults to false.

    * `:width` - defaults to the 80 characters, used when pretty is true or
      when printing to IO devices.

    * `:base` - print integers as :binary, :octal, :decimal, or :hex, defaults
      to :decimal

  """

  defstruct structs: true,
            binaries: :infer,
            char_lists: :infer,
            limit: 50,
            width: 80,
            base: :decimal,
            pretty: false

  @type t :: %__MODULE__{
               structs: boolean,
               binaries: :infer | :as_binaries | :as_strings,
               char_lists: :infer | :as_lists | :as_char_lists,
               limit: pos_integer,
               width: pos_integer | :infinity,
               base: :decimal | :binary | :hex | :octal,
               pretty: boolean}
end

defmodule Inspect.Algebra do
  @moduledoc ~S"""
  A set of functions for creating and manipulating algebra
  documents, as described in ["Strictly Pretty" (2000) by Christian Lindig][0].

  An algebra document is represented by an `Inspect.Algebra` node
  or a regular string.

      iex> Inspect.Algebra.empty
      :doc_nil

      iex> "foo"
      "foo"

  With the functions in this module, we can concatenate different
  elements together and render them:

      iex> doc = Inspect.Algebra.concat(Inspect.Algebra.empty, "foo")
      iex> Inspect.Algebra.format(doc, 80)
      ["foo"]

  The functions `nest/2`, `space/2` and `line/2` help you put the
  document together into a rigid structure. However, the document
  algebra gets interesting when using functions like `break/2`, which
  converts the given string into a line break depending on how much space
  there is to print. Let's glue two docs together with a break and then
  render it:

      iex> doc = Inspect.Algebra.glue("a", " ", "b")
      iex> Inspect.Algebra.format(doc, 80)
      ["a", " ", "b"]

  Notice the break was represented as is, because we haven't reached
  a line limit. Once we do, it is replaced by a newline:

      iex> doc = Inspect.Algebra.glue(String.duplicate("a", 20), " ", "b")
      iex> Inspect.Algebra.format(doc, 10)
      ["aaaaaaaaaaaaaaaaaaaa", "\n", "b"]

  Finally, this module also contains Elixir related functions, a bit
  tied to Elixir formatting, namely `surround/3` and `surround_many/5`.

  ## Implementation details

  The original Haskell implementation of the algorithm by [Wadler][1]
  relies on lazy evaluation to unfold document groups on two alternatives:
  `:flat` (breaks as spaces) and `:break` (breaks as newlines).
  Implementing the same logic in a strict language such as Elixir leads
  to an exponential growth of possible documents, unless document groups
  are encoded explictly as `:flat` or `:break`. Those groups are then reduced
  to a simple document, where the layout is already decided, per [Lindig][0].

  This implementation slightly changes the semantic of Lindig's algorithm
  to allow elements that belong to the same group to be printed together
  in the same line, even if they do not fit the line fully. This was achieved
  by changing `:break` to mean a possible break and `:flat` to force a flat
  structure. Then deciding if a break works as a newline is just a matter
  of checking if we have enough space until the next break that is not
  inside a group (which is still flat).

  Custom pretty printers can be implemented using the documents returned
  by this module and by providing their own rendering functions.

    [0]: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.34.2200
    [1]: http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf

  """

  @surround_separator ","
  @tail_separator " |"
  @newline "\n"
  @nesting 1
  @break " "

  # Functional interface to `doc` records

  @type t :: :doc_nil | :doc_line | doc_cons | doc_nest | doc_break | doc_group | binary

  @typep doc_cons :: {:doc_cons, t, t}
  defmacrop doc_cons(left, right) do
    quote do: {:doc_cons, unquote(left), unquote(right)}
  end

  @typep doc_nest :: {:doc_nest, t, non_neg_integer}
  defmacrop doc_nest(doc, indent) do
    quote do: {:doc_nest, unquote(doc), unquote(indent) }
  end

  @typep doc_break :: {:doc_break, binary}
  defmacrop doc_break(break) do
    quote do: {:doc_break, unquote(break)}
  end

  @typep doc_group :: {:doc_group, t}
  defmacrop doc_group(group) do
    quote do: {:doc_group, unquote(group)}
  end

  defmacrop is_doc(doc) do
    if Macro.Env.in_guard?(__CALLER__) do
      do_is_doc(doc)
    else
      var = quote do: doc
      quote do
        unquote(var) = unquote(doc)
        unquote(do_is_doc(var))
      end
    end
  end

  defp do_is_doc(doc) do
    quote do
      is_binary(unquote(doc)) or
      unquote(doc) in [:doc_nil, :doc_line] or
      (is_tuple(unquote(doc)) and
       elem(unquote(doc), 0) in [:doc_cons, :doc_nest, :doc_break, :doc_group])
    end
  end

  @doc """
  Converts an Elixir structure to an algebra document
  according to the inspect protocol.
  """
  @spec to_doc(any, Inspect.Opts.t) :: t
  def to_doc(%{__struct__: struct} = map, %Inspect.Opts{} = opts) when is_atom(struct) do
    if opts.structs do
      try do
        Inspect.inspect(map, opts)
      rescue
        e ->
          # Because we try to raise a nice error message in case
          # we can't inspect a struct, there is a chance the error
          # message itself relies on the struct being printed, so
          # we need to trap the inspected messages to guarantee
          # we won't try to render any failed instruct when building
          # the error message.
          if Process.get(:inspect_trap) do
            Inspect.Map.inspect(map, opts)
          else
            try do
              Process.put(:inspect_trap, true)
              res = Inspect.Map.inspect(map, opts)
              formatted = IO.iodata_to_binary(format(res, :infinity))
              raise ArgumentError,
                "Got #{inspect e.__struct__} with message " <>
                "\"#{Exception.message(e)}\" while inspecting #{formatted}"
            after
              Process.delete(:inspect_trap)
            end
          end
      end
    else
      Inspect.Map.inspect(map, opts)
    end
  end

  def to_doc(arg, %Inspect.Opts{} = opts) do
    Inspect.inspect(arg, opts)
  end

  @doc """
  Returns a document entity used to represent nothingness.

  ## Examples

      iex> Inspect.Algebra.empty
      :doc_nil

  """
  @spec empty() :: :doc_nil
  def empty, do: :doc_nil

  @doc """
  Concatenates two document entities.

  ## Examples

      iex> doc = Inspect.Algebra.concat "hello", "world"
      iex> Inspect.Algebra.format(doc, 80)
      ["hello", "world"]

  """
  @spec concat(t, t) :: doc_cons
  def concat(x, y) when is_doc(x) and is_doc(y) do
    doc_cons(x, y)
  end

  @doc """
  Concatenates a list of documents.
  """
  @spec concat([t]) :: doc_cons
  def concat(docs) do
    folddoc(docs, &concat(&1, &2))
  end

  @doc ~S"""
  Nests document entity `x` positions deep.

  Nesting will be appended to the line breaks.

  ## Examples

      iex> doc = Inspect.Algebra.nest(Inspect.Algebra.glue("hello", "world"), 5)
      iex> Inspect.Algebra.format(doc, 5)
      ["hello", "\n     ", "world"]

  """
  @spec nest(t, non_neg_integer) :: doc_nest
  def nest(x, 0) when is_doc(x) do
    x
  end

  def nest(x, i) when is_doc(x) and is_integer(i) do
    doc_nest(x, i)
  end

  @doc ~S"""
  Document entity representing a break.

  This break can be rendered as a linebreak or as spaces,
  depending on the `mode` of the chosen layout or the provided
  separator.

  ## Examples

  Let's glue two docs together with a break and then render it:

      iex> doc = Inspect.Algebra.glue("a", " ", "b")
      iex> Inspect.Algebra.format(doc, 80)
      ["a", " ", "b"]

  Notice the break was represented as is, because we haven't reached
  a line limit. Once we do, it is replaced by a newline:

      iex> doc = Inspect.Algebra.glue(String.duplicate("a", 20), " ", "b")
      iex> Inspect.Algebra.format(doc, 10)
      ["aaaaaaaaaaaaaaaaaaaa", "\n", "b"]

  """
  @spec break(binary) :: doc_break
  def break(s) when is_binary(s), do: doc_break(s)

  @spec break() :: doc_break
  def break(), do: doc_break(@break)

  @doc """
  Inserts a break between two docs. See `break/1` for more info.
  """
  @spec glue(t, t) :: doc_cons
  def glue(x, y), do: concat(x, concat(break, y))

  @doc """
  Inserts a break, passed as the second argument, between two docs,
  the first and the third arguments.
  """
  @spec glue(t, binary, t) :: doc_cons
  def glue(x, g, y) when is_binary(g), do: concat(x, concat(break(g), y))

  @doc ~S"""
  Returns a group containing the specified document.

  ## Examples

      iex> doc = Inspect.Algebra.group(
      ...>   Inspect.Algebra.concat(
      ...>     Inspect.Algebra.group(
      ...>       Inspect.Algebra.concat(
      ...>         "Hello,",
      ...>         Inspect.Algebra.concat(
      ...>           Inspect.Algebra.break,
      ...>           "A"
      ...>         )
      ...>       )
      ...>     ),
      ...>     Inspect.Algebra.concat(
      ...>       Inspect.Algebra.break,
      ...>       "B"
      ...>     )
      ...> ))
      iex> Inspect.Algebra.format(doc, 80)
      ["Hello,", " ", "A", " ", "B"]
      iex> Inspect.Algebra.format(doc, 6)
      ["Hello,", "\n", "A", " ", "B"]

  """
  @spec group(t) :: doc_group
  def group(d) when is_doc(d) do
    doc_group(d)
  end

  @doc """
  Inserts a mandatory single space between two document entities.

  ## Examples

      iex> doc = Inspect.Algebra.space "Hughes", "Wadler"
      iex> Inspect.Algebra.format(doc, 80)
      ["Hughes", " ", "Wadler"]

  """
  @spec space(t, t) :: doc_cons
  def space(x, y), do: concat(x, concat(" ", y))

  @doc ~S"""
  Inserts a mandatory linebreak between two document entities.

  ## Examples

      iex> doc = Inspect.Algebra.line "Hughes", "Wadler"
      iex> Inspect.Algebra.format(doc, 80)
      ["Hughes", "\n", "Wadler"]

  """
  @spec line(t, t) :: doc_cons
  def line(x, y), do: concat(x, concat(:doc_line, y))

  @doc """
  Folds a list of document entities into a document entity
  using a function that is passed as the first argument.

  ## Examples

      iex> doc = ["A", "B"]
      iex> doc = Inspect.Algebra.folddoc(doc, fn(x,y) ->
      ...>   Inspect.Algebra.concat [x, "!", y]
      ...> end)
      iex> Inspect.Algebra.format(doc, 80)
      ["A", "!", "B"]

  """
  @spec folddoc([t], ((t, t) -> t)) :: t
  def folddoc([], _), do: empty
  def folddoc([doc], _), do: doc
  def folddoc([d|ds], f), do: f.(d, folddoc(ds, f))

  # Elixir conveniences

  @doc ~S"""
  Surrounds a document with characters.

  Puts the document between left and right enclosing and nesting it.
  The document is marked as a group, to show the maximum as possible
  concisely together.

  ## Examples

      iex> doc = Inspect.Algebra.surround "[", Inspect.Algebra.glue("a", "b"), "]"
      iex> Inspect.Algebra.format(doc, 3)
      ["[", "a", "\n ", "b", "]"]

  """
  @spec surround(binary, t, binary) :: t
  def surround(left, doc, right) do
    group concat left, concat(nest(doc, @nesting), right)
  end

  @doc ~S"""
  Maps and glues a collection of items.

  It uses the given left and right as surrounding and a separator for
  each item. A limit can be passed which, once reached, stops gluing
  and outputs "..." instead.

  ## Examples

      iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]",
      ...>         %Inspect.Opts{limit: :infinity}, fn i, _opts -> to_string(i) end)
      iex> Inspect.Algebra.format(doc, 5) |> IO.iodata_to_binary
      "[1,\n 2,\n 3,\n 4,\n 5]"

      iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]",
      ...>         %Inspect.Opts{limit: 3}, fn i, _opts -> to_string(i) end)
      iex> Inspect.Algebra.format(doc, 20) |> IO.iodata_to_binary
      "[1, 2, 3, ...]"

      iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]",
      ...>         %Inspect.Opts{limit: 3}, fn i, _opts -> to_string(i) end, "!")
      iex> Inspect.Algebra.format(doc, 20) |> IO.iodata_to_binary
      "[1! 2! 3! ...]"
  """
  @spec surround_many(binary, [any], binary, Inspect.Opts.t, (term, Inspect.Opts.t -> t), binary) :: t
  def surround_many(left, docs, right, opts, fun, separator \\ @surround_separator) do
    do_surround_many(left, docs, right, opts.limit, opts, fun, separator)
  end

  defp do_surround_many(left, [], right, _, _opts, _fun, _) do
    concat(left, right)
  end

  defp do_surround_many(left, docs, right, limit, _opts, fun, sep) do
    surround(left, do_surround_many(docs, limit, _opts, fun, sep), right)
  end

  defp do_surround_many(_, 0, _opts, _fun, _sep) do
    "..."
  end

  defp do_surround_many([h], limit, opts, fun, _sep) do
    fun.(h, %{opts | limit: limit})
  end

  defp do_surround_many([h|t], limit, opts, fun, sep) when is_list(t) do
    limit = decrement(limit)
    glue(
      concat(fun.(h, %{opts | limit: limit}), sep),
      do_surround_many(t, limit, opts, fun, sep)
    )
  end

  defp do_surround_many([h|t], limit, opts, fun, _sep) do
    limit = decrement(limit)
    glue(
      concat(fun.(h, %{opts | limit: limit}), @tail_separator),
      fun.(t, %{opts | limit: limit})
    )
  end

  defp decrement(:infinity), do: :infinity
  defp decrement(counter),   do: counter - 1

  @doc """
  The formatting function.

  Takes the maximum width and a document to print as its arguments
  and returns an IO data representation of the best layout for the
  document to fit in the given width.
  """
  @spec format(t, non_neg_integer | :infinity) :: iodata
  def format(d, w) do
    format(w, 0, [{0, default_mode(w), doc_group(d)}])
  end

  defp default_mode(:infinity), do: :flat
  defp default_mode(_),         do: :break

  # Record representing the document mode to be rendered: flat or broken
  @typep mode :: :flat | :break

  @spec fits?(integer, [{integer, mode, t}]) :: boolean
  defp fits?(w, _) when w < 0,                      do: false
  defp fits?(_, []),                                do: true
  defp fits?(_, [{_, _, :doc_line} | _]),           do: true
  defp fits?(w, [{_, _, :doc_nil} | t]),            do: fits?(w, t)
  defp fits?(w, [{i, m, doc_cons(x, y)} | t]),      do: fits?(w, [{i, m, x} | [{i, m, y} | t]])
  defp fits?(w, [{i, m, doc_nest(x, j)} | t]),      do: fits?(w, [{i + j, m, x} | t])
  defp fits?(w, [{i, _, doc_group(x)} | t]),        do: fits?(w, [{i, :flat, x} | t])
  defp fits?(w, [{_, _, s} | t]) when is_binary(s), do: fits?((w - byte_size s), t)
  defp fits?(w, [{_, :flat, doc_break(s)} | t]),    do: fits?((w - byte_size s), t)
  defp fits?(_, [{_, :break, doc_break(_)} | _]),   do: true

  @spec format(integer | :infinity, integer, [{integer, mode, t}]) :: [binary]
  defp format(_, _, []),                                do: []
  defp format(w, _, [{i, _, :doc_line} | t]),           do: [indent(i) | format(w, i, t)]
  defp format(w, k, [{_, _, :doc_nil} | t]),            do: format(w, k, t)
  defp format(w, k, [{i, m, doc_cons(x, y)} | t]),      do: format(w, k, [{i, m, x} | [{i, m, y} | t]])
  defp format(w, k, [{i, m, doc_nest(x, j)} | t]),      do: format(w, k, [{i + j, m, x} | t])
  defp format(w, k, [{i, m, doc_group(x)} | t]),        do: format(w, k, [{i, m, x} | t])
  defp format(w, k, [{_, _, s} | t]) when is_binary(s), do: [s | format(w, (k + byte_size s), t)]
  defp format(w, k, [{_, :flat, doc_break(s)} | t]),    do: [s | format(w, (k + byte_size s), t)]
  defp format(w, k, [{i, :break, doc_break(s)} | t]) do
    k = k + byte_size(s)

    if w == :infinity or fits?(w - k, t) do
      [s | format(w, k, t)]
    else
      [indent(i) | format(w, i, t)]
    end
  end

  defp indent(0), do: @newline
  defp indent(i), do: @newline <> :binary.copy(" ", i)
end


update travis.exs

update travis.exs

use Mix.Config

# We don't run a server during test. If one is required,
# you can enable the server option below.
config :proa_user, ProaUser.Endpoint,
  http: [port: 4001],
  server: false

# Print only warnings and errors during test
config :logger, level: :warn

# Configure your database
config :proa_user, ProaUser.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "postgres",
  password: "",
  database: "proa_user_test",
  hostname: "localhost",
  pool: Ecto.Adapters.SQL.Sandbox

use Mix.Config

# We don't run a server during test. If one is required,
# you can enable the server option below.
config :phoenix_trello, PhoenixTrello.Endpoint,
  http: [port: 4001],
  server: false

# Print only warnings and errors during test
config :logger, level: :warn

# Configure your database
config :phoenix_trello, PhoenixTrello.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "postgres",
  password: "",
  database: "proa_user_test",
  hostname: "localhost",
  pool: Ecto.Adapters.SQL.Sandbox


Use print_readably correctly for lists

Use print_readably correctly for lists

defmodule Mal.Printer do
  def print_str(mal, print_readably \\ true)
  def print_str(mal, _) when is_atom(mal), do: Atom.to_string(mal)
  def print_str(mal, _) when is_integer(mal), do: Integer.to_string(mal)
  def print_str(mal, _) when is_function(mal), do: "#<function>"
  def print_str({:closure, _function}, _), do: "#<function>"
  def print_str({:symbol, value}, _), do: value
  def print_str(mal, false) when is_bitstring(mal), do: mal
  def print_str(mal, true) when is_bitstring(mal) do
    Inspect.Algebra.to_doc(mal, %Inspect.Opts{})
  end

  def print_str(mal, print_readably) when is_list(mal) do
    output = mal
      |> Enum.map(fn(x) -> print_str(x, print_readably) end)
      |> Enum.join(" ")

    "(#{output})"
  end
end

defmodule Mal.Printer do
  def print_str(mal, print_readably \\ true)
  def print_str(mal, _) when is_atom(mal), do: Atom.to_string(mal)
  def print_str(mal, _) when is_integer(mal), do: Integer.to_string(mal)
  def print_str(mal, _) when is_function(mal), do: "#<function>"
  def print_str({:closure, _function}, _), do: "#<function>"
  def print_str({:symbol, value}, _), do: value
  def print_str(mal, false) when is_bitstring(mal), do: mal
  def print_str(mal, true) when is_bitstring(mal) do
    Inspect.Algebra.to_doc(mal, %Inspect.Opts{})
  end

  def print_str(mal, _print_readably) when is_list(mal) do
    output = mal
      |> Enum.map(fn(x) -> print_str(x) end)
      |> Enum.join(" ")

    "(#{output})"
  end
end


Fix false positive for SpaceAroundOperators

Fixes a problem where negative numbers where not recognized.

Fixes #52

defmodule Credo.Check.Consistency.SpaceAroundOperators do
  @moduledoc """
  Don't use spaces after operators like `+`, `-`, `*` and `/`. This is the
  **preferred** way, although other styles are possible, as long as it is
  applied consistently.

      # preferred way
      1 + 2 * 4

      # also okay
      1+2*4

  While this is not necessarily a concern for the correctness of your code,
  you should use a consistent style throughout your codebase.
  """

  @explanation [check: @moduledoc]
  @code_patterns [
    Credo.Check.Consistency.SpaceAroundOperators.WithSpace,
    Credo.Check.Consistency.SpaceAroundOperators.WithoutSpace
  ]
  @default_params [ignore: [:|]]

  alias Credo.Check.Consistency.Helper
  alias Credo.Check.PropertyValue

  use Credo.Check, run_on_all: true, base_priority: :high

  # TODO: add *ignored* operators, so you can add "|" and still write
  #       [head|tail] while enforcing 2 + 3 / 1 ...

  @doc false
  def run(source_files, params \\ []) when is_list(source_files) do
    source_files
    |> Helper.run_code_patterns(@code_patterns, params)
    |> Helper.add_issues_to_source_files(&issue_for/5, params)
  end

  defp issue_for(_issue_meta, _actual_props, nil, _picked_count, _total_count), do: nil
  defp issue_for(_issue_meta, [], _expected_prop, _picked_count, _total_count), do: nil
  defp issue_for(issue_meta, actual_prop, expected_prop, _picked_count, _total_count) do
    line_no = PropertyValue.meta(actual_prop, :line_no)
    column = PropertyValue.meta(actual_prop, :column)
    trigger = PropertyValue.meta(actual_prop, :trigger)
    actual_prop = PropertyValue.get(actual_prop)

    line = issue_meta |> IssueMeta.source_file() |> SourceFile.line_at(line_no)
    params = issue_meta |> IssueMeta.params()
    ignored_triggers = params |> Params.get(:ignore, @default_params)

    if !Enum.member?(ignored_triggers, trigger) && create_issue?(line, column, trigger) do
      format_issue issue_meta,
        message: message_for(actual_prop, expected_prop),
        line_no: line_no,
        column: column,
        trigger: trigger
    end
  end

  defp message_for(:with_space, :without_space) do
    "There are no spaces around operators most of the time, but here there are."
  end
  defp message_for(:without_space, :with_space) do
    "There are spaces around operators most of the time, but not here."
  end

  # Don't create issues for `&Mod.fun/4`
  defp create_issue?(line, column, :/) do
    ~r/\&[a-zA-Z0-9\.\_\?\!]+\/\d+/     # pattern to detect &Mod.fun/4
    |> Regex.run(line, return: :index)
    |> List.wrap
    |> Enum.any?(fn({start_index, end_index}) ->
        start_index < column && end_index > column
      end)
  end
  # Don't create issues for `c = -1`
  defp create_issue?(line, column, operator) when operator in [:+, :-] do
    !number_with_sign?(line, column) &&
      !number_in_range?(line, column) &&
      !(operator == :- && minus_in_binary_size?(line, column))
  end
  defp create_issue?(_, _, _), do: true

  defp number_with_sign?(line, column) do
    line
    |> String.slice(0..column-2) # -2 because we need to substract the operator
    |> String.match?(~r/(\@[a-zA-Z0-9\_]+|[\{\[\(\,\:\=\+\-\*\/])\s*$/)
  end

  defp number_in_range?(line, column) do
    line
    |> String.slice(column..-1)
    |> String.match?(~r/^\d+\.\./)
  end

  # TODO: this implementation is a bit naive. improve it.
  defp minus_in_binary_size?(line, column) do
    binary_pattern_start_before? =
      line
      |> String.slice(0..column-2) # -2 because we need to substract the operator
      |> String.match?(~r/\<\</)
    binary_pattern_end_after? =
      line
      |> String.slice(column..-1) # -2 because we need to substract the operator
      |> String.match?(~r/\>\>/)

    binary_pattern_start_before? && binary_pattern_end_after?
  end
end

defmodule Credo.Check.Consistency.SpaceAroundOperators do
  @moduledoc """
  Don't use spaces after operators like `+`, `-`, `*` and `/`. This is the
  **preferred** way, although other styles are possible, as long as it is
  applied consistently.

      # preferred way
      1 + 2 * 4

      # also okay
      1+2*4

  While this is not necessarily a concern for the correctness of your code,
  you should use a consistent style throughout your codebase.
  """

  @explanation [check: @moduledoc]
  @code_patterns [
    Credo.Check.Consistency.SpaceAroundOperators.WithSpace,
    Credo.Check.Consistency.SpaceAroundOperators.WithoutSpace
  ]
  @default_params [ignore: [:|]]

  alias Credo.Check.Consistency.Helper
  alias Credo.Check.PropertyValue

  use Credo.Check, run_on_all: true, base_priority: :high

  # TODO: add *ignored* operators, so you can add "|" and still write
  #       [head|tail] while enforcing 2 + 3 / 1 ...

  @doc false
  def run(source_files, params \\ []) when is_list(source_files) do
    source_files
    |> Helper.run_code_patterns(@code_patterns, params)
    |> Helper.add_issues_to_source_files(&issue_for/5, params)
  end

  defp issue_for(_issue_meta, _actual_props, nil, _picked_count, _total_count), do: nil
  defp issue_for(_issue_meta, [], _expected_prop, _picked_count, _total_count), do: nil
  defp issue_for(issue_meta, actual_prop, expected_prop, _picked_count, _total_count) do
    line_no = PropertyValue.meta(actual_prop, :line_no)
    column = PropertyValue.meta(actual_prop, :column)
    trigger = PropertyValue.meta(actual_prop, :trigger)
    actual_prop = PropertyValue.get(actual_prop)

    line = issue_meta |> IssueMeta.source_file() |> SourceFile.line_at(line_no)
    params = issue_meta |> IssueMeta.params()
    ignored_triggers = params |> Params.get(:ignore, @default_params)

    if !Enum.member?(ignored_triggers, trigger) && create_issue?(line, column, trigger) do
      format_issue issue_meta,
        message: message_for(actual_prop, expected_prop),
        line_no: line_no,
        column: column,
        trigger: trigger
    end
  end

  defp message_for(:with_space, :without_space) do
    "There are no spaces around operators most of the time, but here there are."
  end
  defp message_for(:without_space, :with_space) do
    "There are spaces around operators most of the time, but not here."
  end

  # Don't create issues for `&Mod.fun/4`
  defp create_issue?(line, column, :/) do
    ~r/\&[a-zA-Z0-9\.\_\?\!]+\/\d+/     # pattern to detect &Mod.fun/4
    |> Regex.run(line, return: :index)
    |> List.wrap
    |> Enum.any?(fn({start_index, end_index}) ->
        start_index < column && end_index > column
      end)
  end
  # Don't create issues for `c = -1`
  defp create_issue?(line, column, operator) when operator in [:+, :-] do
    !number_with_sign?(line, column) &&
      !number_in_range?(line, column) &&
      !(operator == :- && minus_in_binary_size?(line, column))
  end
  defp create_issue?(_, _, _), do: true

  defp number_with_sign?(line, column) do
    line
    |> String.slice(0..column-2) # -2 because we need to substract the operator
    |> String.match?(~r/(\@[a-zA-Z0-9\_]+|[\{\[\(\,\:\=])\s*$/)
  end

  defp number_in_range?(line, column) do
    line
    |> String.slice(column..-1)
    |> String.match?(~r/^\d+\.\./)
  end

  # TODO: this implementation is a bit naive. improve it.
  defp minus_in_binary_size?(line, column) do
    binary_pattern_start_before? =
      line
      |> String.slice(0..column-2) # -2 because we need to substract the operator
      |> String.match?(~r/\<\</)
    binary_pattern_end_after? =
      line
      |> String.slice(column..-1) # -2 because we need to substract the operator
      |> String.match?(~r/\>\>/)

    binary_pattern_start_before? && binary_pattern_end_after?
  end
end