View Source Rewrite.Source (rewrite v1.1.2)
A representation of some source in a project.
The %Source{} contains the content of the file given by path. The module
contains update/3 to update the path and/or the content. The changes are
recorded in the history list.
The struct also holds issues for the source.
The different versions of content and path are available via get/3.
A source is extensible via filetype, see Rewrite.Filetype.
Summary
Types
The struct representing a source.
A timestamp as integer seconds since epoch.
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.
The default path for the source.
Returns iodata showing all diffs of the given source.
Returns true if the file has been modified since it was read.
Sets the filetype for the source.
Formats the given source.
Same as format/2, but raises an exception in case of failure.
Returns true when from matches to value for key :from.
Creates a new %Source{} from the given string.
Gets the value for :content, :path in source or a specific key in
filetype.
Gets the value for :content, :path in source or a specific key in
filetype for the given version.
Returns true if the source has issues for the given version.
Calculates the current hash from the given source.
Returns all issues of the given source.
Returns the owner of the given source.
Assigns a private key and value to the source.
Creates a new %Source{} from the given path.
Tries to delete the file source.
Same as rm/1, but raises a Rewrite.SourceError exception in case of
failure. Otherwise :ok.
Sets the timestamp to the current POSIX timestamp.
Sets the timestamp of the given source to the given timestamp.
Undoes the given number of changes.
Updates the content 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.
Writes the source to disk.
Same as write/1, but raises a Rewrite.SourceError exception in case of
failure.
Types
@type by() :: module()
@type content() :: String.t()
@type extension() :: String.t()
@type filetype() :: map()
@type from() :: :file | :string
@type issue() :: term()
@type key() :: atom()
@type kind() :: :content | :path
@type opts() :: keyword()
@type owner() :: module()
@type t() :: %Rewrite.Source{ content: String.t(), filetype: filetype(), from: from(), hash: String.t(), history: [{kind(), by(), String.t()}], issues: [{version(), issue()}], owner: owner(), path: Path.t() | nil, private: map(), timestamp: timestamp() }
The struct representing a source.
Fields
content- of thesource.filetype- astructimplementing the behaviourRewrite.Filetype. Thefiletypeis nil when no additional implementation for thefiletypeis available.from- contains:fileor:stringdepending on whether thesourceis created from a file or a string.hash- of thesource. Thehashis built from thecontentandpath.history- of thesource.issues- of thesource.owner- of thesource.path- of thesource. Can benilif thesourcewas created by astring.private- a field for user defined data.timestamp- is set to the timestamp of the last modification of the file on disk at the time it was read.If the
sourcewas created by astring, the timestamp is the creation time.The timestamp will be updated when the
sourceis updated.
@type timestamp() :: integer()
A timestamp as integer seconds since epoch.
@type value() :: term()
@type version() :: pos_integer()
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.
The default path for the source.
Returns iodata showing all diffs of the given source.
See TextDiff.format/3 for options.
Examples
iex> code = """
...> def foo( x ) do
...> {:x,
...> x}
...> end
...> """
iex> formatted = code |> Code.format_string!() |> IO.iodata_to_binary()
iex> source = Source.Ex.from_string(code)
iex> source |> Source.diff() |> IO.iodata_to_binary()
""
iex> source
...> |> Source.update(:content, 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 |
"""
Returns true if the file has been modified since it was read.
If the key :from does not contain :file the function returns false.
Examples
iex> File.write("tmp/hello.txt", "hello")
iex> source = Source.read!("tmp/hello.txt")
iex> Source.file_changed?(source)
false
iex> File.write("tmp/hello.txt", "Hello, world!")
iex> Source.file_changed?(source)
true
iex> source = Source.update(source, :path, nil)
iex> Source.file_changed?(source)
true
iex> File.write("tmp/hello.txt", "hello")
iex> Source.file_changed?(source)
false
iex> File.rm!("tmp/hello.txt")
iex> Source.file_changed?(source)
true
iex> source = Source.from_string("hello")
iex> Source.file_changed?(source)
false
Sets the filetype for the source.
Formats the given source.
If the source was formatted the source gets a new :history entry,
otherwise the unchanged source is returned.
Options
by- anatomormodulethat is used as:bykey when thesourceis updated. Defaults toRewrite.dot_formatter- defaults toRewrite.DotFormatter.default/0.Accepts also the same options as
Code.format_string!/2.
Examples
iex> source = Source.Ex.from_string("""
...> defmodule Foo do
...> def foo(x), do: bar x
...> end
...> """)
iex> {:ok, formatted} = Source.format(source, force_do_end_blocks: true)
iex> formatted.content
"""
defmodule Foo do
def foo(x) do
bar(x)
end
end
"""
iex> dot_formatter = DotFormatter.from_formatter_opts(locals_without_parens: [bar: 1])
iex> {:ok, formatted} = Source.format(source,
...> dot_formatter: dot_formatter, force_do_end_blocks: true
...> )
iex> formatted.content
"""
defmodule Foo do
def foo(x) do
bar x
end
end
"""
Same as format/2, but raises an exception in case of failure.
Returns true when from matches to value for key :from.
Examples
iex> source = Source.from_string("hello")
iex> Source.from?(source, :file)
false
iex> Source.from?(source, :string)
true
Creates a new %Source{} from the given string.
Options
:owner- an association to the module that owns thesource.:dot_formatter- a fromatter for thesource.path- the path of thesource.
Examples
iex> source = Source.from_string("hello")
iex> source.content
"hello"
iex> source.path
nil
iex> source.owner
Rewrite
iex> source = Source.from_string("hello", path: "hello.md", owner: MyApp)
iex> source.path
"hello.md"
iex> source.owner
MyApp
Gets the value for :content, :path in source or a specific key in
filetype.
Raises Rewrite.SourceKeyError if the key can't be found.
Gets the value for :content, :path in source or a specific key in
filetype for the given version.
Raises Rewrite.SourceKeyError if the key can't be found.
Examples
iex> bar =
...> """
...> defmodule Bar do
...> def bar, do: :bar
...> end
...> """
iex> foo =
...> """
...> defmodule Foo do
...> def foo, do: :foo
...> end
...> """
iex> source = Source.Ex.from_string(bar)
iex> source = Source.update(source, :content, foo)
iex> Source.get(source, :content) == foo
true
iex> Source.get(source, :content, 2) == foo
true
iex> Source.get(source, :content, 1) == bar
true
iex> source =
...> "hello"
...> |> Source.from_string(path: "some/where/hello.txt")
...> |> Source.update(:path, "some/where/else/hello.txt")
...> Source.get(source, :path, 1)
"some/where/hello.txt"
iex> Source.get(source, :path, 2)
"some/where/else/hello.txt"
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
iex> source =
...> "a + b"
...> |> Source.Ex.from_string(path: "some/where/plus.exs")
...> |> Source.add_issue(%{issue: :foo})
...> |> Source.update(: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, :content, "a - b")
iex> Source.has_issues?(source)
false
iex> Source.has_issues?(source, 2)
true
iex> Source.has_issues?(source, :all)
true
@spec hash(t()) :: non_neg_integer()
Calculates the current hash from the given source.
Returns all issues of the given source.
Returns the owner of the given source.
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
iex> source =
...> "a + b"
...> |> Source.from_string()
...> |> Source.put_private(:origin, :example)
iex> source.private[:origin]
:example
Creates a new %Source{} from the given path.
Examples
iex> source = Source.read!("test/fixtures/source/hello.txt")
iex> source.content
"""
hello
"""
@spec rm(t()) :: :ok | {:error, Rewrite.SourceError.t()}
Tries to delete the file source.
Returns :ok if successful, or {:error, reason} if an error occurs.
Note the file is deleted even if in read-only mode.
@spec rm!(t()) :: :ok
Same as rm/1, but raises a Rewrite.SourceError exception in case of
failure. Otherwise :ok.
Sets the timestamp to the current POSIX timestamp.
Does not touch the underlying file.
Sets the timestamp of the given source to the given timestamp.
Does not touch the underlying file.
@spec undo(t(), non_neg_integer()) :: t()
Undoes the given number of changes.
Examples
iex> a = Source.from_string("test-a", path: "test/foo.txt")
iex> b = Source.update(a, :content, "test-b")
iex> c = Source.update(b, :path, "test/bar.txt")
iex> d = Source.update(c, :content, "test-d")
iex> d |> Source.undo() |> Source.get(:content)
"test-b"
iex> d |> Source.undo(1) |> Source.get(:content)
"test-b"
iex> d |> Source.undo(2) |> Source.get(:path)
"test/foo.txt"
iex> d |> Source.undo(3) |> Source.get(:content)
"test-a"
iex> d |> Source.undo(9) |> Source.get(:content)
"test-a"
iex> d |> Source.undo(9) |> Source.updated?()
false
iex> d |> Source.undo(-9) |> Source.get(:content)
"test-d"
Updates the content or the path of a source.
The given value can be of type value/0 or an updater function that gets
the current value and returns the new value.
Examples
iex> source =
...> "foo"
...> |> Source.from_string()
...> |> Source.update(:path, "test/fixtures/new.exs", by: Example)
...> |> Source.update(:content, "bar")
iex> source.history
[{:content, Rewrite, "foo"}, {:path, Example, nil}]
iex> source.content
"bar"
iex> source =
...> "foo"
...> |> Source.from_string()
...> |> Source.update(:content, fn content -> content <> "bar" end)
iex> source.content
"foobar"With a Rewrite.Source.Ex. Note that the AST is generated by Sourceror.
iex> source =
...> ":a"
...> |> Source.Ex.from_string()
...> |> Source.update(:quoted, fn quoted ->
...> {:__block__, meta, [atom]} = quoted
...> {:__block__, meta, [{:ok, atom}]}
...> end)
iex> source.content
"{:ok, :a}\n"If the new value is equal to the current value, no history will be added.
iex> source =
...> "42"
...> |> Source.from_string()
...> |> Source.update(:content, "21", by: Example)
...> |> Source.update(:content, "21", by: Example)
iex> source.history
[{:content, Example, "42"}]
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
iex> source = Source.from_string("foo")
iex> Source.updated?(source)
false
iex> source = Source.update(source, :content, "bar")
iex> Source.updated?(source)
true
iex> Source.updated?(source, :path)
false
iex> Source.updated?(source, :content)
true
Returns the version of the given source. The value 1 indicates that the
source has no changes.
@spec write(t(), opts()) :: {:ok, t()} | {:error, Rewrite.SourceError.t()}
Writes the source to disk.
Returns {:ok, source} when the file was written successfully. The returned
source does not include any previous changes or issues.
If there's an error, this function returns {:error, error} where error
is a Rewrite.SourceError. You can raise it manually with raise/1.
Returns {:error, error} with reason: :nopath if the current path is nil.
Returns {:error, error} with reason: :changed if the file was changed
since reading. See also file_changed?/1. The option force: true forces
overwriting a changed file.
If the source :path was updated then the old file will be deleted.
Missing directories are created.
Options
:force, default:false- forces the saving to overwrite changed files.:rm, default:true- prevents file deletion when set tofalse.
Examples
iex> ":test" |> Source.from_string() |> Source.write()
{:error, %SourceError{reason: :nopath, path: nil, action: :write}}
iex> path = "tmp/foo.txt"
iex> File.write(path, "foo")
iex> source = path |> Source.read!() |> Source.update(:content, "bar")
iex> Source.updated?(source)
true
iex> {:ok, source} = Source.write(source)
iex> File.read(path)
{:ok, "bar\n"}
iex> Source.updated?(source)
false
iex> source = Source.from_string("bar")
iex> Source.write(source)
{:error, %SourceError{reason: :nopath, path: nil, action: :write}}
iex> source |> Source.update(:path, "tmp/bar.txt") |> Source.write()
iex> File.read("tmp/bar.txt")
{:ok, "bar\n"}
iex> path = "tmp/ping.txt"
iex> File.write(path, "ping")
iex> source = Source.read!(path)
iex> new_path = "tmp/pong.ex"
iex> source = Source.update(source, :path, new_path)
iex> Source.write(source)
iex> File.exists?(path)
false
iex> File.read(new_path)
{:ok, "ping\n"}
iex> path = "tmp/ping.txt"
iex> File.write(path, "ping")
iex> source = Source.read!(path)
iex> new_path = "tmp/pong.ex"
iex> source = Source.update(source, :path, new_path)
iex> Source.write(source, rm: false)
iex> File.exists?(path)
true
iex> path = "tmp/ping.txt"
iex> File.write(path, "ping")
iex> source = path |> Source.read!() |> Source.update(:content, "peng")
iex> File.write(path, "pong")
iex> Source.write(source)
{:error, %SourceError{reason: :changed, path: "tmp/ping.txt", action: :write}}
iex> {:ok, _source} = Source.write(source, force: true)
Same as write/1, but raises a Rewrite.SourceError exception in case of
failure.