nixos/lua-lsp/script/string-merger.lua

145 lines
3.9 KiB
Lua
Raw Normal View History

---@class string.merger.diff
---@field start integer # 替换开始的字节
---@field finish integer # 替换结束的字节
---@field text string # 替换的文本
---@class string.merger.info: string.merger.diff
---@field cstart integer # 转换后的开始字节
---@field cfinish integer # 转换后的结束字节
---@alias string.merger.diffs string.merger.diff[]
---@alias string.merger.infos string.merger.info[]
-- 根据二分法找到最近的开始位置
---@param diffs table
---@param offset any
---@return string.merger.info
local function getNearDiff(diffs, offset, key)
local min = 1
local max = #diffs
while max > min do
local middle = min + (max - min) // 2
local diff = diffs[middle]
local ndiff = diffs[middle + 1]
if diff[key] > offset then
max = middle
goto CONTINUE
end
if not ndiff then
return diff
end
if ndiff[key] > offset then
return diff
end
if min == middle then
min = middle + 1
else
min = middle
end
::CONTINUE::
end
return diffs[min]
end
local m = {}
---把文本与差异进行合并
---@param text string
---@param diffs string.merger.diffs
---@return string
---@return string.merger.infos
function m.mergeDiff(text, diffs)
local info = {}
for i, diff in ipairs(diffs) do
info[i] = {
start = diff.start,
finish = diff.finish,
text = diff.text,
}
end
table.sort(info, function (a, b)
return a.start < b.start
end)
local cur = 1
local buf = {}
local delta = 0
for _, diff in ipairs(info) do
diff.cstart = diff.start + delta
diff.cfinish = diff.cstart + #diff.text - 1
buf[#buf+1] = text:sub(cur, diff.start - 1)
buf[#buf+1] = diff.text
cur = diff.finish + 1
delta = delta + #diff.text - (diff.finish - diff.start + 1)
end
buf[#buf+1] = text:sub(cur)
return table.concat(buf), info
end
---根据转换前的位置获取转换后的位置
---@param info string.merger.infos
---@param offset integer
---@return integer start
---@return integer finish
function m.getOffset(info, offset)
local diff = getNearDiff(info, offset, 'start')
if not diff then
return offset, offset
end
if offset <= diff.finish then
local start, finish
if offset == diff.start then
start = diff.cstart
end
if offset == diff.finish then
finish = diff.cfinish
end
if not start or not finish then
local soff = offset - diff.start
local pos = math.min(diff.cstart + soff, diff.cfinish)
start = start or pos
finish = finish or pos
end
if start > finish then
start = finish
end
return start, finish
end
local pos = offset - diff.finish + diff.cfinish
return pos, pos
end
---根据转换后的位置获取转换前的位置
---@param info string.merger.infos
---@param offset integer
---@return integer start
---@return integer finish
function m.getOffsetBack(info, offset)
local diff = getNearDiff(info, offset, 'cstart')
if not diff then
return offset, offset
end
if offset <= diff.cfinish then
local start, finish
if offset == diff.cstart then
start = diff.start
end
if offset == diff.cfinish then
finish = diff.finish
end
if not start or not finish then
local soff = offset - diff.cstart
local pos = math.min(diff.start + soff, diff.finish)
start = start or pos
finish = finish or pos
end
if start > finish then
start = finish
end
return start, finish
end
local pos = offset - diff.cfinish + diff.finish
return pos, pos
end
return m