semi ok
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
-- Local treesitter setup. Replaces nvim-treesitter + nvim-treesitter-textobjects.
|
||||
-- Parsers: ~/.config/nvim/parser/<lang>.so
|
||||
-- Queries: ~/.config/nvim/queries/<lang>/
|
||||
|
||||
-- Start treesitter highlighting on FileType when a parser is available.
|
||||
vim.api.nvim_create_autocmd("FileType", {
|
||||
callback = function(args)
|
||||
pcall(vim.treesitter.start, args.buf)
|
||||
end,
|
||||
})
|
||||
|
||||
-- Function text objects: af / if
|
||||
-- Walks the textobjects query for the buffer's language and selects the
|
||||
-- smallest @function.outer / @function.inner range containing the cursor.
|
||||
local function select_function(capture)
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local ok, parser = pcall(vim.treesitter.get_parser, bufnr)
|
||||
if not ok or not parser then return end
|
||||
|
||||
local lang = parser:lang()
|
||||
local query = vim.treesitter.query.get(lang, "textobjects")
|
||||
if not query then return end
|
||||
|
||||
local tree = parser:parse()[1]
|
||||
if not tree then return end
|
||||
local root = tree:root()
|
||||
|
||||
local cur = vim.api.nvim_win_get_cursor(0)
|
||||
local crow, ccol = cur[1] - 1, cur[2]
|
||||
|
||||
-- A single textobject (e.g. @function.inner) is often split across
|
||||
-- multiple captures within the same match (e.g. one per statement in
|
||||
-- the body). Use iter_matches and union the ranges of all captures
|
||||
-- with our target name within each match.
|
||||
local best, best_size
|
||||
for _, match in query:iter_matches(root, bufnr, 0, -1, { all = true }) do
|
||||
local min_sr, min_sc, max_er, max_ec
|
||||
for id, nodes in pairs(match) do
|
||||
if query.captures[id] == capture then
|
||||
if type(nodes) ~= "table" then nodes = { nodes } end
|
||||
for _, node in ipairs(nodes) do
|
||||
local sr, sc, er, ec = node:range()
|
||||
if not min_sr or sr < min_sr or (sr == min_sr and sc < min_sc) then
|
||||
min_sr, min_sc = sr, sc
|
||||
end
|
||||
if not max_er or er > max_er or (er == max_er and ec > max_ec) then
|
||||
max_er, max_ec = er, ec
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if min_sr then
|
||||
local contains = (min_sr < crow or (min_sr == crow and min_sc <= ccol))
|
||||
and (max_er > crow or (max_er == crow and max_ec >= ccol))
|
||||
if contains then
|
||||
local size = (max_er - min_sr) * 1e6 + (max_ec - min_sc)
|
||||
if not best_size or size < best_size then
|
||||
best_size = size
|
||||
best = { min_sr, min_sc, max_er, max_ec }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not best then return end
|
||||
local sr, sc, er, ec = best[1], best[2], best[3], best[4]
|
||||
|
||||
-- If we were invoked from visual mode (`vif`, `vaf`, etc.), exit it
|
||||
-- first — otherwise `normal! v` below would toggle visual off instead
|
||||
-- of re-entering it, and we'd end up only moving the cursor.
|
||||
local mode = vim.fn.mode()
|
||||
if mode == "v" or mode == "V" or mode == "\22" then
|
||||
vim.api.nvim_feedkeys(
|
||||
vim.api.nvim_replace_termcodes("<Esc>", true, false, true), "nx", false)
|
||||
end
|
||||
|
||||
vim.api.nvim_win_set_cursor(0, { sr + 1, sc })
|
||||
vim.cmd("normal! v")
|
||||
vim.api.nvim_win_set_cursor(0, { er + 1, math.max(0, ec - 1) })
|
||||
end
|
||||
|
||||
vim.keymap.set({ "x", "o" }, "af", function() select_function("function.outer") end,
|
||||
{ desc = "around function" })
|
||||
vim.keymap.set({ "x", "o" }, "if", function() select_function("function.inner") end,
|
||||
{ desc = "inside function" })
|
||||
Reference in New Issue
Block a user