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:
Harrison (Harry) Cramer
2025-03-01 13:28:02 -05:00
committed by GitHub
parent 3b396a5e6b
commit 9f898aa1a8
23 changed files with 524 additions and 359 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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,

View File

@@ -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

View File

@@ -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)

View File

@@ -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 = ""