View Source Rewrite.Source (rewrite v0.6.0)

A representation of some source in a project.

The %Source{} contains the code of the file given by path. The module contains Source.update/3 to update the path and/or the code. The changes are recorded in the updates list.

The struct also holds issues for the source.

Link to this section Summary

Types

t()

The version of a %Source{}. The version 1 indicates that the source has no changes.

Functions

Adds the given issue to the source.

Adds the given issues to the source.

Returns the AST for the given %Source.

Returns the current code for the given source.

Returns the code of a source for the given version.

Compares the path values of the given sources.

Returns true if the %Source{} was created.

Marks the given source as deleted.

Returns iodata showing all diffs of the given source.

Creates a new %Source{} from the given ast.

Creates a new %Source{} from the given string.

Returns true if the source has issues for the given version.

Returns the current modules for the given source.

Returns the modules of a source for the given version.

Returns the owner of the given source.

Returns the current path for the given source.

Returns the path of a source for the given version.

Assigns a private key and value to the source.

Creates a new %Source{} from the given path.

Saves the source to disk.

Updates the code or the path of a source.

Returns true if the source was updated.

Returns the version of the given source. The value 1 indicates that the source has no changes.

Link to this section Types

@type by() :: module()
@type from() :: :file | :ast | :string
@type id() :: String.t()
@type issue() :: term()
@type kind() :: :code | :path
@type t() :: %Rewrite.Source{
  ast: Macro.t(),
  code: String.t(),
  from: from(),
  hash: String.t(),
  id: id(),
  issues: [issue()],
  modules: [module()],
  owner: module(),
  path: Path.t() | nil,
  private: map(),
  updates: [{kind(), by(), String.t()}]
}
@type version() :: pos_integer()

The version of a %Source{}. The version 1 indicates that the source has no changes.

Link to this section Functions

Link to this function

add_issue(source, issue)

View Source
@spec add_issue(t(), issue()) :: t()

Adds the given issue to the source.

Link to this function

add_issues(source, issues)

View Source
@spec add_issues(t(), [issue()]) :: t()

Adds the given issues to the source.

@spec ast(t()) :: {:ok, Macro.t()} | {:error, term()}

Returns the AST for the given %Source.

The returned extended AST is generated with Sourceror.parse_string/1.

Uses the current code of the source.

examples

Examples

iex> "def foo, do: :foo" |> Source.from_string() |> Source.ast()
{:def, [trailing_comments: [], leading_comments: [], line: 1, column: 1],
  [
    {:foo, [trailing_comments: [], leading_comments: [], line: 1, column: 5], nil},
    [
      {{:__block__,
        [trailing_comments: [], leading_comments: [], format: :keyword, line: 1, column: 10],
        [:do]},
       {:__block__, [trailing_comments: [], leading_comments: [], line: 1, column: 14], [:foo]}}
    ]
  ]
}
@spec code(t()) :: String.t()

Returns the current code for the given source.

@spec code(t(), version()) :: String.t()

Returns the code of a source for the given version.

examples

Examples

iex> bar =
...>   """
...>   defmodule Bar do
...>      def bar, do: :bar
...>   end
...>   """
iex> foo =
...>   """
...>   defmodule Foo do
...>      def foo, do: :foo
...>   end
...>   """
iex> source = Source.from_string(bar)
iex> source = Source.update(source, :example, code: foo)
iex> Source.code(source) == foo
true
iex> Source.code(source, 2) == foo
true
iex> Source.code(source, 1) == bar
true
Link to this function

compare(source1, source2)

View Source
@spec compare(t(), t()) :: :lt | :eq | :gt

Compares the path values of the given sources.

examples

Examples

iex> a = Source.from_string(":foo", "a.exs")
iex> Source.compare(a, a)
:eq
iex> b = Source.from_string(":foo", "b.exs")
iex> Source.compare(a, b)
:lt
iex> Source.compare(b, a)
:gt
@spec created?(t()) :: boolean()

Returns true if the %Source{} was created.

Created means here that a new file is written when saving.

examples

Examples

iex> source = Source.read!("test/fixtures/source/simple.ex")
...> Source.created?(source)
false

iex> source = Source.from_string(":foo")
...> Source.created?(source)
true

iex> source = Source.from_string(":foo", "test/fixtures/new.ex", Test)
...> Source.created?(source)
true
@spec del(t(), nil | module()) :: t()

Marks the given source as deleted.

This function set the path of the given source to nil.

Link to this function

diff(source, opts \\ [])

View Source
@spec diff(
  t(),
  keyword()
) :: iodata()

Returns iodata showing all diffs of the given source.

See Rewrite.TextDiff.format/3 for options.

examples

Examples

iex> code = """
...> def foo( x ) do
...>   {:x,
...>     x}
...> end
...> """
iex> formatted = code |> Code.format_string!() |> IO.iodata_to_binary()
iex> source = Source.from_string(code)
iex> source |> Source.diff() |> IO.iodata_to_binary()
""
iex> source
...> |> Source.update(Test, code: formatted)
...> |> Source.diff(color: false)
...> |> IO.iodata_to_binary()
"""
1   - |def foo( x ) do
2   - |  {:x,
3   - |    x}
  1 + |def foo(x) do
  2 + |  {:x, x}
4 3   |end
5 4   |
"""
Link to this function

from_ast(ast, path \\ nil, owner \\ Rewrite)

View Source
@spec from_ast(Macro.t(), nil | Path.t(), module()) :: t()

Creates a new %Source{} from the given ast.

examples

Examples

iex> ast = Sourceror.parse_string!("a + b")
iex> source = Source.from_ast(ast)
iex> source.modules
[]
iex> source.code
"a + b"
Link to this function

from_string(string, path \\ nil, owner \\ Rewrite)

View Source
@spec from_string(String.t(), nil | Path.t(), module()) :: t()

Creates a new %Source{} from the given string.

examples

Examples

iex> source = Source.from_string("a + b")
iex> source.modules
[]
iex> source.code
"a + b"
Link to this function

has_issues?(source, version \\ :actual)

View Source
@spec has_issues?(t(), version() | :actual | :all) :: boolean()

Returns true if the source has issues for the given version.

The version argument also accepts :actual and :all to check whether the source has problems for the actual version or if there are problems at all.

examples

Examples

iex> source =
...>   "a + b"
...>   |> Source.from_string("some/where/plus.exs")
...>   |> Source.add_issue(%{issue: :foo})
...>   |> Source.update(:example, path: "some/where/else/plus.exs")
...>   |> Source.add_issue(%{issue: :bar})
iex> Source.has_issues?(source)
true
iex> Source.has_issues?(source, 1)
true
iex> Source.has_issues?(source, :all)
true
iex> source = Source.update(source, :example, code: "a - b")
iex> Source.has_issues?(source)
false
iex> Source.has_issues?(source, 2)
true
iex> Source.has_issues?(source, :all)
true
@spec modules(t()) :: [module()]

Returns the current modules for the given source.

Link to this function

modules(source, version)

View Source
@spec modules(t(), version()) :: [module()]

Returns the modules of a source for the given version.

examples

Examples

iex> bar =
...>   """
...>   defmodule Bar do
...>      def bar, do: :bar
...>   end
...>   """
iex> foo =
...>   """
...>   defmodule Foo do
...>      def foo, do: :foo
...>   end
...>   """
iex> source = Source.from_string(bar)
iex> source = Source.update(source, :example, code: bar <> foo)
iex> Source.modules(source)
[Foo, Bar]
iex> Source.modules(source, 2)
[Foo, Bar]
iex> Source.modules(source, 1)
[Bar]
@spec owner(t()) :: module()

Returns the owner of the given source.

@spec path(t()) :: Path.t() | nil

Returns the current path for the given source.

@spec path(t(), version()) :: Path.t() | nil

Returns the path of a source for the given version.

examples

Examples

iex> source =
...>   "a + b"
...>   |> Source.from_string("some/where/plus.exs")
...>   |> Source.update(:example, path: "some/where/else/plus.exs")
...> Source.path(source, 1)
"some/where/plus.exs"
iex> Source.path(source, 2)
"some/where/else/plus.exs"
Link to this function

put_private(source, key, value)

View Source
@spec put_private(t(), key :: term(), value :: term()) :: t()

Assigns a private key and value to the source.

This is not used or accessed by Rewrite, but is intended as private storage for users or libraries that wish to store additional data about a source.

examples

Examples

iex> source =
...>   "a + b"
...>   |> Source.from_string()
...>   |> Source.put_private(:origin, :example)
iex> source.private[:origin]
:example
@spec read!(Path.t()) :: t()

Creates a new %Source{} from the given path.

examples

Examples

iex> source = Source.read!("test/fixtures/source/simple.ex")
iex> source.modules
[MyApp.Simple]
iex> source.code
"""
defmodule MyApp.Simple do
  def foo(x) do
    x * 2
  end
end
"""
@spec save(t()) :: :ok | {:error, :nofile | File.posix()}

Saves the source to disk.

If the source :path was updated then the old file will be deleted. The original file will also deleted when the source was marked as deleted with del/1.

Missing directories are created.

examples

Examples

iex> ":test" |> Source.from_string() |> Source.save()
{:error, :nofile}

iex> path = "tmp/foo.ex"
iex> File.write(path, ":foo")
iex> source = path |> Source.read!() |> Source.update(:test, code: ":bar")
iex> Source.save(source)
:ok
iex> File.read(path)
{:ok, ":bar\n"}
iex> source |> Source.del() |> Source.save()
iex> File.exists?(path)
false

iex> source = Source.from_string(":bar")
iex> Source.save(source)
{:error, :nofile}
iex> source |> Source.update(:test, path: "tmp/bar.ex") |> Source.save()
:ok

iex> path = "tmp/ping.ex"
iex> File.write(path, ":ping")
iex> source = path |> Source.read!()
iex> new_path = "tmp/pong.ex"
iex> source |> Source.update(:test, path: new_path) |> Source.save()
:ok
iex> File.exists?(path)
false
iex> File.read(new_path)
{:ok, ":ping\n"}
Link to this function

update(source, by, list)

View Source
@spec update(
  t(),
  by(),
  [{:code, String.t()}] | [{:ast, Macro.t()}] | [{:path, Path.t()}]
) :: t()

Updates the code or the path of a source.

examples

Examples

iex> source =
...>   "a + b"
...>   |> Source.from_string()
...>   |> Source.update(:example, path: "test/fixtures/new.exs")
...>   |> Source.update(:example, code: "a - b")
iex> source.updates
[{:code, :example, "a + b"}, {:path, :example, nil}]
iex> source.code
"a - b"

If the new value equal to the current value, no updates will be added.

iex> source =
...>   "a = 42"
...>   |> Source.from_string()
...>   |> Source.update(:example, code: "b = 21")
...>   |> Source.update(:example, code: "b = 21")
...>   |> Source.update(:example, code: "b = 21")
iex> source.updates
[{:code, :example, "a = 42"}]
Link to this function

updated?(source, kind \\ :any)

View Source
@spec updated?(t(), kind :: :code | :path | :any) :: boolean()

Returns true if the source was updated.

The optional argument kind specifies whether only :code changes or :path changes are considered. Defaults to :any.

examples

Examples

iex> source = Source.from_string("a = 42")
iex> Source.updated?(source)
false
iex> source = Source.update(source, :example, code: "b = 21")
iex> Source.updated?(source)
true
iex> Source.updated?(source, :path)
false
iex> Source.updated?(source, :code)
true
@spec version(t()) :: version()

Returns the version of the given source. The value 1 indicates that the source has no changes.