Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Syntax.Edit

Span-addressed source edits that refuse rather than corrupt.

An edit is a byte span and its replacement text, which is the only edit vocabulary a tool built on the syntax seams needs: every artifact those seams publish addresses source by exact byte span, so a tool that computes spans can state its whole result as a list of them without ever holding a mutable buffer.

The apply step is where the guarantee lives. Edits are sorted by start offset (a stable sort, so two edits at the same offset keep the order the caller gave them), a span that runs backwards or past the end is refused, overlapping edits are refused, and the spliced result is re-lexed before it is returned. An edit set that would produce a file the lexer cannot read comes back as a structured refusal naming the lexer’s own error, never as a corrupt file. That is the difference between a rename tool and a search-and-replace: this one fails closed.

The re-lex is a lexical check and nothing more. It catches an edit that unbalances a string, a hole, or a numeric literal; it does not claim the result parses, typechecks, or means what the caller intended.

Types

Edit

type Edit = Edit { span: Span, text: String }

One edit: the byte span it replaces and the text that replaces it. An insertion is an empty span, a deletion is empty text.

EditError

type EditError
  = EdBadSpan(Span)
  | EdPastEnd(Span, Int)
  | EdOverlap(Span, Span)
  | EdRelex(LexError)
  deriving (Eq, Show)

Why an edit set was refused. Every case names the offending span, so a tool can point at the edit it got wrong.

Functions and Values

ed_insert_at

ed_insert_at : (Int, String) -> Syntax.Edit.Edit

An insertion at one offset.

ed_delete

ed_delete : (Syntax.Source.Span) -> Syntax.Edit.Edit

A deletion of one span.

ed_replace

ed_replace : (Syntax.Source.Span, String) -> Syntax.Edit.Edit

A replacement of one span.

ed_span_text

ed_span_text : (String, Syntax.Source.Span) -> Option(String)

The current text under a span, or None when the span does not address this text. What a rename reads to check that an occurrence still says what the plan assumed.

ed_message

ed_message : (Syntax.Edit.EditError) -> String

A human-readable account of a refusal.

ed_sort

ed_sort : (List(Syntax.Edit.Edit)) -> List(Syntax.Edit.Edit)

The edits sorted by start offset, stably: two edits starting at the same offset keep their input order, so a caller that means “insert A then B here” gets it.

ed_apply

ed_apply : (String, List(Syntax.Edit.Edit)) -> Result(String, Syntax.Edit.EditError)

Apply an edit set to a source text.

The result is the spliced text when every edit is well formed, the edits do not overlap, and the splice still lexes. Otherwise it is the first refusal found, in that order.

ed_count

ed_count : (List(Syntax.Edit.Edit)) -> Int

How many edits an edit set holds: the count a plan reports before anything is applied.