"A decoration paints over the source without changing it. That one property is why one mechanism can do everything."
Two Kinds of Decoration
CodeMirror 6 gives you exactly two ways to add visual layers over the source. A mark decoration attaches a style to a range of text — make characters 4–8 bold, underline characters 12–16 in red. A widget decoration inserts a piece of UI at a position — a margin note after a word, a whole proposal block below a line. Marks style what's there; widgets insert what isn't. Between them, they cover every "show something extra" a feature could want.
It Paints Over Source, Never Mutates It
The property that makes decorations powerful is what they don't do: they never change the document. A bold mark doesn't insert formatting into the text; it renders characters that are already there as bold. A widget doesn't write into the file; it paints UI at a point in the view. The source string is untouched. Decorations live in a StateField that recomputes a DecorationSet from the document on every edit:
const decoField = StateField.define<DecorationSet>({
create: () => Decoration.none,
update: (deco, tr) => buildDecorations(tr.state), // ranges -> marks + widgets
provide: (f) => EditorView.decorations.from(f),
});
One Primitive, Any "Show Something Extra" Feature
Here's the whole trick. Because a decoration adds a layer without touching the model, any feature that means "show the user something extra over their text" reduces to the same primitive: compute a set of ranges, attach marks or widgets, let CM6 render them. Live Preview is ranges-to-marks. Voice underline is ranges-to-marks-plus-a-widget. CMD+K is a widget holding a proposal. Three features that look unrelated are, underneath, the same computation with different inputs. That's the seed of the entire track.