From 1246c99c3c5669bb83496ddd35978c46626e7c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Fr=C3=B3es=20Franco?= Date: Wed, 3 Jun 2026 12:48:25 +0000 Subject: [PATCH] fix(config): address P3 config-batch audit findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parallel-agent audit of the config batch found: - cycle_bullets used find_marker_span's third element as an indent column, but it's the ABSOLUTE byte offset after the marker — so depth (and the rotated glyph) was wrong for any item not near offset 0 (tests passed by coincidence). Fixed to use the marker span's column (the indentation). Regression test now places the list deep in the document. - :VimwikiColorize interpolated the colour into the template via an unescaped replacement: Lua gsub treats `%` specially, Vim substitute() treats `&`/`\`. Lua now uses a function replacement (verbatim); Vim escapes `\&~`. - render_swapped_table applied table_reduce_last_col before the column swap, so a swap involving the last column moved the narrow slot — now clamped after. - Fixed a stale toc_link_format doc comment (format 1 = [[#anchor]], not a full parent path). Verified-correct by the audit (no change needed): RSS rfc822/cdata/fidelity, prune_orphan_html reverse-mapping + guards, write_escaped_allowing/match_allowed_tag (multibyte-safe), substitute_emoji (byte-boundary-safe), resolve_target_uri create_link/dir_link ordering, all config defaults + From/constructors. Full rust suite + clippy clean; Neovim 307, Vim 301/18/21. Co-Authored-By: Claude Opus 4.8 (1M context) --- autoload/nuwiki/commands.vim | 4 +++- crates/nuwiki-lsp/src/commands.rs | 15 ++++++++++++--- crates/nuwiki-lsp/src/config.rs | 3 +-- crates/nuwiki-lsp/tests/commands_lists.rs | 6 ++++-- lua/nuwiki/commands.lua | 4 +++- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/autoload/nuwiki/commands.vim b/autoload/nuwiki/commands.vim index 6e215cb..a27ff42 100644 --- a/autoload/nuwiki/commands.vim +++ b/autoload/nuwiki/commands.vim @@ -1089,7 +1089,9 @@ function! nuwiki#commands#colorize(...) abort " __COLORFG__ replaced by the colour. Configurable via g:nuwiki_color_tag_template. let l:tpl = get(g:, 'nuwiki_color_tag_template', \ '__CONTENT__') - let l:tpl = substitute(l:tpl, '__COLORFG__', l:color, 'g') + " Escape `\`, `&`, `~` so a colour containing them isn't treated as a + " substitute() replacement metacharacter. + let l:tpl = substitute(l:tpl, '__COLORFG__', escape(l:color, '\&~'), 'g') let l:parts = split(l:tpl, '__CONTENT__', 1) let l:open_tag = get(l:parts, 0, '') let l:close_tag = get(l:parts, 1, '') diff --git a/crates/nuwiki-lsp/src/commands.rs b/crates/nuwiki-lsp/src/commands.rs index 693bb20..8920f72 100644 --- a/crates/nuwiki-lsp/src/commands.rs +++ b/crates/nuwiki-lsp/src/commands.rs @@ -2725,9 +2725,11 @@ pub mod ops { // vimwiki `cycle_bullets`: as an unordered item changes indent level, // rotate its glyph through `bullet_types` (ring buffer keyed by depth). if cycle_bullets && !bullet_types.is_empty() { - if let Some((mspan, marker, col_off)) = find_marker_span(text, item.span) { + if let Some((mspan, marker, _)) = find_marker_span(text, item.span) { if marker.chars().count() == 1 && bullet_types.iter().any(|b| b == marker) { - let old_depth = (col_off / 2) as i32; + // The marker's column is the item's indentation width (the + // item span starts at line start). 2 cols per depth level. + let old_depth = (mspan.start.column / 2) as i32; let new_depth = (old_depth + delta).max(0) as usize; let glyph = &bullet_types[new_depth % bullet_types.len()]; if glyph.as_str() != marker { @@ -3160,10 +3162,17 @@ pub mod ops { ) -> Option { // Swap the column-width slots first so the rendered table // remains visually aligned after the move. - let mut widths = column_widths(table, text, reduce_last_col); + let mut widths = column_widths(table, text, false); if a < widths.len() && b < widths.len() { widths.swap(a, b); } + // Apply reduce_last_col AFTER the swap so the column physically last + // stays minimal (clamping before the swap would move the narrow slot). + if reduce_last_col { + if let Some(last) = widths.last_mut() { + *last = 1; + } + } let mut out = String::new(); for (row_idx, row) in table.rows.iter().enumerate() { out.push('|'); diff --git a/crates/nuwiki-lsp/src/config.rs b/crates/nuwiki-lsp/src/config.rs index 16d4918..a713bbe 100644 --- a/crates/nuwiki-lsp/src/config.rs +++ b/crates/nuwiki-lsp/src/config.rs @@ -124,8 +124,7 @@ pub struct WikiConfig { /// `[[page|Heading]]` (first heading as caption) from `:VimwikiGenerateLinks`. pub generated_links_caption: bool, /// vimwiki `toc_link_format` (default `0`): `0` = `[[#anchor|title]]` - /// (leaf anchor, with description); `1` = `[[#parent#child]]` (full path, - /// no description). + /// (with description); `1` = `[[#anchor]]` (anchor only, no description). pub toc_link_format: u8, /// vimwiki `table_reduce_last_col` (default `false`): don't pad the last /// table column to fill — keep it at its minimum width. diff --git a/crates/nuwiki-lsp/tests/commands_lists.rs b/crates/nuwiki-lsp/tests/commands_lists.rs index 3e3b4b9..f724e44 100644 --- a/crates/nuwiki-lsp/tests/commands_lists.rs +++ b/crates/nuwiki-lsp/tests/commands_lists.rs @@ -275,10 +275,12 @@ fn cycle_bullets_rotates_glyph_on_indent() { // With cycle_bullets + bullet_types [-,*,#], indenting a `-` item (depth 0 // → 1) rotates the glyph to `*` (and dedenting back to `-`). let bullets: Vec = ["-", "*", "#"].iter().map(|s| s.to_string()).collect(); - let src = "- one\n- two\n"; + // Put the list well into the document so depth must come from the item's + // indentation, not its absolute byte offset (regression guard). + let src = "some intro paragraph text here\n\n- one\n- two\n"; let ast = parse(src); let edit = - ops::change_level_edit(src, &ast, &uri(), 1, 1, false, true, true, &bullets).expect("edit"); + ops::change_level_edit(src, &ast, &uri(), 3, 1, false, true, true, &bullets).expect("edit"); let texts: Vec = edit.changes.unwrap()[&uri()] .iter() .map(|e| e.new_text.clone()) diff --git a/lua/nuwiki/commands.lua b/lua/nuwiki/commands.lua index 99651ab..5ff74c9 100644 --- a/lua/nuwiki/commands.lua +++ b/lua/nuwiki/commands.lua @@ -1219,7 +1219,9 @@ function M.colorize(color, visual) local tpl = (require('nuwiki.config').options or {}).color_tag_template or vim.g.nuwiki_color_tag_template or '__CONTENT__' - tpl = tpl:gsub('__COLORFG__', color) + -- Function replacement returns the colour verbatim (a string replacement + -- would treat `%` specially). + tpl = tpl:gsub('__COLORFG__', function() return color end) local open_tag, close_tag = tpl:match('^(.*)__CONTENT__(.*)$') if not open_tag then open_tag, close_tag = string.format('', color), ''