181 lines
4.2 KiB
Lua
181 lines
4.2 KiB
Lua
-- Rust plugin for lint+
|
|
|
|
|
|
--- IMPLEMENTATION ---
|
|
|
|
|
|
local common = require "core.common"
|
|
local core = require "core"
|
|
|
|
local lintplus = require "plugins.lintplus"
|
|
local json = require "plugins.lintplus.json"
|
|
|
|
|
|
-- common functions
|
|
|
|
|
|
local function no_op() end
|
|
|
|
|
|
local function parent_directories(filename)
|
|
|
|
return function ()
|
|
filename = lintplus.fs.parent_directory(filename)
|
|
return filename
|
|
end
|
|
|
|
end
|
|
|
|
|
|
-- message processing
|
|
|
|
local function message_spans_multiple_lines(message, line)
|
|
if #message.spans == 0 then return false end
|
|
for _, span in ipairs(message.spans) do
|
|
if span.line_start ~= line then
|
|
return true
|
|
end
|
|
end
|
|
for _, child in ipairs(message.children) do
|
|
local child_spans_multiple_lines = message_spans_multiple_lines(child, line)
|
|
if child_spans_multiple_lines then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function process_message(
|
|
context,
|
|
message,
|
|
out_messages,
|
|
rail
|
|
)
|
|
local msg = message.message
|
|
local span = message.spans[1]
|
|
|
|
local kind do
|
|
local l = message.level
|
|
if l == "error" or l == "warning" then
|
|
kind = l
|
|
elseif l == "error: internal compiler error" then
|
|
kind = "error"
|
|
else
|
|
kind = "info"
|
|
end
|
|
end
|
|
|
|
local nonprimary_spans = 0
|
|
for _, sp in ipairs(message.spans) do
|
|
if not sp.is_primary then
|
|
nonprimary_spans = nonprimary_spans + 1
|
|
end
|
|
end
|
|
|
|
-- only assign a rail if there are children or multiple non-primary spans
|
|
if span ~= nil then
|
|
local filename = context.workspace_root .. '/' .. span.file_name
|
|
local line, column = span.line_start, span.column_start
|
|
|
|
if rail == nil then
|
|
if message_spans_multiple_lines(message, line) then
|
|
rail = context:create_gutter_rail()
|
|
end
|
|
end
|
|
|
|
for _, sp in ipairs(message.spans) do
|
|
if sp.label ~= nil and not sp.is_primary then
|
|
local s_filename = context.workspace_root .. '/' .. span.file_name
|
|
local s_line, s_column = sp.line_start, sp.column_start
|
|
table.insert(out_messages,
|
|
{ s_filename, s_line, s_column, "info", sp.label, rail })
|
|
end
|
|
end
|
|
|
|
if span.suggested_replacement ~= nil then
|
|
local suggestion = span.suggested_replacement:match("(.-)\r?\n")
|
|
if suggestion ~= nil then
|
|
msg = msg .. " `" .. suggestion .. '`'
|
|
end
|
|
end
|
|
table.insert(out_messages, { filename, line, column, kind, msg, rail })
|
|
end
|
|
|
|
for _, child in ipairs(message.children) do
|
|
process_message(context, child, out_messages, rail)
|
|
end
|
|
end
|
|
|
|
|
|
local function get_messages(context, event)
|
|
-- filename, line, column, kind, message
|
|
local messages = {}
|
|
process_message(context, event.message, messages)
|
|
return messages
|
|
end
|
|
|
|
|
|
-- linter
|
|
|
|
lintplus.add("rust") {
|
|
filename = "%.rs$",
|
|
procedure = {
|
|
|
|
init = function (filename, context)
|
|
local process = lintplus.ipc.start_process({
|
|
"cargo", "locate-project", "--workspace"
|
|
}, lintplus.fs.parent_directory(filename))
|
|
while true do
|
|
local exit, _ = process:poll(function (line)
|
|
local ok, process_result = pcall(json.decode, line)
|
|
if not ok then return end
|
|
context.workspace_root =
|
|
lintplus.fs.parent_directory(process_result.root)
|
|
end)
|
|
if exit ~= nil then break end
|
|
end
|
|
end,
|
|
|
|
command = lintplus.command {
|
|
set_cwd = true,
|
|
"cargo", "clippy",
|
|
"--message-format", "json",
|
|
"--color", "never",
|
|
-- "--tests",
|
|
},
|
|
|
|
interpreter = function (filename, line, context)
|
|
-- initial checks
|
|
if context.workspace_root == nil then
|
|
core.error(
|
|
"lint+/rust: "..filename.." is not situated in a cargo crate"
|
|
)
|
|
return no_op
|
|
end
|
|
if line:match("^ *Blocking") then
|
|
return "bail"
|
|
end
|
|
|
|
local ok, event = pcall(json.decode, line)
|
|
if not ok then return no_op end
|
|
|
|
if event.reason == "compiler-message" then
|
|
local messages = get_messages(context, event)
|
|
local i = 1
|
|
|
|
return function ()
|
|
local msg = messages[i]
|
|
if msg ~= nil then
|
|
i = i + 1
|
|
return table.unpack(msg)
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
else
|
|
return no_op
|
|
end
|
|
end,
|
|
|
|
},
|
|
}
|