Dotfiles/site-modules/core/files/vim/plugin/codeclimate.lua

142 lines
4.7 KiB
Lua

-- Plugin to add the output of codeclimate issues into vim.
--
-- https://github.com/codeclimate/codeclimate
--
-- For this to work you must install the codeclimate cli tool and run the below
-- command to create the `/tmp/cc.json` where all the issues are stored. Once
-- this file is created vims diagnostics will be populated with codeclimate
-- issues.
--
-- $ codeclimate analyze -f json > /tmp/cc.json
--
-- If you have duplication you can run `:CodeClimateOtherLocations` with your
-- cousor on the issue and the quickfix list will be populated with the other
-- locations the duplication exists.
--
local codeclimate_namespace = vim.api.nvim_create_namespace("codeclimate")
local codeclimate_auto_command_group = vim.api.nvim_create_augroup("codeclimate", { clear = true })
-- Get the relative path of a `buffer` to the vim current working directory
local function get_relative_path(buffer)
return vim.api.nvim_buf_get_name(buffer):sub(#vim.fn.getcwd() + 2, -1)
end
-- Get the content of a `line_number` from a `file_path`. This will read the
-- file and return content at line `n` If the file can not be opened a empty
-- string is returned.
local function read_line(file_path, line_number)
local file = io.open(file_path, "r")
if file == nil then
return ""
end
local index = 0;
while index < line_number do
file:read()
index = index + 1
end
local line = file:read()
file:close()
return line
end
-- Populates the vim diagnostics with the codeclimate issues from '/tmp/cc.json'
local function codeclimate_add_diagnostics()
local file = io.open("/tmp/cc.json", "rb")
if not file then return nil end
local issues = vim.json.decode(file:read("*a"))
local buffer = vim.api.nvim_win_get_buf(0)
local buffer_name = get_relative_path(buffer)
local diagnostics = {}
for index = 1, #issues do
if issues[index]["location"] ~= nil and issues[index]["location"]["path"] == buffer_name then
local start_line = 0;
local end_line = 0;
if issues[index]["location"]["lines"] ~= nil then
start_line = issues[index]["location"]["lines"]["begin"] - 1
end_line = issues[index]["location"]["lines"]["end"] - 1
elseif issues[index]["location"]["positions"] ~= nil then
-- TODO(ade): We get column positions here so this can be implemented
start_line = issues[index]["location"]["positions"]["begin"]["line"] - 1
end_line = issues[index]["location"]["positions"]["end"]["line"] - 1
else
goto continue
end
-- Limit the number of lines an issue spans to x amount of lines. If we
-- don't do this then some issues will highlight the whole file and its
-- really off putting.
if (end_line - start_line) > 10 then
end_line = start_line
end
table.insert(diagnostics, {
source = "Code Climate",
lnum = start_line,
col = 0,
end_lnum = end_line,
end_col = 0,
message = issues[index]["description"],
severity = vim.diagnostic.severity.ERROR,
user_data = {
other_locations = issues[index]["other_locations"]
}
})
::continue::
end
end
vim.diagnostic.set(codeclimate_namespace, buffer, diagnostics)
end
local function other_to_quick_fix()
local point = vim.api.nvim_win_get_cursor(0)
local diagnostics = vim.diagnostic.get(0, {lnum = point[1] - 1})
local issues = {}
-- TODO(ade): Add in a message to say this issues dose not have any other
-- locations
if #diagnostics == 0 or #diagnostics[1]["user_data"]["other_locations"] == nil then
return
end
local diagnostic = diagnostics[1]
table.insert(issues, {
text = vim.api.nvim_buf_get_lines(diagnostic["bufnr"], diagnostic["lnum"], diagnostic["lnum"] + 1, true)[1],
filename = get_relative_path(diagnostic["bufnr"]),
lnum = diagnostic["lnum"] + 1,
end_lnum = diagnostic["end_lnum"],
})
for other_index = 1, #diagnostic["user_data"]["other_locations"] do
local other_location = diagnostic["user_data"]["other_locations"][other_index]
table.insert(issues, {
text = read_line(other_location["path"], other_location["lines"]["begin"] - 1),
filename = other_location["path"],
lnum = other_location["lines"]["begin"],
end_lnum = other_location["lines"]["end"],
})
end
vim.fn.setqflist({}, "r", {title = "Code Climate Other Locations", items = issues})
vim.cmd "copen"
end
vim.api.nvim_create_autocmd("BufEnter", {
group = codeclimate_auto_command_group,
desc = "",
pattern = "*",
callback = function()
codeclimate_add_diagnostics()
end,
})
vim.api.nvim_create_user_command("CodeClimateOtherLocations", other_to_quick_fix, { bang = true, desc = "" })