From 54444c9f33459e5cd37d613ee5036f1b4e049d44 Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Fri, 24 Oct 2025 09:34:24 -0400 Subject: [PATCH] refactor(lua): reformat and clean up Lua configuration files --- lua/community.lua | 2 +- lua/plugins/astrocore.lua | 87 +-- lua/plugins/astrolsp.lua | 1366 ++++++++++++++++++------------------- lua/plugins/claude.lua | 4 +- lua/plugins/mason.lua | 34 + lua/plugins/snacks.lua | 2 +- lua/plugins/template.lua | 101 ++- 7 files changed, 771 insertions(+), 825 deletions(-) create mode 100644 lua/plugins/mason.lua diff --git a/lua/community.lua b/lua/community.lua index d7b4834..868ebd2 100644 --- a/lua/community.lua +++ b/lua/community.lua @@ -16,7 +16,7 @@ return { { import = "astrocommunity.pack.go" }, { import = "astrocommunity.pack.golangci-lint" }, { import = "astrocommunity.pack.html-css" }, - { import = "astrocommunity.pack.oxlint" }, + -- oxlint configured manually in astrolsp.lua via nvim-lint { import = "astrocommunity.pack.toml" }, { import = "astrocommunity.pack.templ" }, { import = "astrocommunity.pack.tailwindcss" }, diff --git a/lua/plugins/astrocore.lua b/lua/plugins/astrocore.lua index 2d07513..b612172 100644 --- a/lua/plugins/astrocore.lua +++ b/lua/plugins/astrocore.lua @@ -16,7 +16,7 @@ return { "AstroNvim/astrocore", ---@type AstroCoreOpts opts = { - + sessions = { -- Configure auto saving autosave = { @@ -86,9 +86,15 @@ return { local filepath = vim.fn.expand "%:p" local filename = vim.fn.expand "%:t" - -- If no file is open, show Nvim only + -- If no file is open, try to show just the repo name if filename == "" then - vim.opt.titlestring = "Nvim" + local cwd = vim.fn.getcwd() + local github_match = cwd:match "github%.com/([^/]+/[^/]+)" + if github_match then + vim.opt.titlestring = github_match + else + vim.opt.titlestring = "Nvim" + end return end @@ -96,8 +102,8 @@ return { local github_match = filepath:match "github%.com/([^/]+/[^/]+)" if github_match then - -- Format: filename (org/repo) | Nvim - vim.opt.titlestring = string.format("%s (%s) | Nvim", filename, github_match) + -- Format: filename | org/repo + vim.opt.titlestring = string.format("%s | %s", filename, github_match) else -- Fallback: filename | Nvim vim.opt.titlestring = string.format("%s | Nvim", filename) @@ -105,13 +111,13 @@ return { end, }, }, - -- Show recent files or git repos on startup + -- Show smart picker on startup startup_picker = { { event = "VimEnter", - desc = "Show recent files for git repo or git repos picker on startup", + desc = "Show smart picker on startup", callback = function() - -- Skip if opening with arguments (files, directories, stdin) + -- Skip if opening with arguments if vim.fn.argc() > 0 then return end -- Skip if current buffer has content @@ -120,58 +126,8 @@ return { -- Delay to ensure everything is loaded vim.defer_fn(function() - -- Check if we're in a git repo - local git_dir = vim.fn.systemlist("git rev-parse --git-dir 2>/dev/null")[1] - local is_git_repo = vim.v.shell_error == 0 and git_dir ~= nil and git_dir ~= "" - - if is_git_repo then - -- In a git repo: show recent files - require("snacks").picker.recent() - else - -- Not in a git repo: show git repos using ghq - if vim.fn.executable "ghq" == 1 then - -- Get list of repos from ghq - local repos = vim.fn.systemlist "ghq list --full-path" - if #repos > 0 then - -- Build items list with formatted display - local items = {} - for _, repo_path in ipairs(repos) do - -- Extract org/repo from path - local display = repo_path:match "github%.com/([^/]+/[^/]+)" or vim.fn.fnamemodify(repo_path, ":t") - table.insert(items, { - text = display, - path = repo_path, - }) - end - - -- Use snacks picker to show repos - require("snacks").picker({ - title = "Git Repositories", - layout = { preview = false }, - items = items, - format = function(item) - return { { item.text } } - end, - confirm = function(picker, item) - picker:close() - -- Change to the selected repo directory - vim.cmd("cd " .. item.path) - -- Show recent files from the new directory - vim.defer_fn(function() - require("snacks").picker.recent() - end, 50) - end, - }) - else - -- Fallback to recent files if no repos found - require("snacks").picker.recent() - end - else - -- ghq not available, just show recent files - require("snacks").picker.recent() - end - end - end, 10) + require("snacks").picker.smart() + end, 50) end, }, }, @@ -511,10 +467,10 @@ return { }, -- AI/Assistant mappings (Claude Code) [""] = { nil, desc = "AI/Claude Code" }, - [""] = { function() _G.claude_at_root("ClaudeCode")() end, desc = "Toggle Claude" }, - ["f"] = { function() _G.claude_at_root("ClaudeCodeFocus")() end, desc = "Focus Claude" }, - ["C"] = { function() _G.claude_at_root("ClaudeCode --resume")() end, desc = "Resume Claude" }, - ["c"] = { function() _G.claude_at_root("ClaudeCode --continue")() end, desc = "Continue Claude" }, + [""] = { function() _G.claude_at_root "ClaudeCode"() end, desc = "Toggle Claude" }, + ["f"] = { function() _G.claude_at_root "ClaudeCodeFocus"() end, desc = "Focus Claude" }, + ["C"] = { function() _G.claude_at_root "ClaudeCode --resume"() end, desc = "Resume Claude" }, + ["c"] = { function() _G.claude_at_root "ClaudeCode --continue"() end, desc = "Continue Claude" }, ["m"] = { "ClaudeCodeSelectModel", desc = "Select Claude model" }, ["b"] = { "ClaudeCodeAdd %", desc = "Add current buffer" }, ["y"] = { "ClaudeCodeDiffAccept", desc = "Accept diff" }, @@ -575,7 +531,8 @@ return { }, ["ua"] = { function() require("snacks").toggle.animate():toggle() end, - desc = "Toggle Animations", }, + desc = "Toggle Animations", + }, ["uS"] = { function() require("snacks").toggle.scroll():toggle() end, desc = "Toggle Smooth Scroll", diff --git a/lua/plugins/astrolsp.lua b/lua/plugins/astrolsp.lua index a320059..31e17fa 100644 --- a/lua/plugins/astrolsp.lua +++ b/lua/plugins/astrolsp.lua @@ -15,750 +15,712 @@ return { "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", - }, + -- Enable LSP features + features = { + autoformat = true, + codelens = true, + inlay_hints = true, -- Enable inlay hints for TS/Go + lsp_handlers = true, + semantic_tokens = true, }, - -- Disable LSP formatting for these (use conform.nvim/oxfmt instead) - disabled = { - "tsserver", -- Use oxfmt via conform - "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, + -- LSP UI defaults + defaults = { + hover = { border = "rounded", silent = true }, -- Rounded border for hover windows + signature_help = { border = "rounded", silent = true }, -- Enable signature help }, - }, - -- 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, - }, + -- 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", }, }, - }, - - -- TypeScript configuration for Vite/React/Cloudflare - tsserver = { - 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, - }, - }, + -- 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 }, - }, - - -- 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) + 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, - settings = { - tailwindCSS = { - experimental = { - classRegex = { - -- Support for cn() and clsx() utilities - { "cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]" }, - { "clsx\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]" }, - { "cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]" }, + }, + + -- 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, }, - }, - 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, - }, - }, - }, - }, - - -- Protobuf LSP - bufls = { - settings = { - -- Add any buf-specific settings - }, - }, - - -- 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", + -- Staticcheck integration + staticcheck = true, + -- Inlay hints + hints = { + assignVariableTypes = true, + compositeLiteralFields = true, + compositeLiteralTypes = true, + constantValues = true, + functionTypeParameters = true, + parameterNames = true, + rangeVariableTypes = true, }, - -- Package.json - { - fileMatch = { "package.json" }, - url = "https://json.schemastore.org/package.json", + -- 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", }, - -- 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", + -- Semantic tokens + semanticTokens = true, + -- Code lenses + codelenses = { + gc_details = true, + generate = true, + regenerate_cgo = true, + test = true, + tidy = true, + upgrade_dependency = true, + vendor = true, }, }, }, }, - }, - }, - -- LSP-specific keymappings (keeping existing bindings) - mappings = { - n = { - -- LSP Code Actions - ["a"] = { - function() vim.lsp.buf.code_action() end, - desc = "LSP Code Action", + -- 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, + }, + }, + }, }, - -- LSP Hover - ["h"] = { - function() vim.lsp.buf.hover() end, - desc = "LSP Hover", + -- Volar for Vue (also helpful for some React scenarios) + volar = { + filetypes = { "vue", "typescript", "javascript", "typescriptreact", "javascriptreact" }, }, - -- LSP Rename (using inc-rename plugin) - ["r"] = { - function() return ":IncRename " .. vim.fn.expand("") end, - expr = true, - desc = "Rename current symbol", - cond = "textDocument/rename", - }, - - -- LSP Diagnostics (using snacks.picker) - ["d"] = { - function() require("snacks").picker.diagnostics() end, - desc = "Find LSP diagnostics", - }, - - -- LSP Symbols (using snacks.picker) - ["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 + -- 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, + }, + }, + }, + }, + + -- Protobuf LSP + bufls = { + settings = { + -- Add any buf-specific settings + }, + }, + + -- 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, + }, + }, + }, }, }, - }, - }, - -- ============================================================================ - -- 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 oxlint for TypeScript/JavaScript - lint.linters.oxlint = { - cmd = "oxlint", - stdin = false, - args = { - "--format=json", + -- 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", + }, + }, + }, + }, }, - stream = "stdout", - ignore_exitcode = true, - parser = function(output) - if output == "" or output == nil then return {} end + }, - local ok, decoded = pcall(vim.fn.json_decode, output) - if not ok then return {} end + -- LSP-specific keymappings (keeping existing bindings) + mappings = { + n = { + -- LSP Code Actions + ["a"] = { + function() vim.lsp.buf.code_action() end, + desc = "LSP Code Action", + }, - local diagnostics = {} - for _, item in ipairs(decoded) do - if item.diagnostics then - for _, diagnostic in ipairs(item.diagnostics) do - table.insert(diagnostics, { - lnum = diagnostic.start.line - 1, - end_lnum = diagnostic["end"].line - 1, - col = diagnostic.start.column - 1, - end_col = diagnostic["end"].column - 1, - severity = vim.diagnostic.severity.WARN, - source = "oxlint", - message = diagnostic.message, - code = diagnostic.rule_id, + -- LSP Hover + ["h"] = { + function() vim.lsp.buf.hover() end, + desc = "LSP Hover", + }, + + -- LSP Rename (using inc-rename plugin) + ["r"] = { + function() return ":IncRename " .. vim.fn.expand "" end, + expr = true, + desc = "Rename current symbol", + cond = "textDocument/rename", + }, + + -- LSP Diagnostics (using snacks.picker) + ["d"] = { + function() require("snacks").picker.diagnostics() end, + desc = "Find LSP diagnostics", + }, + + -- LSP Symbols (using snacks.picker) + ["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 + 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 - return diagnostics - end, - } - - -- 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", + 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 - 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", - }, - }, + end, }, - }, - -- ============================================================================ - -- 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, - }, - }, - }, - }, + -- ============================================================================ + -- Linting - nvim-lint + -- ============================================================================ + { + "mfussenegger/nvim-lint", + event = { "BufReadPre", "BufNewFile" }, + config = function() + local lint = require "lint" - -- Auto-close and auto-rename JSX tags - { - "windwp/nvim-ts-autotag", - event = { "BufReadPost", "BufNewFile" }, - ft = { - "html", - "javascript", - "typescript", - "javascriptreact", - "typescriptreact", - "xml", - "vue", - "svelte", + -- 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, }, - opts = { - autotag = { + + -- ============================================================================ + -- 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", + }) + end + end, + }, + + -- Treesitter context for React component awareness + { + "nvim-treesitter/nvim-treesitter-context", + event = { "BufReadPost", "BufNewFile" }, + opts = { 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, { - "tsserver", - "tailwindcss", - "cssls", - "html", - "jsonls", - }) - end, - }, - - -- Mason tool installer for linters/formatters - { - "WhoIsSethDaniel/mason-tool-installer.nvim", - dependencies = { "williamboman/mason.nvim" }, - cmd = { "MasonToolsInstall", "MasonToolsUpdate" }, - opts = { - ensure_installed = { - -- Formatters - "stylua", - "rustfmt", - "taplo", - -- Linters - "stylelint", - "markdownlint", - -- Note: oxlint and oxfmt should be installed via cargo/npm separately - }, - auto_update = false, - run_on_start = true, - }, - }, - - -- ============================================================================ - -- 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" }, + 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", + }, }, }, }, - }, - -- 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 } + -- ============================================================================ + -- 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, + }, + }, + }, + }, - -- 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, - }, + -- 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, + }, + }, + }, - -- 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 = "", - ptogglemode = "z,", + -- ============================================================================ + -- 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 = "", + ptogglemode = "z,", + }, }, }, }, diff --git a/lua/plugins/claude.lua b/lua/plugins/claude.lua index b39c482..442aaf2 100644 --- a/lua/plugins/claude.lua +++ b/lua/plugins/claude.lua @@ -12,9 +12,7 @@ return { -- Helper function to get git root and change directory local function ensure_git_root() local git_root = vim.fn.systemlist("git rev-parse --show-toplevel")[1] - if git_root and vim.v.shell_error == 0 then - vim.api.nvim_set_current_dir(git_root) - end + if git_root and vim.v.shell_error == 0 then vim.api.nvim_set_current_dir(git_root) end end -- Wrapper function to run commands at git root diff --git a/lua/plugins/mason.lua b/lua/plugins/mason.lua new file mode 100644 index 0000000..167a067 --- /dev/null +++ b/lua/plugins/mason.lua @@ -0,0 +1,34 @@ +-- Customize Mason +-- This file configures non-LSP tools (formatters, linters, debuggers, etc.) +-- LSP servers are configured in astrolsp.lua via mason-lspconfig + +---@type LazySpec +return { + -- use mason-tool-installer for automatically installing Mason packages + { + "WhoIsSethDaniel/mason-tool-installer.nvim", + -- overrides `require("mason-tool-installer").setup(...)` + opts = { + -- Make sure to use the names found in `:Mason` + ensure_installed = { + -- Formatters (non-LSP) + "stylua", + "rustfmt", + "taplo", + + -- Linters + "stylelint", + "markdownlint", + + -- Debuggers + "debugpy", + + -- Other tools + "tree-sitter-cli", + + -- Note: oxlint and oxfmt should be installed separately via cargo/npm + -- Note: LSP servers are configured in astrolsp.lua + }, + }, + }, +} diff --git a/lua/plugins/snacks.lua b/lua/plugins/snacks.lua index 6f358fe..b92c939 100644 --- a/lua/plugins/snacks.lua +++ b/lua/plugins/snacks.lua @@ -36,7 +36,7 @@ return { self.esc_timer = self.esc_timer or (vim.uv or vim.loop).new_timer() if self.esc_timer:is_active() then self.esc_timer:stop() - vim.cmd("stopinsert") + vim.cmd "stopinsert" else self.esc_timer:start(200, 0, function() end) return "" diff --git a/lua/plugins/template.lua b/lua/plugins/template.lua index e3573a5..a964d5f 100644 --- a/lua/plugins/template.lua +++ b/lua/plugins/template.lua @@ -23,6 +23,52 @@ return { template.register("{{_dir_}}", function() return vim.fn.expand "%:p:h" end) template.register("{{_project_}}", function() return vim.fn.fnamemodify(vim.fn.getcwd(), ":t") end) + -- Helper function to insert template + local function insert_template(lines) + local cursor_line = nil + local processed = {} + + for i, line in ipairs(lines) do + -- Replace template markers + line = line:gsub("{{_date_}}", os.date "%Y-%m-%d %H:%M:%S") + line = line:gsub("{{_author_}}", "Prad") + line = line:gsub("{{_email_}}", "prad@sonr.io") + line = line:gsub("{{_file_name_}}", vim.fn.expand "%:t:r") + line = line:gsub("{{_upper_file_}}", vim.fn.expand("%:t:r"):upper()) + + -- Convert snake_case to CamelCase + local file_name = vim.fn.expand "%:t:r" + local camel_case = file_name:gsub("_(%w)", function(c) return c:upper() end) + camel_case = camel_case:gsub("^%w", string.upper) + line = line:gsub("{{_camel_case_file_}}", camel_case) + + -- Handle lua expressions + line = line:gsub("{{_lua:(.-)_}}", function(expr) + local func = load("return " .. expr) + if func then + local ok, result = pcall(func) + if ok then return tostring(result) end + end + return "" + end) + + -- Track cursor position + if line:match "{{_cursor_}}" then + cursor_line = i + line = line:gsub("{{_cursor_}}", "") + end + + table.insert(processed, line) + end + + -- Insert lines at cursor + local row = vim.api.nvim_win_get_cursor(0)[1] + vim.api.nvim_buf_set_lines(0, row - 1, row - 1, false, processed) + + -- Set cursor position if marker found + if cursor_line then vim.api.nvim_win_set_cursor(0, { row + cursor_line - 1, 0 }) end + end + -- Custom template picker using Snacks vim.api.nvim_create_user_command("TemplateSnacks", function(opts) local temp_dir = vim.fn.stdpath "config" .. "/templates" @@ -36,7 +82,6 @@ return { for _, file in ipairs(template_files) do if vim.fn.isdirectory(file) == 0 then local filename = vim.fn.fnamemodify(file, ":t") - local relative_path = vim.fn.fnamemodify(file, ":~:.") local category = vim.fn.fnamemodify(file, ":h:t") -- Extract filetype from template @@ -90,9 +135,7 @@ return { -- Skip filetype marker line if not line:match "^;; %w+" then -- Check for variable placeholder - if line:match "{{_variable_}}" and not variable_name then - needs_variable = true - end + if line:match "{{_variable_}}" and not variable_name then needs_variable = true end table.insert(processed, line) end end @@ -120,54 +163,6 @@ return { } end, { nargs = "?", desc = "Open template picker" }) - -- Helper function to insert template - function insert_template(lines) - local cursor_line = nil - local processed = {} - - for i, line in ipairs(lines) do - -- Replace template markers - line = line:gsub("{{_date_}}", os.date "%Y-%m-%d %H:%M:%S") - line = line:gsub("{{_author_}}", "Prad") - line = line:gsub("{{_email_}}", "prad@sonr.io") - line = line:gsub("{{_file_name_}}", vim.fn.expand "%:t:r") - line = line:gsub("{{_upper_file_}}", vim.fn.expand("%:t:r"):upper()) - - -- Convert snake_case to CamelCase - local file_name = vim.fn.expand "%:t:r" - local camel_case = file_name:gsub("_(%w)", function(c) return c:upper() end) - camel_case = camel_case:gsub("^%w", string.upper) - line = line:gsub("{{_camel_case_file_}}", camel_case) - - -- Handle lua expressions - line = line:gsub("{{_lua:(.-)_}}", function(expr) - local func, err = load("return " .. expr) - if func then - local ok, result = pcall(func) - if ok then return tostring(result) end - end - return "" - end) - - -- Track cursor position - if line:match "{{_cursor_}}" then - cursor_line = i - line = line:gsub("{{_cursor_}}", "") - end - - table.insert(processed, line) - end - - -- Insert lines at cursor - local row = vim.api.nvim_win_get_cursor(0)[1] - vim.api.nvim_buf_set_lines(0, row - 1, row - 1, false, processed) - - -- Set cursor position if marker found - if cursor_line then - vim.api.nvim_win_set_cursor(0, { row + cursor_line - 1, 0 }) - end - end - -- Command to create new templates easily vim.api.nvim_create_user_command("TemplateCreate", function(opts) local temp_dir = vim.fn.stdpath "config" .. "/templates" @@ -199,7 +194,7 @@ return { vim.notify("Created template: " .. template_path, vim.log.levels.INFO) end, { nargs = "+", - complete = function(_, line) + complete = function(_, _line) local temp_dir = vim.fn.stdpath "config" .. "/templates" local categories = {}