Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 58 additions & 8 deletions lua/wildfire/init.lua
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
local api = vim.api

local keymap = vim.keymap
local ts_utils = require("nvim-treesitter.ts_utils")
local parsers = require("nvim-treesitter.parsers")
local utils = require("wildfire.utils")

local M = {}
Expand Down Expand Up @@ -30,7 +28,24 @@ local count = 1
function M.unsurround_coordinates(node_or_range, buf)
-- local lines = vim.split(s, "\n")
local srow, scol, erow, ecol = utils.get_range(node_or_range)
local lines = vim.api.nvim_buf_get_text(buf, srow - 1, scol - 1, erow - 1, ecol, {})

-- Validate buffer bounds to prevent index out of bounds error
local line_count = vim.api.nvim_buf_line_count(buf)
if srow < 1 or erow > line_count or srow > erow then
return false, { srow, scol, erow, ecol }
end

-- Validate column bounds for the specific lines
if scol < 1 or ecol < 0 then
return false, { srow, scol, erow, ecol }
end

-- Use pcall to safely get text, handle any edge cases
local ok, lines = pcall(vim.api.nvim_buf_get_text, buf, srow - 1, scol - 1, erow - 1, ecol, {})
if not ok or not lines or #lines == 0 then
return false, { srow, scol, erow, ecol }
end

local node_text = table.concat(lines, "\n")
local match_brackets = nil
for _, pair in ipairs(M.options.surrounds) do
Expand Down Expand Up @@ -102,8 +117,17 @@ local function init_by_node(node)
end
function M.init_selection()
count = vim.v.count1
local node = ts_utils.get_node_at_cursor()
local node = vim.treesitter.get_node()
if not node then
-- No treesitter node available, try to handle gracefully
-- Check if treesitter is available for this filetype
local buf = api.nvim_get_current_buf()
local ok, parser = pcall(vim.treesitter.get_parser, buf)
if not ok or not parser then
-- No parser available for this filetype
vim.notify("Wildfire: No treesitter parser available for this filetype", vim.log.levels.WARN)
return
end
return
end
init_by_node(node)
Expand Down Expand Up @@ -134,9 +158,21 @@ local function select_incremental(get_parent)

-- Initialize incremental selection with current selection
if not nodes or #nodes == 0 then
local root = parsers.get_parser():parse()[1]:root()
-- Use native vim.treesitter API to get the parser
local ok, parser = pcall(vim.treesitter.get_parser, buf)
if not ok or not parser then
-- No parser available for this filetype, fallback to visual selection
return
end
local tree = parser:parse()[1]
if not tree then
return
end
local root = tree:root()
local node = root:named_descendant_for_range(csrow - 1, cscol - 1, cerow - 1, cecol)
update_selection_by_node(node)
if node then
update_selection_by_node(node)
end
return
end

Expand All @@ -147,15 +183,29 @@ local function select_incremental(get_parent)
if not parent or parent == node then
-- Keep searching in the main tree
-- TODO: we should search on the parent tree of the current node.
local root = parsers.get_parser():parse()[1]:root()
local ok, parser = pcall(vim.treesitter.get_parser, buf)
if not ok or not parser then
utils.update_selection(buf, node)
return
end
local tree = parser:parse()[1]
if not tree then
utils.update_selection(buf, node)
return
end
local root = tree:root()
parent = root:named_descendant_for_range(csrow - 1, cscol - 1, cerow - 1, cecol)
if not parent or root == node or parent == node then
utils.update_selection(buf, node)
return
end
end
node = parent
local nsrow, nscol, nerow, necol = ts_utils.get_vim_range({ node:range() })
local nsrow, nscol, nerow, necol = vim.treesitter.get_node_range(node)
-- Convert 0-based to 1-based indexing to match vim coordinates
-- Note: treesitter end_col is exclusive, but vim coordinates are inclusive
nsrow, nscol, nerow, necol = nsrow + 1, nscol + 1, nerow + 1, necol
-- necol: 0-based exclusive == 1-based inclusive, so no +1 needed

local larger_range = utils.range_larger({ nsrow, nscol, nerow, necol }, { csrow, cscol, cerow, cecol })

Expand Down
55 changes: 45 additions & 10 deletions lua/wildfire/utils.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
local api = vim.api

local ts_utils = require("nvim-treesitter.ts_utils")
local ts = vim.treesitter

local M = {}
Expand All @@ -9,8 +8,14 @@ function M.get_range(node_or_range)
if type(node_or_range) == "table" then
start_row, start_col, end_row, end_col = unpack(node_or_range)
else
local buf = api.nvim_get_current_buf()
start_row, start_col, end_row, end_col = ts_utils.get_vim_range({ ts.get_node_range(node_or_range) }, buf)
start_row, start_col, end_row, end_col = ts.get_node_range(node_or_range)
-- Convert 0-based to 1-based indexing to match vim coordinates
-- Note: treesitter end_col is exclusive, but vim coordinates are inclusive
start_row = start_row + 1
start_col = start_col + 1
end_row = end_row + 1
-- end_col is already exclusive in treesitter (0-based), converting to 1-based inclusive means no change needed
-- because: 0-based exclusive position == 1-based inclusive position
end
return start_row, start_col, end_row, end_col ---@type integer, integer, integer, integer
end
Expand Down Expand Up @@ -63,15 +68,15 @@ end

function M.print_selection(node_or_range)
local bufnr = api.nvim_get_current_buf()
local lines
local node_text
if type(node_or_range) == "table" then
local srow, scol, erow, ecol
srow, scol, erow, ecol = unpack(node_or_range)
lines = vim.api.nvim_buf_get_text(bufnr, srow - 1, scol - 1, erow - 1, ecol, {})
local lines = vim.api.nvim_buf_get_text(bufnr, srow - 1, scol - 1, erow - 1, ecol, {})
node_text = table.concat(lines, "\n")
else
lines = ts_utils.get_node_text(node_or_range, bufnr)
node_text = vim.treesitter.get_node_text(node_or_range, bufnr)
end
local node_text = table.concat(lines, "\n")
print(node_text)
end

Expand All @@ -81,9 +86,39 @@ function M.update_selection(buf, node_or_range, selection_mode)
if type(node_or_range) == "table" then
start_row, start_col, end_row, end_col = unpack(node_or_range)
else
start_row, start_col, end_row, end_col = ts_utils.get_vim_range({ ts.get_node_range(node_or_range) }, buf)
start_row, start_col, end_row, end_col = ts.get_node_range(node_or_range)
-- Convert 0-based to 1-based indexing to match vim coordinates
-- Note: treesitter end_col is exclusive, but vim coordinates are inclusive
start_row = start_row + 1
start_col = start_col + 1
end_row = end_row + 1
-- end_col is already exclusive in treesitter (0-based), converting to 1-based inclusive means no change needed
-- because: 0-based exclusive position == 1-based inclusive position
end

-- Validate buffer bounds to prevent cursor position errors
local line_count = api.nvim_buf_line_count(buf)
if start_row < 1 or end_row < 1 or start_row > line_count or end_row > line_count then
-- Invalid row range, cannot update selection
return
end

-- Validate column bounds
if start_col < 1 or end_col < 0 then
-- Invalid column range, cannot update selection
return
end

-- Get the actual line lengths to validate column positions
local start_line_text = api.nvim_buf_get_lines(buf, start_row - 1, start_row, false)[1] or ""
local end_line_text = api.nvim_buf_get_lines(buf, end_row - 1, end_row, false)[1] or ""
local start_line_len = #start_line_text
local end_line_len = #end_line_text

-- Clamp column positions to valid ranges (0-indexed for nvim_win_set_cursor)
start_col = math.max(0, math.min(start_col - 1, start_line_len))
end_col = math.max(0, math.min(end_col - 1, end_line_len))

local v_table = { charwise = "v", linewise = "V", blockwise = "<C-v>" }
selection_mode = selection_mode or "charwise"

Expand All @@ -103,8 +138,8 @@ function M.update_selection(buf, node_or_range, selection_mode)
api.nvim_cmd({ cmd = "normal", bang = true, args = { selection_mode } }, {})
end

api.nvim_win_set_cursor(0, { start_row, start_col - 1 })
api.nvim_win_set_cursor(0, { start_row, start_col })
vim.cmd("normal! o")
api.nvim_win_set_cursor(0, { end_row, end_col - 1 })
api.nvim_win_set_cursor(0, { end_row, end_col })
end
return M
Loading