Test Markdown-Oxide in Neovim
Overview
markdown-oxide is an LSP server for markdown/Obsidian vaults. It cannot be tested via browser or direct CLI invocation -- it requires an editor with LSP support. Neovim v0.11+ is the recommended testing environment.
Outcome
Verify that markdown-oxide LSP features work correctly in Neovim, including wiki link completions, block linking (with block ID insertion via :wall), go-to-definition, hover with backlinks, and tag completions against the TestFiles/ directory. Testing is done in two recorded phases: first reproduce/demonstrate the current behavior, then validate the fix or expected behavior.
Procedure
1. Build the markdown-oxide binary
cd ~/repos/markdown-oxide && cargo build
sudo cp target/debug/markdown-oxide /usr/local/bin/markdown-oxide
Verify it's on PATH: which markdown-oxide
For release builds (slower but optimized): cargo build --release then copy from target/release/.
2. Install Neovim (if not installed)
curl -fsSL -o /tmp/nvim.appimage https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.appimage
chmod +x /tmp/nvim.appimage
cd /tmp && /tmp/nvim.appimage --appimage-extract
sudo mv /tmp/squashfs-root /opt/nvim
sudo ln -sf /opt/nvim/usr/bin/nvim /usr/local/bin/nvim
Verify: nvim --version | head -1 (should be v0.11+)
On headless/VM environments, the AppImage may fail with FUSE errors. Use --appimage-extract to extract without FUSE.
3. Configure Neovim for markdown-oxide
Create ~/.config/nvim/init.lua:
-- Minimal Neovim config for testing markdown-oxide LSP
vim.opt.number = true
vim.opt.signcolumn = "yes"
vim.opt.completeopt = { "menu", "menuone", "noselect" }
vim.lsp.config('markdown_oxide', {
cmd = { 'markdown-oxide' },
filetypes = { 'markdown' },
root_markers = { '.obsidian', '.moxide.toml', '.git' },
capabilities = {
workspace = {
didChangeWatchedFiles = {
dynamicRegistration = true,
},
},
},
})
vim.lsp.enable('markdown_oxide')
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args)
local opts = { buffer = args.buf }
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, opts)
vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action, opts)
vim.keymap.set('i', '<C-Space>', function()
vim.lsp.completion.trigger()
end, opts)
vim.lsp.completion.enable(true, args.data.client_id, args.buf, { autotrigger = true })
local client = vim.lsp.get_client_by_id(args.data.client_id)
if client and client.name == "markdown_oxide" then
vim.api.nvim_create_user_command("Daily", function(cmd_args)
vim.lsp.buf.execute_command({ command = "jump", arguments = { cmd_args.args } })
end, { desc = "Open daily note", nargs = "*" })
end
end,
})
This uses Neovim 0.11+ built-in LSP support (vim.lsp.config / vim.lsp.enable). The dynamicRegistration = true setting is critical for block linking to work.
4. Launch Neovim
Open a terminal emulator (e.g., konsole) and launch Neovim on TestFiles:
konsole --workdir ~/repos/markdown-oxide/TestFiles -e bash -c "nvim Test.md" &
Wait for Neovim to open and the LSP to attach. Verify with: pgrep -a markdown-oxide
5. Test LSP features
Testing is split into two recorded phases:
Phase 1: Reproduce current behavior
Start a screen recording (recording_start). Demonstrate the current state of each feature before any fix. This establishes a baseline and captures any issues:
- Annotate the recording: "Phase 1: Reproducing current behavior in Neovim"
- Exercise each feature below and note what works and what doesn't
- Stop the recording (
recording_stop) when done
Phase 2: Validate the fix
After applying the fix (rebuild markdown-oxide, copy to PATH, quit and relaunch Neovim to restart the LSP):
- Start a new screen recording (
recording_start) - Annotate the recording: "Phase 2: Validating fix in Neovim"
- Re-test each feature and confirm it works correctly
- Stop the recording (
recording_stop) when done
Test each feature:
Wiki Link Completions
- In Normal mode, press
Gto go to end of file, thenoto open a new line - Type
[[-- a completion menu should appear with files, headings, and blocks - Type to fuzzy-filter (e.g.,
[[Resoshould show "Resolved File") - Press
Escapeanduto undo when done
Block Linking (CRITICAL Neovim-specific feature)
- Press
oto open a new line in insert mode - Type
[[(two brackets then a space) -- this triggers the unindexed block completer - A list of text blocks from across the vault appears
- Type to fuzzy-filter (e.g.,
test file with some) - Use
Ctrl+n/Ctrl+pto navigate,Ctrl+yto accept - After accepting, a link like
[[Another Test 2#^f311g|text]]is inserted with a generated block ID - You MUST run
:wallto write all buffers -- the block ID is inserted into the target file as an unsaved buffer edit - Verify the block ID was inserted: check the target file for the
^blockidsuffix
Go-to-Definition
- Navigate to a line with
[[This is another link]](around line 31) - Position cursor inside the link text (e.g.,
fTto find theT) - Press
gd-- should navigate toThis is another link.md - Press
Ctrl+oto go back
Hover
- With cursor on a wiki link like
[[This is another link]] - Press
K(Shift+k) -- a hover popup should show:- File Preview: contents of the linked file
- Backlinks: all files referencing this entity
- Press
Escapeor any key to dismiss
Tag Completions
- Press
oto open a new line, type#ta - Should show hierarchical tags:
tag,tag/subtag,tag/othersubtag,mapofcontent/tag, etc. - Press
Escapeanduto undo when done
References (optional)
- Place cursor on a heading like
# Heading 1 - Press
grto find all references/backlinks
Testing Heading Links Specifically
- Heading links with dashes (
[[File#My-Heading]]) should resolve correctly - Heading links with spaces (
[[File#My Heading]]) should also resolve (lenient matching) - Completions for headings show dash-separated format only when
heading_slugconfig is true - Test headings in
TestFiles/Test.md:# Heading 1,## Here is a nested,### Here is a nested third - Test cross-file heading links using
TestFiles/Resolved File.mdwhich has# Resolved Heading
6. Post recordings to PR
After both recording phases are complete, post the recordings as comments on the PR:
Use
git_comment_on_prto post the Phase 1 recording with a comment like:Phase 1: Reproducing current behavior in Neovim
Use
git_comment_on_prto post the Phase 2 recording with a comment like:Phase 2: Validating fix in Neovim
This provides reviewers with visual evidence of the issue and its resolution.
7. Clean up
Undo any test edits: Escape, then u repeatedly until "Already at oldest change".
Quit without saving: :qa!
Available Test Files
TestFiles/Test.md-- Main test file with headings, wiki links, block refs, tagsTestFiles/Resolved File.md-- Has# Resolved Headingand heading linksTestFiles/Another Test.md-- Has# This is a test headingand## This is a nested test headingTestFiles/This is another link.md-- Target for wiki link navigation tests
Specifications
- Wiki link completions must show files, headings, and block references
- Block linking must insert a
^blockidinto the target file after:wall - Go-to-definition must navigate to the correct target file
- Hover must show file preview and backlinks
- Tag completions must show hierarchical tags
- The LSP server process (
markdown-oxide) must be running (verify withpgrep) - Two screen recordings must be produced: one showing current behavior (reproduce), one showing the fix (validate)
- Both recordings must be posted as comments on the PR
Advice
- Neovim 0.11+ is required for
vim.lsp.config/vim.lsp.enable. Older versions neednvim-lspconfigplugin. - The
dynamicRegistration = truecapability is essential for block linking and the "Create Unresolved File" code action to work. - On headless/VM environments, the AppImage may fail with FUSE errors. Use
--appimage-extractto extract without FUSE. - Block completions are triggered by
[[(with a space after[[). Without the space, you get regular file/heading completions. - After accepting a block completion, the block ID is edited into the target file's buffer but NOT saved. You must run
:wallto persist it. - For a richer completion UI, install
nvim-cmpwithcmp-nvim-lsp. The built-invim.lsp.completionworks butnvim-cmpprovides better UX. - Use
:LspLogto inspect LSP communication for debugging. - After rebuilding the binary, you must quit Neovim and relaunch to restart the LSP (or copy binary while LSP is running and it may pick up changes).
- Neovim's built-in completion can be slow to trigger -- use
Ctrl+Spaceto force trigger. cargo build(debug) is much faster thancargo build --release-- use debug for testing iterations.
Forbidden Actions
- Do not modify the TestFiles content permanently (undo all test edits)
- Do not force push or modify the main branch