BREAKING CHANGE: This is a breaking change, it deprecates the Delta Reviewer (#81)

BREAKING

This is a breaking change which deprecates support for the Delta reviewer. Now, only Diffview is supported.
This commit is contained in:
Harrison (Harry) Cramer
2023-11-10 17:38:28 -05:00
committed by GitHub
parent a032c6434e
commit ffdaf83784
16 changed files with 127 additions and 431 deletions

View File

@@ -20,7 +20,6 @@ https://github.com/harrisoncramer/gitlab.nvim/assets/32515581/50f44eaf-5f99-4cb3
## Quick Start ## Quick Start
1. Install Go 1. Install Go
2. Install reviewer: <a href="https://github.com/dandavison/delta">delta</a> or <a href="https://github.com/sindrets/diffview.nvim">diffview</a>
3. Add configuration (see Installation section) 3. Add configuration (see Installation section)
4. Checkout your feature branch: `git checkout feature-branch` 4. Checkout your feature branch: `git checkout feature-branch`
5. Open Neovim 5. Open Neovim
@@ -36,12 +35,13 @@ return {
dependencies = { dependencies = {
"MunifTanjim/nui.nvim", "MunifTanjim/nui.nvim",
"nvim-lua/plenary.nvim", "nvim-lua/plenary.nvim",
"sindrets/diffview.nvim",
"stevearc/dressing.nvim", -- Recommended but not required. Better UI for pickers. "stevearc/dressing.nvim", -- Recommended but not required. Better UI for pickers.
enabled = true, enabled = true,
}, },
build = function () require("gitlab.server").build(true) end, -- Builds the Go binary build = function () require("gitlab.server").build(true) end, -- Builds the Go binary
config = function() config = function()
require("gitlab").setup() -- Uses delta reviewer by default require("gitlab").setup()
end, end,
} }
``` ```
@@ -54,6 +54,7 @@ use {
requires = { requires = {
"MunifTanjim/nui.nvim", "MunifTanjim/nui.nvim",
"nvim-lua/plenary.nvim" "nvim-lua/plenary.nvim"
"sindrets/diffview.nvim",
}, },
run = function() require("gitlab.server").build(true) end, run = function() require("gitlab.server").build(true) end,
config = function() config = function()
@@ -87,7 +88,6 @@ require("gitlab").setup({
port = nil, -- The port of the Go server, which runs in the background, if omitted or `nil` the port will be chosen automatically port = nil, -- The port of the Go server, which runs in the background, if omitted or `nil` the port will be chosen automatically
log_path = vim.fn.stdpath("cache") .. "/gitlab.nvim.log", -- Log path for the Go server log_path = vim.fn.stdpath("cache") .. "/gitlab.nvim.log", -- Log path for the Go server
debug = { go_request = false, go_response = false }, -- Which values to log debug = { go_request = false, go_response = false }, -- Which values to log
reviewer = "delta", -- The reviewer type ("delta" or "diffview")
attachment_dir = nil, -- The local directory for files (see the "summary" section) attachment_dir = nil, -- The local directory for files (see the "summary" section)
popup = { -- The popup for comment creation, editing, and replying popup = { -- The popup for comment creation, editing, and replying
exit = "<Esc>", exit = "<Esc>",
@@ -109,19 +109,6 @@ require("gitlab").setup({
resolved = '', -- Symbol to show next to resolved discussions resolved = '', -- Symbol to show next to resolved discussions
unresolved = '', -- Symbol to show next to unresolved discussions unresolved = '', -- Symbol to show next to unresolved discussions
}, },
review_pane = { -- Specific settings for different reviewers
delta = {
added_file = "", -- The symbol to show next to added files
modified_file = "", -- The symbol to show next to modified files
removed_file = "", -- The symbol to show next to removed files
}
},
dialogue = { -- The confirmation dialogue for deleting comments
focus_next = { "j", "<Down>", "<Tab>" },
focus_prev = { "k", "<Up>", "<S-Tab>" },
close = { "<Esc>", "<C-c>" },
submit = { "<CR>", "<Space>" },
},
pipeline = { pipeline = {
created = "", created = "",
pending = "", pending = "",
@@ -170,14 +157,12 @@ The `review` action will open a diff of the changes. You can leave comments usin
```lua ```lua
require("gitlab").review() require("gitlab").review()
require("gitlab").create_comment() require("gitlab").create_comment()
require("gitlab").create_multiline_comment() -- Only supported for diffview reviewer require("gitlab").create_multiline_comment()
require("gitlab").create_comment_suggestion() -- Only supported for diffview reviewer require("gitlab").create_comment_suggestion()
``` ```
For suggesting changes you can use `create_comment_suggestion` in visual mode which works similar to `create_multiline_comment` but prefills the comment window with gitlab [suggest changes](https://docs.gitlab.com/ee/user/project/merge_requests/reviews/suggestions.html) code block with prefilled code from visual selection. For suggesting changes you can use `create_comment_suggestion` in visual mode which works similar to `create_multiline_comment` but prefills the comment window with gitlab [suggest changes](https://docs.gitlab.com/ee/user/project/merge_requests/reviews/suggestions.html) code block with prefilled code from visual selection.
The reviewer is Delta by default, but you can configure the plugin to use Diffview instead.
### Discussions and Notes ### Discussions and Notes
Gitlab groups threads of comments together into "discussions." Gitlab groups threads of comments together into "discussions."

View File

@@ -38,7 +38,7 @@ M.add_popup = function(type)
table.insert(current_ids, choice.id) table.insert(current_ids, choice.id)
local body = { ids = current_ids } local body = { ids = current_ids }
job.run_job("/mr/" .. type, "PUT", body, function(data) job.run_job("/mr/" .. type, "PUT", body, function(data)
vim.notify(data.message, vim.log.levels.INFO) u.notify(data.message, vim.log.levels.INFO)
state.INFO[plural] = data[plural] state.INFO[plural] = data[plural]
end) end)
end) end)
@@ -59,7 +59,7 @@ M.delete_popup = function(type)
local ids = u.extract(M.filter_eligible(current, { choice }), "id") local ids = u.extract(M.filter_eligible(current, { choice }), "id")
local body = { ids = ids } local body = { ids = ids }
job.run_job("/mr/" .. type, "PUT", body, function(data) job.run_job("/mr/" .. type, "PUT", body, function(data)
vim.notify(data.message, vim.log.levels.INFO) u.notify(data.message, vim.log.levels.INFO)
state.INFO[plural] = data[plural] state.INFO[plural] = data[plural]
end) end)
end) end)

View File

@@ -46,11 +46,6 @@ M.create_comment_suggestion = function()
local backticks = "```" local backticks = "```"
local selected_lines = reviewer.get_lines(start_line, end_line) local selected_lines = reviewer.get_lines(start_line, end_line)
if selected_lines == nil then
-- TODO: remove when delta is supported
return
end
for line in ipairs(selected_lines) do for line in ipairs(selected_lines) do
if string.match(line, "^```$") then if string.match(line, "^```$") then
backticks = "````" backticks = "````"
@@ -65,7 +60,7 @@ M.create_comment_suggestion = function()
suggestion_start = backticks .. "suggestion:-" .. range .. "+0" suggestion_start = backticks .. "suggestion:-" .. range .. "+0"
else else
-- This should never happen afaik -- This should never happen afaik
vim.notify("Unexpected suggestion position", vim.log.levels.ERROR) u.notify("Unexpected suggestion position", vim.log.levels.ERROR)
return return
end end
suggestion_start = suggestion_start suggestion_start = suggestion_start
@@ -117,14 +112,14 @@ end
---@param unlinked boolean | nil if true, the comment is not linked to a line ---@param unlinked boolean | nil if true, the comment is not linked to a line
M.confirm_create_comment = function(text, range, unlinked) M.confirm_create_comment = function(text, range, unlinked)
if text == nil then if text == nil then
vim.notify("Reviewer did not provide text of change", vim.log.levels.ERROR) u.notify("Reviewer did not provide text of change", vim.log.levels.ERROR)
return return
end end
if unlinked then if unlinked then
local body = { comment = text } local body = { comment = text }
job.run_job("/comment", "POST", body, function(data) job.run_job("/comment", "POST", body, function(data)
vim.notify("Note created!", vim.log.levels.INFO) u.notify("Note created!", vim.log.levels.INFO)
discussions.add_discussion({ data = data, unlinked = true }) discussions.add_discussion({ data = data, unlinked = true })
end) end)
return return
@@ -149,7 +144,7 @@ M.confirm_create_comment = function(text, range, unlinked)
} }
job.run_job("/comment", "POST", body, function(data) job.run_job("/comment", "POST", body, function(data)
vim.notify("Comment created!", vim.log.levels.INFO) u.notify("Comment created!", vim.log.levels.INFO)
discussions.add_discussion({ data = data, unlinked = false }) discussions.add_discussion({ data = data, unlinked = false })
end) end)
end end

View File

@@ -3,7 +3,6 @@
-- and marking discussions as resolved/unresolved. -- and marking discussions as resolved/unresolved.
local Split = require("nui.split") local Split = require("nui.split")
local Popup = require("nui.popup") local Popup = require("nui.popup")
local Menu = require("nui.menu")
local NuiTree = require("nui.tree") local NuiTree = require("nui.tree")
local Layout = require("nui.layout") local Layout = require("nui.layout")
local job = require("gitlab.job") local job = require("gitlab.job")
@@ -40,7 +39,7 @@ M.toggle = function()
job.run_job("/discussions", "POST", { blacklist = state.settings.discussion_tree.blacklist }, function(data) job.run_job("/discussions", "POST", { blacklist = state.settings.discussion_tree.blacklist }, function(data)
if type(data.discussions) ~= "table" and type(data.unlinked_discussions) ~= "table" then if type(data.discussions) ~= "table" and type(data.unlinked_discussions) ~= "table" then
vim.notify("No discussions or notes for this MR", vim.log.levels.WARN) u.notify("No discussions or notes for this MR", vim.log.levels.WARN)
return return
end end
@@ -86,7 +85,7 @@ M.send_reply = function(tree, discussion_id)
return function(text) return function(text)
local body = { discussion_id = discussion_id, reply = text } local body = { discussion_id = discussion_id, reply = text }
job.run_job("/reply", "POST", body, function(data) job.run_job("/reply", "POST", body, function(data)
vim.notify("Sent reply!", vim.log.levels.INFO) u.notify("Sent reply!", vim.log.levels.INFO)
M.add_reply_to_tree(tree, data.note, discussion_id) M.add_reply_to_tree(tree, data.note, discussion_id)
end) end)
end end
@@ -94,74 +93,48 @@ end
-- This function (settings.discussion_tree.delete_comment) will trigger a popup prompting you to delete the current comment -- This function (settings.discussion_tree.delete_comment) will trigger a popup prompting you to delete the current comment
M.delete_comment = function(tree, unlinked) M.delete_comment = function(tree, unlinked)
local menu = Menu({ vim.ui.select({ "Confirm", "Cancel" }, {
position = "50%", prompt = "Delete comment?",
size = { }, function(choice)
width = 25, if choice == "Cancel" then
}, return
border = { end
style = "single", M.send_deletion(tree, unlinked)
text = { end)
top = "Delete Comment?",
top_align = "center",
},
},
win_options = {
winhighlight = "Normal:Normal,FloatBorder:Normal",
},
}, {
lines = {
Menu.item("Confirm"),
Menu.item("Cancel"),
},
max_width = 20,
keymap = {
focus_next = state.settings.dialogue.focus_next,
focus_prev = state.settings.dialogue.focus_prev,
close = state.settings.dialogue.close,
submit = state.settings.dialogue.submit,
},
on_submit = function(item)
M.send_deletion(tree, item, unlinked)
end,
})
menu:mount()
end end
-- This function will actually send the deletion to Gitlab -- This function will actually send the deletion to Gitlab
-- when you make a selection, and re-render the tree -- when you make a selection, and re-render the tree
M.send_deletion = function(tree, item, unlinked) M.send_deletion = function(tree, unlinked)
if item.text == "Confirm" then local current_node = tree:get_node()
local current_node = tree:get_node()
local note_node = M.get_note_node(tree, current_node) local note_node = M.get_note_node(tree, current_node)
local root_node = M.get_root_node(tree, current_node) local root_node = M.get_root_node(tree, current_node)
local note_id = note_node.is_root and root_node.root_note_id or note_node.id local note_id = note_node.is_root and root_node.root_note_id or note_node.id
local body = { discussion_id = root_node.id, note_id = note_id } local body = { discussion_id = root_node.id, note_id = note_id }
job.run_job("/comment", "DELETE", body, function(data) job.run_job("/comment", "DELETE", body, function(data)
vim.notify(data.message, vim.log.levels.INFO) u.notify(data.message, vim.log.levels.INFO)
if not note_node.is_root then if not note_node.is_root then
tree:remove_node("-" .. note_id) -- Note is not a discussion root, safe to remove tree:remove_node("-" .. note_id) -- Note is not a discussion root, safe to remove
tree:render() tree:render()
else
if unlinked then
M.unlinked_discussions = u.remove_first_value(M.unlinked_discussions)
M.rebuild_unlinked_discussion_tree()
else else
if unlinked then M.discussions = u.remove_first_value(M.discussions)
M.unlinked_discussions = u.remove_first_value(M.unlinked_discussions) M.rebuild_discussion_tree()
M.rebuild_unlinked_discussion_tree()
else
M.discussions = u.remove_first_value(M.discussions)
M.rebuild_discussion_tree()
end
end end
M.switch_can_edit_bufs(true) end
M.add_empty_titles({ M.switch_can_edit_bufs(true)
{ M.linked_section_bufnr, M.discussions, "No Discussions for this MR" }, M.add_empty_titles({
{ M.unlinked_section_bufnr, M.unlinked_discussions, "No Notes (Unlinked Discussions) for this MR" }, { M.linked_section_bufnr, M.discussions, "No Discussions for this MR" },
}) { M.unlinked_section_bufnr, M.unlinked_discussions, "No Notes (Unlinked Discussions) for this MR" },
M.switch_can_edit_bufs(false) })
end) M.switch_can_edit_bufs(false)
end end)
end end
-- This function (settings.discussion_tree.edit_comment) will open the edit popup for the current comment in the discussion tree -- This function (settings.discussion_tree.edit_comment) will open the edit popup for the current comment in the discussion tree
@@ -199,7 +172,7 @@ M.send_edits = function(discussion_id, note_id, unlinked)
comment = text, comment = text,
} }
job.run_job("/comment", "PATCH", body, function(data) job.run_job("/comment", "PATCH", body, function(data)
vim.notify(data.message, vim.log.levels.INFO) u.notify(data.message, vim.log.levels.INFO)
if unlinked then if unlinked then
M.unlinked_discussions = M.replace_text(M.unlinked_discussions, discussion_id, note_id, text) M.unlinked_discussions = M.replace_text(M.unlinked_discussions, discussion_id, note_id, text)
M.rebuild_unlinked_discussion_tree() M.rebuild_unlinked_discussion_tree()
@@ -225,7 +198,7 @@ M.toggle_resolved = function(tree)
} }
job.run_job("/comment", "PATCH", body, function(data) job.run_job("/comment", "PATCH", body, function(data)
vim.notify(data.message, vim.log.levels.INFO) u.notify(data.message, vim.log.levels.INFO)
M.redraw_resolved_status(tree, note, not note.resolved) M.redraw_resolved_status(tree, note, not note.resolved)
end) end)
end end
@@ -234,7 +207,7 @@ end
M.jump_to_reviewer = function(tree) M.jump_to_reviewer = function(tree)
local file_name, new_line, old_line, error = M.get_note_location(tree) local file_name, new_line, old_line, error = M.get_note_location(tree)
if error ~= nil then if error ~= nil then
vim.notify(error, vim.log.levels.ERROR) u.notify(error, vim.log.levels.ERROR)
return return
end end
reviewer.jump(file_name, new_line, old_line) reviewer.jump(file_name, new_line, old_line)
@@ -244,7 +217,7 @@ end
M.jump_to_file = function(tree) M.jump_to_file = function(tree)
local file_name, new_line, old_line, error = M.get_note_location(tree) local file_name, new_line, old_line, error = M.get_note_location(tree)
if error ~= nil then if error ~= nil then
vim.notify(error, vim.log.levels.ERROR) u.notify(error, vim.log.levels.ERROR)
return return
end end
vim.cmd.tabnew() vim.cmd.tabnew()

View File

@@ -6,7 +6,7 @@ local M = {}
M.open_in_browser = function() M.open_in_browser = function()
local url = state.INFO.web_url local url = state.INFO.web_url
if url == nil then if url == nil then
vim.notify("Could not get Gitlab URL", vim.log.levels.ERROR) u.notify("Could not get Gitlab URL", vim.log.levels.ERROR)
return return
end end
if vim.fn.has("mac") == 1 then if vim.fn.has("mac") == 1 then
@@ -14,21 +14,21 @@ M.open_in_browser = function()
elseif vim.fn.has("unix") == 1 then elseif vim.fn.has("unix") == 1 then
vim.fn.jobstart({ "xdg-open", url }) vim.fn.jobstart({ "xdg-open", url })
else else
vim.notify("Opening a Gitlab URL is not supported on this OS!", vim.log.levels.ERROR) u.notify("Opening a Gitlab URL is not supported on this OS!", vim.log.levels.ERROR)
end end
end end
M.attach_file = function() M.attach_file = function()
local attachment_dir = state.settings.attachment_dir local attachment_dir = state.settings.attachment_dir
if not attachment_dir or attachment_dir == "" then if not attachment_dir or attachment_dir == "" then
vim.notify("Must provide valid attachment_dir in plugin setup", vim.log.levels.ERROR) u.notify("Must provide valid attachment_dir in plugin setup", vim.log.levels.ERROR)
return return
end end
local files = u.list_files_in_folder(attachment_dir) local files = u.list_files_in_folder(attachment_dir)
if files == nil then if files == nil then
vim.notify(string.format("Could not list files in %s", attachment_dir), vim.log.levels.ERROR) u.notify(string.format("Could not list files in %s", attachment_dir), vim.log.levels.ERROR)
return return
end end

View File

@@ -14,7 +14,7 @@ local function get_pipeline()
local pipeline = state.INFO.head_pipeline or state.INFO.pipeline local pipeline = state.INFO.head_pipeline or state.INFO.pipeline
if type(pipeline) ~= "table" or (type(pipeline) == "table" and u.table_size(pipeline) == 0) then if type(pipeline) ~= "table" or (type(pipeline) == "table" and u.table_size(pipeline) == 0) then
vim.notify("Pipeline not found", vim.log.levels.WARN) u.notify("Pipeline not found", vim.log.levels.WARN)
return return
end end
return pipeline return pipeline
@@ -86,12 +86,12 @@ M.retrigger = function()
end end
local body = { pipeline_id = pipeline.id } local body = { pipeline_id = pipeline.id }
if pipeline.status ~= "failed" then if pipeline.status ~= "failed" then
vim.notify("Pipeline is not in a failed state!", vim.log.levels.WARN) u.notify("Pipeline is not in a failed state!", vim.log.levels.WARN)
return return
end end
job.run_job("/pipeline", "POST", body, function() job.run_job("/pipeline", "POST", body, function()
vim.notify("Pipeline re-triggered!", vim.log.levels.INFO) u.notify("Pipeline re-triggered!", vim.log.levels.INFO)
end) end)
end end
@@ -101,7 +101,7 @@ M.see_logs = function()
local text = u.get_line_content(bufnr, linnr) local text = u.get_line_content(bufnr, linnr)
local last_word = u.get_last_chunk(text) local last_word = u.get_last_chunk(text)
if last_word == nil then if last_word == nil then
vim.notify("Cannot find job name", vim.log.levels.ERROR) u.notify("Cannot find job name", vim.log.levels.ERROR)
return return
end end
@@ -113,7 +113,7 @@ M.see_logs = function()
end end
if j == nil then if j == nil then
vim.notify("Cannot find job in state", vim.log.levels.ERROR) u.notify("Cannot find job in state", vim.log.levels.ERROR)
return return
end end
@@ -121,7 +121,7 @@ M.see_logs = function()
job.run_job("/job", "GET", body, function(data) job.run_job("/job", "GET", body, function(data)
local file = data.file local file = data.file
if file == "" then if file == "" then
vim.notify("Log trace is empty", vim.log.levels.WARN) u.notify("Log trace is empty", vim.log.levels.WARN)
return return
end end
@@ -131,7 +131,7 @@ M.see_logs = function()
end end
if #lines == 0 then if #lines == 0 then
vim.notify("Log trace lines could not be parsed", vim.log.levels.ERROR) u.notify("Log trace lines could not be parsed", vim.log.levels.ERROR)
return return
end end

View File

@@ -63,7 +63,7 @@ M.edit_summary = function()
local title = u.get_buffer_text(M.title_bufnr):gsub("\n", " ") local title = u.get_buffer_text(M.title_bufnr):gsub("\n", " ")
local body = { title = title, description = description } local body = { title = title, description = description }
job.run_job("/mr/summary", "PUT", body, function(data) job.run_job("/mr/summary", "PUT", body, function(data)
vim.notify(data.message, vim.log.levels.INFO) u.notify(data.message, vim.log.levels.INFO)
state.INFO.description = data.mr.description state.INFO.description = data.mr.description
state.INFO.title = data.mr.title state.INFO.title = data.mr.title
M.layout:unmount() M.layout:unmount()

View File

@@ -3,6 +3,7 @@
local server = require("gitlab.server") local server = require("gitlab.server")
local job = require("gitlab.job") local job = require("gitlab.job")
local state = require("gitlab.state") local state = require("gitlab.state")
local u = require("gitlab.utils")
local M = {} local M = {}
@@ -48,7 +49,7 @@ M.sequence = function(dependencies, cb)
handler:init(cb) handler:init(cb)
if not state.is_gitlab_project then if not state.is_gitlab_project then
vim.notify("The gitlab.nvim state was not set. Do you have a .gitlab.nvim file configured?", vim.log.levels.ERROR) u.notify("The gitlab.nvim state was not set. Do you have a .gitlab.nvim file configured?", vim.log.levels.ERROR)
return return
end end

View File

@@ -22,10 +22,11 @@ return {
end end
server.build() -- Builds the Go binary if it doesn't exist server.build() -- Builds the Go binary if it doesn't exist
state.setPluginConfiguration() -- Sets configuration from `.gitlab.nvim` file state.setPluginConfiguration() -- Sets configuration from `.gitlab.nvim` file
state.merge_settings(args) -- Sets keymaps and other settings from setup function if not state.merge_settings(args) then -- Sets keymaps and other settings from setup function
return
end
require("gitlab.colors") -- Sets colors require("gitlab.colors") -- Sets colors
reviewer.init() -- Picks and initializes reviewer (default is Delta) reviewer.init()
u.has_reviewer(args.reviewer or "delta")
end, end,
-- Global Actions 🌎 -- Global Actions 🌎
summary = async.sequence({ info }, summary.summary), summary = async.sequence({ info }, summary.summary),

View File

@@ -1,6 +1,7 @@
-- This module is responsible for making API calls to the Go server and -- This module is responsible for making API calls to the Go server and
-- running the callbacks associated with those jobs when the JSON is returned -- running the callbacks associated with those jobs when the JSON is returned
local Job = require("plenary.job") local Job = require("plenary.job")
local u = require("gitlab.utils")
local M = {} local M = {}
M.run_job = function(endpoint, method, body, callback) M.run_job = function(endpoint, method, body, callback)
@@ -27,7 +28,7 @@ M.run_job = function(endpoint, method, body, callback)
if type(output) == "string" then if type(output) == "string" then
msg = string.format(msg .. ", got: '%s'", output) msg = string.format(msg .. ", got: '%s'", output)
end end
vim.notify(string.format(msg, endpoint, output), vim.log.levels.WARN) u.notify(string.format(msg, endpoint, output), vim.log.levels.WARN)
return return
end end
if data ~= nil then if data ~= nil then
@@ -36,23 +37,23 @@ M.run_job = function(endpoint, method, body, callback)
callback(data) callback(data)
elseif status == "success" then elseif status == "success" then
local message = string.format("%s", data.message) local message = string.format("%s", data.message)
vim.notify(message, vim.log.levels.INFO) u.notify(message, vim.log.levels.INFO)
else else
local message = string.format("%s: %s", data.message, data.details) local message = string.format("%s: %s", data.message, data.details)
vim.notify(message, vim.log.levels.ERROR) u.notify(message, vim.log.levels.ERROR)
end end
end end
end, 0) end, 0)
end, end,
on_stderr = function() on_stderr = function()
vim.defer_fn(function() vim.defer_fn(function()
vim.notify("Could not run command!", vim.log.levels.ERROR) u.notify("Could not run command!", vim.log.levels.ERROR)
end, 0) end, 0)
end, end,
on_exit = function(_, status) on_exit = function(_, status)
vim.defer_fn(function() vim.defer_fn(function()
if status ~= 0 then if status ~= 0 then
vim.notify(string.format("Go server exited with non-zero code: %d", status), vim.log.levels.ERROR) u.notify(string.format("Go server exited with non-zero code: %d", status), vim.log.levels.ERROR)
end end
end, 0) end, 0)
end, end,

View File

@@ -1,254 +0,0 @@
-- This Module contains all of the code specific to the Delta reviewer.
local state = require("gitlab.state")
local u = require("gitlab.utils")
local M = {
bufnr = nil,
}
-- Public Functions
-- These functions are exposed externally and are used
-- when the reviewer is consumed by other code. They must follow the specification
-- outlined in the reviewer/init.lua file
M.open = function()
local current_buf = vim.api.nvim_get_current_buf()
if current_buf == state.discussion_buf then
vim.api.nvim_command("wincmd w")
end
vim.cmd.enew()
if M.bufnr ~= nil then
vim.api.nvim_set_current_buf(M.bufnr)
return
end
local term_command_template =
"GIT_PAGER='delta --hunk-header-style omit --line-numbers --paging never --file-added-label %s --file-removed-label %s --file-modified-label %s' git diff %s...HEAD"
local term_command = string.format(
term_command_template,
state.settings.review_pane.delta.added_file,
state.settings.review_pane.delta.removed_file,
state.settings.review_pane.delta.modified_file,
state.INFO.target_branch
)
vim.fn.termopen(term_command) -- Calls delta and sends the output to the currently blank buffer
M.bufnr = vim.api.nvim_get_current_buf()
M.winnr = vim.api.nvim_get_current_win()
end
M.jump = function(file_name, new_line, old_line)
local linnr, error = M.get_jump_location(file_name, new_line, old_line)
if error ~= nil then
vim.notify(error, vim.log.levels.ERROR)
return
end
vim.api.nvim_set_current_win(M.winnr)
u.jump_to_buffer(M.bufnr, linnr)
end
---Get the location of a line within the delta buffer. If range is specified, then also the location
---of the lines in range.
---@param range LineRange | nil Line range to get location for
---@return ReviewerInfo | nil nil is returned only if error was encountered
M.get_location = function(range)
if M.bufnr == nil then
vim.notify("Delta reviewer must be initialized first", vim.log.levels.ERROR)
return
end
if range then
vim.notify("Multiline comments are not yet supported for delta reviewer", vim.log.levels.ERROR)
return
end
local bufnr = vim.api.nvim_get_current_buf()
if bufnr ~= M.bufnr then
vim.notify("Line location can only be determined within reviewer window")
return
end
local line_num = u.get_current_line_number()
local file_name = M.get_file_from_review_buffer(u.get_current_line_number())
local review_range, error = M.get_review_buffer_range(file_name)
if error ~= nil then
vim.notify(error, vim.log.levels.ERROR)
return
end
if review_range == nil then
vim.notify("Review buffer range could not be identified", vim.log.levels.ERROR)
return
end
-- In case the comment is left on a line without change information, we
-- iterate backward until we find it within the range of the changes
local current_line_changes = nil
local num = line_num
while review_range ~= nil and num >= review_range[1] and current_line_changes == nil do
local content = u.get_line_content(M.bufnr, num)
local change_nums = M.get_change_nums(content)
current_line_changes = change_nums
num = num - 1
end
if current_line_changes == nil then
vim.notify("Could not find current line change information", vim.log.levels.ERROR)
return
end
local new_line_num = line_num + 1
local next_line_changes = nil
while review_range ~= nil and new_line_num <= review_range[2] and next_line_changes == nil do
local content = u.get_line_content(M.bufnr, new_line_num)
local change_nums = M.get_change_nums(content)
next_line_changes = change_nums
new_line_num = new_line_num + 1
end
if next_line_changes == nil then
vim.notify("Could not find next line change information", vim.log.levels.ERROR)
return
end
local result = { file_name = file_name }
-- This is actually a modified line if these conditions are met
if
current_line_changes.old_line
and not current_line_changes.new_line
and not next_line_changes.old_line
and next_line_changes.new_line
then
result.old_line = current_line_changes.old
result.new_line = next_line_changes.new_line
else
vim.notify("Could not determine line location", vim.log.levels.ERROR)
return
end
return result
end
-- Helper Functions 🤝
-- These functions are not exported and should be private
-- to the delta reviewer, they are used to support the public functions
M.get_jump_location = function(file_name, new_line, old_line)
local range, error = M.get_review_buffer_range(file_name)
if error ~= nil then
return nil, error
end
if range == nil then
return nil, "Review buffer range could not be identified"
end
local linnr = nil
local lines = M.get_review_buffer_lines(range)
for _, line in ipairs(lines) do
local line_data = M.get_change_nums(line.line_content)
if line_data and old_line == line_data.old_line and new_line == line_data.new_line then
linnr = line.line_number
break
end
end
if linnr == nil then
return nil, "Could not find matching line"
end
return linnr, nil
end
M.get_file_from_review_buffer = function(linenr)
for i = linenr, 0, -1 do
local line_content = u.get_line_content(M.bufnr, i)
if M.starts_with_file_symbol(line_content) then
local file_name = u.get_last_chunk(line_content)
return file_name
end
end
end
M.get_change_nums = function(line)
local data, _ = line:match("(.-)" .. "" .. "(.*)")
local line_data = {}
if data == nil then
return nil
end
if data ~= nil and data ~= "" then
local old_line = u.trim(u.get_first_chunk(data, "[^" .. "" .. "]+"))
local new_line = u.trim(u.get_last_chunk(data, "[^" .. "" .. "]+"))
line_data.new_line = tonumber(new_line)
line_data.old_line = tonumber(old_line)
end
if line_data.new_line == nil and line_data.old_line == nil then
return nil
end
return line_data
end
M.get_review_buffer_range = function(file_name)
if M.bufnr == nil then
return nil, "Delta reviewer must be initialized first"
end
local lines = vim.api.nvim_buf_get_lines(M.bufnr, 0, -1, false)
local start = nil
local stop = nil
for i, line in ipairs(lines) do
if start ~= nil and stop ~= nil then
return { start, stop }
end
if M.starts_with_file_symbol(line) then
-- Check if the file name matches the node name
local delta_file_name = u.get_last_chunk(line)
if file_name == delta_file_name then
start = i
elseif start ~= nil then
stop = i
end
end
end
-- We've reached the end of the file, set "stop" in case we already found start
stop = #lines
if start ~= nil and stop ~= nil then
return { start, stop }
end
end
M.starts_with_file_symbol = function(line)
for _, substring in ipairs({
state.settings.review_pane.delta.added_file,
state.settings.review_pane.delta.removed_file,
state.settings.review_pane.delta.modified_file,
}) do
if string.sub(line, 1, string.len(substring)) == substring then
return true
end
end
return false
end
M.get_review_buffer_lines = function(review_buffer_range)
local lines = {}
for i = review_buffer_range[1], review_buffer_range[2], 1 do
local line_content = vim.api.nvim_buf_get_lines(M.bufnr, i - 1, i, false)[1]
if string.find(line_content, "") then
table.insert(lines, { line_content = line_content, line_number = i })
end
end
return lines
end
--- This function is not supported for delta
M.get_lines = function()
vim.notify("Getting lines in delta is not supported yet", vim.log.levels.ERROR)
return nil
end
return M

View File

@@ -1,4 +1,4 @@
-- This Module contains all of the code specific to the Diffview reviewer. -- This Module contains all of the reviewer code for diffview
local u = require("gitlab.utils") local u = require("gitlab.utils")
local state = require("gitlab.state") local state = require("gitlab.state")
local async_ok, async = pcall(require, "diffview.async") local async_ok, async = pcall(require, "diffview.async")
@@ -8,10 +8,6 @@ local M = {
tabnr = nil, tabnr = nil,
} }
-- Public Functions
-- These functions are exposed externally and are used
-- when the reviewer is consumed by other code. They must follow the specification
-- outlined in the reviewer/init.lua file
M.open = function() M.open = function()
vim.api.nvim_command(string.format("DiffviewOpen %s", state.INFO.target_branch)) vim.api.nvim_command(string.format("DiffviewOpen %s", state.INFO.target_branch))
M.tabnr = vim.api.nvim_get_current_tabpage() M.tabnr = vim.api.nvim_get_current_tabpage()
@@ -19,14 +15,14 @@ end
M.jump = function(file_name, new_line, old_line) M.jump = function(file_name, new_line, old_line)
if M.tabnr == nil then if M.tabnr == nil then
vim.notify("Can't jump to Diffvew. Is it open?", vim.log.levels.ERROR) u.notify("Can't jump to Diffvew. Is it open?", vim.log.levels.ERROR)
return return
end end
vim.api.nvim_set_current_tabpage(M.tabnr) vim.api.nvim_set_current_tabpage(M.tabnr)
vim.cmd("DiffviewFocusFiles") vim.cmd("DiffviewFocusFiles")
local view = require("diffview.lib").get_current_view() local view = require("diffview.lib").get_current_view()
if view == nil then if view == nil then
vim.notify("Could not find Diffview view", vim.log.levels.ERROR) u.notify("Could not find Diffview view", vim.log.levels.ERROR)
return return
end end
local files = view.panel:ordered_file_list() local files = view.panel:ordered_file_list()
@@ -34,7 +30,7 @@ M.jump = function(file_name, new_line, old_line)
for _, file in ipairs(files) do for _, file in ipairs(files) do
if file.path == file_name then if file.path == file_name then
if not async_ok then if not async_ok then
vim.notify("Could not load Diffview async", vim.log.levels.ERROR) u.notify("Could not load Diffview async", vim.log.levels.ERROR)
return return
end end
async.await(view:set_file(file)) async.await(view:set_file(file))
@@ -56,7 +52,7 @@ end
---@return ReviewerInfo | nil nil is returned only if error was encountered ---@return ReviewerInfo | nil nil is returned only if error was encountered
M.get_location = function(range) M.get_location = function(range)
if M.tabnr == nil then if M.tabnr == nil then
vim.notify("Diffview reviewer must be initialized first") u.notify("Diffview reviewer must be initialized first")
return return
end end
local bufnr = vim.api.nvim_get_current_buf() local bufnr = vim.api.nvim_get_current_buf()
@@ -65,14 +61,14 @@ M.get_location = function(range)
-- check if we are in the diffview tab -- check if we are in the diffview tab
local tabnr = vim.api.nvim_get_current_tabpage() local tabnr = vim.api.nvim_get_current_tabpage()
if tabnr ~= M.tabnr then if tabnr ~= M.tabnr then
vim.notify("Line location can only be determined within reviewer window") u.notify("Line location can only be determined within reviewer window")
return return
end end
-- check if we are in the diffview buffer -- check if we are in the diffview buffer
local view = require("diffview.lib").get_current_view() local view = require("diffview.lib").get_current_view()
if view == nil then if view == nil then
vim.notify("Could not find Diffview view", vim.log.levels.ERROR) u.notify("Could not find Diffview view", vim.log.levels.ERROR)
return return
end end
local layout = view.cur_layout local layout = view.cur_layout
@@ -90,13 +86,13 @@ M.get_location = function(range)
type = "new" type = "new"
is_new = true is_new = true
else else
vim.notify("Line location can only be determined within reviewer window") u.notify("Line location can only be determined within reviewer window")
return return
end end
local hunks = u.parse_hunk_headers(result.file_name, state.INFO.target_branch) local hunks = u.parse_hunk_headers(result.file_name, state.INFO.target_branch)
if hunks == nil then if hunks == nil then
vim.notify("Could not parse hunks", vim.log.levels.ERROR) u.notify("Could not parse hunks", vim.log.levels.ERROR)
return return
end end

View File

@@ -1,7 +1,4 @@
-- This Module will pick the reviewer set in the user's
-- settings and then map all of it's functions
local state = require("gitlab.state") local state = require("gitlab.state")
local delta = require("gitlab.reviewer.delta")
local diffview = require("gitlab.reviewer.diffview") local diffview = require("gitlab.reviewer.diffview")
local M = { local M = {
@@ -9,14 +6,16 @@ local M = {
} }
local reviewer_map = { local reviewer_map = {
delta = delta,
diffview = diffview, diffview = diffview,
} }
M.init = function() M.init = function()
local reviewer = reviewer_map[state.settings.reviewer] local reviewer = reviewer_map[state.settings.reviewer]
if reviewer == nil then if reviewer == nil then
vim.notify(string.format("gitlab.nvim could not find reviewer %s", state.settings.reviewer), vim.log.levels.ERROR) vim.notify(
string.format("gitlab.nvim could not find reviewer %s, only diffview is supported", state.settings.reviewer),
vim.log.levels.ERROR
)
return return
end end

View File

@@ -48,7 +48,7 @@ M.start = function(callback)
callback() callback()
callback_called = true callback_called = true
elseif not callback_called then elseif not callback_called then
vim.notify("Failed to parse server port", vim.log.levels.ERROR) u.notify("Failed to parse server port", vim.log.levels.ERROR)
end end
end, end,
on_stderr = function(_, errors) on_stderr = function(_, errors)
@@ -60,18 +60,15 @@ M.start = function(callback)
end end
if err_msg ~= "" then if err_msg ~= "" then
vim.notify(err_msg, vim.log.levels.ERROR) u.notify(err_msg, vim.log.levels.ERROR)
end end
end, end,
on_exit = function(job_id, exit_code) on_exit = function(job_id, exit_code)
vim.notify( u.notify("Golang gitlab server exited: job_id: " .. job_id .. ", exit_code: " .. exit_code, vim.log.levels.ERROR)
"Golang gitlab server exited: job_id: " .. job_id .. ", exit_code: " .. exit_code,
vim.log.levels.ERROR
)
end, end,
}) })
if job_id <= 0 then if job_id <= 0 then
vim.notify("Could not start gitlab.nvim binary", vim.log.levels.ERROR) u.notify("Could not start gitlab.nvim binary", vim.log.levels.ERROR)
end end
end end
@@ -96,7 +93,7 @@ M.build = function(override)
local null = u.is_windows() and " >NUL" or " > /dev/null" local null = u.is_windows() and " >NUL" or " > /dev/null"
local installCode = os.execute(command .. null) local installCode = os.execute(command .. null)
if installCode ~= 0 then if installCode ~= 0 then
vim.notify("Could not install gitlab.nvim!", vim.log.levels.ERROR) u.notify("Could not install gitlab.nvim!", vim.log.levels.ERROR)
return false return false
end end
return true return true

View File

@@ -11,7 +11,7 @@ M.settings = {
port = nil, -- choose random port port = nil, -- choose random port
debug = { go_request = false, go_response = false }, debug = { go_request = false, go_response = false },
log_path = (vim.fn.stdpath("cache") .. "/gitlab.nvim.log"), log_path = (vim.fn.stdpath("cache") .. "/gitlab.nvim.log"),
reviewer = "delta", reviewer = "diffview",
attachment_dir = "", attachment_dir = "",
popup = { popup = {
exit = "<Esc>", exit = "<Esc>",
@@ -33,13 +33,6 @@ M.settings = {
resolved = "", resolved = "",
unresolved = "", unresolved = "",
}, },
review_pane = {
delta = {
added_file = "",
modified_file = "",
removed_file = "",
},
},
pipeline = { pipeline = {
created = "", created = "",
pending = "", pending = "",
@@ -51,12 +44,6 @@ M.settings = {
success = "", success = "",
failed = "", failed = "",
}, },
dialogue = {
focus_next = { "j", "<Down>", "<Tab>" },
focus_prev = { "k", "<Up>", "<S-Tab>" },
close = { "<Esc>", "<C-c>" },
submit = { "<CR>", "<Space>" },
},
go_server_running = false, go_server_running = false,
is_gitlab_project = false, is_gitlab_project = false,
colors = { colors = {
@@ -70,10 +57,35 @@ M.settings = {
-- Merges user settings into the default settings, overriding them -- Merges user settings into the default settings, overriding them
M.merge_settings = function(args) M.merge_settings = function(args)
if args == nil then
return
end
M.settings = u.merge(M.settings, args) M.settings = u.merge(M.settings, args)
-- Check deprecated settings and alert users!
if M.settings.dialogue ~= nil then
u.notify("The dialogue field has been deprecated, please remove it from your setup function", vim.log.levels.WARN)
end
if M.settings.reviewer == "delta" then
u.notify(
"Delta is no longer a supported reviewer, please use diffview and update your setup function",
vim.log.levels.ERROR
)
return false
end
local diffview_ok, _ = pcall(require, "diffview")
if not diffview_ok then
u.notify("Please install diffview, it is required")
return false
end
if M.settings.review_pane ~= nil then
u.notify(
"The review_pane field is only relevant for Delta, which has been deprecated, please remove it from your setup function",
vim.log.levels.WARN
)
end
return true
end end
M.print_settings = function() M.print_settings = function()

View File

@@ -1,6 +1,10 @@
local Job = require("plenary.job") local Job = require("plenary.job")
local M = {} local M = {}
M.notify = function(msg, lvl)
vim.notify("gitlab.nvim: " .. msg, lvl)
end
M.get_colors_for_group = function(group) M.get_colors_for_group = function(group)
local normal_fg = vim.fn.synIDattr(vim.fn.synIDtrans((vim.fn.hlID(group))), "fg") local normal_fg = vim.fn.synIDattr(vim.fn.synIDtrans((vim.fn.hlID(group))), "fg")
local normal_bg = vim.fn.synIDattr(vim.fn.synIDtrans((vim.fn.hlID(group))), "bg") local normal_bg = vim.fn.synIDattr(vim.fn.synIDtrans((vim.fn.hlID(group))), "bg")
@@ -11,20 +15,6 @@ M.get_current_line_number = function()
return vim.api.nvim_call_function("line", { "." }) return vim.api.nvim_call_function("line", { "." })
end end
M.has_reviewer = function(reviewer)
if reviewer == "diffview" then
local diffview_ok, _ = pcall(require, "diffview")
if not diffview_ok then
error("Please install diffview or change your reviewer")
end
else
local has_reviewer = vim.fn.executable("delta") == 1
if not has_reviewer then
error(string.format("Please install delta or change your reviewer", reviewer))
end
end
end
M.is_windows = function() M.is_windows = function()
if vim.fn.has("win32") == 1 or vim.fn.has("win32unix") == 1 then if vim.fn.has("win32") == 1 or vim.fn.has("win32unix") == 1 then
return true return true
@@ -350,7 +340,7 @@ M.parse_hunk_headers = function(file_path, base_branch)
end end
end end
else else
vim.notify("Failed to get git diff: " .. j:stderr(), vim.log.levels.WARN) M.notify("Failed to get git diff: " .. j:stderr(), vim.log.levels.WARN)
end end
end, end,
}) })
@@ -444,7 +434,7 @@ end
M.check_visual_mode = function() M.check_visual_mode = function()
local mode = vim.api.nvim_get_mode().mode local mode = vim.api.nvim_get_mode().mode
if mode ~= "v" and mode ~= "V" then if mode ~= "v" and mode ~= "V" then
vim.notify("Code suggestions are only available in visual mode", vim.log.levels.WARN) M.notify("Code suggestions are only available in visual mode", vim.log.levels.WARN)
return false return false
end end
return true return true