phase 9: editor glue — plugin, LSP wiring, install, health, docs
CI / cargo fmt --check (push) Successful in 30s
CI / cargo clippy (push) Successful in 1m23s
CI / cargo test (push) Successful in 1m29s

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>
This commit is contained in:
2026-05-11 00:38:09 +00:00
parent 4d461d2425
commit ee61d40872
14 changed files with 736 additions and 30 deletions
+109 -2
View File
@@ -1,6 +1,113 @@
-- lua/nuwiki/install.lua — binary download / build-from-source.
-- Implementation deferred to Phase 9 (Editor Glue).
-- lua/nuwiki/install.lua — install / build the `nuwiki-ls` binary.
--
-- Strategy (matches SPEC §7.2):
-- 1. Look for `bin/nuwiki-ls[.exe]` next to the plugin.
-- 2. If missing or `g:nuwiki_build_from_source` is set, build with cargo.
-- 3. Otherwise try to download a release asset for the current target.
-- On failure, fall back to cargo.
local M = {}
local function plugin_root()
local source = debug.getinfo(1, 'S').source:sub(2)
return vim.fn.fnamemodify(source, ':h:h:h')
end
local function exe_suffix()
return vim.fn.has('win32') == 1 and '.exe' or ''
end
function M.expected_path()
return plugin_root() .. '/bin/nuwiki-ls' .. exe_suffix()
end
function M.is_installed()
return vim.fn.executable(M.expected_path()) == 1
end
local function target_triple()
if vim.fn.has('win32') == 1 then
return 'x86_64-pc-windows-msvc'
elseif vim.fn.has('mac') == 1 then
if vim.fn.has('arm64') == 1 or jit and jit.arch == 'arm64' then
return 'aarch64-apple-darwin'
end
return 'x86_64-apple-darwin'
elseif vim.fn.has('linux') == 1 then
local uname = vim.uv and vim.uv.os_uname() or vim.loop.os_uname()
local musl = (vim.fn.system('ldd --version 2>&1'):find('musl') ~= nil)
if uname.machine == 'aarch64' or uname.machine == 'arm64' then
return musl and 'aarch64-unknown-linux-musl' or 'aarch64-unknown-linux-gnu'
end
return musl and 'x86_64-unknown-linux-musl' or 'x86_64-unknown-linux-gnu'
end
return nil
end
local function build_from_source(dest)
if vim.fn.executable('cargo') ~= 1 then
vim.notify('nuwiki: cargo not found, cannot build from source',
vim.log.levels.ERROR)
return false
end
local root = plugin_root()
vim.notify('nuwiki: building from source (cargo build --release) …', vim.log.levels.INFO)
local _ = vim.fn.system({
'cargo', 'build', '--release', '-p', 'nuwiki-ls',
'--manifest-path', root .. '/Cargo.toml',
})
if vim.v.shell_error ~= 0 then
vim.notify('nuwiki: cargo build failed', vim.log.levels.ERROR)
return false
end
local built = root .. '/target/release/nuwiki-ls' .. exe_suffix()
vim.fn.system({ 'cp', built, dest })
vim.fn.system({ 'chmod', '+x', dest })
return vim.fn.executable(dest) == 1
end
local function download_release(dest)
local target = target_triple()
if not target then
return false
end
if vim.fn.executable('curl') ~= 1 or vim.fn.executable('tar') ~= 1 then
return false
end
local url = string.format(
'https://code.gfran.co/gffranco/nuwiki/releases/latest/download/nuwiki-ls-%s.tar.gz',
target
)
local archive = vim.fn.tempname() .. '.tar.gz'
vim.fn.system({ 'curl', '-fsSL', '-o', archive, url })
if vim.v.shell_error ~= 0 then
return false
end
local extract = vim.fn.tempname()
vim.fn.mkdir(extract, 'p')
vim.fn.system({ 'tar', '-xzf', archive, '-C', extract })
if vim.v.shell_error ~= 0 then
return false
end
vim.fn.system({ 'cp', extract .. '/nuwiki-ls' .. exe_suffix(), dest })
vim.fn.system({ 'chmod', '+x', dest })
return vim.fn.executable(dest) == 1
end
function M.install()
local dest = M.expected_path()
vim.fn.mkdir(vim.fn.fnamemodify(dest, ':h'), 'p')
if vim.g.nuwiki_build_from_source == 1 then
return build_from_source(dest)
end
if download_release(dest) then
vim.notify('nuwiki: installed ' .. dest, vim.log.levels.INFO)
return true
end
vim.notify('nuwiki: download failed, falling back to source build',
vim.log.levels.WARN)
return build_from_source(dest)
end
return M