range: add extmark utilities
All checks were successful
NeoVim tests / code-quality (push) Successful in 1m22s

This commit is contained in:
Jonathan Apodaca 2025-06-11 20:04:46 -06:00
parent 6f86bfaa42
commit 859187585b
3 changed files with 88 additions and 0 deletions

View File

@ -1,6 +1,13 @@
local Pos = require 'u.pos'
local ESC = vim.api.nvim_replace_termcodes('<Esc>', true, false, true)
local NS = vim.api.nvim_create_namespace 'u.range'
---@class u.ExtmarkRange
---@field bufnr number
---@field id number
local ExtmarkRange = {}
ExtmarkRange.__index = ExtmarkRange
--- @class u.Range
--- @field start u.Pos
@ -83,6 +90,27 @@ function Range.from_marks(lpos, rpos)
return Range.new(start, stop, mode)
end
--- @param bufnr number
--- @param id number
function Range.from_extmark(bufnr, id)
---@type integer, integer, vim.api.keyset.extmark_details | nil
local start_row0, start_col0, details =
unpack(vim.api.nvim_buf_get_extmark_by_id(bufnr, NS, id, { details = true }))
local start = Pos.new(bufnr, start_row0 + 1, start_col0 + 1)
local stop = details and Pos.new(bufnr, details.end_row + 1, details.end_col)
local max_line_num = vim.api.nvim_buf_line_count(bufnr)
if stop and stop.lnum > max_line_num then
stop.lnum = max_line_num
stop.col = Pos.MAX_COL
stop = stop:as_real()
end
if stop and stop.col == 0 then stop = stop:must_next(-1) end
return Range.new(start, stop, 'v')
end
--- @param bufnr? number
function Range.from_buf_text(bufnr)
if bufnr == nil or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end
@ -272,6 +300,13 @@ function Range:to_linewise()
return r
end
function Range:to_charwise()
local r = self:clone()
r.mode = 'v'
if r.stop:is_col_max() then r.stop = r.stop:as_real() end
return r
end
--- @param x u.Pos | u.Range
function Range:contains(x)
if getmetatable(x) == Pos then
@ -334,6 +369,15 @@ function Range:save_to_marks(left, right)
end
end
function Range:save_to_extmark()
local r = self:to_charwise()
local id = vim.api.nvim_buf_set_extmark(r.start.bufnr, NS, r.start.lnum - 1, r.start.col - 1, {
end_row = r.stop.lnum - 1,
end_col = r.stop.col,
})
return ExtmarkRange.new(r.start.bufnr, id)
end
function Range:set_visual_selection()
if self:is_empty() then return end
if vim.api.nvim_get_current_buf() ~= self.start.bufnr then
@ -596,4 +640,10 @@ function Range:highlight(group, opts)
}
end
function ExtmarkRange.new(bufnr, id) return setmetatable({ bufnr = bufnr, id = id }, ExtmarkRange) end
function ExtmarkRange:range() return Range.from_extmark(self.bufnr, self.id) end
function ExtmarkRange:delete() vim.api.nvim_buf_del_extmark(self.bufnr, NS, self.id) end
return Range

View File

@ -7,6 +7,8 @@ local M = {}
--- @alias QfItem { col: number, filename: string, kind: string, lnum: number, text: string }
--- @alias KeyMaps table<string, fun(): any | string> }
-- luacheck: ignore
--- @alias RawCmdArgs { args: string; bang: boolean; count: number; fargs: string[]; line1: number; line2: number; mods: string; name: string; range: 0|1|2; reg: string; smods: any }
-- luacheck: ignore
--- @alias CmdArgs { args: string; bang: boolean; count: number; fargs: string[]; line1: number; line2: number; mods: string; name: string; range: 0|1|2; reg: string; smods: any; info: u.Range|nil }
--- @generic T
@ -51,6 +53,17 @@ function M.ucmd(name, cmd, opts)
vim.api.nvim_create_user_command(name, cmd2, opts or {})
end
--- @param current_args RawCmdArgs
function M.create_forward_cmd_args(current_args)
local args = { args = current_args.fargs }
if current_args.range == 1 then
args.range = { current_args.line1 }
elseif current_args.range == 2 then
args.range = { current_args.line1, current_args.line2 }
end
return args
end
function M.get_editor_dimensions() return { width = vim.go.columns, height = vim.go.lines } end
return M

View File

@ -617,4 +617,29 @@ describe('Range', function()
}, vim.api.nvim_buf_get_lines(b, 0, -1, false))
end)
end)
it('can save to extmark', function()
withbuf({
'The quick brown',
'fox',
'jumps',
'over',
'the lazy dog',
}, function()
-- Construct a range over 'fox jumps'
local r = Range.new(Pos.new(nil, 2, 1), Pos.new(nil, 3, Pos.MAX_COL), 'V')
local extrange = r:save_to_extmark()
assert.are.same({ 'fox', 'jumps' }, extrange:range():lines())
-- change 'jumps' to 'leaps':
vim.api.nvim_buf_set_text(extrange.bufnr, 2, 0, 2, 4, { 'leap' })
assert.are.same({
'The quick brown',
'fox',
'leaps',
'over',
'the lazy dog',
}, vim.api.nvim_buf_get_lines(extrange.bufnr, 0, -1, false))
assert.are.same({ 'fox', 'leaps' }, extrange:range():lines())
end)
end)
end)