summaryrefslogtreecommitdiff
path: root/script/text-merger.lua
blob: c3b144c38c4fbf66cbed1d537e09e426cf6e190f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
local files   = require 'files'
local util    = require 'utility'
local encoder = require 'encoder'
local client  = require 'client'

local offsetEncoding
local function getOffsetEncoding()
    if not offsetEncoding then
        offsetEncoding = client.getOffsetEncoding():lower():gsub('%-', '')
    end
    return offsetEncoding
end

local function splitRows(text)
    local rows = {}
    for line in util.eachLine(text, true) do
        rows[#rows+1] = line
    end
    return rows
end

local function getLeft(text, char)
    if not text then
        return ''
    end
    local encoding = getOffsetEncoding()
    local left
    local length = encoder.len(encoding, text)

    if char == 0 then
        left = ''
    elseif char >= length then
        left = text
    else
        left = text:sub(1, encoder.offset(encoding, text, char + 1) - 1)
    end

    return left
end

local function getRight(text, char)
    if not text then
        return ''
    end
    local encoding = getOffsetEncoding()
    local right
    local length = encoder.len(encoding, text)

    if char == 0 then
        right = text
    elseif char >= length then
        right = ''
    else
        right = text:sub(encoder.offset(encoding, text, char + 1))
    end

    return right
end

local function mergeRows(rows, change)
    local startLine = change.range['start'].line + 1
    local startChar = change.range['start'].character
    local endLine   = change.range['end'].line + 1
    local endChar   = change.range['end'].character

    local insertRows = splitRows(change.text)
    local newEndLine = startLine + #insertRows - 1
    local left       = getLeft(rows[startLine], startChar)
    local right      = getRight(rows[endLine],  endChar)
    -- 先把双方的行数调整成一致
    if endLine > #rows then
        log.error('NMD, WSM `endLine > #rows` ?')
        for i = #rows + 1, endLine do
            rows[i] = ''
        end
    end
    local delta = #insertRows - (endLine - startLine + 1)
    if delta ~= 0 then
        table.move(rows, endLine, #rows, endLine + delta)
        -- 如果行数变少了,要清除多余的行
        if delta < 0 then
            for i = #rows, #rows + delta + 1, -1 do
                rows[i] = nil
            end
        end
    end
    -- 先处理第一行和最后一行
    if startLine == newEndLine then
        rows[startLine]  = left .. insertRows[1] .. right
    else
        rows[startLine]  = left .. insertRows[1]
        rows[newEndLine] = insertRows[#insertRows] .. right
    end
    -- 修改中间的每一行
    for i = 2, #insertRows - 1 do
        local currentLine = startLine + i - 1
        local insertText  = insertRows[i] or ''
        rows[currentLine] = insertText
    end
end

return function (text, rows, changes)
    for _, change in ipairs(changes) do
        if change.range then
            rows = rows or splitRows(text)
            mergeRows(rows, change)
        else
            rows = nil
            text = change.text
        end
    end
    if rows then
        text = table.concat(rows)
    end
    return text, rows
end