145 lines
3.9 KiB
Lua
145 lines
3.9 KiB
Lua
---@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
|