diff options
Diffstat (limited to 'script-beta/provider/diagnostic.lua')
-rw-r--r-- | script-beta/provider/diagnostic.lua | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/script-beta/provider/diagnostic.lua b/script-beta/provider/diagnostic.lua new file mode 100644 index 00000000..ba95f2bf --- /dev/null +++ b/script-beta/provider/diagnostic.lua @@ -0,0 +1,209 @@ +local await = require 'await' +local proto = require 'proto.proto' +local define = require 'proto.define' +local lang = require 'language' +local files = require 'files' +local config = require 'config' +local core = require 'core.diagnostics' +local util = require 'utility' + +local m = {} +m._start = false +m.cache = {} + +local function concat(t, sep) + if type(t) ~= 'table' then + return t + end + return table.concat(t, sep) +end + +local function buildSyntaxError(uri, err) + local lines = files.getLines(uri) + local text = files.getText(uri) + local message = lang.script('PARSER_'..err.type, err.info) + + if err.version then + local version = err.info and err.info.version or config.config.runtime.version + message = message .. ('(%s)'):format(lang.script('DIAG_NEED_VERSION' + , concat(err.version, '/') + , version + )) + end + + local related = err.info and err.info.related + local relatedInformation + if related then + relatedInformation = {} + for _, rel in ipairs(related) do + local rmessage + if rel.message then + rmessage = lang.script('PARSER_'..rel.message) + else + rmessage = text:sub(rel.start, rel.finish) + end + relatedInformation[#relatedInformation+1] = { + message = rmessage, + location = define.location(uri, define.range(lines, text, rel.start, rel.finish)), + } + end + end + + return { + range = define.range(lines, text, err.start, err.finish), + severity = define.DiagnosticSeverity.Error, + source = lang.script.DIAG_SYNTAX_CHECK, + message = message, + relatedInformation = relatedInformation, + } +end + +local function buildDiagnostic(uri, diag) + local lines = files.getLines(uri) + local text = files.getText(uri) + + local relatedInformation + if diag.related then + relatedInformation = {} + for _, rel in ipairs(diag.related) do + local rtext = files.getText(rel.uri) + local rlines = files.getLines(rel.uri) + relatedInformation[#relatedInformation+1] = { + message = rel.message or rtext:sub(rel.start, rel.finish), + location = define.location(rel.uri, define.range(rlines, rtext, rel.start, rel.finish)) + } + end + end + + return { + range = define.range(lines, text, diag.start, diag.finish), + source = lang.script.DIAG_DIAGNOSTICS, + severity = diag.level, + message = diag.message, + code = diag.code, + tags = diag.tags, + relatedInformation = relatedInformation, + } +end + +local function merge(a, b) + if not a and not b then + return nil + end + local t = {} + if a then + for i = 1, #a do + t[#t+1] = a[i] + end + end + if b then + for i = 1, #b do + t[#t+1] = b[i] + end + end + return t +end + +function m.clear(uri) + if not m.cache[uri] then + return + end + m.cache[uri] = nil + proto.notify('textDocument/publishDiagnostics', { + uri = uri, + diagnostics = {}, + }) +end + +function m.syntaxErrors(uri, ast) + if #ast.errs == 0 then + return nil + end + + local results = {} + + for _, err in ipairs(ast.errs) do + results[#results+1] = buildSyntaxError(uri, err) + end + + return results +end + +function m.diagnostics(uri, syntaxOnly) + if syntaxOnly or not m._start then + return m.cache[uri] + end + + local diags = core(uri) + if not diags then + return nil + end + + local results = {} + for _, diag in ipairs(diags) do + results[#results+1] = buildDiagnostic(uri, diag) + end + + return results +end + +function m.doDiagnostic(uri, syntaxOnly) + local ast = files.getAst(uri) + if not ast then + m.clear(uri) + return + end + + local syntax = m.syntaxErrors(uri, ast) + local diagnostics = m.diagnostics(uri, syntaxOnly) + local full = merge(syntax, diagnostics) + if not full then + m.clear(uri) + return + end + + if util.equal(m.cache[uri], full) then + return + end + m.cache[uri] = full + + proto.notify('textDocument/publishDiagnostics', { + uri = uri, + diagnostics = full, + }) +end + +function m.refresh(uri) + await.create(function () + await.delay(function () + return files.globalVersion + end) + if uri then + m.doDiagnostic(uri, true) + end + if not m._start then + return + end + local clock = os.clock() + if uri then + m.doDiagnostic(uri) + end + for destUri in files.eachFile() do + if destUri ~= uri then + m.doDiagnostic(files.getOriginUri(destUri)) + await.delay(function () + return files.globalVersion + end) + end + end + local passed = os.clock() - clock + log.info(('Finish diagnostics, takes [%.3f] sec.'):format(passed)) + end) +end + +function m.start() + m._start = true + m.refresh() +end + +return m |