summaryrefslogtreecommitdiff
path: root/script/method/textDocument/completion.lua
blob: d622fe736163775a5333159986e2b54018e45e4c (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
local core = require 'core'
local parser = require 'parser'

local function posToRange(lines, start, finish)
    local start_row,  start_col  = lines:rowcol(start)
    local finish_row, finish_col = lines:rowcol(finish)
    return {
        start = {
            line = start_row - 1,
            character = start_col - 1,
        },
        ['end'] = {
            line = finish_row - 1,
            character = finish_col,
        },
    }
end

local function fastCompletion(lsp, params, lines)
    local uri = params.textDocument.uri
    local text, oldText = lsp:getText(uri)
    -- lua是从1开始的,因此都要+1
    local position = lines:positionAsChar(params.position.line + 1, params.position.character)

    local vm = lsp:getVM(uri)
    if not vm then
        vm = lsp:loadVM(uri)
        if not vm then
            return nil
        end
    end

    local items = core.completion(vm, text, position, oldText)
    if not items or #items == 0 then
        vm = lsp:loadVM(uri)
        if not vm then
            return nil
        end
        items = core.completion(vm, text, position)
        if not items or #items == 0 then
            return nil
        end
    end

    return items, position
end

local function finishCompletion(lsp, params, lines)
    local uri = params.textDocument.uri
    local text = lsp:getText(uri)
    -- lua是从1开始的,因此都要+1
    local position = lines:positionAsChar(params.position.line + 1, params.position.character)

    local vm = lsp:loadVM(uri)
    if not vm then
        return nil
    end

    local items = core.completion(vm, text, position)
    if not items or #items == 0 then
        return nil
    end

    return items
end

local function cuterFactory(lines, text, position)
    local start = position
    local head = ''
    for i = position, position - 100, -1 do
        if not text:sub(i, i):match '[%w_]' then
            start = i + 1
            head = text:sub(start, position)
            break
        end
    end
    return function (insertText)
        return {
            newText = insertText,
            range   = posToRange(lines, start, position)
        }
    end
end

return function (lsp, params)
    local uri = params.textDocument.uri
    local text, oldText = lsp:getText(uri)
    if not text then
        return nil
    end

    local lines = parser:lines(text, 'utf8')
    local items, position = fastCompletion(lsp, params, lines)
    --local items = finishCompletion(lsp, params, lines)
    if not items then
        return nil
    end

    -- TODO 在协议阶段将 `insertText` 转化为 `textEdit` ,
    -- 以避免不同客户端对 `insertText` 实现的不一致。
    -- 重构后直接在 core 中使用 `textEdit` 。
    local cuter = cuterFactory(lines, text, position)

    for i, item in ipairs(items) do
        item.sortText = ('%04d'):format(i)
        item.insertTextFormat = 2

        if item.textEdit then
            item.textEdit.range = posToRange(lines, item.textEdit.start, item.textEdit.finish)
            item.textEdit.start = nil
            item.textEdit.finish = nil
        else
            item.textEdit = cuter(item.insertText or item.label)
        end
        if item.additionalTextEdits then
            for _, textEdit in ipairs(item.additionalTextEdits) do
                textEdit.range = posToRange(lines, textEdit.start, textEdit.finish)
                textEdit.start = nil
                textEdit.finish = nil
            end
        end
    end

    local response = {
        isIncomplete = true,
        items = items,
    }

    return response
end