local parsers = { "lua", "vim", "vimdoc", "php", "html", "javascript", "typescript", "tsx", "css", "scss", "json", "markdown", "markdown_inline", "bash", "python", "regex", } local filetypes = { "lua", "vim", "help", "php", "html", "javascript", "javascriptreact", "typescript", "typescriptreact", "css", "scss", "json", "jsonc", "markdown", "sh", "bash", "python", } local function map(modes, lhs, rhs, desc) vim.keymap.set(modes, lhs, rhs, { desc = desc, silent = true }) end return { { "nvim-treesitter/nvim-treesitter", branch = "main", lazy = false, build = ":TSUpdate", config = function() local treesitter = require("nvim-treesitter") -- Keep the configured parser set present without blocking startup. treesitter.install(parsers) local group = vim.api.nvim_create_augroup("treesitter_features", { clear = true }) vim.api.nvim_create_autocmd("FileType", { group = group, pattern = filetypes, desc = "Enable native Tree-sitter highlighting and supported indentation", callback = function(args) local path = vim.api.nvim_buf_get_name(args.buf) local ok, stat = pcall(vim.uv.fs_stat, path) if ok and stat and stat.size > 100 * 1024 then return end local lang = vim.treesitter.language.get_lang(vim.bo[args.buf].filetype) if not lang or not pcall(vim.treesitter.start, args.buf, lang) then return end local has_indent, query = pcall(vim.treesitter.query.get, lang, "indents") if has_indent and query then vim.bo[args.buf].indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()" end end, }) map({ "n", "x" }, "+", function() vim.treesitter.select("parent", vim.v.count1) end, "Tree-sitter: Select parent node") map("x", "-", function() vim.treesitter.select("child", vim.v.count1) end, "Tree-sitter: Select child node") end, }, { "nvim-treesitter/nvim-treesitter-textobjects", branch = "main", dependencies = { "nvim-treesitter/nvim-treesitter" }, config = function() require("nvim-treesitter-textobjects").setup({ select = { lookahead = true }, move = { set_jumps = true }, }) local select = require("nvim-treesitter-textobjects.select") local move = require("nvim-treesitter-textobjects.move") local swap = require("nvim-treesitter-textobjects.swap") local function select_object(capture, query_group) return function() select.select_textobject(capture, query_group or "textobjects") end end for lhs, capture in pairs({ af = "@function.outer", ["if"] = "@function.inner", ac = "@class.outer", ic = "@class.inner", aa = "@parameter.outer", ia = "@parameter.inner", ai = "@conditional.outer", ii = "@conditional.inner", al = "@loop.outer", il = "@loop.inner", ["a/"] = "@comment.outer", }) do map({ "x", "o" }, lhs, select_object(capture), "Tree-sitter: Select " .. capture) end map({ "n", "x" }, "g+", select_object("@local.scope", "locals"), "Tree-sitter: Select enclosing scope") local movements = { { "]f", "goto_next_start", "@function.outer" }, { "]F", "goto_next_end", "@function.outer" }, { "[f", "goto_previous_start", "@function.outer" }, { "[F", "goto_previous_end", "@function.outer" }, { "]c", "goto_next_start", "@class.outer" }, { "]C", "goto_next_end", "@class.outer" }, { "[c", "goto_previous_start", "@class.outer" }, { "[C", "goto_previous_end", "@class.outer" }, { "]a", "goto_next_start", "@parameter.inner" }, { "]A", "goto_next_end", "@parameter.inner" }, { "[a", "goto_previous_start", "@parameter.inner" }, { "[A", "goto_previous_end", "@parameter.inner" }, } for _, movement in ipairs(movements) do local lhs, method, capture = unpack(movement) map({ "n", "x", "o" }, lhs, function() move[method](capture, "textobjects") end, "Tree-sitter: " .. method:gsub("_", " ") .. " " .. capture) end map("n", "a", function() swap.swap_next("@parameter.inner") end, "Tree-sitter: Swap with next parameter") map("n", "A", function() swap.swap_previous("@parameter.inner") end, "Tree-sitter: Swap with previous parameter") end, }, { "windwp/nvim-ts-autotag", dependencies = { "nvim-treesitter/nvim-treesitter" }, opts = { opts = { enable_close = false, enable_rename = false, enable_close_on_slash = false, }, per_filetype = { html = { enable_close = true, enable_rename = true, enable_close_on_slash = true }, php = { enable_close = true, enable_rename = true, enable_close_on_slash = true }, javascript = { enable_close = true, enable_rename = true, enable_close_on_slash = true }, javascriptreact = { enable_close = true, enable_rename = true, enable_close_on_slash = true }, typescript = { enable_close = true, enable_rename = true, enable_close_on_slash = true }, typescriptreact = { enable_close = true, enable_rename = true, enable_close_on_slash = true }, vue = { enable_close = true, enable_rename = true, enable_close_on_slash = true }, }, }, }, }