feat: add devtools (tree-sitter, neovim, formatter, CLI commands)

- Add tree-sitter grammar for syntax highlighting
- Add Neovim plugin with syntax, LSP integration, and commands
- Add code formatter (lux fmt) with check mode
- Add CLI commands: fmt, check, test, watch, init
- Add project initialization with lux.toml template

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 10:30:13 -05:00
parent 961a861822
commit f786d18182
15 changed files with 2578 additions and 7 deletions

View File

@@ -0,0 +1,163 @@
-- Lux language support for Neovim
-- Provides LSP configuration and utilities
local M = {}
-- Default configuration
M.config = {
-- Path to lux binary (will search PATH if not set)
lux_binary = nil,
-- Enable LSP
lsp = {
enabled = true,
-- Auto-start LSP when opening .lux files
autostart = true,
},
-- Formatting options
format = {
-- Format on save
on_save = false,
},
}
-- Find lux binary
local function find_lux_binary()
if M.config.lux_binary then
return M.config.lux_binary
end
-- Try to find in PATH
local handle = io.popen("which lux 2>/dev/null")
if handle then
local result = handle:read("*a"):gsub("%s+", "")
handle:close()
if result ~= "" then
return result
end
end
-- Try common locations
local common_paths = {
"./result/bin/lux",
"~/.local/bin/lux",
"/usr/local/bin/lux",
}
for _, path in ipairs(common_paths) do
local expanded = vim.fn.expand(path)
if vim.fn.executable(expanded) == 1 then
return expanded
end
end
return "lux" -- Fallback, hope it's in PATH
end
-- Setup LSP
function M.setup_lsp()
local lux_binary = find_lux_binary()
-- Check if lspconfig is available
local ok, lspconfig = pcall(require, "lspconfig")
if not ok then
vim.notify("lspconfig not found. Install nvim-lspconfig for LSP support.", vim.log.levels.WARN)
return
end
local configs = require("lspconfig.configs")
-- Register lux LSP if not already registered
if not configs.lux_lsp then
configs.lux_lsp = {
default_config = {
cmd = { lux_binary, "lsp" },
filetypes = { "lux" },
root_dir = function(fname)
return lspconfig.util.find_git_ancestor(fname)
or lspconfig.util.root_pattern("lux.toml")(fname)
or vim.fn.getcwd()
end,
settings = {},
},
}
end
-- Setup the LSP
lspconfig.lux_lsp.setup({
on_attach = function(client, bufnr)
-- Enable completion
vim.bo[bufnr].omnifunc = "v:lua.vim.lsp.omnifunc"
-- Key mappings
local opts = { noremap = true, silent = true, buffer = bufnr }
vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
vim.keymap.set("n", "gi", vim.lsp.buf.implementation, opts)
vim.keymap.set("n", "<C-k>", vim.lsp.buf.signature_help, opts)
vim.keymap.set("n", "gr", vim.lsp.buf.references, 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("n", "[d", vim.diagnostic.goto_prev, opts)
vim.keymap.set("n", "]d", vim.diagnostic.goto_next, opts)
-- Format on save if enabled
if M.config.format.on_save then
vim.api.nvim_create_autocmd("BufWritePre", {
buffer = bufnr,
callback = function()
vim.lsp.buf.format({ async = false })
end,
})
end
end,
capabilities = vim.lsp.protocol.make_client_capabilities(),
})
end
-- Run current file
function M.run_file()
local file = vim.fn.expand("%:p")
local lux_binary = find_lux_binary()
vim.cmd("!" .. lux_binary .. " " .. vim.fn.shellescape(file))
end
-- Format current file
function M.format_file()
local file = vim.fn.expand("%:p")
local lux_binary = find_lux_binary()
vim.cmd("!" .. lux_binary .. " fmt " .. vim.fn.shellescape(file))
vim.cmd("edit!") -- Reload the file
end
-- Run tests
function M.run_tests()
local lux_binary = find_lux_binary()
vim.cmd("!" .. lux_binary .. " test")
end
-- Start REPL in terminal
function M.start_repl()
local lux_binary = find_lux_binary()
vim.cmd("terminal " .. lux_binary)
end
-- Setup function
function M.setup(opts)
-- Merge user config
M.config = vim.tbl_deep_extend("force", M.config, opts or {})
-- Setup LSP if enabled
if M.config.lsp.enabled and M.config.lsp.autostart then
vim.api.nvim_create_autocmd("FileType", {
pattern = "lux",
callback = function()
M.setup_lsp()
end,
once = true,
})
end
-- Create user commands
vim.api.nvim_create_user_command("LuxRun", M.run_file, { desc = "Run current Lux file" })
vim.api.nvim_create_user_command("LuxFormat", M.format_file, { desc = "Format current Lux file" })
vim.api.nvim_create_user_command("LuxTest", M.run_tests, { desc = "Run Lux tests" })
vim.api.nvim_create_user_command("LuxRepl", M.start_repl, { desc = "Start Lux REPL" })
end
return M