summaryrefslogtreecommitdiff
path: root/server/src/method/textDocument/publishDiagnostics.lua
blob: fffd7f9dfd42cee9c8a4c3cb1290933b5dc3f330 (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
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(data, lines)
    local diagnostic = {
        source   = lang.script.DIAG_DIAGNOSTICS,
        range    = getRange(data.start, data.finish, lines),
        severity = data.level,
        message  = data.message,
    }
    if data.related then
        local related = {}
        for i, info in ipairs(data.related) do
            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[i] = {
                message = message,
                location = {
                    uri = info.uri,
                    range = getRange(info.start, info.finish, lines),
                }
            }
        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.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(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