061785c6a039e0397e3102da7aa9f44f2b520bef
34 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f65d861f72 |
feat(client): add Vim split/tab/back link-follow commands + true tab-drop
The plain-Vim client only defined VimwikiFollowLink; the split/vsplit/
tabnew/tabdrop/back command surface existed only in the Neovim branch.
Add :Vimwiki{Split,VSplit,Tabnew,TabDrop,GoBack}Link (+ :Nuwiki* aliases)
to the Vim branch, backed by a new nuwiki#commands#follow_link_drop()
helper that runs `:tab drop` to reuse an already-open tab.
Both clients now implement real tab-drop: open_uri gains a 'tabdrop'
case and M.follow_link_drop(); the Neovim TabDropLink commands and the
<C-S-CR> mappings (Vim + Lua) are repointed off the old tabnew cheat.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
5ee72c40e2 |
feat(client): send diagnostic.link_severity from both clients
Neovim adds a `diagnostic = { link_severity = 'warn' }` default so the value
is actually included in the payload. Vim reads the flat g:nuwiki_link_severity
global and nests it into the same `diagnostic` dict, keeping the two clients'
server-bound payloads identical.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
b922f0d612 |
test(config): verify Vim/Neovim config payload parity
Expose the init_options payload builders publicly in both clients
(M.init_options/M.server_settings in Lua, nuwiki#lsp#settings() in Vim) so
they can be inspected. Add editor harnesses that dump each client's
server-bound config as deterministic key=value lines and diff against a
shared golden file: nvim == golden and vim == golden together prove the two
clients send identical config. Add a server-side test asserting the Vim flat
shape and the Neovim {nuwiki:{...}} wrapper desugar to the same WikiConfig.
Wire both harnesses into CI.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
9bebd86577 |
feat(client): wire full Vimwiki command-parity surface
Register the named :Vimwiki*/:Nuwiki* entry points that previously
existed only as mappings or were missing entirely — including
RemoveSingleCB/RemoveCBInList, CatUrl, TabMakeDiaryNote,
NormalizeLink, Renumber{List,AllLists}, TableAlign, ChangeSymbol(InList),
ListToggle, Increment/DecrementListItem, and the DeleteLink/RenameLink/
GenerateTags compat aliases. Both the Vim (vim-lsp/coc) and Neovim
client functions are added to back them.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
cd9d587aab |
Add missing :Nuwiki* aliases and drop dead deferred-stub code
Every :Vimwiki* command now has a canonical :Nuwiki* counterpart (tables, colorize, clipboard paste, list-level/remove-done, and the Neovim-only split/tab link-follow variants). Remove the now-unused "not yet implemented" stub helpers (_not_yet, deferred, s:notify_deferred) and the stale "deferred" comments, since every command is implemented and server-backed. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
8ab6015405 |
Major clean up and improvements to vimwiki compatibility and documentation.
Reviewed-on: #1 |
||
|
|
9a751037ce |
fix(vim): redraw after :NuwikiExportBrowse shells out to the browser
A silent `:!xdg-open`/`:!open` swaps to the shell's alternate screen and returns without repainting, leaving the editor UI garbled until the next redraw. Force a full repaint after launching the browser. Neovim already avoids this via async jobstart. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
7f6d811a47 |
fix(wiki): build wiki picker list from config so it works before opening a file
The picker fetched the wiki list via workspace/executeCommand, but the LSP only starts once a vimwiki buffer exists — so a wiki could not be selected until one was already open (chicken-and-egg). Read the list straight from config instead (g:nuwiki_wikis / scalar fallback, the same source as open_wiki_path) and open the chosen index directly; that auto-starts the server via the FileType autocmd. - Vim: new public nuwiki#commands#wiki_list(); wiki_ui_select() uses it + inputlist() + :edit. Global :VimwikiUISelect / :NuwikiUISelect commands. - Neovim: config.wiki_cfg(n) + config.wiki_list() (init.lua delegates); commands.wiki_ui_select() uses them + vim.ui.select + :edit; global VimwikiUISelect / NuwikiUISelect user commands registered in setup(). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
0dfe0910fc |
fix(tables): cycle Tab into the next row instead of inserting one
Pressing <Tab> past the last cell unconditionally appended a new row below, even when there was already a next row to jump into. Tabbing through a header → separator → body table from the header row inserted a blank row between header and separator instead of landing in the first body cell. After the final cell, walk forward from the current row to the end of the table, skip any separator row, and jump to the first cell of the next data row. Only insert a fresh row when the cursor is on the table's last row (or only separator rows follow). Applied to both the Vim (autoload/nuwiki/commands.vim) and Neovim (lua/nuwiki/ commands.lua) sides so the two harnesses stay in sync; covered by new keymap tests in scripts/test-keymaps-vim.vim and test-keymaps.lua. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
8d11f7daef |
fix(lsp): send g:nuwiki_wikis to the server so per-wiki roots are known
The server received only wiki_root (the parent directory) and had no
knowledge of the per-wiki sub-roots configured in g:nuwiki_wikis / the
setup() wikis list. It therefore resolved all links relative to the
parent root, causing every link in a sub-wiki to appear broken.
Vim path (autoload/nuwiki/lsp.vim):
s:settings() now includes a 'wikis' key when g:nuwiki_wikis is set,
so the initialization_options and settings payloads carry the full
per-wiki config (root, diary_rel_path, file_extension, …).
Lua path (lua/nuwiki/lsp.lua):
Add resolved_opts() which merges vim.g.nuwiki_wikis into
config.options.wikis when the user configures via VimL rather than
setup({wikis=…}). init_options(), server_settings(), and the
pre-0.11 root_dir_for() fallback all use resolved_opts() so the
server receives the correct per-wiki roots in every code path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||
|
|
b3e2a72023 |
fix(lsp): add coc.nvim dispatch path to all Vim-side LSP calls
Commands were hardwired to vim-lsp (lsp#send_request, :LspDefinition,
:LspReferences, :LspRename). Users running coc.nvim got "vim-lsp not
loaded" on every command despite the plugin correctly detecting coc.
Changes:
autoload/nuwiki/commands.vim
- Add s:has_coc() (checks CocActionAsync exists)
- Add s:coc_wrap() adapter — converts coc's (err, result) callback into
the vim-lsp notification shape {response:{result:…}} so all existing
on_notification handlers work unchanged
- s:exec(): try vim-lsp first, fall back to CocActionAsync('runCommand',
…) with arguments spread as positional params, then error if neither
- Add s:jump_definition() helper — :LspDefinition vs CocActionAsync
- Add nuwiki#commands#backlinks() — :LspReferences vs CocActionAsync
- badd_link(): coc path uses CocAction('getDefinition') to resolve the
target URI without navigating
- follow_link_or_create(): replace inline :LspDefinition checks with
s:jump_definition()
- rename_file(): :LspRename vs CocActionAsync('rename')
ftplugin/vimwiki.vim
- VimwikiFollowLink / NuwikiFollowLink: dispatch through
nuwiki#commands#follow_link_or_create() instead of :LspDefinition
- VimwikiBacklinks / NuwikiBacklinks / VWB: dispatch through
nuwiki#commands#backlinks() instead of :LspReferences
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||
|
|
2c0cefb5c5 |
fix(mappings): respect g:nuwiki_wikis in global entry-point helpers
The global <Leader>ww / diary helpers read g:nuwiki_wiki_root directly, so they always opened the root-level index.wiki even when the user had configured per-wiki roots via g:nuwiki_wikis (e.g. personal_wiki.root = '~/.vimwiki/personal_wiki'). Vim path (autoload): replace the three open_*_path functions with a shared s:wiki_cfg(n) helper that checks g:nuwiki_wikis[n] first, then falls back to the scalar g:nuwiki_* vars and built-in defaults. Lua path (init.lua): replace the separate _wiki_index_path /_diary_path locals with a unified _wiki_cfg() that checks setup() opts.wikis, then vim.g.nuwiki_wikis (for users who configure via VimL rather than setup()), then scalar opts / g: vars. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
ba5c0002ab |
fix(compat): add vimwiki#vars shim for vimwiki-sync and vim-zettel
Third-party plugins such as vimwiki-sync and vim-zettel call
vimwiki#vars#get_wikilocal() — a function only provided by the original
vimwiki plugin. Without it, the after/ftplugin loaded by vimwiki-sync
errors on every wiki buffer open:
E117: Unknown function: vimwiki#vars#get_wikilocal
E121: Undefined variable: g:zettel_dir
Add autoload/vimwiki/vars.vim with lightweight stubs for the two keys
vimwiki-sync uses ('path', 'is_temporary_wiki'), reading values from
nuwiki's own g:nuwiki_wiki_root / g:nuwiki_file_extension config so
users don't have to duplicate settings. get_global() is stubbed as a
no-op for completeness.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||
|
|
9354e1c176 |
fix(mappings): add global entry-point keymaps and correct <Leader>ww/<Leader>wt targets
Two bugs prevented keymaps from working after a Dein install: 1. <Leader>ww and <Leader>wt both called diary_today() instead of wiki_index() / wiki_tab_index(). Fixed in keymaps.lua (Neovim) and ftplugin/vimwiki.vim (Vim). 2. All keymaps were buffer-local (<buffer> / buffer=bufnr), so they only activated inside an already-open .wiki file. Users had no way to reach the wiki from any other buffer, making the plugin appear broken on a fresh Vim start. Fix: register the eight <Leader>w* entry-point mappings globally — in setup() for Neovim (lua/nuwiki/init.lua) and in plugin/nuwiki.vim for plain Vim. To avoid a chicken-and-egg LSP dependency, the global mappings open files directly from config (wiki_root, diary_rel_path, file_extension); the LSP auto-starts via the FileType autocmd once the vimwiki buffer loads. Three new autoload helpers (open_wiki_path, open_diary_path, open_diary_index_path) provide the LSP-free path for the Vim side. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
7a0cec4920 |
fix(lists): skip own indent in smart_return when auto-indent is active
nvim defaults autoindent=on, so the indent inserted by Vim/Nvim after the <CR> we return was being doubled by our own indent prefix — pressing Enter on a sub-item produced a deeper-nested item instead of a same-level one. Detect any auto-indent mechanism (autoindent, smartindent, cindent, indentexpr) and let it own the indentation; otherwise add it ourselves so vim's noautoindent default still works. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
5b6f789c8d |
fix(tables): auto-realign on edit + stop dragging trailing | onto new row
Two bugs in the insert-mode table editing path: 1. <Tab> past the last cell (and <CR> on a table row) used <CR>+typed keystrokes to create a new row, which split the current line at the cursor — pulling the trailing | down onto the next line and mangling the formatting. Both paths now go through helpers that append() a fresh row beside the current one without touching it. 2. The table never realigned to new content. Ported the LSP-side render_aligned_table algorithm to Lua + VimL so smart_tab, smart_shift_tab, and smart_return tighten column widths locally on every navigation. No vim-lsp / nuwiki server roundtrip required. Neovim side schedules the work via vim.schedule (textlock-safe); plain Vim hands off via <Cmd>:call …<CR> to keep insert mode and avoid the cmdline-mode flash. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ae96562969 |
parity(vim): port text objects + folding, fix smart_return textlock
Close the remaining Vim-vs-Neovim functional gaps:
* Text objects — new `autoload/nuwiki/textobjects.vim` mirroring
`lua/nuwiki/textobjects.lua`. All five pairs (ah/ih, aH/iH,
al/il, a\/i\, ac/ic) wired in the Vim path of `ftplugin/vimwiki.vim`
via the classic `:<C-u>call` idiom that works cleanly in both
operator-pending and visual modes.
* Folding — new `autoload/nuwiki/folding.vim` providing a regex
`foldexpr` over headings, plus a tidy `foldtext`. Wired in the
Vim path; opts out via `let g:nuwiki_no_folding = 1`.
* `smart_return` was using `setline()` / `append()` inside an
`<expr>` callback — fine on Neovim but plain Vim's stricter
textlock raised E565. Rewrote as pure keystrokes (matches the
Lua version): table rows insert via `<CR>...<Esc>0li`, empty
list lines break via `<Esc>0DA<CR>`.
* Function-name parity: added
`nuwiki#commands#heading_add_level`,
`nuwiki#commands#heading_remove_level`,
`nuwiki#commands#table_move_column_{left,right}` as thin
aliases over the original short names so the public
`nuwiki#commands#*` surface matches `require('nuwiki.commands').*`.
Test harness:
* `scripts/test-keymaps-vim.vim` extended from 12 to 30 cases:
4 smart_return, 3 smart_tab/<S-Tab>, 3 named-command exists,
4 text-object helpers, 1 `dah` end-to-end, 2 folding, and the
original 12 pure-VimL cases. (Visual-mode mark inspection in
`vim -e -s` stays unreliable, so text objects are exercised
at the helper level plus one operator-pending end-to-end.)
Gates: 456 Rust / 39 Neovim / 30 Vim.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
2562a046db |
parity(6): insert-mode <Tab>/<S-Tab> table cell nav
`<Tab>` / `<S-Tab>` in insert mode navigate between table cells when the cursor is on a `|…|` row, otherwise they pass through to their default insert-mode behaviour (literal tab / shift-tab). Same `<expr>`-mapping idiom as Cluster 5's smart_return. <Tab> next cell; creates a fresh row below if past the last cell <S-Tab> previous cell; no-op past the first Cursor lands immediately after the destination cell's leading `|`, matching upstream vimwiki's `vimwiki#tbl#go_*_cell` behaviour. Both Lua and VimL counterparts wired into ftplugin's existing table_editing block (gated alongside `gqq`/`<A-Left>`). Tests: 4 new in scripts/test-keymaps.lua — next cell, new-row overflow, previous cell, and pass-through outside a table. Gates: 421 Rust / 39 Neovim / 12 Vim. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e347f605ff |
parity(5): smart <CR> in insert mode (VimwikiReturn)
Bind `<CR>` in insert mode as an `<expr>` map to a `smart_return`
helper. Behaviour matches upstream vimwiki's `:VimwikiReturn`:
- On a list line with content → continue the list with the same
marker on a new line (preserving leading checkbox `[ ]` if any).
- On an empty list line (marker only) → clear the marker and break
out of the list with a plain newline.
- Inside a `|…|` table row → insert a fresh empty row below with
the same column count, cursor lands inside the first cell.
- Otherwise → plain `<CR>`.
Implementation note: the helper is `<expr>`-bound, which means
textlock is active and the buffer can't be mutated from inside the
callback. The function returns key sequences only — including
`<Esc>0DA<CR>` for the empty-list break and `<Esc>0li` for the
table-row cursor jump — so every effect flows through Vim's normal
keystroke pipeline and stays undo-coherent.
Both Lua and VimL counterparts shipped.
Tests: 4 new in scripts/test-keymaps.lua covering list continuation,
checkbox preservation, empty-marker break-out, and new table row.
Gates: 421 Rust / 35 Neovim / 12 Vim.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
46ae618b5f |
parity(2): insert-mode list bindings (<C-D>/<C-T>/<C-L><C-?>)
Match upstream vimwiki's insert-mode list editing: <C-D> list_change_level(-1) dedent current item <C-T> list_change_level(+1) indent current item <C-L><C-J> list_cycle_symbol(+1) cycle marker forward <C-L><C-K> list_cycle_symbol(-1) cycle marker backward <C-L><C-M> list_toggle_or_add_chk toggle if has checkbox, else add `[ ]` Cycle order matches vimwiki's canonical run: - → * → # → 1. → 1) → a) → A) → i) → I) → (wrap) Both Lua and VimL paths wired. Lua callbacks fire directly (no `<C-o>` dance) since list_change_level / changeSymbol mutate the buffer via the LSP applyEdit pipeline, which works cleanly in insert mode. VimL keeps the `<C-o>:call …<CR>` idiom since that's the standard there. Harness adds 5 cases covering the new insert-mode bindings plus one direct LSP roundtrip for changeSymbol — surfaced a long-standing stale-binary footgun in test-keymaps.sh (rebuilt only when bin was missing; now rebuilds every run since incremental cargo is fast). Harness also captures server log_messages and dumps them on failure so future swallowed-error bugs (Err → log + Ok(None)) are visible. Gates: 421 Rust / 25 Neovim / 12 Vim. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
ee0309b3c2 |
parity(1): vimwiki per-wiki config keys, table colspan/rowspan,
named link commands, mouse maps
Parity audit Cluster 1 — small wins.
WikiConfig (server) extended with 14 vimwiki globals:
- `index` (`g:vimwiki_index`), wired into `:VimwikiIndex` so users
with a custom index page stem (e.g. `home`) get the right file.
- `diary_frequency`, `diary_start_week_day`, `diary_caption_level`,
`diary_sort`, `diary_header` — diary keys (weekly/monthly/yearly
semantics land in Cluster 4; the keys parse now so config
migration doesn't need to wait).
- `maxhi`, `listsyms`, `listsyms_propagate`, `list_margin`,
`links_space_char`, `nested_syntaxes`, `auto_toc` — list +
highlight knobs the renderer/handlers can consult.
All keys threaded through `RawWiki` → `WikiConfig::from(RawWiki)`,
defaults centralised in `wiki_defaults()` so `empty()`, `from_root()`,
the legacy single-wiki path, and the `RawWiki` impl stay in sync.
Lua front-door (`lua/nuwiki/config.lua`) now documents the multi-wiki
`wikis = {…}` shape with every accepted per-wiki key — users who
prefer Lua tables no longer have to round-trip through
`init_options` raw JSON.
HTML renderer (`crates/nuwiki-core/src/render/html.rs`):
- New `table_spans()` pre-pass resolves vimwiki's `>` (col_span) and
`\\/` (row_span) continuation markers into proper HTML
`colspan="N"` / `rowspan="N"` attributes on the lead cell. Continuation
cells emit nothing, matching what browsers expect.
- The old behaviour (emitting `class="col-span"` / `class="row-span"`
as visual markers) is gone — replaced with real merging so the
rendered HTML matches the source semantics.
Named ex-commands:
- `:VimwikiNextLink` / `:VimwikiPrevLink` — were keymapped only
(`<Tab>`/`<S-Tab>`); now have explicit `:` commands in both editor
paths, dispatching to new `nuwiki.commands.link_next` / `link_prev`
pure-Vim/Lua helpers.
- `:VimwikiBaddLink` — adds the link target's file to the buffer
list without switching focus. Goes through
`textDocument/definition` and runs `:badd <fname>` on the resolved
URI.
Mouse maps (opt-in via `mappings.mouse = true` in Lua or
`g:nuwiki_mouse_mappings = 1` in Vim):
- `<2-LeftMouse>` follows, `<S-2-LeftMouse>` / `<C-2-LeftMouse>`
follow in split/vsplit, `<MiddleMouse>` adds to buflist,
`<RightMouse>` goes back. Mirrors upstream vimwiki's defaults.
Tests: 7 new in `parity_cluster_1.rs` covering the per-wiki config
defaults + raw JSON parse, the legacy-single-wiki path, colspan
folding into a lead cell with no leftover class markers, rowspan
across rows, no-attrs when the table has no spans, and a sanity
paragraph render. Total 421 Rust tests pass; clippy clean; both
keymap harnesses still green (20 nvim + 12 vim).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
de8b8fa30d |
feat(13.1): colorize + color_dic → ColorNode renderer
Closes the last pending §13.1 entries: - **`nuwiki.colorize`** (client-side, Lua + VimL) — wraps the word at cursor (or the visual selection on Neovim) in an inline `<span style="color:%c">…</span>` template. Matches vimwiki's default `color_tag_template`. Bound to `<Leader>wc` in both normal and visual mode on both editor paths; the previous "deferred" stub is gone. - **`color_dic` → renderer** — `HtmlConfig` gains a `color_dic: HashMap<String, String>` field reading the same key out of `initializationOptions` / `didChangeConfiguration`. The HTML export path threads it into `HtmlRenderer::with_colors`; `ColorNode` now picks `style="color:<value>"` when the colour name is in the dict, falling back to the existing `class="color-<name>"` when it isn't. SPEC §13.1 is fully checked off — the table moved from "deferred sketch" to a per-command status table marking all 11 commands done, plus a note on the `color_dic` follow-up landing alongside. README's "Phase 14 list & table edit commands" row now reads ✅ without the deferred-subset caveat, and the §13.1-blocked text-objects note in the keymaps section is rephrased as "planned follow-ups". Tests: 4 new in `phase17_colorize.rs` covering the renderer fallback when the dict is empty, the inline-style emission for a listed name, the fallback path for an unlisted name in a populated dict, and the `initializationOptions` JSON shape. Total 414 Rust tests pass; clippy clean; keymap harness still 20/20. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
4245424d2f |
feat(13.1-B): table rewriters — insert, align, moveColumn
Closes the table-rewriter cluster from SPEC §13.1. Server-side (ops module): - `render_blank_table(cols, rows)` — pure templating: header row, separator `|--|--|--|`, then `rows` empty data rows. Wired through `table_insert` which dispatches a `TextEdit` at the cursor. - `table_align_edit` — locates the `TableNode` containing the cursor line, computes max width per column across every row, re-emits the table with each cell space-padded to its column's width. The parser drops `|---|---|` separator rows but sets `TableNode.has_header`; the renderer re-inserts a padded separator after the header so the result still parses. - `table_move_column_edit` — column-under-cursor swap with the left or right neighbour (`dir = "left" | "right"`). Cursor column is computed by counting pipe separators before the cursor's byte offset on the row's line. Column widths swap alongside the data so the post-swap table stays aligned. No-ops cleanly when the swap would fall off either edge. Editor glue: - `lua/nuwiki/commands.lua` / `autoload/nuwiki/commands.vim` — the three "not yet implemented" stubs are replaced with real LSP dispatchers. `table_insert` accepts optional `cols`/`rows` arguments (defaults: 3×2). - Default keymaps `gqq` / `gq1` / `gww` / `gw1` now call `table_align`; `<A-Left>` / `<A-Right>` call `table_move_column_left` / `_right`. No more "deferred" notifications on the table keys. Tests: 8 new in `cluster_b_table_rewriters.rs` — blank-table shape (3×2 + 1×1), column-width alignment with separator-row repad, swap- right shape, swap-left clamp at column 0, no-table-found fallbacks, COMMANDS list completeness. Total 410 Rust tests pass; clippy clean; Neovim keymap harness still 20/20. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
9d34c8baa2 |
feat(13.1-A): list rewriters — removeDone, renumber, changeSymbol/Level
Closes the four-command list-rewriter cluster from SPEC §13.1. Server-side (commands.rs ops module): - `parse_symbol` / `render_marker` — round-trip between `:VimwikiListChangeSymbol` arg shapes (`-`, `1.`, `a)`, `i)`, `Dash`, `Numeric`, …) and the rendered marker text. `render_marker` knows how to spell `a/b/…/z/aa` and `i/ii/iv/v/…/MMM` for the alphabetic + roman variants. - `remove_done_edit` — walks every list (recursively through blockquotes and sublists), emits delete TextEdits for items whose checkbox is `Done` or `Rejected`. Item span gets extended through the trailing newline so we don't leave behind empty lines. Accepts an optional `position` to scope the operation to the item under cursor + its descendants. - `renumber_edit` — finds the list containing the cursor line (or every list when `whole_file: true`), re-sequences numeric markers in-place. Unordered lists are left alone. - `change_symbol_edit` — rewrites the leading marker on a single item, or every item in a list when `whole_list: true`. Uses `render_marker` so ordered variants get the right index. - `change_level_edit` — re-indents the marker line (or every line of the item subtree when `whole_subtree: true`) by ±2 spaces per level. Dedent clamps at column 0. - `find_list_at_line` + `find_marker_span` — pure helpers reused by the three above. Editor glue: - `lua/nuwiki/commands.lua` / `autoload/nuwiki/commands.vim` — removed all four "not yet implemented" stubs and wired them to the real LSP commands. Lua also exposes `list_change_lvl(direction)` for the `:VimwikiListChangeLvl decrease|increase` compat entry. - Keymaps for `glh` / `gll` / `gLh` / `gLl` (list level single + subtree), `glr` / `gLr` (renumber list + whole-file), `gl<Space>` / `gL<Space>` (remove done items) now hit the real commands instead of printing a "deferred" notification. Tests: 16 new in `cluster_a_list_rewriters.rs` covering symbol parsing, marker rendering (alpha + roman + numeric), removeDone shape, scoped removeDone, no-match cases, renumber sequencing, whole-file walk, changeSymbol single + whole-list, changeLevel single + subtree + clamp, and COMMANDS list completeness. Total 402 Rust tests pass; clippy clean; keymap harness still at 20/20. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
cebc806ce3 |
feat(13.1-C): link helpers — pasteWikilink, pasteUrl, normalize
Closing out §13.1's smallest cluster. Three commands move from "not yet implemented" stubs to real behaviour: - `nuwiki.link.pasteWikilink` (server, executeCommand) — derives the current page name from the source URI + wiki root, returns a `WorkspaceEdit` inserting `[[<page>]]` at the requested cursor position. - `nuwiki.link.pasteUrl` (server) — same lookup, but the inserted text is the page's relative HTML output URL (`<page>.html`, including subdir segments) so the snippet survives when the export root moves. - `nuwiki.link.normalize` (client) — wraps the word at cursor as `[[word]]` without following. Reuses the `wrap_cword_as_wikilink` helper that already powers the `<CR>` two-step. Pure-VimL on the Vim path; pure-Lua on the Neovim path. No LSP round-trip. Keymaps: - `+` (normal + visual) now actually calls `normalize_link` on both editor paths instead of stubbing with a "deferred" notification. Tests: - 5 new Rust unit tests in `cluster_c_link_helpers.rs` covering command-list presence + the page-name derivation for root and subdirectory pages + the URL / wikilink shape strings. - Neovim keymap harness gains a `links.normalize_via_+` case (20 passing now, up from 19). Vim harness inherits the same command-presence check via its existing smoke tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
5fdd7a842e |
test(keymaps): Vim path harness + fix 25 broken one-liner functions
User asked about the Vim path of the keymap suite. Building it
surfaced a real bug: 25 of our autoload functions were written as
`function! foo() abort | call bar() | endfunction` one-liners, which
isn't valid Vim syntax — `function!` requires a multi-line body, and
Vim parses the `|` after `abort` as an unexpected trailing character
(E488). Most invocations of the buggy autoload functions errored
out the moment Vim tried to parse them, which is why the user saw
broken keymaps and confusing diagnostics.
Rewrote all 25 one-liners (`autoload/nuwiki/commands.vim`) into the
standard three-line form. Affected groups: `diary_*`, `toggle_list_item`,
`cycle_list_item`, `reject_list_item`, `heading_add`, `heading_remove`,
`toc_generate`, `links_generate`, `export_current` / `_all` /
`_all_force` / `_rss`, and the §13.1 deferred stubs (`list_change_lvl`,
`list_remove_done`, `table_*`, `colorize`, `paste_link`, `paste_url`).
Added `scripts/test-keymaps-vim.{sh,vim}` — a Vim-side counterpart of
the Neovim harness covering the pure-VimL bindings (header nav,
link nav, `o`/`O` bullet continuation, `<CR>` wrap step). The
LSP-roundtrip bindings (`<C-Space>`, `=`, `-`, …) stay on the Neovim
side because vim-lsp's async layer uses timers that don't fire
inside `vim -e -s` headless mode — their server-side codepath is
already exercised by the Neovim harness and the cargo test suite.
Vim harness covers 12 cases: filetype + 2 command-presence smoke
tests, 4 header-nav (`]]`/`[[`/`]=`/`]u`), `<Tab>` link-nav, `<CR>`
wrap-on-first-press, and 3 bullet-continuation flows.
CI: extended the existing `keymaps` job to install both nvim + vim
and run both harnesses. Verified locally:
Neovim harness: 19 passed, 0 failed
Vim harness: 12 passed, 0 failed
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
4905858d5e |
fix(follow-link): two-step <CR> — first wraps, second follows
User feedback: the smart `<CR>` should give the user a chance to
review/edit the freshly-created wikilink before committing to the
follow. Matches upstream vimwiki's flow.
`follow_link_or_create` (Lua + VimL) now:
1. Cursor already inside `[[…]]` → follow immediately.
2. Cursor on a bare word → wrap it as `[[word]]`, place cursor
inside, and STOP. A second `<CR>` then follows.
3. Neither — fall through to plain definition request so users
can still chord follow without a target word.
Verified: cursor on `MyPage rest` →
1st `<CR>` → `[[MyPage]] rest` (buffer unchanged)
2nd `<CR>` → opens `MyPage.wiki` (the synthesised future page).
381 tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
347e2f02f7 |
fix(follow-link): create missing page + wrap word on <CR>
Two missing pieces in the previous "follow creates page" fix:
1. **`wiki_root` never reached the server.** The Lua glue (and the
Vim autoload glue) was sending the config under `settings` — that
field is for `workspace/didChangeConfiguration` notifications, not
for `initialize`. The server's `Config::from_init_params` reads
`initialization_options`, so `wiki_root` arrived as `None`, the
`Wiki` aggregate was empty, and `wiki_for_uri` returned `None` →
`resolve_target_uri` bailed before reaching my synthesise path,
leaving `<CR>` with "No Location Found".
Both glues now send the same payload under both keys:
- `lua/nuwiki/lsp.lua` → adds `init_options = init_options()`
alongside the existing `settings = …`.
- `autoload/nuwiki/lsp.vim` → adds `initialization_options` to
the `lsp#register_server` call.
The server keeps reading from `initializationOptions` at startup
*and* honouring `didChangeConfiguration` later (Phase 11 plumbing).
2. **No-wiki fallback in the server.** Even with the glue fix above,
users who launch nuwiki on an ad-hoc `.wiki` file outside any
workspace folder would still get an empty `wikis` list. The
`LinkKind::Wiki` arm now falls back to a new
`synthesise_page_uri_next_to(source_uri, path)` which builds the
future page next to the current file. `<CR>` on `[[NewPage]]`
opens `./NewPage.wiki`, save creates it.
3. **`<CR>` on a plain word now wraps + follows.** Mirrors vimwiki's
`:VimwikiFollowLink`: when the cursor isn't already inside
`[[…]]`, the new `nuwiki.commands.follow_link_or_create` /
`nuwiki#commands#follow_link_or_create` first wraps the word
under cursor as `[[word]]`, places the cursor inside the link,
then dispatches `vim.lsp.buf.definition()` /
`:LspDefinition`. Both editor paths now bind `<CR>` /
`<S-CR>` / `<C-CR>` / `<C-S-CR>` to this smart variant.
Verified end-to-end:
- `[[NewPage]]` with the rebuilt binary: definition returns
`file:///tmp/wiki/NewPage.wiki` as a Location, so the editor
opens an empty buffer for it.
- A bare `MyPage` word becomes `[[MyPage]]` in the buffer before
the follow request fires.
Total 381 tests still pass; fmt + clippy clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
c3a8eae9d0 |
fix(vim): vim-lsp detection + drop hang-prone echo lines
`autoload/nuwiki/lsp.vim` probed vim-lsp via `exists('*lsp#register_server')`
but autoload functions aren't loaded until first call — that check
always returned false on the dev script's setup, so the plugin
printed "no supported LSP client found" even after vim-lsp had been
cloned + added to the runtimepath.
vim-lsp's `plugin/lsp.vim` sets `g:lsp_loaded = 1` only when its
hard requirements (`json_encode`, `timers`, `lambda`) are met, so
that's the reliable presence flag. Switched to `exists('g:lsp_loaded')`.
Also replaced `echo` with `echomsg` in the generated `start-vim.sh`
vimrc — the two startup status lines were pushing past `'cmdheight'`
and triggering "Press ENTER or type command to continue", which made
non-interactive invocations exit before the buffer was usable.
`echomsg` writes to `:messages` history without the prompt.
Verified with `vim --clean -u $DEV/vimrc index.wiki`:
- `g:lsp_loaded = 1`
- `filetype = vimwiki`
- `:VimwikiTOC` defined (buffer-local), `b:did_ftplugin = 1`
- No "press ENTER" hang.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
f1bf70d448 |
fix(vim): gate Neovim TS-style highlight groups + correct bin path lookup
Plain Vim spat a wall of `W18: Invalid character in group name` on
every buffer load because `syntax/vimwiki.vim` declared the
LSP-token highlight defaults with Tree-sitter-style names
(`@vimwikiHeading.level1`, `@vimwikiBold`, …). Those work on Neovim
but Vim rejects `@` and `.` in group names.
- `syntax/vimwiki.vim` now guards the `@vimwiki*` block with
`has('nvim')`. The static regex fallback below it (which uses
Vim-compatible `nuwiki*` group names) keeps highlighting working
on plain Vim.
While reproducing, also noticed `autoload/nuwiki/lsp.vim`'s
`s:bin_path` reported the binary at `/home/gfranco/bin/nuwiki-ls`
(only two dirs under `$HOME`) instead of
`/home/gfranco/git-repositories/nuwiki/bin/nuwiki-ls`. Cause:
`expand('<sfile>:p')` inside a function resolves to the *calling*
script at invocation time, not this autoload file. Captured the
plugin root via `<sfile>` at script-load time into a new
`s:plugin_root` and used it in `bin_path()`.
Verified with `vim -u .vimrc index.wiki`:
- `:messages` is empty of W18 warnings.
- The "binary not found" path (when no built binary exists) now
prints the correct repo-relative path.
Total 377 Rust tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
d58a34a2d7 |
feat(vim): close keymap gap with Neovim (47 → 63 maps)
The previous commit registered the parity surface on Neovim but only ported ~75% to plain Vim. Filling the remaining 16: autoload/nuwiki/commands.vim — new functions: - `wiki_ui_select` — Vim has no `vim.ui.select`, so route through `inputlist()` after fetching the wiki list via the LSP. - `open_below_with_bullet` / `open_above_with_bullet` — port of the Lua `o` / `O` continuation that preserves the list marker (and a `[ ]` checkbox when present) on the new line. - `next_header` / `prev_header` / `next_sibling_header` / `prev_sibling_header` / `parent_header` — port of the Lua heading jumper. Pure VimL, no LSP round-trip. ftplugin/vimwiki.vim (Vim path) new mappings: - `<Leader>ws` → `wiki_ui_select` - `]=` / `[=` → next / prev sibling header - `]u` / `[u` → parent header - `o` / `O` → bullet continuation - `gl<Space>` / `gL<Space>` → :VimwikiRemove*CB stubs - `gq1` / `gw1` → :VimwikiTableAlignQ1/W1 stubs - x-mode variants for `+`, `<Leader>wc`, `gln`, `glp`, `glx` Replaced the regex-based `]]` / `[[` (which just searched `^\s*=\+\s`) with calls into the new heading nav so they skip non-heading `=` runs and respect heading levels. Verified end-to-end: `silent map <buffer>` reports 63 maps on Vim, matching Neovim's 63. All 377 Rust tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
04df170791 |
feat(vim): :Vimwiki* / :Nuwiki* commands for plain Vim via vim-lsp
The Phase 19 ftplugin only registered the command surface on Neovim
and finished early for plain Vim, leaving `start-vim.sh` users with
only commentstring + suffixesadd — exactly the "no commands" report.
`autoload/nuwiki/commands.vim` now ships the Vim-side dispatch:
- `s:exec` sends `workspace/executeCommand` through vim-lsp's
`lsp#send_request`. Optional callback for commands that return data.
- `s:open_uri_from` reads `{ uri }` out of the LSP response and runs
`:edit` (or `:tabedit` for the tab variants).
- `s:results_to_qf` lifts `checkLinks` / `findOrphans` / `tags.search`
arrays into the quickfix list and opens `:copen` — same UX as the
Neovim path.
- `s:open_browser` fires `open` / `xdg-open` after `export.browse`.
- §13.1 deferred commands stub out with a "not yet implemented"
notification so users get the same signal as on Neovim.
`ftplugin/vimwiki.vim` defines the same `:Vimwiki*` / `:Nuwiki*`
command set on the plain-Vim path, each delegating to its
`nuwiki#commands#…` autoload counterpart. `:VimwikiFollowLink` /
`:VimwikiBacklinks` map straight to vim-lsp's `:LspDefinition` /
`:LspReferences`. coc.nvim users can still use `:CocCommand` directly
and ignore the aliases entirely.
Verified with `vim -e -s -u .vimrc index.wiki`:
filetype=vimwiki, did_ftplugin=1,
:VimwikiTOC, :NuwikiIndex, :VimwikiToggleListItem all defined.
Total 377 tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
ee61d40872 |
phase 9: editor glue — plugin, LSP wiring, install, health, docs
P5 + P6 resolved (SPEC §11): Neovim 0.11+ and Vim 9.1+.
Universal Vim entry:
- plugin/nuwiki.vim dispatches Vim vs Neovim. Neovim path waits for
the user's `require('nuwiki').setup()` call; Vim path starts the
LSP client on `FileType vimwiki` and exposes `:NuwikiInstall`.
- ftdetect/nuwiki.vim associates `*.wiki` with the `vimwiki` filetype.
- ftplugin/nuwiki.vim sets commentstring + comments + formatoptions +
suffixesadd + iskeyword, with a clean `b:undo_ftplugin`.
- syntax/nuwiki.vim ships default `@vimwiki*` highlight links for the
semantic-token legend (with `level1..6` + `centered` modifiers),
plus a minimal regex fallback so static highlighting works before
the LSP attaches.
Lua glue (Neovim 0.11+):
- init.lua exposes setup() / install() / health() — wiring only per
SPEC §6.2.
- config.lua holds the §7.5 schema (wiki_root / file_extension /
syntax / log_level) and a tbl_deep_extend apply().
- lsp.lua registers via `vim.lsp.config{} + vim.lsp.enable` on 0.11+;
falls back to a FileType autocmd calling `vim.lsp.start` on older
builds. Root dir walks up for `.git` / `.nuwiki`, then uses
wiki_root.
- install.lua does target-triple detection (Linux gnu/musl x x86_64/
aarch64, macOS arm/x86, Windows), curl+tar download from the
release URL, cp/chmod into bin/, with a cargo fallback. Honours
`g:nuwiki_build_from_source`.
- health.lua implements `:checkhealth nuwiki` per §7.6.
VimL glue (Vim 9.1+):
- autoload/nuwiki/lsp.vim follows §7.4 preference order:
vim-lsp (calls `lsp#register_server`) → coc.nvim (prints the
coc-settings.json snippet) → error.
- scripts/download_bin.vim mirrors the Lua installer for Dein /
vim-plug build hooks — same target triples, same download →
fallback → cargo build chain.
Help: doc/nuwiki.txt with tags for install (lazy.nvim / vim-plug /
Dein), config options, health, LSP feature list, `:NuwikiInstall`.
Rust workspace unchanged this phase; 172 tests still green locally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
cf336ee839 |
phase 0: scaffold workspace, plugin layout, and CI
Lay down the empty repo skeleton defined in SPEC.md §5 so subsequent phases have somewhere to land: - Cargo workspace (resolver v2, edition 2021, MSRV 1.83) with nuwiki-core, nuwiki-lsp, nuwiki-ls — dep direction matches §6.2. - Vim/Neovim plugin directory layout (plugin, lua, autoload, ftdetect, ftplugin, syntax, doc, scripts) with header-only stubs. - .gitea/workflows/ci.yaml running fmt, clippy -D warnings, and tests pinned to Rust 1.83. - README, dual MIT/Apache-2.0 license texts, .gitignore. Verified: cargo check / fmt --check / clippy / test all clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |