Bug Fixes (#470)
* fix: Restore buffer local settings outside reviewer (#446) * fix: do not show healthcheck alert for warnings (#468) * feat: Add MR URL to the summary details (#467) * fix: make cycling reviewed files faster (#474) * feat(pipeline): display trigger jobs for a pipeline in the pipelines popup (#465) * fix: Jumping to renamed files (#484) --------- Co-authored-by: Jakub F. Bortlík <jakub.bortlik@proton.me> Co-authored-by: Ashish Alex <ashish.alex10@gmail.com>
This commit is contained in:
committed by
GitHub
parent
3b396a5e6b
commit
9f898aa1a8
@@ -15,7 +15,6 @@ local reviewer = require("gitlab.reviewer")
|
||||
local Location = require("gitlab.reviewer.location")
|
||||
|
||||
local M = {
|
||||
current_win = nil,
|
||||
start_line = nil,
|
||||
end_line = nil,
|
||||
draft_popup = nil,
|
||||
@@ -25,10 +24,9 @@ local M = {
|
||||
---Fires the API that sends the comment data to the Go server, called when you "confirm" creation
|
||||
---via the M.settings.keymaps.popup.perform_action keybinding
|
||||
---@param text string comment text
|
||||
---@param visual_range LineRange | nil range of visual selection or nil
|
||||
---@param unlinked boolean if true, the comment is not linked to a line
|
||||
---@param discussion_id string | nil The ID of the discussion to which the reply is responding, nil if not a reply
|
||||
local confirm_create_comment = function(text, visual_range, unlinked, discussion_id)
|
||||
local confirm_create_comment = function(text, unlinked, discussion_id)
|
||||
if text == nil then
|
||||
u.notify("Reviewer did not provide text of change", vim.log.levels.ERROR)
|
||||
return
|
||||
@@ -75,30 +73,16 @@ local confirm_create_comment = function(text, visual_range, unlinked, discussion
|
||||
return
|
||||
end
|
||||
|
||||
local reviewer_data = reviewer.get_reviewer_data()
|
||||
if reviewer_data == nil then
|
||||
u.notify("Error getting reviewer data", vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
local location = Location.new(reviewer_data, visual_range)
|
||||
location:build_location_data()
|
||||
local location_data = location.location_data
|
||||
if location_data == nil then
|
||||
u.notify("Error getting location information", vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
local revision = state.MR_REVISIONS[1]
|
||||
local position_data = {
|
||||
file_name = reviewer_data.file_name,
|
||||
old_file_name = reviewer_data.old_file_name,
|
||||
file_name = M.location.reviewer_data.file_name,
|
||||
old_file_name = M.location.reviewer_data.old_file_name,
|
||||
base_commit_sha = revision.base_commit_sha,
|
||||
start_commit_sha = revision.start_commit_sha,
|
||||
head_commit_sha = revision.head_commit_sha,
|
||||
old_line = location_data.old_line,
|
||||
new_line = location_data.new_line,
|
||||
line_range = location_data.line_range,
|
||||
old_line = M.location.location_data.old_line,
|
||||
new_line = M.location.location_data.new_line,
|
||||
line_range = M.location.location_data.line_range,
|
||||
}
|
||||
|
||||
-- Creating a new comment (linked to specific changes)
|
||||
@@ -148,10 +132,10 @@ M.confirm_edit_comment = function(discussion_id, note_id, unlinked)
|
||||
end
|
||||
|
||||
---@class LayoutOpts
|
||||
---@field ranged boolean
|
||||
---@field unlinked boolean
|
||||
---@field discussion_id string|nil
|
||||
---@field reply boolean|nil
|
||||
---@field file_name string|nil
|
||||
|
||||
---This function sets up the layout and popups needed to create a comment, note and
|
||||
---multi-line comment. It also sets up the basic keybindings for switching between
|
||||
@@ -163,21 +147,24 @@ M.create_comment_layout = function(opts)
|
||||
local title
|
||||
local user_settings
|
||||
if opts.discussion_id ~= nil then
|
||||
title = "Reply"
|
||||
title = "Reply" .. (opts.file_name and string.format(" [%s]", opts.file_name) or "")
|
||||
user_settings = popup_settings.reply
|
||||
elseif opts.unlinked then
|
||||
title = "Note"
|
||||
user_settings = popup_settings.note
|
||||
else
|
||||
title = "Comment"
|
||||
-- TODO: investigate why `old_file_name` is in fact the new name for renamed files!
|
||||
local file_name = M.location.reviewer_data.old_file_name ~= "" and M.location.reviewer_data.old_file_name
|
||||
or M.location.reviewer_data.file_name
|
||||
title =
|
||||
popup.create_title("Comment", file_name, M.location.visual_range.start_line, M.location.visual_range.end_line)
|
||||
user_settings = popup_settings.comment
|
||||
end
|
||||
local settings = u.merge(popup_settings, user_settings or {})
|
||||
|
||||
M.current_win = vim.api.nvim_get_current_win()
|
||||
local current_win = vim.api.nvim_get_current_win()
|
||||
M.comment_popup = Popup(popup.create_popup_state(title, settings))
|
||||
M.draft_popup = Popup(popup.create_box_popup_state("Draft", false, settings))
|
||||
M.start_line, M.end_line = u.get_visual_selection_boundaries()
|
||||
|
||||
local internal_layout = Layout.Box({
|
||||
Layout.Box(M.comment_popup, { grow = 1 }),
|
||||
@@ -194,22 +181,21 @@ M.create_comment_layout = function(opts)
|
||||
}, internal_layout)
|
||||
|
||||
popup.set_cycle_popups_keymaps({ M.comment_popup, M.draft_popup })
|
||||
popup.set_up_autocommands(M.comment_popup, layout, M.current_win)
|
||||
popup.set_up_autocommands(M.comment_popup, layout, current_win)
|
||||
|
||||
local range = opts.ranged and { start_line = M.start_line, end_line = M.end_line } or nil
|
||||
local unlinked = opts.unlinked or false
|
||||
|
||||
---Keybinding for focus on draft section
|
||||
popup.set_popup_keymaps(M.draft_popup, function()
|
||||
local text = u.get_buffer_text(M.comment_popup.bufnr)
|
||||
confirm_create_comment(text, range, unlinked, opts.discussion_id)
|
||||
vim.api.nvim_set_current_win(M.current_win)
|
||||
confirm_create_comment(text, unlinked, opts.discussion_id)
|
||||
vim.api.nvim_set_current_win(current_win)
|
||||
end, miscellaneous.toggle_bool, popup.non_editable_popup_opts)
|
||||
|
||||
---Keybinding for focus on text section
|
||||
popup.set_popup_keymaps(M.comment_popup, function(text)
|
||||
confirm_create_comment(text, range, unlinked, opts.discussion_id)
|
||||
vim.api.nvim_set_current_win(M.current_win)
|
||||
confirm_create_comment(text, unlinked, opts.discussion_id)
|
||||
vim.api.nvim_set_current_win(current_win)
|
||||
end, miscellaneous.attach_file, popup.editable_popup_opts)
|
||||
|
||||
vim.schedule(function()
|
||||
@@ -223,44 +209,43 @@ end
|
||||
--- This function will open a comment popup in order to create a comment on the changed/updated
|
||||
--- line in the current MR
|
||||
M.create_comment = function()
|
||||
M.location = Location.new()
|
||||
if not M.can_create_comment(false) then
|
||||
return
|
||||
end
|
||||
|
||||
local layout = M.create_comment_layout({ ranged = false, unlinked = false })
|
||||
local layout = M.create_comment_layout({ unlinked = false })
|
||||
layout:mount()
|
||||
end
|
||||
|
||||
--- This function will open a multi-line comment popup in order to create a multi-line comment
|
||||
--- on the changed/updated line in the current MR
|
||||
M.create_multiline_comment = function()
|
||||
M.location = Location.new()
|
||||
if not M.can_create_comment(true) then
|
||||
u.press_escape()
|
||||
return
|
||||
end
|
||||
|
||||
local layout = M.create_comment_layout({ ranged = true, unlinked = false })
|
||||
local layout = M.create_comment_layout({ unlinked = false })
|
||||
layout:mount()
|
||||
end
|
||||
|
||||
--- This function will open a a popup to create a "note" (e.g. unlinked comment)
|
||||
--- on the changed/updated line in the current MR
|
||||
M.create_note = function()
|
||||
local layout = M.create_comment_layout({ ranged = false, unlinked = true })
|
||||
local layout = M.create_comment_layout({ unlinked = true })
|
||||
layout:mount()
|
||||
end
|
||||
|
||||
---Given the current visually selected area of text, builds text to fill in the
|
||||
---comment popup with a suggested change
|
||||
---@return LineRange|nil
|
||||
---@return integer
|
||||
local build_suggestion = function()
|
||||
local current_line = vim.api.nvim_win_get_cursor(0)[1]
|
||||
M.start_line, M.end_line = u.get_visual_selection_boundaries()
|
||||
|
||||
local range_length = M.end_line - M.start_line
|
||||
local range_length = M.location.visual_range.end_line - M.location.visual_range.start_line
|
||||
local backticks = "```"
|
||||
local selected_lines = u.get_lines(M.start_line, M.end_line)
|
||||
local selected_lines = u.get_lines(M.location.visual_range.start_line, M.location.visual_range.end_line)
|
||||
|
||||
for _, line in ipairs(selected_lines) do
|
||||
if string.match(line, "^```%S*$") then
|
||||
@@ -270,14 +255,14 @@ local build_suggestion = function()
|
||||
end
|
||||
|
||||
local suggestion_start
|
||||
if M.start_line == current_line then
|
||||
if M.location.visual_range.start_line == current_line then
|
||||
suggestion_start = backticks .. "suggestion:-0+" .. range_length
|
||||
elseif M.end_line == current_line then
|
||||
elseif M.location.visual_range.end_line == current_line then
|
||||
suggestion_start = backticks .. "suggestion:-" .. range_length .. "+0"
|
||||
else
|
||||
--- This should never happen afaik
|
||||
u.notify("Unexpected suggestion position", vim.log.levels.ERROR)
|
||||
return nil, 0
|
||||
return nil
|
||||
end
|
||||
suggestion_start = suggestion_start
|
||||
local suggestion_lines = {}
|
||||
@@ -285,21 +270,22 @@ local build_suggestion = function()
|
||||
vim.list_extend(suggestion_lines, selected_lines)
|
||||
table.insert(suggestion_lines, backticks)
|
||||
|
||||
return suggestion_lines, range_length
|
||||
return suggestion_lines
|
||||
end
|
||||
|
||||
--- This function will open a a popup to create a suggestion comment
|
||||
--- on the changed/updated line in the current MR
|
||||
--- See: https://docs.gitlab.com/ee/user/project/merge_requests/reviews/suggestions.html
|
||||
M.create_comment_suggestion = function()
|
||||
M.location = Location.new()
|
||||
if not M.can_create_comment(true) then
|
||||
u.press_escape()
|
||||
return
|
||||
end
|
||||
|
||||
local suggestion_lines, range_length = build_suggestion()
|
||||
local suggestion_lines = build_suggestion()
|
||||
|
||||
local layout = M.create_comment_layout({ ranged = range_length > 0, unlinked = false })
|
||||
local layout = M.create_comment_layout({ unlinked = false })
|
||||
layout:mount()
|
||||
|
||||
vim.schedule(function()
|
||||
@@ -368,6 +354,11 @@ M.can_create_comment = function(must_be_visual)
|
||||
return false
|
||||
end
|
||||
|
||||
if M.location == nil or M.location.location_data == nil then
|
||||
u.notify("Error getting location information", vim.log.levels.ERROR)
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -265,7 +265,7 @@ M.get_line_number_from_node = function(root_node)
|
||||
end
|
||||
|
||||
-- This function (settings.keymaps.discussion_tree.jump_to_reviewer) will jump the cursor to the reviewer's location associated with the note. The implementation depends on the reviewer
|
||||
M.jump_to_reviewer = function(tree, callback)
|
||||
M.jump_to_reviewer = function(tree)
|
||||
local node = tree:get_node()
|
||||
local root_node = M.get_root_node(tree, node)
|
||||
if root_node == nil then
|
||||
@@ -277,8 +277,7 @@ M.jump_to_reviewer = function(tree, callback)
|
||||
u.notify("Could not get line number", vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
reviewer.jump(root_node.file_name, line_number, is_new_sha)
|
||||
callback()
|
||||
reviewer.jump(root_node.file_name, root_node.old_file_name, line_number, is_new_sha)
|
||||
end
|
||||
|
||||
-- This function (settings.keymaps.discussion_tree.jump_to_file) will jump to the file changed in a new tab
|
||||
@@ -302,7 +301,7 @@ M.jump_to_file = function(tree)
|
||||
end
|
||||
vim.cmd.tabnew()
|
||||
local line_number = get_new_line(root_node) or get_old_line(root_node)
|
||||
if line_number == nil then
|
||||
if line_number == nil or line_number == 0 then
|
||||
line_number = 1
|
||||
end
|
||||
local bufnr = vim.fn.bufnr(root_node.file_name)
|
||||
|
||||
@@ -215,7 +215,7 @@ M.open_confirmation_popup = function(mr)
|
||||
return
|
||||
end
|
||||
|
||||
local layout, title_popup, description_popup, target_popup, delete_branch_popup, squash_popup, forked_project_id_popup =
|
||||
local layout, title_popup, description_popup, target_popup, source_popup, delete_branch_popup, squash_popup, forked_project_id_popup =
|
||||
M.create_layout()
|
||||
|
||||
local popups = {
|
||||
@@ -224,6 +224,7 @@ M.open_confirmation_popup = function(mr)
|
||||
delete_branch_popup,
|
||||
squash_popup,
|
||||
target_popup,
|
||||
source_popup,
|
||||
}
|
||||
|
||||
if state.settings.create_mr.fork.enabled then
|
||||
@@ -261,6 +262,7 @@ M.open_confirmation_popup = function(mr)
|
||||
vim.api.nvim_buf_set_lines(M.description_bufnr, 0, -1, false, description_lines)
|
||||
vim.api.nvim_buf_set_lines(M.title_bufnr, 0, -1, false, { mr.title })
|
||||
vim.api.nvim_buf_set_lines(M.target_bufnr, 0, -1, false, { mr.target })
|
||||
vim.api.nvim_buf_set_lines(M.source_bufnr, 0, -1, false, { require("gitlab.git").get_current_branch() })
|
||||
vim.api.nvim_buf_set_lines(M.delete_branch_bufnr, 0, -1, false, { u.bool_to_string(delete_branch) })
|
||||
vim.api.nvim_buf_set_lines(M.squash_bufnr, 0, -1, false, { u.bool_to_string(squash) })
|
||||
if state.settings.create_mr.fork.enabled then
|
||||
@@ -281,6 +283,7 @@ M.open_confirmation_popup = function(mr)
|
||||
popup.set_popup_keymaps(description_popup, M.create_mr, miscellaneous.attach_file, popup_opts)
|
||||
popup.set_popup_keymaps(title_popup, M.create_mr, nil, popup_opts)
|
||||
popup.set_popup_keymaps(target_popup, M.create_mr, M.select_new_target, popup_opts)
|
||||
popup.set_popup_keymaps(source_popup, M.create_mr, nil, popup_opts)
|
||||
popup.set_popup_keymaps(delete_branch_popup, M.create_mr, miscellaneous.toggle_bool, popup_opts)
|
||||
popup.set_popup_keymaps(squash_popup, M.create_mr, miscellaneous.toggle_bool, popup_opts)
|
||||
popup.set_popup_keymaps(forked_project_id_popup, M.create_mr, nil, popup_opts)
|
||||
@@ -336,6 +339,8 @@ M.create_layout = function()
|
||||
M.description_bufnr = description_popup.bufnr
|
||||
local target_branch_popup = Popup(popup.create_box_popup_state("Target branch", false, settings))
|
||||
M.target_bufnr = target_branch_popup.bufnr
|
||||
local source_branch_popup = Popup(popup.create_box_popup_state("Source branch", false, settings))
|
||||
M.source_bufnr = source_branch_popup.bufnr
|
||||
local delete_title = vim.o.columns > 110 and "Delete source branch" or "Delete source"
|
||||
local delete_branch_popup = Popup(popup.create_box_popup_state(delete_title, false, settings))
|
||||
M.delete_branch_bufnr = delete_branch_popup.bufnr
|
||||
@@ -352,6 +357,7 @@ M.create_layout = function()
|
||||
table.insert(boxes, Layout.Box(delete_branch_popup, { size = { width = #delete_title + 4 } }))
|
||||
table.insert(boxes, Layout.Box(squash_popup, { size = { width = #squash_title + 4 } }))
|
||||
table.insert(boxes, Layout.Box(target_branch_popup, { grow = 1 }))
|
||||
table.insert(boxes, Layout.Box(source_branch_popup, { grow = 1 }))
|
||||
|
||||
local internal_layout = Layout.Box({
|
||||
Layout.Box({
|
||||
@@ -378,6 +384,7 @@ M.create_layout = function()
|
||||
title_popup,
|
||||
description_popup,
|
||||
target_branch_popup,
|
||||
source_branch_popup,
|
||||
delete_branch_popup,
|
||||
squash_popup,
|
||||
forked_project_id_popup
|
||||
|
||||
@@ -15,7 +15,6 @@ local List = require("gitlab.utils.list")
|
||||
local tree_utils = require("gitlab.actions.discussions.tree")
|
||||
local discussions_tree = require("gitlab.actions.discussions.tree")
|
||||
local draft_notes = require("gitlab.actions.draft_notes")
|
||||
local diffview_lib = require("diffview.lib")
|
||||
local signs = require("gitlab.indicators.signs")
|
||||
local diagnostics = require("gitlab.indicators.diagnostics")
|
||||
local winbar = require("gitlab.actions.discussions.winbar")
|
||||
@@ -74,37 +73,25 @@ end
|
||||
M.initialize_discussions = function()
|
||||
state.discussion_tree.last_updated = os.time()
|
||||
signs.setup_signs()
|
||||
reviewer.set_callback_for_file_changed(function()
|
||||
M.refresh_diagnostics()
|
||||
M.modifiable(false)
|
||||
reviewer.set_reviewer_keymaps()
|
||||
reviewer.set_callback_for_file_changed(function(args)
|
||||
diagnostics.place_diagnostics(args.buf)
|
||||
reviewer.update_winid_for_buffer(args.buf)
|
||||
end)
|
||||
reviewer.set_callback_for_reviewer_enter(function()
|
||||
M.modifiable(false)
|
||||
M.refresh_diagnostics()
|
||||
end)
|
||||
reviewer.set_callback_for_buf_read(function(args)
|
||||
vim.api.nvim_buf_set_option(args.buf, "modifiable", false)
|
||||
reviewer.set_keymaps(args.buf)
|
||||
reviewer.set_reviewer_autocommands(args.buf)
|
||||
end)
|
||||
reviewer.set_callback_for_reviewer_leave(function()
|
||||
signs.clear_signs()
|
||||
diagnostics.clear_diagnostics()
|
||||
M.modifiable(true)
|
||||
reviewer.del_reviewer_keymaps()
|
||||
end)
|
||||
end
|
||||
|
||||
--- Ensures that the both buffers in the reviewer are/not modifiable. Relevant if the user is using
|
||||
--- the --imply-local setting
|
||||
M.modifiable = function(bool)
|
||||
local view = diffview_lib.get_current_view()
|
||||
local a = view.cur_layout.a.file.bufnr
|
||||
local b = view.cur_layout.b.file.bufnr
|
||||
if a ~= nil and vim.api.nvim_buf_is_loaded(a) then
|
||||
vim.api.nvim_buf_set_option(a, "modifiable", bool)
|
||||
end
|
||||
if b ~= nil and vim.api.nvim_buf_is_loaded(b) then
|
||||
vim.api.nvim_buf_set_option(b, "modifiable", bool)
|
||||
end
|
||||
end
|
||||
|
||||
--- Take existing data and refresh the diagnostics, the winbar, and the signs
|
||||
--- Take existing data and refresh the diagnostics and the signs
|
||||
M.refresh_diagnostics = function()
|
||||
if state.settings.discussion_signs.enabled then
|
||||
diagnostics.refresh_diagnostics()
|
||||
@@ -115,7 +102,9 @@ end
|
||||
---Opens the discussion tree, sets the keybindings. It also
|
||||
---creates the tree for notes (which are not linked to specific lines of code)
|
||||
---@param callback function?
|
||||
M.open = function(callback)
|
||||
---@param view_type "discussions"|"notes" Defines the view type to select (useful for overriding the default view type when jumping to discussion tree when it's closed).
|
||||
M.open = function(callback, view_type)
|
||||
view_type = view_type and view_type or state.settings.discussion_tree.default_view
|
||||
state.DISCUSSION_DATA.discussions = u.ensure_table(state.DISCUSSION_DATA.discussions)
|
||||
state.DISCUSSION_DATA.unlinked_discussions = u.ensure_table(state.DISCUSSION_DATA.unlinked_discussions)
|
||||
state.DRAFT_NOTES = u.ensure_table(state.DRAFT_NOTES)
|
||||
@@ -136,7 +125,7 @@ M.open = function(callback)
|
||||
|
||||
-- Initialize winbar module with data from buffers
|
||||
winbar.set_buffers(M.linked_bufnr, M.unlinked_bufnr)
|
||||
winbar.switch_view_type(state.settings.discussion_tree.default_view)
|
||||
winbar.switch_view_type(view_type)
|
||||
|
||||
local current_window = vim.api.nvim_get_current_win() -- Save user's current window in case they switched while content was loading
|
||||
vim.api.nvim_set_current_win(M.split.winid)
|
||||
@@ -146,7 +135,7 @@ M.open = function(callback)
|
||||
M.rebuild_unlinked_discussion_tree()
|
||||
|
||||
-- Set default buffer
|
||||
local default_buffer = winbar.bufnr_map[state.settings.discussion_tree.default_view]
|
||||
local default_buffer = winbar.bufnr_map[view_type]
|
||||
vim.api.nvim_set_current_buf(default_buffer)
|
||||
common.switch_can_edit_bufs(false, M.linked_bufnr, M.unlinked_bufnr)
|
||||
|
||||
@@ -192,12 +181,13 @@ M.move_to_discussion_tree = function()
|
||||
discussion_node:expand()
|
||||
end
|
||||
M.discussion_tree:render()
|
||||
vim.api.nvim_win_set_cursor(M.split.winid, { line_number, 0 })
|
||||
vim.api.nvim_set_current_win(M.split.winid)
|
||||
winbar.switch_view_type("discussions")
|
||||
vim.api.nvim_win_set_cursor(M.split.winid, { line_number, 0 })
|
||||
end
|
||||
|
||||
if not M.split_visible then
|
||||
M.toggle(jump_after_tree_opened)
|
||||
M.open(jump_after_tree_opened, "discussions")
|
||||
else
|
||||
jump_after_tree_opened()
|
||||
end
|
||||
@@ -247,10 +237,10 @@ M.reply = function(tree)
|
||||
local comment = require("gitlab.actions.comment")
|
||||
local unlinked = tree.bufnr == M.unlinked_bufnr
|
||||
local layout = comment.create_comment_layout({
|
||||
ranged = false,
|
||||
discussion_id = discussion_id,
|
||||
unlinked = unlinked,
|
||||
reply = true,
|
||||
file_name = discussion_node.file_name,
|
||||
})
|
||||
|
||||
layout:mount()
|
||||
@@ -284,7 +274,6 @@ end
|
||||
|
||||
-- This function (settings.keymaps.discussion_tree.edit_comment) will open the edit popup for the current comment in the discussion tree
|
||||
M.edit_comment = function(tree, unlinked)
|
||||
local edit_popup = Popup(popup.create_popup_state("Edit Comment", state.settings.popup.edit))
|
||||
local current_node = tree:get_node()
|
||||
local note_node = common.get_note_node(tree, current_node)
|
||||
local root_node = common.get_root_node(tree, current_node)
|
||||
@@ -292,6 +281,9 @@ M.edit_comment = function(tree, unlinked)
|
||||
u.notify("Could not get root or note node", vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
local title = "Edit Comment"
|
||||
title = root_node.file_name ~= nil and string.format("%s [%s]", title, root_node.file_name) or title
|
||||
local edit_popup = Popup(popup.create_popup_state(title, state.settings.popup.edit))
|
||||
|
||||
popup.set_up_autocommands(edit_popup, nil, vim.api.nvim_get_current_win())
|
||||
|
||||
@@ -587,7 +579,7 @@ M.set_tree_keymaps = function(tree, bufnr, unlinked)
|
||||
if keymaps.discussion_tree.jump_to_reviewer then
|
||||
vim.keymap.set("n", keymaps.discussion_tree.jump_to_reviewer, function()
|
||||
if M.is_current_node_note(tree) then
|
||||
common.jump_to_reviewer(tree, M.refresh_diagnostics)
|
||||
common.jump_to_reviewer(tree)
|
||||
end
|
||||
end, { buffer = bufnr, desc = "Jump to reviewer", nowait = keymaps.discussion_tree.jump_to_reviewer_nowait })
|
||||
end
|
||||
@@ -757,6 +749,16 @@ M.set_tree_keymaps = function(tree, bufnr, unlinked)
|
||||
})
|
||||
end
|
||||
|
||||
if keymaps.discussion_tree.print_node then
|
||||
vim.keymap.set("n", keymaps.discussion_tree.print_node, function()
|
||||
common.print_node(tree)
|
||||
end, {
|
||||
buffer = bufnr,
|
||||
desc = "Print current node (for debugging)",
|
||||
nowait = keymaps.discussion_tree.print_node_nowait,
|
||||
})
|
||||
end
|
||||
|
||||
if keymaps.discussion_tree.add_emoji then
|
||||
vim.keymap.set("n", keymaps.discussion_tree.add_emoji, function()
|
||||
M.add_emoji_to_note(tree, unlinked)
|
||||
|
||||
@@ -28,6 +28,8 @@ M.add_discussions_to_table = function(items, unlinked)
|
||||
local root_note_id = ""
|
||||
---@type string?
|
||||
local root_file_name = ""
|
||||
---@type string?
|
||||
local root_old_file_name = ""
|
||||
---@type string
|
||||
local root_id
|
||||
local root_text_nodes = {}
|
||||
@@ -43,6 +45,7 @@ M.add_discussions_to_table = function(items, unlinked)
|
||||
if j == 1 then
|
||||
_, root_text, root_text_nodes = M.build_note(note, { resolved = note.resolved, resolvable = note.resolvable })
|
||||
root_file_name = (type(note.position) == "table" and note.position.new_path or nil)
|
||||
root_old_file_name = (type(note.position) == "table" and note.position.old_path or nil)
|
||||
root_new_line = (type(note.position) == "table" and note.position.new_line or nil)
|
||||
root_old_line = (type(note.position) == "table" and note.position.old_line or nil)
|
||||
root_id = discussion.id
|
||||
@@ -79,6 +82,7 @@ M.add_discussions_to_table = function(items, unlinked)
|
||||
id = root_id,
|
||||
root_note_id = root_note_id,
|
||||
file_name = root_file_name,
|
||||
old_file_name = root_old_file_name,
|
||||
new_line = root_new_line,
|
||||
old_line = root_old_line,
|
||||
resolvable = resolvable,
|
||||
|
||||
@@ -254,9 +254,8 @@ M.get_mode = function()
|
||||
end
|
||||
end
|
||||
|
||||
---Sets the current view type (if provided an argument)
|
||||
---and then updates the view
|
||||
---@param override any
|
||||
---Toggles the current view type (or sets it to `override`) and then updates the view.
|
||||
---@param override "discussions"|"notes" Defines the view type to select.
|
||||
M.switch_view_type = function(override)
|
||||
if override then
|
||||
M.current_view_type = override
|
||||
|
||||
@@ -7,41 +7,77 @@ local job = require("gitlab.job")
|
||||
local u = require("gitlab.utils")
|
||||
local popup = require("gitlab.popup")
|
||||
local M = {
|
||||
pipeline_jobs = nil,
|
||||
pipeline_jobs = {},
|
||||
latest_pipeline = nil,
|
||||
pipeline_popup = nil,
|
||||
}
|
||||
|
||||
local function get_latest_pipeline()
|
||||
local pipeline = state.PIPELINE and state.PIPELINE.latest_pipeline
|
||||
if type(pipeline) ~= "table" or (type(pipeline) == "table" and u.table_size(pipeline) == 0) then
|
||||
u.notify("Pipeline not found", vim.log.levels.WARN)
|
||||
return
|
||||
local function get_latest_pipelines(count)
|
||||
count = count or 1 -- Default to 1 if count is not provided
|
||||
local pipelines = {}
|
||||
|
||||
if not state.PIPELINE then
|
||||
u.notify("Pipeline state is not initialized", vim.log.levels.WARN)
|
||||
return nil
|
||||
end
|
||||
return pipeline
|
||||
|
||||
for i = 1, math.max(count, #state.PIPELINE) do
|
||||
local pipeline = state.PIPELINE[i].latest_pipeline
|
||||
if type(pipeline) == "table" and u.table_size(pipeline) > 0 then
|
||||
table.insert(pipelines, pipeline)
|
||||
end
|
||||
end
|
||||
|
||||
if #pipelines == 0 then
|
||||
u.notify("No valid pipelines found", vim.log.levels.WARN)
|
||||
return nil
|
||||
end
|
||||
return pipelines
|
||||
end
|
||||
|
||||
local function get_pipeline_jobs()
|
||||
M.latest_pipeline = get_latest_pipeline()
|
||||
if not M.latest_pipeline then
|
||||
return
|
||||
end
|
||||
return u.reverse(type(state.PIPELINE.jobs) == "table" and state.PIPELINE.jobs or {})
|
||||
local function get_pipeline_jobs(idx)
|
||||
return u.reverse(type(state.PIPELINE[idx].jobs) == "table" and state.PIPELINE[idx].jobs or {})
|
||||
end
|
||||
|
||||
-- The function will render the Pipeline state in a popup
|
||||
M.open = function()
|
||||
M.pipeline_jobs = get_pipeline_jobs()
|
||||
M.latest_pipeline = get_latest_pipeline()
|
||||
if M.latest_pipeline == nil then
|
||||
M.latest_pipelines = get_latest_pipelines()
|
||||
if not M.latest_pipelines then
|
||||
return
|
||||
end
|
||||
if not M.latest_pipelines or #M.latest_pipelines == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local width = string.len(M.latest_pipeline.web_url) + 10
|
||||
local height = 6 + #M.pipeline_jobs + 3
|
||||
local max_width = 0
|
||||
local total_height = 0
|
||||
local pipelines_data = {}
|
||||
|
||||
for idx, pipeline in ipairs(M.latest_pipelines) do
|
||||
local width = string.len(pipeline.web_url) + 10
|
||||
max_width = math.max(max_width, width)
|
||||
|
||||
local pipeline_jobs = get_pipeline_jobs(idx)
|
||||
for _, j in ipairs(pipeline_jobs) do
|
||||
table.insert(M.pipeline_jobs, j)
|
||||
end
|
||||
|
||||
local pipeline_status = M.get_pipeline_status(idx, false)
|
||||
local height = 6 + #pipeline_jobs + 3
|
||||
total_height = total_height + height
|
||||
|
||||
table.insert(pipelines_data, {
|
||||
pipeline = pipeline,
|
||||
pipeline_status = pipeline_status,
|
||||
jobs = pipeline_jobs,
|
||||
width = width,
|
||||
height = 6 + #pipeline_jobs + 3,
|
||||
lines = {},
|
||||
})
|
||||
end
|
||||
|
||||
local pipeline_popup =
|
||||
Popup(popup.create_popup_state("Loading Pipeline...", state.settings.popup.pipeline, width, height, 60))
|
||||
Popup(popup.create_popup_state("Loading Pipelines...", state.settings.popup.pipeline, max_width, total_height, 60))
|
||||
popup.set_up_autocommands(pipeline_popup, nil, vim.api.nvim_get_current_win())
|
||||
M.pipeline_popup = pipeline_popup
|
||||
pipeline_popup:mount()
|
||||
@@ -49,68 +85,103 @@ M.open = function()
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
vim.opt_local.wrap = false
|
||||
|
||||
local lines = {}
|
||||
|
||||
u.switch_can_edit_buf(bufnr, true)
|
||||
table.insert(lines, "Status: " .. M.get_pipeline_status(false))
|
||||
table.insert(lines, "")
|
||||
table.insert(lines, string.format("Last Run: %s", u.time_since(M.latest_pipeline.created_at)))
|
||||
table.insert(lines, string.format("Url: %s", M.latest_pipeline.web_url))
|
||||
table.insert(lines, string.format("Triggered By: %s", M.latest_pipeline.source))
|
||||
|
||||
table.insert(lines, "")
|
||||
table.insert(lines, "Jobs:")
|
||||
local all_lines = {}
|
||||
for i, data in ipairs(pipelines_data) do
|
||||
local pipeline = data.pipeline
|
||||
local lines = data.lines
|
||||
|
||||
local longest_title = u.get_longest_string(u.map(M.pipeline_jobs, function(v)
|
||||
return v.name
|
||||
end))
|
||||
table.insert(lines, data.pipeline_status)
|
||||
table.insert(lines, "")
|
||||
table.insert(lines, string.format("Last Run: %s", u.time_since(pipeline.created_at)))
|
||||
table.insert(lines, string.format("Url: %s", pipeline.web_url))
|
||||
table.insert(lines, string.format("Triggered By: %s", pipeline.source))
|
||||
table.insert(lines, "")
|
||||
table.insert(lines, "Jobs:")
|
||||
|
||||
local function row_offset(name)
|
||||
local offset = longest_title - string.len(name)
|
||||
local res = string.rep(" ", offset + 5)
|
||||
return res
|
||||
end
|
||||
local longest_title = u.get_longest_string(u.map(data.jobs, function(v)
|
||||
return v.name
|
||||
end))
|
||||
|
||||
for _, pipeline_job in ipairs(M.pipeline_jobs) do
|
||||
local offset = row_offset(pipeline_job.name)
|
||||
local row = string.format(
|
||||
"%s%s %s (%s)",
|
||||
pipeline_job.name,
|
||||
offset,
|
||||
state.settings.pipeline[pipeline_job.status] or "*",
|
||||
pipeline_job.status or ""
|
||||
)
|
||||
local function row_offset(name)
|
||||
local offset = longest_title - string.len(name)
|
||||
local res = string.rep(" ", offset + 5)
|
||||
return res
|
||||
end
|
||||
|
||||
table.insert(lines, row)
|
||||
for _, pipeline_job in ipairs(data.jobs) do
|
||||
local offset = row_offset(pipeline_job.name)
|
||||
local row = string.format(
|
||||
"%s%s %s (%s)",
|
||||
pipeline_job.name,
|
||||
offset,
|
||||
state.settings.pipeline[pipeline_job.status] or "*",
|
||||
pipeline_job.status or ""
|
||||
)
|
||||
table.insert(lines, row)
|
||||
end
|
||||
|
||||
-- Add separator between pipelines
|
||||
if i < #pipelines_data then
|
||||
table.insert(lines, "")
|
||||
table.insert(lines, string.rep("-", max_width))
|
||||
table.insert(lines, "")
|
||||
end
|
||||
|
||||
for _, line in ipairs(lines) do
|
||||
table.insert(all_lines, line)
|
||||
end
|
||||
end
|
||||
|
||||
vim.schedule(function()
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
|
||||
M.color_status(M.latest_pipeline.status, bufnr, lines[1], 1)
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, all_lines)
|
||||
|
||||
for i, pipeline_job in ipairs(M.pipeline_jobs) do
|
||||
M.color_status(pipeline_job.status, bufnr, lines[7 + i], 7 + i)
|
||||
local line_offset = 0
|
||||
for _, data in ipairs(pipelines_data) do
|
||||
local pipeline = data.pipeline
|
||||
local lines = data.lines
|
||||
|
||||
M.color_status(pipeline.status, bufnr, all_lines[line_offset + 1], line_offset + 1)
|
||||
|
||||
for j, pipeline_job in ipairs(data.jobs) do
|
||||
M.color_status(pipeline_job.status, bufnr, all_lines[line_offset + 7 + j], line_offset + 7 + j)
|
||||
end
|
||||
|
||||
line_offset = line_offset + #lines
|
||||
end
|
||||
|
||||
pipeline_popup.border:set_text("top", "Pipeline Status", "center")
|
||||
pipeline_popup.border:set_text("top", "Pipelines Status", "center")
|
||||
popup.set_popup_keymaps(pipeline_popup, M.retrigger, M.see_logs)
|
||||
u.switch_can_edit_buf(bufnr, false)
|
||||
end)
|
||||
end
|
||||
|
||||
M.retrigger = function()
|
||||
M.latest_pipeline = get_latest_pipeline()
|
||||
if not M.latest_pipeline then
|
||||
return
|
||||
end
|
||||
if M.latest_pipeline.status ~= "failed" then
|
||||
u.notify("Pipeline is not in a failed state!", vim.log.levels.WARN)
|
||||
local pipelines = get_latest_pipelines()
|
||||
if not pipelines then
|
||||
return
|
||||
end
|
||||
|
||||
job.run_job("/pipeline/" .. M.latest_pipeline.id, "POST", nil, function()
|
||||
u.notify("Pipeline re-triggered!", vim.log.levels.INFO)
|
||||
end)
|
||||
local failed_pipelines = {}
|
||||
|
||||
for idx, pipeline in ipairs(pipelines) do
|
||||
local pipeline_jobs = get_pipeline_jobs(idx)
|
||||
for _, pjob in ipairs(pipeline_jobs) do
|
||||
if pjob.status == "failed" then
|
||||
if pipeline.status ~= "failed" then
|
||||
u.notify("Pipeline is not in a failed state!", vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
if not failed_pipelines[pipeline.id] then
|
||||
job.run_job("/pipeline/trigger/" .. pipeline.id, "POST", nil, function()
|
||||
u.notify("Pipeline " .. pipeline.id .. " re-triggered!", vim.log.levels.INFO)
|
||||
end)
|
||||
failed_pipelines[pipeline.id] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
M.see_logs = function()
|
||||
@@ -173,12 +244,8 @@ end
|
||||
---colorize the pipeline icon.
|
||||
---@param wrap_with_color boolean
|
||||
---@return string
|
||||
M.get_pipeline_icon = function(wrap_with_color)
|
||||
M.latest_pipeline = get_latest_pipeline()
|
||||
if not M.latest_pipeline then
|
||||
return ""
|
||||
end
|
||||
local symbol = state.settings.pipeline[M.latest_pipeline.status]
|
||||
M.get_pipeline_icon = function(idx, wrap_with_color)
|
||||
local symbol = state.settings.pipeline[state.PIPELINE[idx].latest_pipeline.status]
|
||||
if not wrap_with_color then
|
||||
return symbol
|
||||
end
|
||||
@@ -196,12 +263,13 @@ end
|
||||
---colorize the pipeline icon.
|
||||
---@param wrap_with_color boolean
|
||||
---@return string
|
||||
M.get_pipeline_status = function(wrap_with_color)
|
||||
M.latest_pipeline = get_latest_pipeline()
|
||||
if not M.latest_pipeline then
|
||||
return ""
|
||||
end
|
||||
return string.format("%s (%s)", M.get_pipeline_icon(wrap_with_color), M.latest_pipeline.status)
|
||||
M.get_pipeline_status = function(idx, wrap_with_color)
|
||||
return string.format(
|
||||
"[%s]: Status: %s (%s)",
|
||||
state.PIPELINE[idx].name,
|
||||
M.get_pipeline_icon(idx, wrap_with_color),
|
||||
state.PIPELINE[idx].latest_pipeline.status
|
||||
)
|
||||
end
|
||||
|
||||
M.color_status = function(status, bufnr, status_line, linnr)
|
||||
|
||||
@@ -139,6 +139,7 @@ M.build_info_lines = function()
|
||||
return pipeline.status
|
||||
end,
|
||||
},
|
||||
web_url = { title = "MR URL", content = info.web_url },
|
||||
}
|
||||
|
||||
local longest_used = ""
|
||||
|
||||
Reference in New Issue
Block a user