nvim-treesitter-textobjects
nvim-treesitter-textobjects copied to clipboard
feat: text object subsets (move/select/delete)
I'd like to be able to (move/select/delete) the parent node without (m/s/d)ing the contents - for changing code quickly
Describe the solution you'd like Example:
"@function.outer _ @function.inner"
Would select the function declaration without its body
Alternatives Here's some code I've written which deletes the function while preserving the contents (and deindenting them)
function delete_function_declaration_lines()
funcrowStart, funccolStart, funcrowEnd, funccolEnd = function_range_surrounding_cursor()
bodyrowStart, bodycolStart, bodyrowEnd, bodycolEnd = function_body_range_surrounding_cursor()
functionStartDelta = bodyrowStart - funcrowStart
functionEndDelta = funcrowEnd - bodyrowEnd
-- print("functionEndDelta: " .. tostring(functionEndDelta))
if funcrowStart == bodyrowStart then
print("WARN: function start and body start are on the same line. Unable to do interpolation")
return nil
end
if funcrowEnd == bodyrowEnd then
-- Then go into column mode
-- vim.api.nvim_buf_set_text(buf, startLine - 1, startColumn - 1, endLine, endColumn, lines)
-- XXX probably can handle brace syntax here
print("WARN: function end and body end are on the same line. Unable to do interpolation")
return nil
end
-- Delete the end first to preverse the line numbers
vim.api.nvim_buf_set_lines(0, bodyrowEnd+1, bodyrowEnd+functionEndDelta+1, false, {})
-- Deindent - convert from index to line number
deindent_lines(bodyrowStart+1, bodyrowEnd+1)
-- delete the beginning second
vim.api.nvim_buf_set_lines(0, funcrowStart, funcrowStart+functionStartDelta, false, {})
end
-- < Retrieve the name of the function the cursor is in.
function function_surrounding_cursor()
ts_utils = require('nvim-treesitter.ts_utils')
if not ts_utils then
return "<tsutils>"
end
local current_node = ts_utils.get_node_at_cursor()
if not current_node then
return "<none1>"
end
local func = current_node
while func do
if func:type() == 'function_declaration' then
break
end
func = func:parent()
end
if not func then
prev_function_node = nil
prev_function_name = ""
return "<none>"
end
if func == prev_function_node then
return prev_function_name
end
prev_function_node = func
local find_name
find_name = function(node)
for i = 0, node:named_child_count() - 1, 1 do
print("i :" .. tostring(i))
local child = node:named_child(i)
local type = child:type()
print("child type: " .. type)
if type == 'identifier' or type == 'operator_name' then
return vim.treesitter.get_node_text(child, 0)
else
local name = find_name(child)
if name then
return name
end
end
end
return nil
end
prev_function_name = find_name(func)
return prev_function_name
end
function function_range_surrounding_cursor()
-- XXX ok so the issue I'm having is that
-- The retured range is inccorect from
-- using ts_utils
-- require "nvim-treesitter.parsers"
ts_utils = require('nvim-treesitter.ts_utils')
local current_node = ts_utils.get_node_at_cursor()
if not current_node then
return ""
end
local func = current_node
while func do
if func:type() == 'function_declaration' then
break
end
func = func:parent()
end
if not func then
prev_function_node = nil
prev_function_name = ""
return "<none>"
end
if func == prev_function_node then
return prev_function_name
end
print(func:type())
return vim.treesitter.get_node_range(func, 0)
-- return func:range()
end
-- print("DONT ME")
function test_me()
print("HI")
end
-- print("DONT ME2")
function generateRange(startNum, endNum)
local range = {}
for i = startNum, endNum do
table.insert(range, i)
end
return range
end
function printTable(table_)
for _, item in ipairs(table_) do
print(item)
end
end
function deindent_lines(startRow, endRow)
-- arguments are line numbers and not an index
-- startRow and endRow are inclusive
-- Get the current buffer handle
local buf = vim.api.nvim_get_current_buf()
-- Get the shiftwidth value (number of spaces per tabstop)
local shiftwidth = vim.api.nvim_buf_get_option(buf, 'shiftwidth')
-- Get the lines to be deindented
local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
-- Deindent the specified lines by the tabstop value
local range = generateRange(startRow, endRow)
for _, lineNum in ipairs(range) do
local line = lines[lineNum]
local deindentedLine = line:gsub("^" .. string.rep(" ", shiftwidth), "")
lines[lineNum] = deindentedLine
end
-- Update the buffer with the modified lines
vim.api.nvim_buf_set_text(buf, 0, 0, -1, 0, lines)
end
function function_body_range_surrounding_cursor()
ts_utils = require('nvim-treesitter.ts_utils')
local current_node = ts_utils.get_node_at_cursor()
if not current_node then
return ""
end
local func = current_node
while func do
if func:type() == 'function_declaration' then
break
end
func = func:parent()
end
if not func then
prev_function_node = nil
prev_function_name = ""
return "<none>"
end
if func == prev_function_node then
return prev_function_name
end
local find_body
find_body = function(node)
for i = 0, node:named_child_count() - 1, 1 do
-- print("i :" .. tostring(i))
local child = node:named_child(i)
local type = child:type()
-- print("child type: " .. type)
if type == "block" then
return vim.treesitter.get_node_range(child, 0)
end
end
return nil
end
return find_body(func)
end
Additional context Add any other context or screenshots about the feature request here.