From 75e53341f37eeeda7d9be7f934249f7e5e4397e9 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 23 Feb 2023 15:19:52 +0000 Subject: perf(treesitter): smarter languagetree invalidation Problem: Treesitter injections are slow because all injected trees are invalidated on every change. Solution: Implement smarter invalidation to avoid reparsing injected regions. - In on_bytes, try and update self._regions as best we can. This PR just offsets any regions after the change. - Add valid flags for each region in self._regions. - Call on_bytes recursively for all children. - We still need to run the query every time for the top level tree. I don't know how to avoid this. However, if the new injection ranges don't change, then we re-use the old trees and avoid reparsing children. This should result in roughly a 2-3x reduction in tree parsing when the comment injections are enabled. --- runtime/lua/vim/treesitter/_range.lua | 126 ++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 runtime/lua/vim/treesitter/_range.lua (limited to 'runtime/lua/vim/treesitter/_range.lua') diff --git a/runtime/lua/vim/treesitter/_range.lua b/runtime/lua/vim/treesitter/_range.lua new file mode 100644 index 0000000000..b87542c20f --- /dev/null +++ b/runtime/lua/vim/treesitter/_range.lua @@ -0,0 +1,126 @@ +local api = vim.api + +local M = {} + +---@alias Range4 {[1]: integer, [2]: integer, [3]: integer, [4]: integer} +---@alias Range6 {[1]: integer, [2]: integer, [3]: integer, [4]: integer, [5]: integer, [6]: integer} + +---@private +---@param a_row integer +---@param a_col integer +---@param b_row integer +---@param b_col integer +---@return integer +--- 1: a > b +--- 0: a == b +--- -1: a < b +local function cmp_pos(a_row, a_col, b_row, b_col) + if a_row == b_row then + if a_col > b_col then + return 1 + elseif a_col < b_col then + return -1 + else + return 0 + end + elseif a_row > b_row then + return 1 + end + + return -1 +end + +M.cmp_pos = { + lt = function(...) + return cmp_pos(...) == -1 + end, + le = function(...) + return cmp_pos(...) ~= 1 + end, + gt = function(...) + return cmp_pos(...) == 1 + end, + ge = function(...) + return cmp_pos(...) ~= -1 + end, + eq = function(...) + return cmp_pos(...) == 0 + end, + ne = function(...) + return cmp_pos(...) ~= 0 + end, +} + +setmetatable(M.cmp_pos, { __call = cmp_pos }) + +---@private +---@param r1 Range4|Range6 +---@param r2 Range4|Range6 +---@return boolean +function M.intercepts(r1, r2) + local off_1 = #r1 == 6 and 1 or 0 + local off_2 = #r1 == 6 and 1 or 0 + + local srow_1, scol_1, erow_1, ecol_1 = r1[1], r2[2], r1[3 + off_1], r1[4 + off_1] + local srow_2, scol_2, erow_2, ecol_2 = r2[1], r2[2], r2[3 + off_2], r2[4 + off_2] + + -- r1 is above r2 + if M.cmp_pos.le(erow_1, ecol_1, srow_2, scol_2) then + return false + end + + -- r1 is below r2 + if M.cmp_pos.ge(srow_1, scol_1, erow_2, ecol_2) then + return false + end + + return true +end + +---@private +---@param r1 Range4|Range6 +---@param r2 Range4|Range6 +---@return boolean whether r1 contains r2 +function M.contains(r1, r2) + local off_1 = #r1 == 6 and 1 or 0 + local off_2 = #r1 == 6 and 1 or 0 + + local srow_1, scol_1, erow_1, ecol_1 = r1[1], r2[2], r1[3 + off_1], r1[4 + off_1] + local srow_2, scol_2, erow_2, ecol_2 = r2[1], r2[2], r2[3 + off_2], r2[4 + off_2] + + -- start doesn't fit + if M.cmp_pos.gt(srow_1, scol_1, srow_2, scol_2) then + return false + end + + -- end doesn't fit + if M.cmp_pos.lt(erow_1, ecol_1, erow_2, ecol_2) then + return false + end + + return true +end + +---@private +---@param source integer|string +---@param range Range4 +---@return Range6 +function M.add_bytes(source, range) + local start_row, start_col, end_row, end_col = range[1], range[2], range[3], range[4] + local start_byte = 0 + local end_byte = 0 + -- TODO(vigoux): proper byte computation here, and account for EOL ? + if type(source) == 'number' then + -- Easy case, this is a buffer parser + start_byte = api.nvim_buf_get_offset(source, start_row) + start_col + end_byte = api.nvim_buf_get_offset(source, end_row) + end_col + elseif type(source) == 'string' then + -- string parser, single `\n` delimited string + start_byte = vim.fn.byteidx(source, start_col) + end_byte = vim.fn.byteidx(source, end_col) + end + + return { start_row, start_col, start_byte, end_row, end_col, end_byte } +end + +return M -- cgit From f0a2ffab2923202f4454860ba1a7c7bd0e035ed2 Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Fri, 3 Mar 2023 20:05:59 +0900 Subject: fix(treesitter): typos in _range.lua fix(treesitter): typos _range.lua --- runtime/lua/vim/treesitter/_range.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/treesitter/_range.lua') diff --git a/runtime/lua/vim/treesitter/_range.lua b/runtime/lua/vim/treesitter/_range.lua index b87542c20f..8decd3a1fd 100644 --- a/runtime/lua/vim/treesitter/_range.lua +++ b/runtime/lua/vim/treesitter/_range.lua @@ -61,7 +61,7 @@ function M.intercepts(r1, r2) local off_1 = #r1 == 6 and 1 or 0 local off_2 = #r1 == 6 and 1 or 0 - local srow_1, scol_1, erow_1, ecol_1 = r1[1], r2[2], r1[3 + off_1], r1[4 + off_1] + local srow_1, scol_1, erow_1, ecol_1 = r1[1], r1[2], r1[3 + off_1], r1[4 + off_1] local srow_2, scol_2, erow_2, ecol_2 = r2[1], r2[2], r2[3 + off_2], r2[4 + off_2] -- r1 is above r2 @@ -85,7 +85,7 @@ function M.contains(r1, r2) local off_1 = #r1 == 6 and 1 or 0 local off_2 = #r1 == 6 and 1 or 0 - local srow_1, scol_1, erow_1, ecol_1 = r1[1], r2[2], r1[3 + off_1], r1[4 + off_1] + local srow_1, scol_1, erow_1, ecol_1 = r1[1], r1[2], r1[3 + off_1], r1[4 + off_1] local srow_2, scol_2, erow_2, ecol_2 = r2[1], r2[2], r2[3 + off_2], r2[4 + off_2] -- start doesn't fit -- cgit From f9a46391ab5961fe6c6b7d1efdc96befdd495c11 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 6 Mar 2023 10:57:14 +0000 Subject: refactor(treesitter): simplify some range functions --- runtime/lua/vim/treesitter/_range.lua | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'runtime/lua/vim/treesitter/_range.lua') diff --git a/runtime/lua/vim/treesitter/_range.lua b/runtime/lua/vim/treesitter/_range.lua index 8decd3a1fd..bec24a23a5 100644 --- a/runtime/lua/vim/treesitter/_range.lua +++ b/runtime/lua/vim/treesitter/_range.lua @@ -53,6 +53,26 @@ M.cmp_pos = { setmetatable(M.cmp_pos, { __call = cmp_pos }) +---@private +---Check if a variable is a valid range object +---@param r any +---@return boolean +function M.validate(r) + if type(r) ~= 'table' or #r ~= 6 and #r ~= 4 then + return false + end + + for _, e in + ipairs(r --[[@as any[] ]]) + do + if type(e) ~= 'number' then + return false + end + end + + return true +end + ---@private ---@param r1 Range4|Range6 ---@param r2 Range4|Range6 -- cgit From 276b647fdba07bf1762d8dd371c4b655b8a418df Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 8 Mar 2023 17:22:28 +0000 Subject: refactor(treesitter): delegate region calculation to treesitter (#22553) --- runtime/lua/vim/treesitter/_range.lua | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'runtime/lua/vim/treesitter/_range.lua') diff --git a/runtime/lua/vim/treesitter/_range.lua b/runtime/lua/vim/treesitter/_range.lua index bec24a23a5..21e46a560a 100644 --- a/runtime/lua/vim/treesitter/_range.lua +++ b/runtime/lua/vim/treesitter/_range.lua @@ -78,11 +78,8 @@ end ---@param r2 Range4|Range6 ---@return boolean function M.intercepts(r1, r2) - local off_1 = #r1 == 6 and 1 or 0 - local off_2 = #r1 == 6 and 1 or 0 - - local srow_1, scol_1, erow_1, ecol_1 = r1[1], r1[2], r1[3 + off_1], r1[4 + off_1] - local srow_2, scol_2, erow_2, ecol_2 = r2[1], r2[2], r2[3 + off_2], r2[4 + off_2] + local srow_1, scol_1, erow_1, ecol_1 = M.unpack4(r1) + local srow_2, scol_2, erow_2, ecol_2 = M.unpack4(r2) -- r1 is above r2 if M.cmp_pos.le(erow_1, ecol_1, srow_2, scol_2) then @@ -97,16 +94,21 @@ function M.intercepts(r1, r2) return true end +---@private +---@param r Range4|Range6 +---@return integer, integer, integer, integer +function M.unpack4(r) + local off_1 = #r == 6 and 1 or 0 + return r[1], r[2], r[3 + off_1], r[4 + off_1] +end + ---@private ---@param r1 Range4|Range6 ---@param r2 Range4|Range6 ---@return boolean whether r1 contains r2 function M.contains(r1, r2) - local off_1 = #r1 == 6 and 1 or 0 - local off_2 = #r1 == 6 and 1 or 0 - - local srow_1, scol_1, erow_1, ecol_1 = r1[1], r1[2], r1[3 + off_1], r1[4 + off_1] - local srow_2, scol_2, erow_2, ecol_2 = r2[1], r2[2], r2[3 + off_2], r2[4 + off_2] + local srow_1, scol_1, erow_1, ecol_1 = M.unpack4(r1) + local srow_2, scol_2, erow_2, ecol_2 = M.unpack4(r2) -- start doesn't fit if M.cmp_pos.gt(srow_1, scol_1, srow_2, scol_2) then @@ -123,9 +125,13 @@ end ---@private ---@param source integer|string ----@param range Range4 +---@param range Range4|Range6 ---@return Range6 function M.add_bytes(source, range) + if type(range) == 'table' and #range == 6 then + return range --[[@as Range6]] + end + local start_row, start_col, end_row, end_col = range[1], range[2], range[3], range[4] local start_byte = 0 local end_byte = 0 -- cgit From b9f19d3e286d95d9209afbc479fa2eb908067fb1 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 8 Mar 2023 17:59:45 +0000 Subject: Revert "refactor(treesitter): delegate region calculation to treesitter" (#22575) Revert "refactor(treesitter): delegate region calculation to treesitter (#22553)" This reverts commit 276b647fdba07bf1762d8dd371c4b655b8a418df. --- runtime/lua/vim/treesitter/_range.lua | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'runtime/lua/vim/treesitter/_range.lua') diff --git a/runtime/lua/vim/treesitter/_range.lua b/runtime/lua/vim/treesitter/_range.lua index 21e46a560a..bec24a23a5 100644 --- a/runtime/lua/vim/treesitter/_range.lua +++ b/runtime/lua/vim/treesitter/_range.lua @@ -78,8 +78,11 @@ end ---@param r2 Range4|Range6 ---@return boolean function M.intercepts(r1, r2) - local srow_1, scol_1, erow_1, ecol_1 = M.unpack4(r1) - local srow_2, scol_2, erow_2, ecol_2 = M.unpack4(r2) + local off_1 = #r1 == 6 and 1 or 0 + local off_2 = #r1 == 6 and 1 or 0 + + local srow_1, scol_1, erow_1, ecol_1 = r1[1], r1[2], r1[3 + off_1], r1[4 + off_1] + local srow_2, scol_2, erow_2, ecol_2 = r2[1], r2[2], r2[3 + off_2], r2[4 + off_2] -- r1 is above r2 if M.cmp_pos.le(erow_1, ecol_1, srow_2, scol_2) then @@ -94,21 +97,16 @@ function M.intercepts(r1, r2) return true end ----@private ----@param r Range4|Range6 ----@return integer, integer, integer, integer -function M.unpack4(r) - local off_1 = #r == 6 and 1 or 0 - return r[1], r[2], r[3 + off_1], r[4 + off_1] -end - ---@private ---@param r1 Range4|Range6 ---@param r2 Range4|Range6 ---@return boolean whether r1 contains r2 function M.contains(r1, r2) - local srow_1, scol_1, erow_1, ecol_1 = M.unpack4(r1) - local srow_2, scol_2, erow_2, ecol_2 = M.unpack4(r2) + local off_1 = #r1 == 6 and 1 or 0 + local off_2 = #r1 == 6 and 1 or 0 + + local srow_1, scol_1, erow_1, ecol_1 = r1[1], r1[2], r1[3 + off_1], r1[4 + off_1] + local srow_2, scol_2, erow_2, ecol_2 = r2[1], r2[2], r2[3 + off_2], r2[4 + off_2] -- start doesn't fit if M.cmp_pos.gt(srow_1, scol_1, srow_2, scol_2) then @@ -125,13 +123,9 @@ end ---@private ---@param source integer|string ----@param range Range4|Range6 +---@param range Range4 ---@return Range6 function M.add_bytes(source, range) - if type(range) == 'table' and #range == 6 then - return range --[[@as Range6]] - end - local start_row, start_col, end_row, end_col = range[1], range[2], range[3], range[4] local start_byte = 0 local end_byte = 0 -- cgit From ae263aff9547b8b513c4fedaceb4cbf93c57b866 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 9 Mar 2023 16:09:39 +0000 Subject: refactor(treesitter): use byte ranges from treesitter (#22589) --- runtime/lua/vim/treesitter/_range.lua | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) (limited to 'runtime/lua/vim/treesitter/_range.lua') diff --git a/runtime/lua/vim/treesitter/_range.lua b/runtime/lua/vim/treesitter/_range.lua index bec24a23a5..02918da23f 100644 --- a/runtime/lua/vim/treesitter/_range.lua +++ b/runtime/lua/vim/treesitter/_range.lua @@ -78,11 +78,8 @@ end ---@param r2 Range4|Range6 ---@return boolean function M.intercepts(r1, r2) - local off_1 = #r1 == 6 and 1 or 0 - local off_2 = #r1 == 6 and 1 or 0 - - local srow_1, scol_1, erow_1, ecol_1 = r1[1], r1[2], r1[3 + off_1], r1[4 + off_1] - local srow_2, scol_2, erow_2, ecol_2 = r2[1], r2[2], r2[3 + off_2], r2[4 + off_2] + local srow_1, scol_1, erow_1, ecol_1 = M.unpack4(r1) + local srow_2, scol_2, erow_2, ecol_2 = M.unpack4(r2) -- r1 is above r2 if M.cmp_pos.le(erow_1, ecol_1, srow_2, scol_2) then @@ -97,16 +94,28 @@ function M.intercepts(r1, r2) return true end +---@private +---@param r Range4|Range6 +---@return integer, integer, integer, integer +function M.unpack4(r) + local off_1 = #r == 6 and 1 or 0 + return r[1], r[2], r[3 + off_1], r[4 + off_1] +end + +---@private +---@param r Range6 +---@return integer, integer, integer, integer, integer, integer +function M.unpack6(r) + return r[1], r[2], r[3], r[4], r[5], r[6] +end + ---@private ---@param r1 Range4|Range6 ---@param r2 Range4|Range6 ---@return boolean whether r1 contains r2 function M.contains(r1, r2) - local off_1 = #r1 == 6 and 1 or 0 - local off_2 = #r1 == 6 and 1 or 0 - - local srow_1, scol_1, erow_1, ecol_1 = r1[1], r1[2], r1[3 + off_1], r1[4 + off_1] - local srow_2, scol_2, erow_2, ecol_2 = r2[1], r2[2], r2[3 + off_2], r2[4 + off_2] + local srow_1, scol_1, erow_1, ecol_1 = M.unpack4(r1) + local srow_2, scol_2, erow_2, ecol_2 = M.unpack4(r2) -- start doesn't fit if M.cmp_pos.gt(srow_1, scol_1, srow_2, scol_2) then @@ -123,9 +132,13 @@ end ---@private ---@param source integer|string ----@param range Range4 +---@param range Range4|Range6 ---@return Range6 function M.add_bytes(source, range) + if type(range) == 'table' and #range == 6 then + return range --[[@as Range6]] + end + local start_row, start_col, end_row, end_col = range[1], range[2], range[3], range[4] local start_byte = 0 local end_byte = 0 -- cgit From 9d70fe062ca01ac0673faa6ccbb88345916aeea7 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 10 Mar 2023 16:10:05 +0000 Subject: feat(treesitter)!: consolidate query util functions - And address more type errors. - Removed the `concat` option from `get_node_text` since it was applied inconsistently and made typing awkward. --- runtime/lua/vim/treesitter/_range.lua | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/treesitter/_range.lua') diff --git a/runtime/lua/vim/treesitter/_range.lua b/runtime/lua/vim/treesitter/_range.lua index 02918da23f..0017a567ec 100644 --- a/runtime/lua/vim/treesitter/_range.lua +++ b/runtime/lua/vim/treesitter/_range.lua @@ -2,8 +2,19 @@ local api = vim.api local M = {} ----@alias Range4 {[1]: integer, [2]: integer, [3]: integer, [4]: integer} ----@alias Range6 {[1]: integer, [2]: integer, [3]: integer, [4]: integer, [5]: integer, [6]: integer} +---@class Range4 +---@field [1] integer start row +---@field [2] integer start column +---@field [3] integer end row +---@field [4] integer end column + +---@class Range6 +---@field [1] integer start row +---@field [2] integer start column +---@field [3] integer start bytes +---@field [4] integer end row +---@field [5] integer end column +---@field [6] integer end bytes ---@private ---@param a_row integer -- cgit From 58bbc2ea0b3dfed13471e8cc0447d7598be24276 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Fri, 10 Mar 2023 16:40:27 +0000 Subject: refactor(treesitter): add Range type aliase for Range4|Range6 --- runtime/lua/vim/treesitter/_range.lua | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'runtime/lua/vim/treesitter/_range.lua') diff --git a/runtime/lua/vim/treesitter/_range.lua b/runtime/lua/vim/treesitter/_range.lua index 0017a567ec..f4db5016ac 100644 --- a/runtime/lua/vim/treesitter/_range.lua +++ b/runtime/lua/vim/treesitter/_range.lua @@ -16,6 +16,8 @@ local M = {} ---@field [5] integer end column ---@field [6] integer end bytes +---@alias Range Range4|Range6 + ---@private ---@param a_row integer ---@param a_col integer @@ -85,8 +87,8 @@ function M.validate(r) end ---@private ----@param r1 Range4|Range6 ----@param r2 Range4|Range6 +---@param r1 Range +---@param r2 Range ---@return boolean function M.intercepts(r1, r2) local srow_1, scol_1, erow_1, ecol_1 = M.unpack4(r1) @@ -106,7 +108,7 @@ function M.intercepts(r1, r2) end ---@private ----@param r Range4|Range6 +---@param r Range ---@return integer, integer, integer, integer function M.unpack4(r) local off_1 = #r == 6 and 1 or 0 @@ -121,8 +123,8 @@ function M.unpack6(r) end ---@private ----@param r1 Range4|Range6 ----@param r2 Range4|Range6 +---@param r1 Range +---@param r2 Range ---@return boolean whether r1 contains r2 function M.contains(r1, r2) local srow_1, scol_1, erow_1, ecol_1 = M.unpack4(r1) @@ -143,7 +145,7 @@ end ---@private ---@param source integer|string ----@param range Range4|Range6 +---@param range Range ---@return Range6 function M.add_bytes(source, range) if type(range) == 'table' and #range == 6 then -- cgit From 6b19170d44ca56cf65542ee184d2bc89c6d622a9 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Tue, 16 May 2023 16:41:47 +0100 Subject: fix(treesitter): correctly calculate bytes for text sources (#23655) Fixes #20419 --- runtime/lua/vim/treesitter/_range.lua | 38 ++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 12 deletions(-) (limited to 'runtime/lua/vim/treesitter/_range.lua') diff --git a/runtime/lua/vim/treesitter/_range.lua b/runtime/lua/vim/treesitter/_range.lua index f4db5016ac..35081c6400 100644 --- a/runtime/lua/vim/treesitter/_range.lua +++ b/runtime/lua/vim/treesitter/_range.lua @@ -143,6 +143,29 @@ function M.contains(r1, r2) return true end +--- @param source integer|string +--- @param index integer +--- @return integer +local function get_offset(source, index) + if index == 0 then + return 0 + end + + if type(source) == 'number' then + return api.nvim_buf_get_offset(source, index) + end + + local byte = 0 + local next_offset = source:gmatch('()\n') + local line = 1 + while line <= index do + byte = next_offset() --[[@as integer]] + line = line + 1 + end + + return byte +end + ---@private ---@param source integer|string ---@param range Range @@ -152,19 +175,10 @@ function M.add_bytes(source, range) return range --[[@as Range6]] end - local start_row, start_col, end_row, end_col = range[1], range[2], range[3], range[4] - local start_byte = 0 - local end_byte = 0 + local start_row, start_col, end_row, end_col = M.unpack4(range) -- TODO(vigoux): proper byte computation here, and account for EOL ? - if type(source) == 'number' then - -- Easy case, this is a buffer parser - start_byte = api.nvim_buf_get_offset(source, start_row) + start_col - end_byte = api.nvim_buf_get_offset(source, end_row) + end_col - elseif type(source) == 'string' then - -- string parser, single `\n` delimited string - start_byte = vim.fn.byteidx(source, start_col) - end_byte = vim.fn.byteidx(source, end_col) - end + local start_byte = get_offset(source, start_row) + start_col + local end_byte = get_offset(source, end_row) + end_col return { start_row, start_col, start_byte, end_row, end_col, end_byte } end -- cgit From 2ca076e45fb3f1c08f6a1a374834df0701b8d778 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 10 Aug 2023 14:21:56 +0100 Subject: feat(treesitter)!: incremental injection parsing Problem: Treesitter highlighting is slow for large files with lots of injections. Solution: Only parse injections we are going to render during a redraw cycle. --- - `LanguageTree:parse()` will no longer parse injections by default and now requires an explicit range argument to be passed. - `TSHighlighter` now parses injections incrementally during on_win callbacks for the line range being rendered. - Plugins which require certain injections to be parsed must run `parser:parse({ start_row, end_row })` before using the tree. --- runtime/lua/vim/treesitter/_range.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/treesitter/_range.lua') diff --git a/runtime/lua/vim/treesitter/_range.lua b/runtime/lua/vim/treesitter/_range.lua index 35081c6400..8d727c3c52 100644 --- a/runtime/lua/vim/treesitter/_range.lua +++ b/runtime/lua/vim/treesitter/_range.lua @@ -2,6 +2,10 @@ local api = vim.api local M = {} +---@class Range2 +---@field [1] integer start row +---@field [2] integer end row + ---@class Range4 ---@field [1] integer start row ---@field [2] integer start column @@ -16,7 +20,7 @@ local M = {} ---@field [5] integer end column ---@field [6] integer end bytes ----@alias Range Range4|Range6 +---@alias Range Range2|Range4|Range6 ---@private ---@param a_row integer @@ -111,6 +115,9 @@ end ---@param r Range ---@return integer, integer, integer, integer function M.unpack4(r) + if #r == 2 then + return r[1], 0, r[2], 0 + end local off_1 = #r == 6 and 1 or 0 return r[1], r[2], r[3 + off_1], r[4 + off_1] end -- cgit