# Flash language — Neovim / Vim support Two independent pieces: regex **syntax highlighting** (works in any Vim/Neovim, zero build) and the **language server** `flashd` (Neovim's built-in LSP client, live diagnostics). ## Language server (flashd) `flashd` speaks LSP over stdio and publishes compiler diagnostics: parse errors as you type, semantic findings on save — straight from the self-hosted compiler frontend running in-process, so the messages are exactly what `flashc` would say. Build it once (`zig build lsp` puts it at `zig-out/bin/flashd`), then add to your `init.lua`, with the `cmd` path pointing at your clone: ```lua vim.api.nvim_create_autocmd("FileType", { pattern = "flash", callback = function(args) vim.lsp.start({ name = "flashd", cmd = { "/path/to/Flash/zig-out/bin/flashd" }, root_dir = vim.fs.dirname(args.file), }) end, }) ``` Open a `.flash` file with an error and the squiggle appears; fix it and the squiggle clears (`vim.diagnostic` defaults apply — `:h vim.diagnostic` to style it). The server checks each file on its own — no project-wide analysis yet — and navigation features (hover, go-to-definition) are planned for a later phase. ## Format on save `flashc fmt` rewrites a file to canonical Flash — there is exactly one accepted rendering, and the earlier a file is canonical the less diff churn it causes later. Formatting on save keeps every buffer canonical from the start. `flashc fmt` formats in place on disk, so the recipe routes the unsaved buffer through a temp file on `BufWritePre`: the buffer is formatted *before* the write, and what lands on disk is already canonical. Add to your `init.lua`, with the path pointing at your clone: ```lua vim.api.nvim_create_autocmd("BufWritePre", { pattern = "*.flash", callback = function(args) local lines = vim.api.nvim_buf_get_lines(args.buf, 0, -1, false) local tmp = vim.fn.tempname() .. ".flash" vim.fn.writefile(lines, tmp) vim.fn.system({ "/path/to/Flash/zig-out/bin/flashc", "fmt", tmp }) if vim.v.shell_error == 0 then local formatted = vim.fn.readfile(tmp) if not vim.deep_equal(lines, formatted) then vim.api.nvim_buf_set_lines(args.buf, 0, -1, false, formatted) end end vim.fn.delete(tmp) end, }) ``` A file with a parse error is left untouched — the formatter refuses rather than destroys, and `vim.v.shell_error` guards the buffer from being replaced on a refusal. The diagnostics squiggle from `flashd` tells you what to fix. ## Syntax highlighting Regex syntax highlighting for `.flash` files via a classic Vim syntax file. Zero build, zero plugin manager: it links Flash tokens to the standard highlight groups (`Keyword`, `Type`, `String`, `Comment`, `Function`, `Number`, …) so your colorscheme paints them — `.flash` files look like every other language. Three files: - `ftdetect/flash.vim` — associates `*.flash` with the `flash` filetype. - `syntax/flash.vim` — the highlighting rules. - `ftplugin/flash.vim` — forces the regex syntax over treesitter for `.flash` buffers (see "Why regex, not treesitter" below). Optional for a plain Neovim; **required** if your config (e.g. NvChad) auto-starts a treesitter highlighter for `flash`. ## Install (Neovim) Symlink all three into your Neovim config so they survive updates. Run from the repository root so `$PWD` resolves correctly: ```sh mkdir -p ~/.config/nvim/syntax ~/.config/nvim/ftdetect ~/.config/nvim/ftplugin ln -s "$PWD/editors/nvim/syntax/flash.vim" ~/.config/nvim/syntax/flash.vim ln -s "$PWD/editors/nvim/ftdetect/flash.vim" ~/.config/nvim/ftdetect/flash.vim ln -s "$PWD/editors/nvim/ftplugin/flash.vim" ~/.config/nvim/ftplugin/flash.vim ``` Open any `examples/*.flash` file — it highlights immediately. Check detection with `:set filetype?` (expect `filetype=flash`). For classic Vim, use `~/.vim/` instead of `~/.config/nvim/`. ## Uninstall ```sh rm ~/.config/nvim/syntax/flash.vim ~/.config/nvim/ftdetect/flash.vim ~/.config/nvim/ftplugin/flash.vim ``` Removing just `ftplugin/flash.vim` reverts standalone `.flash` files to whatever your config did before (e.g. the treesitter-zig reuse). ## Why regex, not treesitter Flash has no treesitter parser of its own. A common workaround is to reuse the **zig** parser, since Flash is Zig-shaped. It mostly works — but Flash-specific syntax (`use X link Y` imports, `:=`) is a parse error to the zig grammar: on a real Flash file like `examples/fsh.flash` that is ~0.5 ERROR nodes per line, and treesitter leaves those regions uncolored. So standalone `.flash` files use this **regex** syntax instead: coarser than a real parser (no semantic function/parameter distinction), but Flash-exact with zero parse errors — every keyword, type, string, and `#builtin` colored. The `ftplugin` stops treesitter and switches on `syntax=flash` for `flash` buffers. This is scoped to `filetype=flash` buffers only. If you also reuse the zig parser for ```flash code fences inside markdown (e.g. a tutorial), that is a separate injection path on a `markdown` buffer and is **not** affected. ## Tree-sitter (optional upgrade) A real `tree-sitter-flash` grammar (structural *and* Flash-exact: better nesting, injections, incremental reparse) is the nicer long-term path, but needs a `grammar.js`, `tree-sitter generate`, a C toolchain, and an `nvim-treesitter` parser registration. Not built yet; ask if you want it — it would supersede this regex syntax. ## Source of truth The token set mirrors `src/token.zig` and `src/lexer.zig`. On a new keyword or literal form, update `syntax/flash.vim` to match.