summaryrefslogtreecommitdiff
path: root/script/text-merger.lua
blob: afd3343f6afd3f301e6131418bfa46650c22f8ad (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
local files   = require 'files'
local util    = require 'utility'
local encoder = require 'encoder'

-- TODO
local offsetEncoding = 'utf16'

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 left
    local length = encoder.len(offsetEncoding, text)

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

    return left
end

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

    if char == 0 then
        right = text
    elseif char >= length then
        right = ''
    else
        right = text:sub(encoder.offset(offsetEncoding, 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 (uri, changes)
    local text
    for _, change in ipairs(changes) do
        if change.range then
            local rows = files.getCachedRows(uri)
            if not rows then
                text = text or files.getOriginText(uri) or ''
                rows = splitRows(text)
            end
            mergeRows(rows, change)
            files.setCachedRows(uri, rows)
        else
            files.setCachedRows(uri, nil)
            text = change.text
        end
    end
    local rows = files.getCachedRows(uri)
    if rows then
        text = table.concat(rows)
    end
    return text
end