722 lines
22 KiB
Lua
722 lines
22 KiB
Lua
--- Comprehensive LSP, formatting, and linting configuration
|
|
--- Optimized for tech stack:
|
|
--- - Cloudflare Workers/Pages
|
|
--- - Golang (Cosmos SDK)
|
|
--- - TypeScript/React/Vite
|
|
--- - Protobufs
|
|
--- - Bun/ES Modules/WASM
|
|
--- @module plugins.astrolsp
|
|
|
|
return {
|
|
-- ============================================================================
|
|
-- AstroLSP - Core LSP Configuration
|
|
-- ============================================================================
|
|
{
|
|
"AstroNvim/astrolsp",
|
|
---@type AstroLSPOpts
|
|
opts = {
|
|
-- Enable LSP features
|
|
features = {
|
|
autoformat = true,
|
|
codelens = true,
|
|
inlay_hints = true, -- Enable inlay hints for TS/Go
|
|
lsp_handlers = true,
|
|
semantic_tokens = true,
|
|
},
|
|
|
|
-- LSP UI defaults
|
|
defaults = {
|
|
hover = { border = "rounded", silent = true }, -- Rounded border for hover windows
|
|
signature_help = { border = "rounded", silent = true }, -- Enable signature help
|
|
},
|
|
|
|
-- LSP client flags
|
|
flags = {
|
|
exit_timeout = 5000, -- Time to wait for LSP server to exit before force kill
|
|
debounce_text_changes = 150, -- Debounce didChange notifications
|
|
},
|
|
|
|
-- LSP Formatting Configuration
|
|
formatting = {
|
|
format_on_save = {
|
|
enabled = true,
|
|
-- Allow specific filetypes (prefer conform.nvim for JS/TS)
|
|
allow_filetypes = {
|
|
"go",
|
|
"proto",
|
|
"toml",
|
|
"lua",
|
|
},
|
|
},
|
|
-- Disable LSP formatting for these (use conform.nvim/oxfmt instead)
|
|
disabled = {
|
|
"ts_ls", -- Use oxfmt via conform
|
|
"tsserver", -- Old name, keeping for compatibility
|
|
"vtsls", -- Use oxfmt via conform
|
|
"html", -- Use conform
|
|
"cssls", -- Use conform
|
|
"tailwindcss", -- Doesn't format
|
|
},
|
|
timeout_ms = 5000, -- Increase timeout for large Go files
|
|
filter = function(client)
|
|
-- Prefer conform.nvim for JS/TS formatting
|
|
if
|
|
vim.bo.filetype == "typescript"
|
|
or vim.bo.filetype == "javascript"
|
|
or vim.bo.filetype == "typescriptreact"
|
|
or vim.bo.filetype == "javascriptreact"
|
|
then
|
|
return false
|
|
end
|
|
-- Use gopls for Go formatting
|
|
if vim.bo.filetype == "go" then return client.name == "gopls" end
|
|
return true
|
|
end,
|
|
},
|
|
|
|
-- Enable file operations for better refactoring
|
|
file_operations = {
|
|
timeout = 10000,
|
|
operations = {
|
|
willCreate = true,
|
|
didCreate = true,
|
|
willRename = true,
|
|
didRename = true,
|
|
willDelete = true,
|
|
didDelete = true,
|
|
},
|
|
},
|
|
|
|
-- Language server configurations
|
|
config = {
|
|
-- Golang configuration optimized for Cosmos SDK
|
|
gopls = {
|
|
settings = {
|
|
gopls = {
|
|
-- Code analysis
|
|
analyses = {
|
|
unusedparams = true,
|
|
shadow = true,
|
|
nilness = true,
|
|
unusedwrite = true,
|
|
useany = true,
|
|
unusedvariable = true,
|
|
},
|
|
-- Staticcheck integration
|
|
staticcheck = true,
|
|
-- Inlay hints
|
|
hints = {
|
|
assignVariableTypes = true,
|
|
compositeLiteralFields = true,
|
|
compositeLiteralTypes = true,
|
|
constantValues = true,
|
|
functionTypeParameters = true,
|
|
parameterNames = true,
|
|
rangeVariableTypes = true,
|
|
},
|
|
-- Code completion
|
|
usePlaceholders = true,
|
|
completeUnimported = true,
|
|
-- Build flags for Cosmos SDK
|
|
buildFlags = { "-tags=ledger,test" },
|
|
-- Workspace settings
|
|
directoryFilters = {
|
|
"-**/node_modules",
|
|
"-**/.git",
|
|
"-**/.vscode",
|
|
"-**/.idea",
|
|
"-**/.devbox",
|
|
"-**/dist",
|
|
"-**/build",
|
|
},
|
|
-- Semantic tokens
|
|
semanticTokens = true,
|
|
-- Code lenses
|
|
codelenses = {
|
|
gc_details = true,
|
|
generate = true,
|
|
regenerate_cgo = true,
|
|
test = true,
|
|
tidy = true,
|
|
upgrade_dependency = true,
|
|
vendor = true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
-- TypeScript configuration for Vite/React/Cloudflare
|
|
ts_ls = {
|
|
settings = {
|
|
typescript = {
|
|
inlayHints = {
|
|
includeInlayParameterNameHints = "all",
|
|
includeInlayParameterNameHintsWhenArgumentMatchesName = true,
|
|
includeInlayFunctionParameterTypeHints = true,
|
|
includeInlayVariableTypeHints = true,
|
|
includeInlayVariableTypeHintsWhenTypeMatchesName = true,
|
|
includeInlayPropertyDeclarationTypeHints = true,
|
|
includeInlayFunctionLikeReturnTypeHints = true,
|
|
includeInlayEnumMemberValueHints = true,
|
|
},
|
|
suggest = {
|
|
completeFunctionCalls = true,
|
|
},
|
|
-- Cloudflare Workers types
|
|
preferences = {
|
|
importModuleSpecifier = "relative",
|
|
importModuleSpecifierEnding = "minimal",
|
|
},
|
|
},
|
|
javascript = {
|
|
inlayHints = {
|
|
includeInlayParameterNameHints = "all",
|
|
includeInlayParameterNameHintsWhenArgumentMatchesName = true,
|
|
includeInlayFunctionParameterTypeHints = true,
|
|
includeInlayVariableTypeHints = true,
|
|
includeInlayPropertyDeclarationTypeHints = true,
|
|
includeInlayFunctionLikeReturnTypeHints = true,
|
|
includeInlayEnumMemberValueHints = true,
|
|
},
|
|
suggest = {
|
|
completeFunctionCalls = true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
-- Volar for Vue (also helpful for some React scenarios)
|
|
volar = {
|
|
filetypes = { "vue", "typescript", "javascript", "typescriptreact", "javascriptreact" },
|
|
},
|
|
|
|
-- Tailwind CSS for React/shadcn components
|
|
tailwindcss = {
|
|
root_dir = function(fname)
|
|
local util = require "lspconfig.util"
|
|
return util.root_pattern("tailwind.config.js", "tailwind.config.ts", "postcss.config.js")(fname)
|
|
end,
|
|
settings = {
|
|
tailwindCSS = {
|
|
experimental = {
|
|
classRegex = {
|
|
-- Support for cn() and clsx() utilities
|
|
{ "cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]" },
|
|
{ "clsx\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]" },
|
|
{ "cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]" },
|
|
},
|
|
},
|
|
validate = true,
|
|
lint = {
|
|
cssConflict = "warning",
|
|
invalidApply = "error",
|
|
invalidScreen = "error",
|
|
invalidVariant = "error",
|
|
invalidConfigPath = "error",
|
|
invalidTailwindDirective = "error",
|
|
recommendedVariantOrder = "warning",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
-- CSS LSP
|
|
cssls = {},
|
|
|
|
-- HTML for React/templates
|
|
html = {
|
|
filetypes = { "html", "typescriptreact", "javascriptreact" },
|
|
settings = {
|
|
html = {
|
|
format = {
|
|
templating = true,
|
|
wrapLineLength = 100,
|
|
wrapAttributes = "auto",
|
|
},
|
|
hover = {
|
|
documentation = true,
|
|
references = true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
-- JSON/JSONC for config files
|
|
jsonls = {
|
|
settings = {
|
|
json = {
|
|
schemas = {
|
|
-- Cloudflare wrangler.toml
|
|
{
|
|
fileMatch = { "wrangler.toml", "wrangler.json" },
|
|
url = "https://raw.githubusercontent.com/cloudflare/wrangler/main/wrangler-schema.json",
|
|
},
|
|
-- Package.json
|
|
{
|
|
fileMatch = { "package.json" },
|
|
url = "https://json.schemastore.org/package.json",
|
|
},
|
|
-- tsconfig.json
|
|
{
|
|
fileMatch = { "tsconfig.json", "tsconfig.*.json" },
|
|
url = "https://json.schemastore.org/tsconfig.json",
|
|
},
|
|
},
|
|
validate = { enable = true },
|
|
},
|
|
},
|
|
},
|
|
|
|
-- YAML for configs
|
|
yamlls = {
|
|
settings = {
|
|
yaml = {
|
|
schemaStore = {
|
|
enable = true,
|
|
url = "https://www.schemastore.org/api/json/catalog.json",
|
|
},
|
|
schemas = {
|
|
["https://json.schemastore.org/github-workflow.json"] = ".github/workflows/*.yml",
|
|
["https://raw.githubusercontent.com/compose-spec/compose-spec/master/schema/compose-spec.json"] = "docker-compose*.yml",
|
|
},
|
|
validate = true,
|
|
hover = true,
|
|
completion = true,
|
|
},
|
|
},
|
|
},
|
|
|
|
-- Lua for Neovim config
|
|
lua_ls = {
|
|
settings = {
|
|
Lua = {
|
|
hint = {
|
|
enable = true,
|
|
arrayIndex = "Auto",
|
|
setType = true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
-- Capabilities configuration
|
|
capabilities = {
|
|
textDocument = {
|
|
completion = {
|
|
completionItem = {
|
|
documentationFormat = { "markdown", "plaintext" },
|
|
snippetSupport = true,
|
|
preselectSupport = true,
|
|
insertReplaceSupport = true,
|
|
labelDetailsSupport = true,
|
|
deprecatedSupport = true,
|
|
commitCharactersSupport = true,
|
|
tagSupport = { valueSet = { 1 } },
|
|
resolveSupport = {
|
|
properties = {
|
|
"documentation",
|
|
"detail",
|
|
"additionalTextEdits",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
-- LSP-specific keymappings (keeping existing bindings)
|
|
mappings = {
|
|
n = {
|
|
-- LSP Code Actions
|
|
["<C-a>a"] = {
|
|
function() vim.lsp.buf.code_action() end,
|
|
desc = "LSP Code Action",
|
|
},
|
|
|
|
-- LSP Hover
|
|
["<C-a>h"] = {
|
|
function() vim.lsp.buf.hover() end,
|
|
desc = "LSP Hover",
|
|
},
|
|
|
|
-- LSP Rename (using inc-rename plugin)
|
|
["<C-a>r"] = {
|
|
function() return ":IncRename " .. vim.fn.expand "<cword>" end,
|
|
expr = true,
|
|
desc = "Rename current symbol",
|
|
cond = "textDocument/rename",
|
|
},
|
|
|
|
-- LSP Diagnostics (using snacks.picker)
|
|
["<C-a>d"] = {
|
|
function() require("snacks").picker.diagnostics() end,
|
|
desc = "Find LSP diagnostics",
|
|
},
|
|
|
|
-- LSP Symbols (using snacks.picker)
|
|
["<C-a>s"] = {
|
|
function() require("snacks").picker.lsp_symbols() end,
|
|
desc = "Find LSP symbols",
|
|
},
|
|
|
|
-- Navigate diagnostics
|
|
K = {
|
|
function() vim.diagnostic.goto_prev() end,
|
|
desc = "Previous Diagnostic",
|
|
},
|
|
J = {
|
|
function() vim.diagnostic.goto_next() end,
|
|
desc = "Next Diagnostic",
|
|
},
|
|
},
|
|
},
|
|
|
|
-- Autocmds for LSP-specific behavior
|
|
autocmds = {
|
|
-- Disable inlay hints in insert mode for cleaner editing
|
|
no_insert_inlay_hints = {
|
|
cond = vim.lsp.inlay_hint and "textDocument/inlayHint" or false,
|
|
{
|
|
event = "InsertEnter",
|
|
desc = "Disable inlay hints on insert",
|
|
callback = function(args)
|
|
local filter = { bufnr = args.buf }
|
|
if vim.lsp.inlay_hint.is_enabled(filter) then
|
|
vim.lsp.inlay_hint.enable(false, filter)
|
|
vim.api.nvim_create_autocmd("InsertLeave", {
|
|
buffer = args.buf,
|
|
once = true,
|
|
callback = function() vim.lsp.inlay_hint.enable(true, filter) end,
|
|
})
|
|
end
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
-- ============================================================================
|
|
-- Formatting - Conform.nvim
|
|
-- ============================================================================
|
|
{
|
|
"stevearc/conform.nvim",
|
|
event = "BufWritePre",
|
|
cmd = { "ConformInfo" },
|
|
opts = {
|
|
formatters_by_ft = {
|
|
typescript = { "oxfmt" },
|
|
typescriptreact = { "oxfmt" },
|
|
javascript = { "oxfmt" },
|
|
javascriptreact = { "oxfmt" },
|
|
json = { "oxfmt" },
|
|
jsonc = { "oxfmt" },
|
|
css = { "oxfmt" },
|
|
scss = { "oxfmt" },
|
|
html = { "oxfmt" },
|
|
markdown = { "oxfmt" },
|
|
yaml = { "oxfmt" },
|
|
lua = { "stylua" },
|
|
rust = { "rustfmt" },
|
|
toml = { "taplo" },
|
|
},
|
|
format_on_save = function(bufnr)
|
|
-- Disable with a global or buffer-local variable
|
|
if vim.g.disable_autoformat or vim.b[bufnr].disable_autoformat then return end
|
|
return {
|
|
timeout_ms = 500,
|
|
lsp_fallback = true,
|
|
}
|
|
end,
|
|
formatters = {
|
|
-- oxfmt uses default configuration
|
|
},
|
|
},
|
|
config = function(_, opts)
|
|
require("conform").setup(opts)
|
|
|
|
-- Command to toggle format on save
|
|
vim.api.nvim_create_user_command("FormatToggle", function(args)
|
|
if args.bang then
|
|
-- Toggle buffer-local setting
|
|
vim.b.disable_autoformat = not vim.b.disable_autoformat
|
|
else
|
|
-- Toggle global setting
|
|
vim.g.disable_autoformat = not vim.g.disable_autoformat
|
|
end
|
|
local status = vim.g.disable_autoformat and "disabled" or "enabled"
|
|
vim.notify("Format on save " .. status, vim.log.levels.INFO)
|
|
end, {
|
|
desc = "Toggle format on save",
|
|
bang = true,
|
|
})
|
|
end,
|
|
},
|
|
|
|
-- ============================================================================
|
|
-- Linting - nvim-lint
|
|
-- ============================================================================
|
|
{
|
|
"mfussenegger/nvim-lint",
|
|
event = { "BufReadPre", "BufNewFile" },
|
|
config = function()
|
|
local lint = require "lint"
|
|
|
|
-- Configure linters per filetype
|
|
lint.linters_by_ft = {
|
|
typescript = { "oxlint" },
|
|
typescriptreact = { "oxlint" },
|
|
javascript = { "oxlint" },
|
|
javascriptreact = { "oxlint" },
|
|
css = { "stylelint" },
|
|
scss = { "stylelint" },
|
|
markdown = { "markdownlint" },
|
|
}
|
|
|
|
-- Auto-lint on save and text changed
|
|
local lint_augroup = vim.api.nvim_create_augroup("lint", { clear = true })
|
|
vim.api.nvim_create_autocmd({ "BufEnter", "BufWritePost", "InsertLeave", "TextChanged" }, {
|
|
group = lint_augroup,
|
|
callback = function()
|
|
-- Only lint if file exists on disk
|
|
if vim.fn.filereadable(vim.api.nvim_buf_get_name(0)) == 1 then lint.try_lint() end
|
|
end,
|
|
})
|
|
end,
|
|
},
|
|
|
|
-- ============================================================================
|
|
-- Treesitter Configuration
|
|
-- ============================================================================
|
|
{
|
|
"nvim-treesitter/nvim-treesitter",
|
|
optional = true,
|
|
opts = function(_, opts)
|
|
if opts.ensure_installed ~= "all" then
|
|
opts.ensure_installed = require("astrocore").list_insert_unique(opts.ensure_installed, {
|
|
"typescript",
|
|
"tsx",
|
|
"javascript",
|
|
"jsdoc",
|
|
"json",
|
|
"jsonc",
|
|
"css",
|
|
"scss",
|
|
"html",
|
|
"markdown",
|
|
"markdown_inline",
|
|
"regex",
|
|
"bash",
|
|
"vim",
|
|
"lua",
|
|
"proto", -- Protobuf syntax highlighting
|
|
})
|
|
end
|
|
end,
|
|
},
|
|
|
|
-- Treesitter context for React component awareness
|
|
{
|
|
"nvim-treesitter/nvim-treesitter-context",
|
|
event = { "BufReadPost", "BufNewFile" },
|
|
opts = {
|
|
enable = true,
|
|
max_lines = 3,
|
|
patterns = {
|
|
default = {
|
|
"class",
|
|
"function",
|
|
"method",
|
|
"for",
|
|
"while",
|
|
"if",
|
|
"switch",
|
|
"case",
|
|
"interface",
|
|
"struct",
|
|
"enum",
|
|
},
|
|
typescript = {
|
|
"class_declaration",
|
|
"abstract_class_declaration",
|
|
"function_declaration",
|
|
"method_definition",
|
|
"arrow_function",
|
|
"function_expression",
|
|
"lexical_declaration",
|
|
"variable_declaration",
|
|
},
|
|
typescriptreact = {
|
|
"class_declaration",
|
|
"function_declaration",
|
|
"method_definition",
|
|
"arrow_function",
|
|
"function_expression",
|
|
"jsx_element",
|
|
"jsx_self_closing_element",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
-- ============================================================================
|
|
-- TypeScript Tooling
|
|
-- ============================================================================
|
|
{
|
|
"pmizio/typescript-tools.nvim",
|
|
ft = { "typescript", "typescriptreact", "javascript", "javascriptreact" },
|
|
dependencies = { "nvim-lua/plenary.nvim", "neovim/nvim-lspconfig" },
|
|
opts = {
|
|
settings = {
|
|
separate_diagnostic_server = true,
|
|
publish_diagnostic_on = "insert_leave",
|
|
expose_as_code_action = "all",
|
|
tsserver_file_preferences = {
|
|
includeInlayParameterNameHints = "all",
|
|
includeInlayParameterNameHintsWhenArgumentMatchesName = true,
|
|
includeInlayFunctionParameterTypeHints = true,
|
|
includeInlayVariableTypeHints = true,
|
|
includeInlayVariableTypeHintsWhenTypeMatchesName = true,
|
|
includeInlayPropertyDeclarationTypeHints = true,
|
|
includeInlayFunctionLikeReturnTypeHints = true,
|
|
includeInlayEnumMemberValueHints = true,
|
|
},
|
|
tsserver_format_options = {
|
|
allowIncompleteCompletions = false,
|
|
allowRenameOfImportPath = true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
-- Auto-close and auto-rename JSX tags
|
|
{
|
|
"windwp/nvim-ts-autotag",
|
|
event = { "BufReadPost", "BufNewFile" },
|
|
ft = {
|
|
"html",
|
|
"javascript",
|
|
"typescript",
|
|
"javascriptreact",
|
|
"typescriptreact",
|
|
"xml",
|
|
"vue",
|
|
"svelte",
|
|
},
|
|
opts = {
|
|
autotag = {
|
|
enable = true,
|
|
enable_rename = true,
|
|
enable_close = true,
|
|
enable_close_on_slash = true,
|
|
},
|
|
},
|
|
},
|
|
|
|
-- ============================================================================
|
|
-- Mason Configuration
|
|
-- ============================================================================
|
|
{
|
|
"williamboman/mason-lspconfig.nvim",
|
|
optional = true,
|
|
opts = function(_, opts)
|
|
opts.ensure_installed = require("astrocore").list_insert_unique(opts.ensure_installed, {
|
|
-- TypeScript/JavaScript
|
|
"ts_ls", -- tsserver was renamed to ts_ls
|
|
"tailwindcss",
|
|
"html",
|
|
|
|
-- Styling
|
|
"cssls",
|
|
|
|
-- Config files
|
|
"jsonls",
|
|
"yamlls",
|
|
|
|
-- Lua
|
|
"lua_ls",
|
|
|
|
-- Go (Cosmos SDK)
|
|
"gopls",
|
|
})
|
|
end,
|
|
},
|
|
|
|
-- Note: mason-tool-installer is configured in lua/plugins/mason.lua
|
|
|
|
-- ============================================================================
|
|
-- Package Management Tools
|
|
-- ============================================================================
|
|
{
|
|
"vuki656/package-info.nvim",
|
|
event = "BufRead package.json",
|
|
dependencies = { "MunifTanjim/nui.nvim" },
|
|
opts = {},
|
|
},
|
|
|
|
-- Crates.io integration (useful for Rust tooling in Workers)
|
|
{
|
|
"saecki/crates.nvim",
|
|
event = "BufRead Cargo.toml",
|
|
dependencies = { "nvim-lua/plenary.nvim" },
|
|
opts = {
|
|
lsp = {
|
|
enabled = true,
|
|
actions = true,
|
|
completion = true,
|
|
hover = true,
|
|
},
|
|
},
|
|
},
|
|
|
|
-- ============================================================================
|
|
-- Diagnostics UI
|
|
-- ============================================================================
|
|
{
|
|
"folke/trouble.nvim",
|
|
optional = true,
|
|
opts = {
|
|
modes = {
|
|
lsp = {
|
|
win = { position = "right" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
-- Inline linting hints
|
|
{
|
|
"https://git.sr.ht/~whynothugo/lsp_lines.nvim",
|
|
event = { "BufReadPost", "BufNewFile" },
|
|
config = function()
|
|
require("lsp_lines").setup()
|
|
-- Disable virtual_text since lsp_lines will show diagnostics
|
|
vim.diagnostic.config { virtual_text = false }
|
|
|
|
-- Toggle command
|
|
vim.api.nvim_create_user_command("LspLinesToggle", function()
|
|
local new_value = not require("lsp_lines").toggle()
|
|
vim.diagnostic.config { virtual_text = new_value }
|
|
end, { desc = "Toggle LSP lines" })
|
|
end,
|
|
},
|
|
|
|
-- Better quickfix for showing lint errors
|
|
{
|
|
"kevinhwang91/nvim-bqf",
|
|
ft = "qf",
|
|
opts = {
|
|
auto_resize_height = true,
|
|
func_map = {
|
|
vsplit = "v",
|
|
split = "s",
|
|
tab = "t",
|
|
tabb = "T",
|
|
tabc = "<C-t>",
|
|
ptogglemode = "z,",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|