Editor support
vct-lsp is a language server for .vct songs. It speaks LSP over
stdin/stdout and gives any LSP editor:
- Diagnostics: the parser's errors and warnings as you type, underlining
the token at fault (the same checks as
vct check). - Highlighting: semantic tokens from the lexer: header keys and values, section names, lengths, modifiers, strings, markers, lyrics, comments.
- Completion: the song editor's autocomplete: header keys and values,
section names (
Verse 3afterVerse 2) with a length, modifiers not already on the line, marker and lyric snippets. The part to type over is a snippet placeholder. - Hover: what a header key or modifier does; on a section, marker or lyric line, where the section falls in the song (bars, start time, tempo, meter). The song has to parse for the second one.
- Outline (document symbols): the headers, then each section with its markers and lyrics. Folding: a section with its markers and lyrics.
- Formatting, on save or on demand: lines the song up in columns, the
way
examples/example-song.vctis written. Header values in one column; section lengths in a column, then each modifier position in its own; marker and lyric text lined up with the lengths; trailing comments kept in their column where they fit. Only the spaces between words change: names, values, marker text and lyrics stay as written, and if the result would play differently in any way, the file is left alone.
Build it and put it on PATH:
mise run build:vct-lsp
ln -s "$PWD/build/vct-lsp" ~/.local/bin/vct-lsp # or anywhere on PATH
Neovim (0.11+)
editors/nvim is a plugin directory: filetype detection, ftplugin
settings (# comments, two-space indent) and an lsp/vct.lua config that
plugin/vct.lua enables. Add it to the runtime path.
With lazy.nvim:
{ dir = "/path/to/StageDisplay/editors/nvim" }
Or in init.lua:
vim.opt.rtp:append("/path/to/StageDisplay/editors/nvim")
If vct-lsp isn't on PATH, point at it:
vim.lsp.config("vct", { cmd = { "/path/to/StageDisplay/build/vct-lsp" } })
Saving a .vct file formats it. To turn that off:
vim.g.vct_format_on_save = false
:lua vim.lsp.buf.format() formats on demand.
Semantic highlighting is on by default. Completion appears through nvim-cmp or blink.cmp if you use one. With neither, turn on the built-in completion:
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(ev)
local client = vim.lsp.get_client_by_id(ev.data.client_id)
if client and client.name == "vct" then
vim.lsp.completion.enable(true, client.id, ev.buf, { autotrigger = true })
end
end,
})
Zed
editors/zed is a Zed extension: the VCT language (.vct, # comments)
and a small Rust shim that starts vct-lsp from PATH. To install it:
run zed: install dev extension from the command palette and choose
editors/zed. Zed compiles the shim itself, so Rust must be installed
through rustup.
The extension has no tree-sitter grammar, so its colours come from the
server's semantic tokens. Zed turns those off by default; turn them on for
VCT in settings.json:
{
"languages": {
"VCT": { "semantic_tokens": "full" }
}
}
Zed formats on save by default, using the language server when there's
no other formatter, so saving lines the song up. To be explicit, or to turn
it off ("format_on_save": "off"):
{
"languages": {
"VCT": {
"semantic_tokens": "full",
"format_on_save": "on",
"formatter": "language_server"
}
}
}
editor: format formats on demand.
Other editors
Any LSP client works. Run vct-lsp (it accepts --stdio) for files with
the .vct extension. The token types are the standard ones (comment,
property, string, class, number, operator, keyword,
parameter, variable), so themes colour them without extra setup.