# `StatifierBlocks.SourceView`
[🔗](https://github.com/riddler/statifier_blocks/blob/v0.44.1/lib/statifier_blocks/source_view.ex#L1)

The generated SCXML as numbered lines of owned spans, for reading.

ADR-0004 decision 5 keys the provenance map two ways, and this is the
second key turned around. Error routing asks "which block owns the byte
this finding points at"; a reader asks the same question of every byte at
once, and gets back a listing in which each run of bytes carries the block
that emitted it. Nothing here decompiles anything: the chart is still a
build product, the document is still the source of truth, and this is a
read of what one compile produced.

## One element per line, because the serializer emits none

`StatifierBlocks.Compiler.Serializer` writes the whole chart as a single
line - a chart's bytes are identity-bearing (st-ADR-0052) and whitespace
the serializer does not need is whitespace that would have to be hashed.
So the lines here are the reader's, not the file's: a line begins at every
`<`, which the serializer's own escaping makes an exact rule rather than a
heuristic (`&`, `<` and `>` are escaped in text and in attribute values,
so a raw `<` in the output always opens a tag). `indent` is the nesting
depth the same walk counts, and it is presentation: no byte of the
generated chart is moved, added or dropped.

That is the property the whole surface rests on. Concatenating every span
of every line, in order, reproduces the compiled bytes exactly, which is
what makes an offset in this listing the same offset
`StatifierBlocks.Provenance.owner_at/2` was asked about.

## A span is one owner's run of bytes

The cut points are the provenance map's own span endpoints plus the line
starts, so within one span the innermost owner never changes. The owner is
read once per span with `owner_at/2` - the *innermost* containing span,
which is what makes an attribute value's owner its config field rather
than the element around it - and `config_key` is the field name an author
typed the value into.

A span whose `block_id` is `nil` is one no span in the map contains. The
map is total over the emission, so in practice this is the prologue an
element span does not reach; it is drawn as plain text rather than
dropped, because a listing that silently omitted bytes would stop
reproducing the chart.

## Staleness rather than a recompile per keystroke

A compile is not cheap and this runs off one, so a caller recomputes only
while something is showing the result - the same discipline
`StatifierBlocks.Runtime.FixtureRuns` is held to. What that leaves is the
mid-edit document that does not compile at all. Handing back
`:compile_error` there would empty the panel on the first half-typed
expression; handing back the previous listing without saying so would show
a chart the document no longer produces. So `build/3` takes the previous
value as `:previous` and, when the new compile fails and the previous one
succeeded, returns that listing with `stale?` set and the new findings
beside it. The panel says which document it is looking at, and no
keystroke recompiles anything.

## Unguarded, on purpose

Like `StatifierBlocks.Runtime.FixtureRuns`, this module names no LiveView
module and carries no `Code.ensure_loaded?/1` guard: it is a pure function
of a document and a palette, and the headless suite exercises it directly.
Drawing it is `StatifierBlocks.Editor.Drawer`'s job.

# `status`

```elixir
@type status() :: :pending | :ready | :compile_error
```

`:pending` - nothing has been compiled yet, which is the value a caller
holds before it first asks. `:compile_error` - the document does not
compile and there is no earlier listing to fall back on (normal mid-edit,
not exceptional); `findings` carries why. `:ready` - `lines` is the
compiled chart, and `stale?` says whether it is the current document's.

# `t`

```elixir
@type t() :: %StatifierBlocks.SourceView{
  findings: [StatifierBlocks.Compiler.Finding.t()],
  line_count: non_neg_integer(),
  lines: [StatifierBlocks.SourceView.Line.t()],
  stale?: boolean(),
  status: status()
}
```

# `build`

```elixir
@spec build(StatifierBlocks.Document.t(), StatifierBlocks.Palette.t(), keyword()) ::
  t()
```

Compiles `document` against `palette` and returns the listing.

`opts` is the compiler's own option list, plus one option of this
module's own. Every key but `:previous` is forwarded verbatim to
`StatifierBlocks.Compiler.compile/3`, because the listing is a read of
what one compile produced and a caller reading a chart it did not
compile the way its host does is reading the wrong chart: `terminate:`,
`child_use:`, `known_invoke_types:` and `datamodel:` each change the
emitted bytes, so each changes the listing. Nothing here validates the
list - the compiler is the authority on its own options.

  * `:declare` - the host's raw `{id, expr}` declaration list, the
    compiler's own `:declare` option. Defaults to `[]`.
  * `:previous` - the value this call replaces, and the one key this
    module keeps for itself. When the compile fails and the previous
    value was `:ready`, that listing comes back with `stale?` set rather
    than being thrown away - see the moduledoc.

# `spans_of`

```elixir
@spec spans_of(t(), StatifierBlocks.Block.id()) :: [
  StatifierBlocks.SourceView.Span.t()
]
```

Every span in `view` the block `block_id` owns, in document order.

What the panel highlights, and the same question the drawer's markup asks
per span - here as a function, so a caller that wants the answer without
the markup has one.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
