Current section
Files
Jump to
Current section
Files
lib/ex_ast/diff.ex
defmodule ExAST.Diff do
@moduledoc """
Syntax-aware diffing for Elixir source code.
Parses two source strings with Sourceror, maps structurally similar AST nodes,
and produces a diff result with insert, delete, update, and move edits.
"""
alias ExAST.Diff.Annotated
alias ExAST.Diff.Classifier
alias ExAST.Diff.Matcher
alias ExAST.Diff.Patcher
alias ExAST.Diff.Render
alias ExAST.Diff.Result
@type option :: {:include_moves, boolean()}
@spec diff(String.t(), String.t(), [option()]) :: Result.t()
def diff(left_source, right_source, opts \\ []) do
left = Annotated.from_source(left_source)
right = Annotated.from_source(right_source)
mappings = Matcher.match(left, right)
edits = Classifier.classify(left, right, mappings, opts)
%Result{
left: left,
right: right,
mappings: mappings,
edits: edits,
summary: Render.summary(edits)
}
end
@spec diff_files(String.t(), String.t(), [option()]) :: Result.t()
def diff_files(left_path, right_path, opts \\ []) do
diff(File.read!(left_path), File.read!(right_path), opts)
end
@doc """
Applies a diff result to the left source, producing the patched output.
Updates and deletes are applied via range replacement. Inserts are placed
at the resolved insertion point based on sibling/parent context.
Move edits are skipped (content unchanged, only order differs).
"""
@spec apply(Result.t()) :: String.t()
defdelegate apply(result), to: Patcher
end