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:
163
editors/nvim/lua/lux/init.lua
Normal file
163
editors/nvim/lua/lux/init.lua
Normal 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
|
||||
Reference in New Issue
Block a user