Sexps_rewriter
This is thin wrapper on top of File_rewriter
that specializes in applying rewrites to sexp files.
The primary motivation for which this library is created is to automatically apply some linting rules to the lots of dune files present in a large monorepo.
A note on editing files with a single sexp (rather than a list of them): it should just work, since one sexp is just a sub case of many sexps.
module Parse_error : sig ... end
val create :
path:Fpath.t ->
original_contents:string ->
(t, Parse_error.t) Stdlib.Result.t
A t
shall be created from a string that would yield a successful parsing of many sexps and positions from a file, using the Parsexp
library. The path
is provided to create a Loc.t
for error messages, but no I/O is actually performed on disk - the sexps are parsed from the string provided by the parameter original_contents
.
module Visitor_decision : sig ... end
While we visit the sexps from the original file, we may decide what to do at each step of the iteration:
val visit :
t ->
f:
(Sexplib0.Sexp.t ->
range:Loc.Range.t ->
file_rewriter:File_rewriter.t ->
Visitor_decision.t) ->
unit
Visiting the input, perhaps recursively. Unless you are breaking the execution with Break
or Skip
, the function f
provided will be called on all the sexps (including recursively on all internal ones) of the input.
The expected way to use this function is to do some pattern matching on the Sexp.t
currently at hand, and use the File_rewriter
api if some rewrites are required. If the current sexp is not one you are targeting you may simply ignore it and return Continue
, to be called again on other parts of the input.
Note that you may visit the same input multiple times (that is, calling visit
multiple times, with different invocations of f
), however, be mindful that the file_rewriter
that you manipulate is the same each time, thus the final computation of the output will fail if you enqueue incompatible rewrites in it.
val reset : t -> unit
You may decide at a given moment that you want to discard all the rewrites done so far, and just start over. This has the same effect than starting fresh with a newly created t
.
val contents : t -> string
Produce the resulting buffer, with all the rewrites applied. Note that t
may continue to be used further from here, and you can call contents
again later. This raises File_rewriter.Invalid_rewrites
if inconsistent rewrites have been submitted to t's file_rewriter
.
val contents_result :
t ->
(string, File_rewriter.Invalid_rewrites.t) Stdlib.Result.t
Same as contents
but wraps the output into a result type rather than raising an exception.
val file_rewriter : t -> File_rewriter.t
If you need access to the internal file_rewriter, this accessor is exposed.
val original_sexps : t -> Sexplib0.Sexp.t list
Access the raw sexps that were parsed from the original contents.
val path : t -> Fpath.t
Retrieve the path provided when t
was created.
module Position : sig ... end
An exception raised when trying to locate the position of an unknown sexp
when traversing the input. We don't expose functions returning results rather than raising this exception in this interface because we can't think of an actual use case for the erroring case. If you are trying to locate positions from an invalid sexp, we envision that this is the result of a programming error which should be fixed.
val position : t -> Sexplib0.Sexp.t -> Parsexp.Positions.range
Assuming the supplied Sexp.t
is phys_equal to a Sexp.t
found during a call to visit
, the function position
will be able to return its position. This is using Parsexp.Positions.t
under the hood, which keeps track of the positions of all the sexps that have been parsed. If the Sexp.t
cannot be found as phys_equal to one of the parsed ones, this function will raise Position_not_found
.
val loc : t -> Sexplib0.Sexp.t -> Loc.t
This is just a shortcut to calling position
and converting its result with Position.loc
. It raises if position
raises.
val range : t -> Sexplib0.Sexp.t -> Loc.Range.t
This is just a shortcut to calling position
and converting its result with Position.range
. It raises if position
raises.
val start_offset : t -> Sexplib0.Sexp.t -> Loc.Offset.t
val stop_offset : t -> Sexplib0.Sexp.t -> Loc.Offset.t
module Private : sig ... end