This commit is contained in:
parent
d03807afba
commit
3c7dd84ff2
@ -43,25 +43,23 @@ end
|
|||||||
|
|
||||||
function Pos.invalid() return Pos.new(0, 0, 0, 0) end
|
function Pos.invalid() return Pos.new(0, 0, 0, 0) end
|
||||||
|
|
||||||
function Pos.is(x) return getmetatable(x) == Pos end
|
|
||||||
|
|
||||||
function Pos.__lt(a, b) return a.lnum < b.lnum or (a.lnum == b.lnum and a.col < b.col) end
|
function Pos.__lt(a, b) return a.lnum < b.lnum or (a.lnum == b.lnum and a.col < b.col) end
|
||||||
function Pos.__le(a, b) return a < b or a == b end
|
function Pos.__le(a, b) return a < b or a == b end
|
||||||
function Pos.__eq(a, b)
|
function Pos.__eq(a, b)
|
||||||
return Pos.is(a) and Pos.is(b) and a.bufnr == b.bufnr and a.lnum == b.lnum and a.col == b.col
|
return getmetatable(a) == Pos and getmetatable(b) == Pos and a.bufnr == b.bufnr and a.lnum == b.lnum and a.col == b.col
|
||||||
end
|
end
|
||||||
function Pos.__add(x, y)
|
function Pos.__add(x, y)
|
||||||
if type(x) == 'number' then
|
if type(x) == 'number' then
|
||||||
x, y = y, x
|
x, y = y, x
|
||||||
end
|
end
|
||||||
if not Pos.is(x) or type(y) ~= 'number' then return nil end
|
if getmetatable(x) ~= Pos or type(y) ~= 'number' then return nil end
|
||||||
return x:next(y)
|
return x:next(y)
|
||||||
end
|
end
|
||||||
function Pos.__sub(x, y)
|
function Pos.__sub(x, y)
|
||||||
if type(x) == 'number' then
|
if type(x) == 'number' then
|
||||||
x, y = y, x
|
x, y = y, x
|
||||||
end
|
end
|
||||||
if not Pos.is(x) or type(y) ~= 'number' then return nil end
|
if getmetatable(x) ~= Pos or type(y) ~= 'number' then return nil end
|
||||||
return x:next(-y)
|
return x:next(-y)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
187
lua/u/range.lua
187
lua/u/range.lua
@ -1,5 +1,10 @@
|
|||||||
|
--- @module
|
||||||
|
--- @brief Utilities for manipulating ranges of text.
|
||||||
|
|
||||||
local Pos = require 'u.pos'
|
local Pos = require 'u.pos'
|
||||||
|
|
||||||
|
-- Certain functions in the Range class yank text. In order to prevent unwanted
|
||||||
|
-- highlighting, we intercept and discard some calls to the `on_yank` callback.
|
||||||
local orig_on_yank = (vim.hl or vim.highlight).on_yank
|
local orig_on_yank = (vim.hl or vim.highlight).on_yank
|
||||||
local on_yank_enabled = true;
|
local on_yank_enabled = true;
|
||||||
((vim.hl or vim.highlight) --[[@as any]]).on_yank = function(opts)
|
((vim.hl or vim.highlight) --[[@as any]]).on_yank = function(opts)
|
||||||
@ -36,6 +41,10 @@ function Range.__tostring(self)
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Range constructors:
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
--- @param start u.Pos
|
--- @param start u.Pos
|
||||||
--- @param stop u.Pos|nil
|
--- @param stop u.Pos|nil
|
||||||
--- @param mode? 'v'|'V'
|
--- @param mode? 'v'|'V'
|
||||||
@ -51,7 +60,20 @@ function Range.new(start, stop, mode)
|
|||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
|
|
||||||
function Range.is(x) return getmetatable(x) == Range end
|
--- @param ranges (u.Range|nil)[]
|
||||||
|
function Range.smallest(ranges)
|
||||||
|
--- @type u.Range[]
|
||||||
|
ranges = vim.iter(ranges):filter(function(r) return r ~= nil and not r:is_empty() end):totable()
|
||||||
|
if #ranges == 0 then return nil end
|
||||||
|
|
||||||
|
-- find smallest match
|
||||||
|
local smallest = ranges[1]
|
||||||
|
for _, r in ipairs(ranges) do
|
||||||
|
local start, stop = r.start, r.stop
|
||||||
|
if start > smallest.start and stop < smallest.stop then smallest = r end
|
||||||
|
end
|
||||||
|
return smallest
|
||||||
|
end
|
||||||
|
|
||||||
--- @param lpos string
|
--- @param lpos string
|
||||||
--- @param rpos string
|
--- @param rpos string
|
||||||
@ -221,7 +243,6 @@ function Range.from_cmd_args(args)
|
|||||||
return Range.new(start, stop, mode)
|
return Range.new(start, stop, mode)
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
|
||||||
function Range.find_nearest_brackets()
|
function Range.find_nearest_brackets()
|
||||||
return Range.smallest {
|
return Range.smallest {
|
||||||
Range.from_motion('a<', { contains_cursor = true }),
|
Range.from_motion('a<', { contains_cursor = true }),
|
||||||
@ -239,28 +260,15 @@ function Range.find_nearest_quotes()
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param ranges (u.Range|nil)[]
|
--------------------------------------------------------------------------------
|
||||||
function Range.smallest(ranges)
|
-- Structural utilities:
|
||||||
--- @type u.Range[]
|
--------------------------------------------------------------------------------
|
||||||
ranges = vim.iter(ranges):filter(function(r) return r ~= nil and not r:is_empty() end):totable()
|
|
||||||
if #ranges == 0 then return nil end
|
|
||||||
|
|
||||||
-- find smallest match
|
|
||||||
local smallest = ranges[1]
|
|
||||||
for _, r in ipairs(ranges) do
|
|
||||||
local start, stop = r.start, r.stop
|
|
||||||
if start > smallest.start and stop < smallest.stop then smallest = r end
|
|
||||||
end
|
|
||||||
return smallest
|
|
||||||
end
|
|
||||||
|
|
||||||
function Range:clone()
|
function Range:clone()
|
||||||
return Range.new(self.start:clone(), self.stop ~= nil and self.stop:clone() or nil, self.mode)
|
return Range.new(self.start:clone(), self.stop ~= nil and self.stop:clone() or nil, self.mode)
|
||||||
end
|
end
|
||||||
function Range:line_count()
|
|
||||||
if self:is_empty() then return 0 end
|
function Range:is_empty() return self.stop == nil end
|
||||||
return self.stop.lnum - self.start.lnum + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
function Range:to_linewise()
|
function Range:to_linewise()
|
||||||
local r = self:clone()
|
local r = self:clone()
|
||||||
@ -272,32 +280,6 @@ function Range:to_linewise()
|
|||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
|
|
||||||
function Range:is_empty() return self.stop == nil end
|
|
||||||
|
|
||||||
function Range:trim_start()
|
|
||||||
if self:is_empty() then return end
|
|
||||||
|
|
||||||
local r = self:clone()
|
|
||||||
while r.start:char():match '%s' do
|
|
||||||
local next = r.start:next(1)
|
|
||||||
if next == nil then break end
|
|
||||||
r.start = next
|
|
||||||
end
|
|
||||||
return r
|
|
||||||
end
|
|
||||||
|
|
||||||
function Range:trim_stop()
|
|
||||||
if self:is_empty() then return end
|
|
||||||
|
|
||||||
local r = self:clone()
|
|
||||||
while r.stop:char():match '%s' do
|
|
||||||
local next = r.stop:next(-1)
|
|
||||||
if next == nil then break end
|
|
||||||
r.stop = next
|
|
||||||
end
|
|
||||||
return r
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param x u.Pos | u.Range
|
--- @param x u.Pos | u.Range
|
||||||
function Range:contains(x)
|
function Range:contains(x)
|
||||||
if getmetatable(x) == Pos then
|
if getmetatable(x) == Pos then
|
||||||
@ -336,6 +318,52 @@ function Range:difference(other)
|
|||||||
return left, right
|
return left, right
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @param left string
|
||||||
|
--- @param right string
|
||||||
|
function Range:save_to_pos(left, right)
|
||||||
|
if self:is_empty() then
|
||||||
|
self.start:save_to_pos(left)
|
||||||
|
self.start:save_to_pos(right)
|
||||||
|
else
|
||||||
|
self.start:save_to_pos(left)
|
||||||
|
self.stop:save_to_pos(right)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param left string
|
||||||
|
--- @param right string
|
||||||
|
function Range:save_to_marks(left, right)
|
||||||
|
if self:is_empty() then
|
||||||
|
self.start:save_to_mark(left)
|
||||||
|
self.start:save_to_mark(right)
|
||||||
|
else
|
||||||
|
self.start:save_to_mark(left)
|
||||||
|
self.stop:save_to_mark(right)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Range:set_visual_selection()
|
||||||
|
if self:is_empty() then return end
|
||||||
|
if vim.api.nvim_get_current_buf() ~= self.start.bufnr then
|
||||||
|
error 'Range:set_visual_selection() called on a buffer other than the current buffer'
|
||||||
|
end
|
||||||
|
|
||||||
|
local curr_mode = vim.fn.mode()
|
||||||
|
if curr_mode ~= self.mode then vim.cmd.normal { args = { self.mode }, bang = true } end
|
||||||
|
|
||||||
|
self.start:save_to_pos '.'
|
||||||
|
vim.cmd.normal { args = { 'o' }, bang = true }
|
||||||
|
self.stop:save_to_pos '.'
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Range.from_* functions:
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Text access/manipulation utilities:
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
function Range:length()
|
function Range:length()
|
||||||
if self:is_empty() then return 0 end
|
if self:is_empty() then return 0 end
|
||||||
|
|
||||||
@ -353,6 +381,35 @@ function Range:length()
|
|||||||
return len
|
return len
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Range:line_count()
|
||||||
|
if self:is_empty() then return 0 end
|
||||||
|
return self.stop.lnum - self.start.lnum + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function Range:trim_start()
|
||||||
|
if self:is_empty() then return end
|
||||||
|
|
||||||
|
local r = self:clone()
|
||||||
|
while r.start:char():match '%s' do
|
||||||
|
local next = r.start:next(1)
|
||||||
|
if next == nil then break end
|
||||||
|
r.start = next
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function Range:trim_stop()
|
||||||
|
if self:is_empty() then return end
|
||||||
|
|
||||||
|
local r = self:clone()
|
||||||
|
while r.stop:char():match '%s' do
|
||||||
|
local next = r.stop:next(-1)
|
||||||
|
if next == nil then break end
|
||||||
|
r.stop = next
|
||||||
|
end
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
--- @param i number 1-based
|
--- @param i number 1-based
|
||||||
--- @param j? number 1-based
|
--- @param j? number 1-based
|
||||||
function Range:sub(i, j)
|
function Range:sub(i, j)
|
||||||
@ -507,44 +564,6 @@ function Range:must_shrink(amount)
|
|||||||
return shrunk
|
return shrunk
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param left string
|
|
||||||
--- @param right string
|
|
||||||
function Range:save_to_pos(left, right)
|
|
||||||
if self:is_empty() then
|
|
||||||
self.start:save_to_pos(left)
|
|
||||||
self.start:save_to_pos(right)
|
|
||||||
else
|
|
||||||
self.start:save_to_pos(left)
|
|
||||||
self.stop:save_to_pos(right)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param left string
|
|
||||||
--- @param right string
|
|
||||||
function Range:save_to_marks(left, right)
|
|
||||||
if self:is_empty() then
|
|
||||||
self.start:save_to_mark(left)
|
|
||||||
self.start:save_to_mark(right)
|
|
||||||
else
|
|
||||||
self.start:save_to_mark(left)
|
|
||||||
self.stop:save_to_mark(right)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Range:set_visual_selection()
|
|
||||||
if self:is_empty() then return end
|
|
||||||
if vim.api.nvim_get_current_buf() ~= self.start.bufnr then
|
|
||||||
error 'Range:set_visual_selection() called on a buffer other than the current buffer'
|
|
||||||
end
|
|
||||||
|
|
||||||
local curr_mode = vim.fn.mode()
|
|
||||||
if curr_mode ~= self.mode then vim.cmd.normal { args = { self.mode }, bang = true } end
|
|
||||||
|
|
||||||
self.start:save_to_pos '.'
|
|
||||||
vim.cmd.normal { args = { 'o' }, bang = true }
|
|
||||||
self.stop:save_to_pos '.'
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param group string
|
--- @param group string
|
||||||
--- @param opts? { timeout?: number, priority?: number, on_macro?: boolean }
|
--- @param opts? { timeout?: number, priority?: number, on_macro?: boolean }
|
||||||
function Range:highlight(group, opts)
|
function Range:highlight(group, opts)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user