refactor(nvim): streamline Neovim configuration and plugin management

This commit is contained in:
Prad Nukala committed 2025-10-22 10:59:55 -04:00
1 parent 06e0bab7dd
commit bdf8ad1bb0
27 files changed
+1797 -476

No files matched your search

+83
View File
@@ -94,6 +94,7 @@ return {
-- first key is the mode
n = {
["<C-!>"] = { "<Cmd>suspend<CR>", desc = "Suspend nvim (return with 'fg')" },
["<C-q>Q"] = { "<Cmd>wqa<CR>", desc = "Save all buffers and quit Neovim" },
-- navigate buffer tabs
["<C-c>"] = { "<Cmd>wa<CR><Cmd>bd<CR>", desc = "Save and close buffer" }, -- Added C-x to save and close buffer
["F"] = { "za", desc = "Toggle fold under cursor" },
@@ -412,8 +413,90 @@ return {
["<C-a>b"] = { "<cmd>ClaudeCodeAdd %<cr>", desc = "Add current buffer" },
["<C-a>y"] = { "<cmd>ClaudeCodeDiffAccept<cr>", desc = "Accept diff" },
["<C-a>n"] = { "<cmd>ClaudeCodeDiffDeny<cr>", desc = "Deny diff" },
["<C-a><C-t>"] = { "<cmd>TemplateSnacks<cr>", desc = "Open template picker" },
-- Taskfile
["<C-t><C-t>"] = { terminals.mk_toggle, desc = "Taskfile Toggle" },
-- UI Toggles with Snacks
["<leader>u"] = { desc = "UI Toggles" },
["<leader>ud"] = {
function() require("snacks").toggle.diagnostics():toggle() end,
desc = "Toggle Diagnostics",
},
["<leader>un"] = {
function() require("snacks").toggle.line_number():toggle() end,
desc = "Toggle Line Numbers",
},
["<leader>ur"] = {
function() require("snacks").toggle.option("relativenumber"):toggle() end,
desc = "Toggle Relative Numbers",
},
["<leader>uw"] = {
function() require("snacks").toggle.option("wrap"):toggle() end,
desc = "Toggle Line Wrap",
},
["<leader>us"] = {
function() require("snacks").toggle.option("spell"):toggle() end,
desc = "Toggle Spelling",
},
["<leader>ui"] = {
function() require("snacks").toggle.inlay_hints():toggle() end,
desc = "Toggle Inlay Hints",
},
["<leader>ut"] = {
function() require("snacks").toggle.treesitter():toggle() end,
desc = "Toggle Treesitter",
},
["<leader>uc"] = {
function() require("snacks").toggle.option("conceallevel", { off = 0, on = 2 }):toggle() end,
desc = "Toggle Conceal",
},
["<leader>uI"] = {
function() require("snacks").toggle.indent():toggle() end,
desc = "Toggle Indent Guides",
},
["<leader>uz"] = {
function() require("snacks").toggle.zen():toggle() end,
desc = "Toggle Zen Mode",
},
["<leader>uZ"] = {
function() require("snacks").toggle.zoom():toggle() end,
desc = "Toggle Zoom",
},
["<leader>ua"] = {
function() require("snacks").toggle.animate():toggle() end,
desc = "Toggle Animations",
},
["<leader>uS"] = {
function() require("snacks").toggle.scroll():toggle() end,
desc = "Toggle Smooth Scroll",
},
["<leader>up"] = {
function() require("snacks").toggle.profiler():toggle() end,
desc = "Toggle Profiler",
},
["<leader>uh"] = {
function() require("snacks").toggle.profiler_highlights():toggle() end,
desc = "Toggle Profiler Highlights",
},
["<leader>ux"] = {
function() require("snacks").toggle.dim():toggle() end,
desc = "Toggle Dim Inactive",
},
["<leader>uf"] = {
function()
vim.g.disable_autoformat = not vim.g.disable_autoformat
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",
},
["<leader>ul"] = {
function()
local new_value = not require("lsp_lines").toggle()
vim.diagnostic.config { virtual_text = new_value }
end,
desc = "Toggle LSP Lines",
},
},
i = {
["<C-c>"] = { "<Cmd>wa<CR><Cmd>bd<CR><Esc>", desc = "Save, close buffer, and return to normal mode" },
+387 -5
View File
@@ -1,4 +1,5 @@
--- AstroLSP configuration optimized for tech stack:
--- Comprehensive LSP, formatting, and linting configuration
--- Optimized for tech stack:
--- - Cloudflare Workers/Pages
--- - Golang (Cosmos SDK)
--- - TypeScript/React/Vite
@@ -7,9 +8,13 @@
--- @module plugins.astrolsp
return {
"AstroNvim/astrolsp",
---@type AstroLSPOpts
opts = {
-- ============================================================================
-- AstroLSP - Core LSP Configuration
-- ============================================================================
{
"AstroNvim/astrolsp",
---@type AstroLSPOpts
opts = {
-- Enable LSP features
features = {
autoformat = true,
@@ -176,8 +181,17 @@ return {
},
},
-- Tailwind for React components
-- 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 = {
@@ -202,8 +216,12 @@ return {
},
},
-- CSS LSP
cssls = {},
-- HTML for React/templates
html = {
filetypes = { "html", "typescriptreact", "javascriptreact" },
settings = {
html = {
format = {
@@ -380,4 +398,368 @@ return {
},
},
},
-- ============================================================================
-- 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",
},
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
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,
})
end
end
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",
})
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, {
"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" },
},
},
},
},
-- 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,",
},
},
},
}
+1
View File
@@ -2,6 +2,7 @@
--- Provides enhanced buffer/tab management with diagnostics and custom close handlers
--- @module plugins.bufferline
---@type LazySpec
return {
"akinsho/bufferline.nvim",
version = "*",
+1
View File
@@ -2,6 +2,7 @@
--- Includes support for bun, vite, and modern JS tooling
--- @module plugins.buildtools
---@type LazySpec
return {
-- Vim support for various config files
{
+5
View File
@@ -1,3 +1,8 @@
--- Claude Code integration for AI-powered development
--- Provides commands and keybindings for Claude Code assistant
--- @module plugins.claude
---@type LazySpec
return {
"coder/claudecode.nvim",
dependencies = { "folke/snacks.nvim" },
-194
View File
@@ -1,194 +0,0 @@
--- Linting and formatting configuration
--- Includes oxlint and oxfmt integration
--- @module plugins.linting
return {
-- Conform for formatting
{
"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,
},
-- Lint with nvim-lint (oxlint integration)
{
"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",
},
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
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,
})
end
end
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,
},
-- 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,
},
},
-- Additional 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,",
},
},
},
}
+3
View File
@@ -5,6 +5,7 @@
local ignore_patterns = require "config.ignore_patterns"
local terminals = require "config.terminals"
---@type LazySpec
return {
{
"neo-tree.nvim",
@@ -65,12 +66,14 @@ return {
event = events.FILE_MOVED,
handler = function(args)
require("astrolsp.file_operations").didRenameFiles { from = args.source, to = args.destination }
require("snacks").rename.on_rename_file(args.source, args.destination)
end,
},
{
event = events.FILE_RENAMED,
handler = function(args)
require("astrolsp.file_operations").didRenameFiles { from = args.source, to = args.destination }
require("snacks").rename.on_rename_file(args.source, args.destination)
end,
},
},
-29
View File
@@ -1,29 +0,0 @@
--- Telescope configuration with Doodle integration
--- Extends telescope.nvim with doodle extension
--- @module plugins.telescope
return {
"nvim-telescope/telescope.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"apdot/doodle",
},
opts = function(_, opts)
-- Ensure extensions table exists
opts.extensions = opts.extensions or {}
-- Add doodle extension configuration
opts.extensions.doodle = {}
return opts
end,
config = function(_, opts)
local telescope = require "telescope"
-- Setup telescope with options
telescope.setup(opts)
-- Load the doodle extension
telescope.load_extension "doodle"
end,
}
+161
View File
@@ -0,0 +1,161 @@
--- Template.nvim integration for quick file scaffolding
--- Provides templates for Cosmos SDK, Protobuf, and Vite+Cloudflare development
--- @module plugins.template
---@type LazySpec
return {
"glepnir/template.nvim",
cmd = { "Template", "TemProject" },
dependencies = { "folke/snacks.nvim" },
config = function()
local template = require "template"
-- Setup template.nvim
template.setup {
temp_dir = vim.fn.stdpath "config" .. "/templates",
author = "Prad",
email = "prad@sonr.io",
}
-- Custom template picker using Snacks
vim.api.nvim_create_user_command("TemplateSnacks", function(opts)
local temp_dir = vim.fn.stdpath "config" .. "/templates"
local current_ft = vim.bo.filetype
-- Find all template files
local template_files = vim.fn.globpath(temp_dir, "**/*", false, true)
-- Filter and format templates
local templates = {}
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
local ext = vim.fn.fnamemodify(file, ":e")
if ext == "tpl" then
-- Read first line to get filetype marker
local first_line = vim.fn.readfile(file, "", 1)[1] or ""
ext = first_line:match "^;; (%w+)" or ext
end
-- Filter by current filetype if specified
local should_include = true
if opts.args == "" and current_ft ~= "" then
-- Only filter if we have a current filetype and no args
should_include = ext == current_ft or ext == "tpl"
end
if should_include then
table.insert(templates, {
text = string.format("[%s] %s", category, filename),
path = file,
filename = filename,
})
end
end
end
if #templates == 0 then
vim.notify("No templates found", vim.log.levels.WARN)
return
end
-- Use Snacks picker
require("snacks").picker.pick {
items = templates,
format = function(item) return item.text end,
confirm = function(item)
if not item then return end
-- Read template content
local content = vim.fn.readfile(item.path)
-- Process template variables
local processed = {}
local needs_variable = false
local variable_name = nil
for _, line in ipairs(content) do
-- 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
table.insert(processed, line)
end
end
-- Prompt for variable if needed
if needs_variable then
require("snacks").input({ prompt = "Variable name:" }, function(input)
if not input then return end
variable_name = input
-- Replace variable in content
for i, line in ipairs(processed) do
processed[i] = line:gsub("{{_variable_}}", variable_name)
end
-- Insert template
insert_template(processed)
end)
else
insert_template(processed)
end
end,
}
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
end,
}
-240
View File
@@ -1,240 +0,0 @@
--- TypeScript/TSX development optimizations
--- Includes LSP, treesitter, linting, and React-specific tooling
--- @module plugins.tsx
return {
-- TypeScript LSP with enhanced settings for React/TSX
{
"AstroNvim/astrolsp",
optional = true,
---@type AstroLSPOpts
opts = {
---@diagnostic disable: missing-fields
config = {
-- TypeScript/JavaScript LSP
tsserver = {
settings = {
typescript = {
inlayHints = {
includeInlayParameterNameHints = "all",
includeInlayParameterNameHintsWhenArgumentMatchesName = true,
includeInlayFunctionParameterTypeHints = true,
includeInlayVariableTypeHints = true,
includeInlayVariableTypeHintsWhenTypeMatchesName = true,
includeInlayPropertyDeclarationTypeHints = true,
includeInlayFunctionLikeReturnTypeHints = true,
includeInlayEnumMemberValueHints = true,
},
suggest = {
includeCompletionsForModuleExports = true,
},
preferences = {
importModuleSpecifier = "relative",
jsxAttributeCompletionStyle = "auto",
},
},
javascript = {
inlayHints = {
includeInlayParameterNameHints = "all",
includeInlayParameterNameHintsWhenArgumentMatchesName = true,
includeInlayFunctionParameterTypeHints = true,
includeInlayVariableTypeHints = true,
includeInlayPropertyDeclarationTypeHints = true,
includeInlayFunctionLikeReturnTypeHints = true,
includeInlayEnumMemberValueHints = true,
},
},
},
},
-- Volar for Vue (also helpful for some React scenarios)
volar = {
filetypes = { "vue", "typescript", "javascript", "typescriptreact", "javascriptreact" },
},
-- Tailwind CSS LSP for shadcn/ui
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 = {
{ "cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]" },
{ "cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)" },
{ "cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]" },
},
},
},
},
},
-- CSS LSP
cssls = {},
-- HTML LSP
html = {
filetypes = { "html", "typescriptreact", "javascriptreact" },
},
},
},
},
-- Enhanced treesitter for TSX/React
{
"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,
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 utilities and tools
{
"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,
},
},
},
},
-- Mason LSP configurations
{
"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,
},
-- 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,
},
},
},
-- Package info for package.json
{
"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,
},
},
},
}