View Source Rewrite.Source (rewrite v0.1.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.

Same as debug_info/1 but raises on error.

Returns the debug info for the give source and module.

Marks the given source as deleted.

Returns iodata showing all diffs of the given source.

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 current path for the given source.

Returns the path of a source for the given version.

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.

Returns a Sourceror.Zipper with the AST for the given %Source.

Link to this section Types

@type by() :: module()
@type from() :: :file | :ast | :string
@type id() :: String.t()
@type kind() :: :code | :path
@type t() :: %Rewrite.Source{
  ast: Macro.t(),
  code: String.t(),
  from: from(),
  hash: String.t(),
  id: id(),
  issues: term(),
  modules: [module()],
  owner: module(),
  path: Path.t() | nil,
  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(), Rewrite.Issue.t()) :: t()

Adds the given issue to the source.

Link to this function

add_issues(source, issues)

View Source
@spec add_issues(t(), [Rewrite.Issue.t()]) :: 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
Link to this function

debug_info!(source, module)

View Source
@spec debug_info!(t(), module()) :: term()

Same as debug_info/1 but raises on error.

Link to this function

debug_info(source, module)

View Source
@spec debug_info(t(), module()) :: {:ok, term()} | {:error, term()}

Returns the debug info for the give source and module.

Uses the current code of the source.

examples

Examples

iex> bar =
...>   """
...>   defmodule Bar do
...>      def bar, do: :bar
...>   end
...>   """
iex> {:ok, dbg} = bar |> Source.from_string() |> Source.debug_info(Bar)
iex> dbg.module
Bar
iex> dbg.relative_file
"nofile"
@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.

exampels

Exampels

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_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> alias Rewrite.Issue
iex> source =
...>   "a + b"
...>   |> Source.from_string("some/where/plus.exs")
...>   |> Source.add_issue(Issue.new(:test, "no comment", line: 1))
...>   |> Source.update(:example, path: "some/where/else/plus.exs")
...>   |> Source.add_issue(Issue.new(:test, "no comment", line: 1))
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 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"
@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"}
Link to this function

update(source, by, list)

View Source
@spec update(
  t(),
  by(),
  [{:code, String.t() | Sourceror.Zipper.zipper()}] | [{: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\n"

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.

@spec zipper(t()) :: {:ok, Sourceror.Zipper.zipper()} | {:error, term()}

Returns a Sourceror.Zipper with the AST for the given %Source.