summaryrefslogtreecommitdiff
path: root/server/src/method/textDocument/publishDiagnostics.lua
blob: 0698baeaef2c3462e52704596b4e58ca3ebeb4f3 (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
149
150
151
152
153
154
155
156
157
158
159
160
local core = require 'core'
local lang = require 'language'

local DiagnosticSeverity = {
    Error       = 1,
    Warning     = 2,
    Information = 3,
    Hint        = 4,
}

--[[
/**
 * Represents a related message and source code location for a diagnostic. This should be
 * used to point to code locations that cause or related to a diagnostics, e.g when duplicating
 * a symbol in a scope.
 */
export interface DiagnosticRelatedInformation {
    /**
     * The location of this related diagnostic information.
     */
    location: Location;

    /**
     * The message of this related diagnostic information.
     */
    message: string;
}
]]--

local function getRange(start, finish, lines)
    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,
            -- 这里不用-1,因为前端期待的是匹配完成后的位置
            character = finish_col,
        },
    }
end

local function createInfo(lsp, data, lines)
    local diagnostic = {
        source   = lang.script.DIAG_DIAGNOSTICS,
        range    = getRange(data.start, data.finish, lines),
        severity = data.level,
        message  = data.message,
        code     = data.code,
    }
    if data.related then
        local related = {}
        for _, info in ipairs(data.related) do
            local _, lines = lsp:getVM(info.uri)
            if lines then
                local message = info.message
                if not message then
                    local start_line  = lines:rowcol(info.start)
                    local finish_line = lines:rowcol(info.finish)
                    local chars = {}
                    for n = start_line, finish_line do
                        chars[#chars+1] = lines:line(n)
                    end
                    message = table.concat(chars, '\n')
                end
                related[#related+1] = {
                    message = message,
                    location = {
                        uri = info.uri,
                        range = getRange(info.start, info.finish, lines),
                    }
                }
            end
        end
        diagnostic.relatedInformation = related
    end
    return diagnostic
end

local function buildError(err, lines, uri)
    local diagnostic = {
        source  = lang.script.DIAG_SYNTAX_CHECK,
        message = lang.script('PARSER_'..err.type, err.info)
    }
    if err.version then
        if type(err.version) == 'table' then
            diagnostic.message = ('%s(%s)'):format(diagnostic.message, lang.script('DIAG_NEED_VERSION', table.concat(err.version, '/')))
        else
            diagnostic.message = ('%s(%s)'):format(diagnostic.message, lang.script('DIAG_NEED_VERSION', err.version))
        end
    end
    if err.level == 'error' then
        diagnostic.severity = DiagnosticSeverity.Error
    else
        diagnostic.severity = DiagnosticSeverity.Warning
    end
    local startrow, startcol = lines:rowcol(err.start)
    local endrow, endcol = lines:rowcol(err.finish)
    if err.type == 'UNKNOWN' then
        local _, max = lines:range(endrow)
        endcol = max
    end
    local range = {
        start = {
            line = startrow - 1,
            character = startcol - 1,
        },
        ['end'] = {
            line = endrow - 1,
            character = endcol,
        },
    }
    diagnostic.range = range

    local related = err.info and err.info.related
    if related then
        local start_line  = lines:rowcol(related[1])
        local finish_line = lines:rowcol(related[2])
        local chars = {}
        for n = start_line, finish_line do
            chars[#chars+1] = lines:line(n)
        end
        local message = table.concat(chars, '\n')
        diagnostic.relatedInformation = {
            {
                message = message,
                location = {
                    uri = uri,
                    range = getRange(related[1], related[2], lines),
                }
            }
        }
    end
    return diagnostic
end

return function (lsp, params)
    local vm     = params.vm
    local lines  = params.lines
    local uri    = params.uri
    local errs   = lsp:getAstErrors(uri)

    local diagnostics = {}
    if vm then
        local datas = core.diagnostics(vm, lines, uri)
        for _, data in ipairs(datas) do
            diagnostics[#diagnostics+1] = createInfo(lsp, data, lines)
        end
    end
    if errs then
        for _, err in ipairs(errs) do
            diagnostics[#diagnostics+1] = buildError(err, lines, uri)
        end
    end

    return diagnostics
end