summaryrefslogtreecommitdiff
path: root/server/src/method/textDocument/completion.lua
blob: 2f63897987e285b6612b11ee7211ae916fdedf58 (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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 findStartPos(pos, buf)
    local res = nil
    for i = pos, 1, -1 do
        local c = buf:sub(i, i)
        if c:find '[%w_]' then
            res = i
        else
            break
        end
    end
    if not res then
        for i = pos, 1, -1 do
            local c = buf:sub(i, i)
            if c == '.' or c == ':' then
                res = i
            elseif c:find '[%s%c]' then
            else
                break
            end
        end
    end
    if not res then
        return pos
    end
    return res
end

local function findWord(position, text)
    local word = text
    for i = position, 1, -1 do
        local c = text:sub(i, i)
        if not c:find '[%w_]' then
            word = text:sub(i+1, position)
            break
        end
    end
    return word:match('^([%w_]*)')
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 word = findWord(position, text)
    local startPos = findStartPos(position, text)

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

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

    return items
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 word = findWord(position, text)
    local startPos = findStartPos(position, text)

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

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

    return items
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 = fastCompletion(lsp, params, lines)
    --local items = finishCompletion(lsp, params, lines)
    if not items then
        return
    end

    for i, item in ipairs(items) do
        item.sortText = ('%04d'):format(i)
        if item.textEdit then
            item.textEdit.range = posToRange(lines, item.textEdit.start, item.textEdit.finish)
            item.textEdit.start = nil
            item.textEdit.finish = nil
        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