dotfiles/.config/lite-xl/plugins/lintplus/linters/rust.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,
},
}