Fix duplicate tag links for VimwikiGenerateTagLinks by extending section range detection and allowing any heading level
Release / build aarch64-unknown-linux-gnu (push) Failing after 32s
Release / build aarch64-unknown-linux-musl (push) Failing after 16m23s
Release / build x86_64-unknown-linux-gnu (push) Failing after 55s
Release / build x86_64-unknown-linux-musl (push) Failing after 55s
Release / gitea release (push) Has been skipped
CI / cargo fmt --check (push) Failing after 37s
CI / cargo clippy (push) Successful in 26s
CI / cargo test (push) Failing after 33s
CI / editor keymaps (push) Has been skipped
Release / build aarch64-unknown-linux-gnu (push) Failing after 32s
Release / build aarch64-unknown-linux-musl (push) Failing after 16m23s
Release / build x86_64-unknown-linux-gnu (push) Failing after 55s
Release / build x86_64-unknown-linux-musl (push) Failing after 55s
Release / gitea release (push) Has been skipped
CI / cargo fmt --check (push) Failing after 37s
CI / cargo clippy (push) Successful in 26s
CI / cargo test (push) Failing after 33s
CI / editor keymaps (push) Has been skipped
This commit is contained in:
@@ -1176,9 +1176,27 @@ fn workspace_find_orphans(backend: &Backend, args: Vec<Value>) -> Result<Option<
|
||||
})?))
|
||||
}
|
||||
|
||||
// ===== Pure operations =====
|
||||
/// 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 }
|
||||
}
|
||||
|
||||
pub mod ops {
|
||||
// ===== Pure operations =====
|
||||
|
||||
pub mod ops {
|
||||
use super::*;
|
||||
use crate::edits::text_edit_replace;
|
||||
use nuwiki_core::ast::{
|
||||
@@ -1703,18 +1721,67 @@ pub mod ops {
|
||||
let BlockNode::Heading(h) = block else {
|
||||
continue;
|
||||
};
|
||||
if h.level != 1 {
|
||||
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 {
|
||||
continue;
|
||||
}
|
||||
let start = h.span.start;
|
||||
let end = match ast.children.get(i + 1) {
|
||||
Some(BlockNode::List(list)) => list.span.end,
|
||||
_ => h.span.end,
|
||||
// Determine the end of the section. Historically we only extended
|
||||
// to the next immediate list block, which worked for simple tag
|
||||
// sections like `= Tag: foo =` that contain a single list. For the
|
||||
// full‑tags index (`= Tags =`) the section contains multiple
|
||||
// sub‑headings and lists, so the next block after the heading is a
|
||||
// level‑2 heading, not a list. The old logic therefore stopped at
|
||||
// the heading line, causing only the heading to be replaced and
|
||||
// the existing content to remain, resulting in duplicated links
|
||||
// on subsequent runs.
|
||||
//
|
||||
// New logic walks forward until it finds the next *level‑1*
|
||||
// heading (the start of the next top‑level section) or reaches the
|
||||
// end of the document. The span end is set to the start of that
|
||||
// next heading (exclusive) or to the end of the last block.
|
||||
let mut end = h.span.end;
|
||||
for next_block in ast.children.iter().skip(i + 1) {
|
||||
// If we encounter another level‑1 heading, the current section
|
||||
// ends just before it.
|
||||
if let BlockNode::Heading(next_h) = next_block {
|
||||
if next_h.level == 1 {
|
||||
end = next_h.span.start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Otherwise extend the end to the end of this block.
|
||||
end = match next_block {
|
||||
BlockNode::Heading(h) => h.span.end,
|
||||
BlockNode::Paragraph(p) => p.span.end,
|
||||
BlockNode::HorizontalRule(hr) => hr.span.end,
|
||||
BlockNode::Blockquote(bq) => bq.span.end,
|
||||
BlockNode::Preformatted(pf) => pf.span.end,
|
||||
BlockNode::MathBlock(mb) => mb.span.end,
|
||||
BlockNode::List(list) => list.span.end,
|
||||
BlockNode::DefinitionList(dl) => dl.span.end,
|
||||
BlockNode::Table(t) => t.span.end,
|
||||
BlockNode::Comment(c) => c.span.end,
|
||||
BlockNode::Tag(tag) => tag.span.end,
|
||||
BlockNode::Error(err) => err.span.end,
|
||||
};
|
||||
}
|
||||
return Some((start, end));
|
||||
}
|
||||
None
|
||||
@@ -1809,15 +1876,10 @@ pub mod ops {
|
||||
}
|
||||
None => {
|
||||
let with_sep = format!("{new_text}\n");
|
||||
let insert_pos = eof_position(text);
|
||||
b.edit(
|
||||
uri.clone(),
|
||||
text_edit_insert(
|
||||
LspPosition {
|
||||
line: 0,
|
||||
character: 0,
|
||||
},
|
||||
with_sep,
|
||||
),
|
||||
text_edit_insert(insert_pos, with_sep),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2884,15 +2946,10 @@ pub mod ops {
|
||||
}
|
||||
None => {
|
||||
let with_sep = format!("{body}\n");
|
||||
let insert_pos = eof_position(text);
|
||||
b.edit(
|
||||
uri.clone(),
|
||||
text_edit_insert(
|
||||
LspPosition {
|
||||
line: 0,
|
||||
character: 0,
|
||||
},
|
||||
with_sep,
|
||||
),
|
||||
text_edit_insert(insert_pos, with_sep),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user