

<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

## Notebook editing

[`edit_notebook`](https://MorsCerta-crypto.github.io/nbskill/edit.html#edit_notebook)
is the production notebook mutation adapter. It keeps text transforms
deterministic, applies notebook changes all-or-nothing, and returns
structured diffs without raw `.ipynb` JSON.

The shape is intentionally small: pure text helpers first, then one
notebook/cell adapter. Read tools still provide context; this module
only mutates notebooks.

Craft warnings make risky notebook edits visible without changing edit
semantics. They now flag calls whose definitions remain below the edited
cell, so a notebook stays executable from top to bottom.

## Text and line transforms

The first layer edits plain text. Keeping it independent from notebook
I/O makes the same operations useful in direct Python calls and
structured notebook edits.

#### Pure text helpers

The text helpers do not know about notebooks. They take strings, return
strings, and raise clear `ValueError`s for invalid line bounds or
malformed replacements. That makes cell-level and notebook-level editing
share the same behavior.

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/edit.py#L166"
target="_blank" style="float:right; font-size:smaller">source</a>

### insert_lines

``` python
def insert_lines(
    text, insert_line, new_lines
):
```

*Insert `new_lines` before a 1-based line boundary in `text`; use n+1 to
append.*

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/edit.py#L176"
target="_blank" style="float:right; font-size:smaller">source</a>

### replace_lines

``` python
def replace_lines(
    text, start_line, end_line:NoneType=None, replacement_lines:NoneType=None
):
```

*Replace a 1-based inclusive line range in `text`.*

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/edit.py#L183"
target="_blank" style="float:right; font-size:smaller">source</a>

### delete_lines

``` python
def delete_lines(
    text, start_line:NoneType=None, end_line:NoneType=None, re_filter:NoneType=None, invert_filter:bool=False
):
```

*Delete a 1-based inclusive line range, or lines matching `re_filter`.*

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/edit.py#L199"
target="_blank" style="float:right; font-size:smaller">source</a>

### replace_text

``` python
def replace_text(
    text, old, new, start_line:NoneType=None, end_line:NoneType=None
):
```

*Replace literal `old` with `new`, optionally inside a line range.*

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/edit.py#L218"
target="_blank" style="float:right; font-size:smaller">source</a>

### replace_texts

``` python
def replace_texts(
    text, replacements:NoneType=None, olds:NoneType=None, news:NoneType=None, start_line:NoneType=None,
    end_line:NoneType=None
):
```

*Apply several literal text replacements in order.*

The public helper functions
[`insert_lines`](https://MorsCerta-crypto.github.io/nbskill/edit.html#insert_lines),
[`replace_lines`](https://MorsCerta-crypto.github.io/nbskill/edit.html#replace_lines),
[`delete_lines`](https://MorsCerta-crypto.github.io/nbskill/edit.html#delete_lines),
[`replace_text`](https://MorsCerta-crypto.github.io/nbskill/edit.html#replace_text),
and
[`replace_texts`](https://MorsCerta-crypto.github.io/nbskill/edit.html#replace_texts)
use one-based line bounds for `insert_line`, `start_line`, and
`end_line`, plus the same text semantics that
[`edit_notebook`](https://MorsCerta-crypto.github.io/nbskill/edit.html#edit_notebook)
applies to notebook cells.

``` python
sample = "alpha\nbeta\ngamma"

print(insert_lines(sample, 2, ["inserted"]))
print(replace_lines(sample, 2, 2, ["BETA"]))
print(delete_lines(sample, re_filter="beta"))
print(replace_text(sample, "alpha", "ALPHA"))
print(replace_texts(sample, [dict(old="alpha", new="A"), dict(old="gamma", new="G")]))
```

## Notebook-aware edits

These operations add the notebook concerns: stable cell ids, metadata,
output clearing, validation, and one atomic commit.

#### Notebook adapter

The adapter selects cells, applies one deterministic operation per
selected cell, validates the edited notebook before writing, clears
stale outputs for changed code cells, stamps metadata, exports with
nbdev, and returns structured results. The whole call succeeds or fails
as one unit.

fastcore 2.0 now provides the raw notebook insertion and movement
mechanics through
[`fastcore.nbio.Notebook`](https://fastcore.fast.ai/nbio.html#notebook).
nbskill still builds cells, applies directives, validates code, records
hashes, and formats structured diffs here; only the low-level list
mutation is delegated.

## Import grouping after an edit

An edited cell may contain global imports together with exported code,
tests, or examples. Before committing, the editor moves those imports
into the notebook’s single matching import cell: exported code uses
`exported_import_cell`; all runtime-only cells use
`internal_import_cell`. Function-local imports stay in place.

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/edit.py#L666"
target="_blank" style="float:right; font-size:smaller">source</a>

### edit_notebook

``` python
def edit_notebook(
    path, edits, validate_code:bool=True, default_cell_type:str='code', directive:NoneType=None,
    auto_feedback:bool=True, feedback_timeout:int=10, feedback_safe:bool=True, detail:str='summary',
    dry_run:bool=False
):
```

*Apply structured notebook edits atomically and return structured
details.*

## Editor facade

The facade keeps repeated edits scoped to one notebook while the
lower-level functions remain useful for one-off transformations.

#### A notebook-scoped editor

[`NotebookEditor`](https://MorsCerta-crypto.github.io/nbskill/edit.html#notebookeditor)
is a small facade over
[`edit_notebook`](https://MorsCerta-crypto.github.io/nbskill/edit.html#edit_notebook)
for one notebook, and
[`Notebook.edit`](https://MorsCerta-crypto.github.io/nbskill/edit.html#notebook.edit)
hands one back. Edits accept a `directive` (a
[`Directive`](https://MorsCerta-crypto.github.io/nbskill/foundation.html#directive),
its name, or a comma string such as `export,no_test`) so callers can say
`export and do not test` instead of typing `#|` lines. The directive is
stamped onto code cells, and context strips it back out because the
semantic type already conveys it.

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/edit.py#L752"
target="_blank" style="float:right; font-size:smaller">source</a>

### NotebookEditor

``` python
def NotebookEditor(
    path:object, validate_code:bool=True, auto_feedback:bool=False
)->None:
```

*Notebook-scoped facade over edit_notebook.*

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/edit.py#L767"
target="_blank" style="float:right; font-size:smaller">source</a>

### NotebookEditor.replace_cell

``` python
def replace_cell(
    cell_id, source, directive:NoneType=None, cell_type:NoneType=None, **kw
):
```

*Replace one cell source, optionally stamping directives onto code.*

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/edit.py#L776"
target="_blank" style="float:right; font-size:smaller">source</a>

### NotebookEditor.insert

``` python
def insert(
    anchor_id, source, where:str='after', directive:NoneType=None, cell_type:str='code', **kw
):
```

*Insert one cell relative to anchor_id; pass None to append.*

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/edit.py#L784"
target="_blank" style="float:right; font-size:smaller">source</a>

### NotebookEditor.explode

``` python
def explode(
    cell_id:NoneType=None, **kw
):
```

*Split top-level function definitions in one cell, or matching cells,
into separate cells.*

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/edit.py#L793"
target="_blank" style="float:right; font-size:smaller">source</a>

### NotebookEditor.delete

``` python
def delete(
    cell_id, **kw
):
```

*Delete one cell by id.*

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/edit.py#L799"
target="_blank" style="float:right; font-size:smaller">source</a>

### NotebookEditor.replace_text

``` python
def replace_text(
    old, new:str='', cell_id:NoneType=None, **kw
):
```

*Replace literal text in one cell, or across the notebook when cell_id
is omitted.*

------------------------------------------------------------------------

<a
href="https://github.com/MorsCerta-crypto/nbskill/blob/main/nbskill/edit.py#L808"
target="_blank" style="float:right; font-size:smaller">source</a>

### Notebook.edit

``` python
def edit():
```

*Return a NotebookEditor bound to this notebook path.*

``` python
with write_demo_notebook('editor_directive_demo.ipynb', cells=[mk_cell('def helper():\n    return 1')]) as demo:
    editor = Notebook.from_path(demo).edit()
    print(type(editor).__name__)
    cell_id = read_nb(demo).cells[0].id
    editor.replace_cell(cell_id, 'def helper():\n    return 2', directive='export,no_test')
    print(read_nb(demo).cells[0].source)
```

A notebook-wide rename is just a
[`replace_text`](https://MorsCerta-crypto.github.io/nbskill/edit.html#replace_text)
operation with `target="all"`. That path is the default for
deterministic refactors such as renaming a public symbol across
Markdown, examples, and tests.

``` python
with write_demo_notebook("edit_notebook_example.ipynb", cells=[mk_cell("old_name = 1")]) as example_path:
    result = edit_notebook(
        example_path,
        [dict(op="replace_text", target="all", old="old_name", new="new_name")],
        auto_feedback=False,
    )
    source = read_nb(example_path).cells[0].source
source
```

### Hash-guarded dry runs

An `expected_hash` protects the exact cell source selected by
`context(..., mode="edit")`. Use a dry run to inspect the one-cell diff
and replacement count before writing. Pass `auto_feedback=False` to
suppress automatic feedback; smoke snippets remain an advanced, explicit
option.

``` python
with write_demo_notebook("02_edit_hash_example.ipynb", cells=[mk_cell("value = 1")]) as path:
    cell = read_nb(path).cells[0]
    result = edit_notebook(path, [dict(op="replace_text", cell_id=cell.id, old="1", new="2", expected_hash=source_hash(cell_source(cell)))], auto_feedback=False, dry_run=True)
    print(result["affected_cell_ids"], result["replacement_summary"])
```
