# Edit a document

Traverse a Document, build replacement nodes, and write the edited tree back to LaTeX.

https://texform.dev/docs/edit-a-document

A tool that rewrites formulas — expanding project shorthands, swapping deprecated commands, normalizing markup — needs to walk a parsed `Document`, find the nodes it cares about, and replace them with new structure. TeXForm gives you that working tree directly: you read through node handles, stage replacement subtrees with `create_*` constructors, attach them, and serialize the result back to LaTeX. Edits are validated as you make them and return a structured error on misuse rather than corrupting the tree, and a tree that came back with parse errors is read-only — the model carried over from [Parsing](https://texform.dev/docs/parsing).

The task below expands a project shorthand: every `\R` in the source becomes `\mathbb{R}`. The parser does not know `\R`, so under the default lenient configuration it survives as a plain command node — exactly the kind of node a rewriting tool wants to find and replace.

**Python**

```python
import texform

doc = texform.Parser().parse(r"x \in \R, y \in \R")["document"]
print("before:", doc.to_latex())

# Find the shorthand command, then replace each occurrence with a freshly
# built \mathbb{R}. A slot takes a detached node or source text.
for node in doc.find_commands("R"):
    letter = doc.create_char("R")                 # stage a detached child
    replacement = doc.create_command("mathbb", [letter])
    doc.replace_with(node, replacement)

print("after :", doc.to_latex())
```

```text
before: x \in \R , y \in \R
after : x \in \mathbb { R } , y \in \mathbb { R }
```

**JavaScript**

```js
import { Parser } from 'texform';

const doc = new Parser().parse('x \\in \\R, y \\in \\R').document;
console.log('before:', doc.toLatex());

// Find the shorthand command, then replace each occurrence with a freshly
// built \mathbb{R}. A slot takes a detached node or source text.
for (const node of doc.findCommands('R')) {
  const letter = doc.createChar('R');             // stage a detached child
  const replacement = doc.createCommand('mathbb', [letter]);
  doc.replaceWith(node, replacement);
}

console.log('after :', doc.toLatex());
```

```text
before: x \in \R , y \in \R
after : x \in \mathbb { R } , y \in \mathbb { R }
```

The workflow is the same for any rewrite: traverse with finders like `find_commands` (or the navigation accessors `children`, `descendants`, `parent`), build detached nodes with the `create_*` constructors, attach them with `replace_with`, `append_child`, `insert_before`, and friends, then read the result with `to_latex()`. Staged nodes must be detached when you attach them — reusing an argument that is still bound to another node fails, so build fresh content rather than re-parenting live nodes.

Because edits are fallible, misuse surfaces as a structured error instead of a silent corruption or a crash. The clearest case is a tree that parsed with errors: it is read-only, and every editing method rejects the attempt.

**Python**

```python
import texform

doc = texform.Parser().parse(r"x^")["document"]   # incomplete superscript
print(doc.has_errors(), doc.is_read_only())

try:
    doc.append_child(doc.root(), doc.create_char("y"))
except texform.EditError as exc:
    print("EditError:", exc)
```

```text
True True
EditError: document is read-only
```

**JavaScript**

```js
import { Parser } from 'texform';

const doc = new Parser().parse('x^').document;     // incomplete superscript
console.log(doc.hasErrors(), doc.isReadOnly());

try {
  doc.appendChild(doc.root(), doc.createChar('y'));
} catch (exc) {
  console.log('error:', exc.constructor.name, '-', exc.message);
}
```

```text
true true
error: TexformEditError - document is read-only
```

The other guard worth knowing is node ownership: a node belongs to the document that created it, and an edit that mixes two documents is rejected before it can corrupt either tree. Python raises `EditError` with the message `node belongs to a different document`, JavaScript throws `TexformEditError` for the same misuse, and in both the rejection happens before either tree is touched. Building replacements with the `create_*` constructors on the document you are editing, as above, keeps you clear of it.

For the full set of editing methods, the `create_*` constructors, and every `EditError` variant, see the [Document API](https://texform.dev/api/python/document); for the read model these edits build on, see [Parsing](https://texform.dev/docs/parsing).

Editing the exponent node changes x^2 to x^3 while retaining the base node; serialization reads the updated tree.

## Next steps [#next-steps]

- [Document API](https://texform.dev/api/python/document) — Every finder, create_* constructor, and attachment method on the tree.

- [Parsing](https://texform.dev/docs/parsing) — The read model, the three parse states, and why error trees are read-only.

- [Configure output style](https://texform.dev/docs/configure-output-style) — Choose spaced or compact LaTeX output.

- [Troubleshooting](https://texform.dev/docs/troubleshooting) — Ownership, read-only trees, and the other edit failures, with real messages.

