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
+5 -6
View File
@@ -3,10 +3,9 @@
A Vim/Neovim plugin providing full vimwiki syntax support, implemented as a
Rust-based language server (LSP).
> **Status:** Phase 8 (navigation) complete. The server now serves
> definition, references, hover, completion, `workspace/symbol`, plus a
> background-indexed workspace per P8. Only editor glue and the release
> pipeline are still pending.
> **Status:** Phase 9 (editor glue) complete. Plugin installable via
> lazy.nvim / vim-plug / Dein; setup() + LSP wiring + binary downloader +
> :checkhealth ship. Only the release pipeline remains.
See [`SPEC.md`](./SPEC.md) for the full project specification.
@@ -23,8 +22,8 @@ See [`SPEC.md`](./SPEC.md) for the full project specification.
| 6 | LSP Foundation | ✅ done |
| 7 | Semantic Tokens | ✅ done |
| 8 | Navigation | ✅ done |
| 9 | Editor Glue | ⏳ next |
| 10 | CI/CD release pipeline | ⏳ |
| 9 | Editor Glue | ✅ done |
| 10 | CI/CD release pipeline | ⏳ next |
## Repository layout
+3 -3
View File
@@ -1,7 +1,7 @@
# nuwiki — Project Specification
> Last updated: 2026-05-10
> Status: Phase 8 (Navigation) complete — moving to Phase 9 (Editor Glue)
> Status: Phase 9 (Editor Glue) complete — moving to Phase 10 (CI/CD release pipeline)
---
@@ -588,8 +588,8 @@ These decisions are required before or during the phase indicated.
| ~~P2~~ | ~~**MSRV**~~ | ~~Phase 0~~ | ✅ **stable-2 (Rust 1.83)** | |
| ~~P3~~ | ~~**String representation in AST**~~ | ~~Phase 1~~ | ✅ **`String` (owned)** | |
| ~~P4~~ | ~~**Visitor pattern**~~ | ~~Phase 1~~ | ✅ **Defined in Phase 1** | Open-recursion `Visitor` trait + `walk_*` helpers |
| P5 | **Minimum Neovim version** | Phase 9 | 0.8 (`vim.lsp.start`) · 0.11 (`vim.lsp.config`) | 0.8 = broader compat; 0.11 = cleaner Lua API |
| P6 | **Minimum Vim version** | Phase 9 | Vim 8.2 · Vim 9.0 · Vim 9.1 | LSP client plugins work best on 9.0+; recommend documenting 9.1 as minimum |
| ~~P5~~ | ~~**Minimum Neovim version**~~ | ~~Phase 9~~ | ✅ **Neovim 0.11+** | Server registered via `vim.lsp.config{}` + `vim.lsp.enable`; a `vim.lsp.start` autocmd fallback is wired up but only fires when the declarative API is unavailable. |
| ~~P6~~ | ~~**Minimum Vim version**~~ | ~~Phase 9~~ | ✅ **Vim 9.1+** | autoload glue assumes 9.1 idioms; uses `vim-lsp` first, then falls back to `coc.nvim`. |
| ~~P7~~ | ~~**Semantic token type mapping**~~ | ~~Phase 7~~ | ✅ **Custom vimwiki-specific token types** | ~20 types (`vimwikiHeading`, `vimwikiBold`, …) + `level1`..`level6` + `centered` modifiers. Phase 9 ships default highlight groups in `syntax/nuwiki.vim` and the Lua glue. |
| ~~P8~~ | ~~**Workspace indexing strategy**~~ | ~~Phase 8~~ | ✅ **Lazy + background with progress** | Initial scan runs as a background tokio task; nav features answer with partial data until complete; progress reported via `window/workDoneProgress`. |
| P9 | **macOS runner** | Phase 10 | Stay manual · Register macOS runner
+74 -1
View File
@@ -1,2 +1,75 @@
" autoload/nuwiki/lsp.vim — Vim (non-Neovim) LSP client wiring.
" Implementation deferred to Phase 9 (Editor Glue).
"
" Spec §7.4 preference order:
" 1. vim-lsp (matoto/vim-lsp)
" 2. coc.nvim
" 3. Print an error if neither is available.
let s:started = 0
function! nuwiki#lsp#start() abort
if s:started
return
endif
let s:started = 1
let l:bin = s:bin_path()
if !executable(l:bin)
echohl ErrorMsg
echom 'nuwiki: language server not found at ' . l:bin
echom 'Run :NuwikiInstall to install the binary'
echohl None
return
endif
if exists('*lsp#register_server')
" vim-lsp
call lsp#register_server({
\ 'name': 'nuwiki',
\ 'cmd': {server_info -> [l:bin]},
\ 'allowlist': ['vimwiki'],
\ 'config': { 'settings': { 'nuwiki': s:settings() } },
\ })
return
endif
if exists(':CocConfig') == 2 || exists('*coc#config')
" coc.nvim — coc reads its server list from coc-settings.json.
" We can't inject it dynamically without owning the file, so just point
" the user at the snippet.
echohl WarningMsg
echom 'nuwiki: coc.nvim detected. Add this to your coc-settings.json:'
echom ' "languageserver": {'
echom ' "nuwiki": {'
echom ' "command": "' . l:bin . '",'
echom ' "filetypes": ["vimwiki"]'
echom ' }'
echom ' }'
echohl None
return
endif
echohl ErrorMsg
echom 'nuwiki: no supported LSP client found.'
echom 'Install vim-lsp (https://github.com/prabirshrestha/vim-lsp)'
echom 'or coc.nvim (https://github.com/neoclide/coc.nvim).'
echohl None
endfunction
function! s:bin_path() abort
if exists('g:nuwiki_binary_path') && filereadable(g:nuwiki_binary_path)
return g:nuwiki_binary_path
endif
let l:plugin_dir = fnamemodify(resolve(expand('<sfile>:p')), ':h:h:h')
let l:suffix = has('win32') ? '.exe' : ''
return l:plugin_dir . '/bin/nuwiki-ls' . l:suffix
endfunction
function! s:settings() abort
return {
\ 'wiki_root': get(g:, 'nuwiki_wiki_root', '~/vimwiki'),
\ 'file_extension': get(g:, 'nuwiki_file_extension', '.wiki'),
\ 'syntax': get(g:, 'nuwiki_syntax', 'vimwiki'),
\ 'log_level': get(g:, 'nuwiki_log_level', 'warn'),
\ }
endfunction
+159 -1
View File
@@ -1,5 +1,163 @@
*nuwiki.txt* LSP-backed vimwiki support for Vim and Neovim.
Documentation deferred to Phase 9 (Editor Glue).
Author: Gabriel Fróes Franco
License: Dual MIT / Apache-2.0
==============================================================================
CONTENTS *nuwiki-contents*
1. Introduction ............ |nuwiki-introduction|
2. Requirements ............ |nuwiki-requirements|
3. Installation ............ |nuwiki-installation|
4. Configuration ........... |nuwiki-config|
5. Health check ............ |nuwiki-health|
6. LSP features ............ |nuwiki-features|
7. Commands ................ |nuwiki-commands|
==============================================================================
1. INTRODUCTION *nuwiki-introduction*
nuwiki brings full vimwiki syntax support to Vim and Neovim via a Rust-
based language server (|nuwiki-ls|). It is architecturally distinct from
vimwiki itself — installable through any standard plugin manager, with the
heavy lifting done in a single binary that speaks LSP over stdio.
==============================================================================
2. REQUIREMENTS *nuwiki-requirements*
* Neovim 0.11+ (preferred path)
* Vim 9.1+ with one of:
- vim-lsp (prabirshrestha/vim-lsp)
- coc.nvim (neoclide/coc.nvim)
* `curl` and `tar` (only for binary download; not needed if you build
from source)
* `cargo` and a Rust toolchain (only if you opt into source builds)
==============================================================================
3. INSTALLATION *nuwiki-installation*
Install via your plugin manager of choice; the build hook will download
the pre-built `nuwiki-ls` binary into the plugin's `bin/` directory.
lazy.nvim: >
{
"gffranco/nuwiki",
build = "lua require('nuwiki').install()",
ft = { "vimwiki" },
opts = {},
}
<vim-plug: >
Plug 'gffranco/nuwiki', { 'do': 'vim -e -s -c "source scripts/download_bin.vim" -c "q"' }
<dein.vim: >
call dein#add('gffranco/nuwiki', {
\ 'build': 'vim -e -s -c "source scripts/download_bin.vim" -c "q"',
\ })
<To force a build from source (no download), set: >
let g:nuwiki_build_from_source = 1
<in Vim before re-running the build hook, or in Neovim: >
vim.g.nuwiki_build_from_source = 1
require('nuwiki').install()
<==============================================================================
4. CONFIGURATION *nuwiki-config*
Neovim (`require('nuwiki').setup{}`): >
require('nuwiki').setup({
wiki_root = '~/vimwiki',
file_extension = '.wiki',
syntax = 'vimwiki',
log_level = 'warn',
})
<Vim sets the equivalent globals before plugin load: >
let g:nuwiki_wiki_root = '~/vimwiki'
let g:nuwiki_file_extension = '.wiki'
let g:nuwiki_syntax = 'vimwiki'
let g:nuwiki_log_level = 'warn'
<Options:
*g:nuwiki_wiki_root*
`wiki_root` ~/vimwiki
Root directory of the wiki. Used as a fallback `root_dir` when no
`.git` / `.nuwiki` marker is found near the buffer.
*g:nuwiki_file_extension*
`file_extension` .wiki
File extension associated with the wiki filetype. Note that
`ftdetect/nuwiki.vim` hardcodes `.wiki`; users with a custom
extension should add their own ftdetect autocmd.
*g:nuwiki_syntax*
`syntax` vimwiki
Future-proofing for additional syntaxes (e.g. `markdown`). Phase 9
only ships the vimwiki plugin.
*g:nuwiki_log_level*
`log_level` warn
`error` | `warn` | `info` | `debug`. Forwarded to the language server.
*g:nuwiki_build_from_source*
`g:nuwiki_build_from_source` 0
When 1, the install step always builds from source (cargo) and skips
the release-asset download path.
*g:nuwiki_binary_path*
`g:nuwiki_binary_path`
Override the auto-discovered server path. If set and the file is
readable, `autoload/nuwiki/lsp.vim` uses it instead of the
`{plugin}/bin/nuwiki-ls` default.
==============================================================================
5. HEALTH CHECK *nuwiki-health*
Neovim users can run: >
:checkhealth nuwiki
<This reports whether the binary exists, responds to `--version`, whether
`wiki_root` exists, whether `.wiki` filetype detection is wired up, and
whether an LSP client is currently attached.
==============================================================================
6. LSP FEATURES *nuwiki-features*
The language server provides every method listed in SPEC §6.9:
* Syntax highlighting via semantic tokens
`textDocument/semanticTokens/full` + `/range`
* Document outline
`textDocument/documentSymbol`
* Diagnostics (parse errors)
`textDocument/publishDiagnostics`
* Go-to-definition (follow wikilinks)
`textDocument/definition`
* Backlinks
`textDocument/references`
* Hover preview (page title + outline)
`textDocument/hover`
* Completion on `[[`
`textDocument/completion`
* Workspace symbol search
`workspace/symbol`
==============================================================================
7. COMMANDS *nuwiki-commands*
*:NuwikiInstall*
:NuwikiInstall
Install (or re-install) the `nuwiki-ls` binary into the plugin's
`bin/` directory. Equivalent to running the build hook by hand.
vim:tw=78:ts=8:ft=help:norl:
+7 -2
View File
@@ -1,2 +1,7 @@
" ftdetect/nuwiki.vim — filetype detection for .wiki files.
" Implementation deferred to Phase 9 (Editor Glue).
" ftdetect/nuwiki.vim — associate *.wiki files with the `vimwiki` filetype.
"
" Users who configure a custom `file_extension` in setup() should add their
" own `:autocmd BufRead,BufNewFile *.myext setfiletype vimwiki` mapping;
" we hardcode `.wiki` since ftdetect runs before any user setup() call.
autocmd! BufRead,BufNewFile *.wiki setfiletype vimwiki
+18 -2
View File
@@ -1,2 +1,18 @@
" ftplugin/nuwiki.vim — per-buffer filetype settings for .wiki buffers.
" Implementation deferred to Phase 9 (Editor Glue).
" ftplugin/nuwiki.vim — per-buffer settings for `.wiki` buffers.
"
" Keeps this minimal: only the options that affect editing ergonomics for
" markup files. Anything richer (folding, navigation keymaps, …) ships
" downstream once users opt in.
if exists('b:did_ftplugin')
finish
endif
let b:did_ftplugin = 1
setlocal commentstring=%%\ %s
setlocal comments=:%%
setlocal formatoptions+=ron
setlocal suffixesadd=.wiki
setlocal iskeyword+=-
let b:undo_ftplugin = 'setlocal commentstring< comments< formatoptions< suffixesadd< iskeyword<'
+16 -2
View File
@@ -1,6 +1,20 @@
-- lua/nuwiki/config.lua — user configuration schema and defaults.
-- Implementation deferred to Phase 9 (Editor Glue).
-- lua/nuwiki/config.lua — defaults and user-config merge.
--
-- Schema mirrors SPEC §7.5. Keep this aligned with `doc/nuwiki.txt`.
local M = {}
M.defaults = {
wiki_root = '~/vimwiki',
file_extension = '.wiki',
syntax = 'vimwiki',
log_level = 'warn',
}
M.options = vim.deepcopy(M.defaults)
function M.apply(user)
M.options = vim.tbl_deep_extend('force', M.defaults, user or {})
end
return M
+59
View File
@@ -0,0 +1,59 @@
-- lua/nuwiki/health.lua — `:checkhealth nuwiki` target.
--
-- Checks per SPEC §7.6: binary exists, is executable, LSP client running,
-- filetype detection works for `.wiki`.
local M = {}
function M.check()
local install = require('nuwiki.install')
local config = require('nuwiki.config')
vim.health.start('nuwiki')
local bin = install.expected_path()
if install.is_installed() then
vim.health.ok('binary present at ' .. bin)
local out = vim.fn.system({ bin, '--version' })
if vim.v.shell_error == 0 then
vim.health.ok('binary --version: ' .. (out:gsub('\n', ' ')):gsub('^%s+', ''))
else
vim.health.warn(
'binary did not respond to --version',
{ 'Re-install with :lua require("nuwiki").install()' }
)
end
else
vim.health.error(
'binary not found at ' .. bin,
{ 'Run :lua require("nuwiki").install() to install' }
)
end
local root = vim.fn.expand(config.options.wiki_root or '')
if root ~= '' then
if vim.fn.isdirectory(root) == 1 then
vim.health.ok('wiki_root exists: ' .. root)
else
vim.health.warn('wiki_root does not exist: ' .. root)
end
end
if vim.fn.exists('#filetypedetect#BufRead#*.wiki') ~= 0
or vim.filetype.match({ filename = 'test.wiki' }) == 'vimwiki' then
vim.health.ok('filetype detection for *.wiki is wired up')
else
vim.health.warn('filetype detection for *.wiki not active')
end
local clients = vim.lsp.get_clients and vim.lsp.get_clients({ name = 'nuwiki' })
or vim.lsp.get_active_clients({ name = 'nuwiki' })
if clients and #clients > 0 then
vim.health.ok('LSP client attached (' .. #clients .. ' instance(s))')
else
vim.health.info('LSP client not yet attached — open a .wiki buffer')
end
end
return M
+25 -2
View File
@@ -1,6 +1,29 @@
-- lua/nuwiki/init.lua — Neovim entry point. Exposes setup() and install().
-- Implementation deferred to Phase 9 (Editor Glue).
-- lua/nuwiki/init.lua — Neovim entry point.
--
-- Public surface:
-- require('nuwiki').setup(opts) — apply user config + register LSP
-- require('nuwiki').install() — install / build the language server
-- require('nuwiki').health() — :checkhealth nuwiki target
--
-- Per SPEC §6.2/§6.3 this layer is wiring only. Everything substantive
-- happens in the Rust language server.
local M = {}
local config = require('nuwiki.config')
local lsp = require('nuwiki.lsp')
function M.setup(opts)
config.apply(opts or {})
lsp.register()
end
function M.install()
return require('nuwiki.install').install()
end
function M.health()
return require('nuwiki.health').check()
end
return M
+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
+60 -2
View File
@@ -1,6 +1,64 @@
-- lua/nuwiki/lsp.lua — vim.lsp.start wiring for the nuwiki-ls binary.
-- Implementation deferred to Phase 9 (Editor Glue).
-- lua/nuwiki/lsp.lua — registration of the nuwiki LSP server in Neovim.
--
-- Prefers Neovim 0.11+'s declarative `vim.lsp.config{}` + `vim.lsp.enable`.
-- Falls back to a FileType autocmd that calls `vim.lsp.start` for older
-- versions (we still officially support only 0.11+; the fallback exists so
-- the plugin degrades gracefully rather than erroring out).
local M = {}
local config = require('nuwiki.config')
local install = require('nuwiki.install')
local function command()
return { install.expected_path() }
end
local function server_settings()
return {
nuwiki = config.options,
}
end
local function root_dir_for(buf_file)
-- Walk up from the buffer until we hit a workspace marker, or fall back
-- to the configured wiki_root.
local found = vim.fs.find(
{ '.git', '.nuwiki' },
{ upward = true, path = vim.fs.dirname(buf_file) }
)[1]
if found then
return vim.fs.dirname(found)
end
return vim.fn.expand(config.options.wiki_root)
end
function M.register()
if vim.lsp.config and vim.lsp.enable then
-- Neovim 0.11+: declarative server registration.
vim.lsp.config('nuwiki', {
cmd = command(),
filetypes = { 'vimwiki' },
root_markers = { '.git', '.nuwiki' },
settings = server_settings(),
})
vim.lsp.enable('nuwiki')
return
end
-- Pre-0.11 fallback path.
vim.api.nvim_create_autocmd('FileType', {
pattern = 'vimwiki',
desc = 'start nuwiki language server',
callback = function(ev)
vim.lsp.start({
name = 'nuwiki',
cmd = command(),
root_dir = root_dir_for(ev.file),
settings = server_settings(),
})
end,
})
end
return M
+24 -3
View File
@@ -1,8 +1,29 @@
" plugin/nuwiki.vim — universal entry point for Vim and Neovim.
" Loaded once per session by all plugin managers.
" Implementation deferred to Phase 9 (Editor Glue).
" plugin/nuwiki.vim — universal Vim/Neovim entry point.
"
" Neovim 0.11+ users are expected to call `require('nuwiki').setup({})` from
" their config (lazy.nvim does this automatically through `opts`). On Vim
" we start the LSP client when a vimwiki buffer is loaded.
"
" Per SPEC §6.2 / §6.3 this file contains wiring only — all logic lives in
" the Rust language server.
if exists('g:loaded_nuwiki')
finish
endif
let g:loaded_nuwiki = 1
if has('nvim')
" Neovim path is initialised by `require('nuwiki').setup()`. Nothing else
" to do at vimscript boot — wait until the user runs setup.
finish
endif
" Vim path: start the LSP client the first time a vimwiki buffer is opened.
augroup nuwiki_vim_lsp_start
autocmd!
autocmd FileType vimwiki call nuwiki#lsp#start()
augroup END
" :NuwikiInstall — convenience wrapper around scripts/download_bin.vim, so
" users don't have to invoke a plugin-manager build hook by hand.
command! -nargs=0 NuwikiInstall execute 'source' fnamemodify(resolve(expand('<sfile>:p')), ':h:h') . '/scripts/download_bin.vim'
+88 -2
View File
@@ -1,2 +1,88 @@
" scripts/download_bin.vim — VimL binary download for Dein/vim-plug build hooks.
" Implementation deferred to Phase 9 (Editor Glue).
" scripts/download_bin.vim — install `nuwiki-ls` from a release tarball,
" falling back to `cargo build --release` on failure.
"
" Invoked by Dein / vim-plug build hooks:
" vim -e -s -c "source scripts/download_bin.vim" -c "q"
let s:plugin_dir = fnamemodify(resolve(expand('<sfile>:p')), ':h:h')
let s:bin_dir = s:plugin_dir . '/bin'
let s:suffix = has('win32') ? '.exe' : ''
let s:bin = s:bin_dir . '/nuwiki-ls' . s:suffix
call mkdir(s:bin_dir, 'p')
function! s:target() abort
if has('win32')
return 'x86_64-pc-windows-msvc'
elseif has('mac')
if has('arm64')
return 'aarch64-apple-darwin'
endif
return 'x86_64-apple-darwin'
elseif has('linux')
let l:ldd = system('ldd --version 2>&1')
let l:musl = stridx(l:ldd, 'musl') >= 0
let l:uname_m = trim(system('uname -m'))
if l:uname_m ==# 'aarch64' || l:uname_m ==# 'arm64'
return l:musl ? 'aarch64-unknown-linux-musl' : 'aarch64-unknown-linux-gnu'
endif
return l:musl ? 'x86_64-unknown-linux-musl' : 'x86_64-unknown-linux-gnu'
endif
return ''
endfunction
function! s:build_from_source() abort
if !executable('cargo')
echohl ErrorMsg | echom 'nuwiki: cargo not found, cannot build from source' | echohl None
return 0
endif
echom 'nuwiki: cargo build --release …'
call system('cargo build --release -p nuwiki-ls --manifest-path ' . shellescape(s:plugin_dir . '/Cargo.toml'))
if v:shell_error != 0
echohl ErrorMsg | echom 'nuwiki: cargo build failed' | echohl None
return 0
endif
let l:built = s:plugin_dir . '/target/release/nuwiki-ls' . s:suffix
call system('cp ' . shellescape(l:built) . ' ' . shellescape(s:bin))
call system('chmod +x ' . shellescape(s:bin))
return executable(s:bin)
endfunction
function! s:download_release() abort
let l:target = s:target()
if empty(l:target)
return 0
endif
if !executable('curl') || !executable('tar')
return 0
endif
let l:url = 'https://code.gfran.co/gffranco/nuwiki/releases/latest/download/nuwiki-ls-'
\ . l:target . '.tar.gz'
let l:archive = tempname() . '.tar.gz'
call system('curl -fsSL -o ' . shellescape(l:archive) . ' ' . shellescape(l:url))
if v:shell_error != 0
return 0
endif
let l:extract = tempname()
call mkdir(l:extract, 'p')
call system('tar -xzf ' . shellescape(l:archive) . ' -C ' . shellescape(l:extract))
if v:shell_error != 0
return 0
endif
call system('cp ' . shellescape(l:extract . '/nuwiki-ls' . s:suffix) . ' ' . shellescape(s:bin))
call system('chmod +x ' . shellescape(s:bin))
return executable(s:bin)
endfunction
if get(g:, 'nuwiki_build_from_source', 0)
if !s:build_from_source()
echohl ErrorMsg | echom 'nuwiki: source build failed' | echohl None
endif
elseif s:download_release()
echom 'nuwiki: installed ' . s:bin
else
echohl WarningMsg | echom 'nuwiki: download failed, falling back to source build' | echohl None
if !s:build_from_source()
echohl ErrorMsg | echom 'nuwiki: install failed' | echohl None
endif
endif
+89 -2
View File
@@ -1,2 +1,89 @@
" syntax/nuwiki.vim — static fallback syntax highlighting (no LSP required).
" Implementation deferred to Phase 9 (Editor Glue).
" syntax/nuwiki.vim — default highlight groups for nuwiki.
"
" Two responsibilities:
" 1. Default `@vimwiki*` highlight groups for the LSP semantic tokens
" (SPEC §11 P7). Loaded eagerly so the very first hover/highlight
" cycle after server startup already has colours.
" 2. A small regex-based fallback so static highlighting works on
" buffers where the LSP isn't running yet.
if exists('b:current_syntax')
finish
endif
" ===== Semantic-token defaults (Neovim's TS-style @group names) =====
" Headings — level modifiers map to progressively softer accents.
highlight default link @vimwikiHeading Title
highlight default link @vimwikiHeading.level1 Title
highlight default link @vimwikiHeading.level2 Function
highlight default link @vimwikiHeading.level3 Identifier
highlight default link @vimwikiHeading.level4 Type
highlight default link @vimwikiHeading.level5 Constant
highlight default link @vimwikiHeading.level6 PreProc
highlight default link @vimwikiHeading.centered Title
" Inline text styling.
highlight default link @vimwikiBold markdownBold
highlight default link @vimwikiItalic markdownItalic
highlight default link @vimwikiBoldItalic markdownBoldItalic
highlight default link @vimwikiStrikethrough Comment
highlight default link @vimwikiSuperscript Number
highlight default link @vimwikiSubscript Number
" Code + math.
highlight default link @vimwikiCode String
highlight default link @vimwikiCodeBlock String
highlight default link @vimwikiMath Number
highlight default link @vimwikiMathBlock Number
" Links + transclusions.
highlight default link @vimwikiWikilink Underlined
highlight default link @vimwikiExternalLink Underlined
highlight default link @vimwikiUrl Underlined
highlight default link @vimwikiTransclusion Identifier
" Misc.
highlight default link @vimwikiKeyword Todo
highlight default link @vimwikiColor Constant
highlight default link @vimwikiComment Comment
highlight default link @vimwikiDefinitionTerm Statement
" ===== Static fallback (no LSP required) =====
syntax case match
syntax match nuwikiHeading /^\s*=\{1,6}\s.\{-}\s=\{1,6}\s*$/
syntax match nuwikiHorizontal /^-\{4,}\s*$/
syntax match nuwikiCommentLine /^%%.*$/
syntax region nuwikiCommentBlock start=/^%%+/ end=/+%%/ keepend
syntax region nuwikiPreformatted start=/^{{{/ end=/^}}}/ keepend
syntax region nuwikiMathBlock start=/^{{\$/ end=/^}}\$/ keepend
syntax match nuwikiPlaceholder /^%\(title\|nohtml\|template\|date\)\>.*$/
syntax match nuwikiBold /\*[^*]\+\*/
syntax match nuwikiItalic /\<_[^_]\+_\>/
syntax match nuwikiCode /`[^`]\+`/
syntax match nuwikiMathInline /\$[^$]\+\$/
syntax match nuwikiKeyword /\<\(TODO\|DONE\|STARTED\|FIXME\|FIXED\|XXX\)\>/
syntax region nuwikiWikilink start=/\[\[/ end=/\]\]/ keepend
syntax region nuwikiTransclusion start=/{{[^{]/ end=/}}/ keepend
syntax match nuwikiUrl /\<\(https\?\|ftp\|mailto\|file\):\/\?\/\?\S\+/
highlight default link nuwikiHeading Title
highlight default link nuwikiHorizontal Comment
highlight default link nuwikiCommentLine Comment
highlight default link nuwikiCommentBlock Comment
highlight default link nuwikiPreformatted String
highlight default link nuwikiMathBlock Number
highlight default link nuwikiPlaceholder PreProc
highlight default link nuwikiBold Bold
highlight default link nuwikiItalic Italic
highlight default link nuwikiCode String
highlight default link nuwikiMathInline Number
highlight default link nuwikiKeyword Todo
highlight default link nuwikiWikilink Underlined
highlight default link nuwikiTransclusion Identifier
highlight default link nuwikiUrl Underlined
let b:current_syntax = 'vimwiki'