diff options
Diffstat (limited to 'script')
-rw-r--r-- | script/files.lua | 34 | ||||
-rw-r--r-- | script/parser/guide.lua | 2 | ||||
-rw-r--r-- | script/string-merger.lua | 102 |
3 files changed, 133 insertions, 5 deletions
diff --git a/script/files.lua b/script/files.lua index d5ff5981..679aa0cb 100644 --- a/script/files.lua +++ b/script/files.lua @@ -10,6 +10,7 @@ local timer = require 'timer' local plugin = require 'plugin' local util = require 'utility' local guide = require 'parser.guide' +local smerger = require 'string-merger' local m = {} @@ -97,6 +98,34 @@ function m.asKey(uri) return uri end +function m.setDiffInfo(uri, info) + uri = getUriKey(uri) + local file = m.fileMap[uri] + if not file then + return + end + file._diffInfo = info +end + +local function pluginOnSetText(uri, text) + m.setDiffInfo(nil) + local suc, result = plugin.dispatch('OnSetText', uri, text) + if not suc then + return text + end + if type(result) == 'string' then + return result + elseif type(result) == 'table' then + local diffs + suc, result, diffs = xpcall(smerger.mergeDiff, log.warn, text, result) + if suc then + m.setDiffInfo(diffs) + return result + end + end + return text +end + --- 设置文件文本 ---@param uri uri ---@param text string @@ -116,10 +145,7 @@ function m.setText(uri, text) create = true m._pairsCache = nil end - local suc, newText = plugin.dispatch('OnSetText', originUri, text) - if not suc then - newText = text - end + local newText = pluginOnSetText(originUri, text) local file = m.fileMap[uri] if file.text == newText then return diff --git a/script/parser/guide.lua b/script/parser/guide.lua index 5f1bc230..5e4652b3 100644 --- a/script/parser/guide.lua +++ b/script/parser/guide.lua @@ -1370,7 +1370,7 @@ function m.getCallValue(source) end if call.node.special == 'pcall' or call.node.special == 'xpcall' then - return call.args[1], call.args, index - 1 + return call.args and call.args[1], call.args, index - 1 end return call.node, call.args, index end diff --git a/script/string-merger.lua b/script/string-merger.lua new file mode 100644 index 00000000..e6650f04 --- /dev/null +++ b/script/string-merger.lua @@ -0,0 +1,102 @@ +---@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 +function m.getOffset(info, offset) + local diff = getNearDiff(info, offset, 'start') + if offset <= diff.finish then + return diff.cstart + end + return offset - diff.finish + diff.cfinish +end + +---根据转换后的位置获取转换前的位置 +---@param info string.merger.infos +---@param offset integer +---@return integer +function m.getOffsetBack(info, offset) + local diff = getNearDiff(info, offset, 'cstart') + if offset <= diff.cfinish then + return diff.start + end + return offset - diff.cfinish + diff.finish +end + +return m |