diff --git a/crates/nuwiki-lsp/src/commands.rs b/crates/nuwiki-lsp/src/commands.rs index 1b9a9ff..ca6b92d 100644 --- a/crates/nuwiki-lsp/src/commands.rs +++ b/crates/nuwiki-lsp/src/commands.rs @@ -1176,27 +1176,30 @@ fn workspace_find_orphans(backend: &Backend, args: Vec) -> Result LspPosition { - let mut line = 0u32; - let mut last_nl_col = 0u32; - let mut col = 0u32; - for (i, b) in text.as_bytes().iter().copied().enumerate() { - if b == b'\n' { - line += 1; - last_nl_col = (i + 1) as u32; - col = 0; - } else { - col = (i + 1 - last_nl_col as usize) as u32; - } +/// Return the end-of-file `LspPosition` (1-based lines; 0-based +/// character column) for the given text, suitable for appending. +fn eof_position(text: &str) -> LspPosition { + let mut line = 0u32; + let mut last_nl_col = 0u32; + let mut col = 0u32; + for (i, b) in text.as_bytes().iter().copied().enumerate() { + if b == b'\n' { + line += 1; + last_nl_col = (i + 1) as u32; + col = 0; + } else { + col = (i + 1 - last_nl_col as usize) as u32; } - LspPosition { line, character: col } } + LspPosition { + line, + character: col, + } +} - // ===== Pure operations ===== +// ===== Pure operations ===== - pub mod ops { +pub mod ops { use super::*; use crate::edits::text_edit_replace; use nuwiki_core::ast::{ @@ -1718,24 +1721,24 @@ fn workspace_find_orphans(backend: &Backend, args: Vec) -> Result Option<(AstPosition, AstPosition)> { let needle = heading_name.to_ascii_lowercase(); for (i, block) in ast.children.iter().enumerate() { - let BlockNode::Heading(h) = block else { - continue; - }; - // Accept any heading level for section replacement. Previously only - // level‑1 headings were considered, which caused `VimwikiGenerateTagLinks` - // to miss existing tag sections if they were not level‑1 (e.g. `== Tag:`). - // Removing the level check makes the edit idempotent across heading - // levels. - // NOTE: All callers (TOC, Links, Tag links) expect a top‑level heading, - // but supporting any level is safe because headings are unique by name - // within a page and the operation replaces the whole section. - // If multiple headings share the same title at different levels, the - // first encountered will be replaced – this matches prior behaviour for - // level‑1. - // The level check is therefore removed. - // if h.level != 1 { - // continue; - // } + let BlockNode::Heading(h) = block else { + continue; + }; + // Accept any heading level for section replacement. Previously only + // level‑1 headings were considered, which caused `VimwikiGenerateTagLinks` + // to miss existing tag sections if they were not level‑1 (e.g. `== Tag:`). + // Removing the level check makes the edit idempotent across heading + // levels. + // NOTE: All callers (TOC, Links, Tag links) expect a top‑level heading, + // but supporting any level is safe because headings are unique by name + // within a page and the operation replaces the whole section. + // If multiple headings share the same title at different levels, the + // first encountered will be replaced – this matches prior behaviour for + // level‑1. + // The level check is therefore removed. + // if h.level != 1 { + // continue; + // } let title = heading_title(h).to_ascii_lowercase(); if title != needle { @@ -1877,10 +1880,7 @@ fn workspace_find_orphans(backend: &Backend, args: Vec) -> Result { let with_sep = format!("{new_text}\n"); let insert_pos = eof_position(text); - b.edit( - uri.clone(), - text_edit_insert(insert_pos, with_sep), - ); + b.edit(uri.clone(), text_edit_insert(insert_pos, with_sep)); } } Some(b.build()) @@ -2947,10 +2947,7 @@ fn workspace_find_orphans(backend: &Backend, args: Vec) -> Result { let with_sep = format!("{body}\n"); let insert_pos = eof_position(text); - b.edit( - uri.clone(), - text_edit_insert(insert_pos, with_sep), - ); + b.edit(uri.clone(), text_edit_insert(insert_pos, with_sep)); } } Some(b.build())