feat(toc): insert a fresh TOC at the cursor line
:NuwikiTOC inserted a new table of contents at the top of the file. It now inserts at the cursor line instead, so you can place the TOC where you want it (a blank line separates it from preceding text). An existing TOC is still refreshed in place, and auto_toc-on-save is unaffected. The Vim/Lua clients send the 0-based cursor line with nuwiki.toc.generate; the server threads it into toc_edit (clamped to the document) and falls back to the top of the file when absent. toc_rebuild_edit passes None. Tests: toc_edit_inserts_at_cursor_line + toc_edit_cursor_line_clamped_and_existing_replaced_in_place. 575 passed, clippy clean, keymap harnesses green. Doc updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -484,7 +484,8 @@ fn toc_edit_inserts_at_top_when_no_existing_toc() {
|
||||
let src = "= One =\n== Two ==\n";
|
||||
let ast = parse(src);
|
||||
let uri = Url::parse("file:///tmp/page.wiki").unwrap();
|
||||
let edit = ops::toc_edit(src, &ast, &uri, true, "Contents", 1, 0, 0).expect("got an edit");
|
||||
let edit =
|
||||
ops::toc_edit(src, &ast, &uri, true, "Contents", 1, 0, 0, None).expect("got an edit");
|
||||
let changes = edit.changes.expect("changes map");
|
||||
let edits = &changes[&uri];
|
||||
assert_eq!(edits.len(), 1);
|
||||
@@ -497,12 +498,47 @@ fn toc_edit_inserts_at_top_when_no_existing_toc() {
|
||||
assert!(te.new_text.contains("[[#One|One]]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn toc_edit_inserts_at_cursor_line() {
|
||||
// A fresh TOC goes at the cursor line (0-based) the client sends.
|
||||
let src = "= One =\nbody text\n== Two ==\nmore\n";
|
||||
let ast = parse(src);
|
||||
let uri = Url::parse("file:///tmp/page.wiki").unwrap();
|
||||
// Cursor on line 1 ("body text").
|
||||
let edit = ops::toc_edit(src, &ast, &uri, true, "Contents", 1, 0, 0, Some(1)).expect("edit");
|
||||
let te = &edit.changes.unwrap()[&uri][0];
|
||||
// Zero-width insert at line 1, column 0.
|
||||
assert_eq!(te.range.start, te.range.end);
|
||||
assert_eq!(te.range.start.line, 1);
|
||||
assert_eq!(te.range.start.character, 0);
|
||||
// A leading blank separates it from the preceding text.
|
||||
assert!(
|
||||
te.new_text.starts_with("\n= Contents ="),
|
||||
"got: {:?}",
|
||||
te.new_text
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn toc_edit_cursor_line_clamped_and_existing_replaced_in_place() {
|
||||
// An existing TOC is replaced in place regardless of the cursor line.
|
||||
let src = "= Contents =\n- [[#stale|Stale]]\n\n= Real =\n";
|
||||
let ast = parse(src);
|
||||
let uri = Url::parse("file:///tmp/page.wiki").unwrap();
|
||||
let edit = ops::toc_edit(src, &ast, &uri, true, "Contents", 1, 0, 0, Some(99)).expect("edit");
|
||||
let te = &edit.changes.unwrap()[&uri][0];
|
||||
// Replacement starts at line 0 (the existing section), not the cursor.
|
||||
assert_eq!(te.range.start.line, 0);
|
||||
assert!(te.new_text.contains("[[#Real|Real]]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn toc_edit_replaces_existing_toc() {
|
||||
let src = "= Contents =\n- [[#stale|Stale]]\n\n= Real =\n";
|
||||
let ast = parse(src);
|
||||
let uri = Url::parse("file:///tmp/page.wiki").unwrap();
|
||||
let edit = ops::toc_edit(src, &ast, &uri, true, "Contents", 1, 0, 0).expect("got an edit");
|
||||
let edit =
|
||||
ops::toc_edit(src, &ast, &uri, true, "Contents", 1, 0, 0, None).expect("got an edit");
|
||||
let changes = edit.changes.expect("changes map");
|
||||
let edits = &changes[&uri];
|
||||
let te = &edits[0];
|
||||
@@ -517,7 +553,7 @@ fn toc_edit_returns_none_for_empty_doc() {
|
||||
let src = "";
|
||||
let ast = parse(src);
|
||||
let uri = Url::parse("file:///tmp/empty.wiki").unwrap();
|
||||
assert!(ops::toc_edit(src, &ast, &uri, true, "Contents", 1, 0, 0).is_none());
|
||||
assert!(ops::toc_edit(src, &ast, &uri, true, "Contents", 1, 0, 0, None).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user