local u = require("gitlab.utils") local List = require("gitlab.utils.list") local state = require("gitlab.state") local M = { bufnr_map = { discussions = nil, notes = nil, }, current_view_type = state.settings.discussion_tree.default_view, } M.set_buffers = function(linked_bufnr, unlinked_bufnr) M.bufnr_map = { discussions = linked_bufnr, notes = unlinked_bufnr, } end ---@param nodes Discussion[]|UnlinkedDiscussion[]|nil ---@return number, number, number local get_data = function(nodes) local total_resolvable = 0 local total_resolved = 0 local total_non_resolvable = 0 if nodes == nil or nodes == vim.NIL then return total_resolvable, total_resolved, total_non_resolvable end total_resolvable = List.new(nodes):reduce(function(agg, d) local first_child = d.notes[1] if first_child and first_child.resolvable then agg = agg + 1 end return agg end, 0) total_non_resolvable = List.new(nodes):reduce(function(agg, d) local first_child = d.notes[1] if first_child and not first_child.resolvable then agg = agg + 1 end return agg end, 0) total_resolved = List.new(nodes):reduce(function(agg, d) local first_child = d.notes[1] if first_child and first_child.resolved then agg = agg + 1 end return agg end, 0) return total_resolvable, total_resolved, total_non_resolvable end local spinner_index = 0 state.discussion_tree.last_updated = nil local function content() local updated if state.discussion_tree.last_updated then local last_update = tostring(os.date("!%Y-%m-%dT%H:%M:%S", state.discussion_tree.last_updated)) updated = u.time_since(last_update) .. " ⟳" else spinner_index = (spinner_index % #state.settings.discussion_tree.spinner_chars) + 1 updated = state.settings.discussion_tree.spinner_chars[spinner_index] end local resolvable_discussions, resolved_discussions, non_resolvable_discussions = get_data(state.DISCUSSION_DATA.discussions) local resolvable_notes, resolved_notes, non_resolvable_notes = get_data(state.DISCUSSION_DATA.unlinked_discussions) local draft_notes = require("gitlab.actions.draft_notes") local inline_draft_notes, unlinked_draft_notes = List.new(state.DRAFT_NOTES):partition(function(note) if note.discussion_id == "" then return draft_notes.has_position(note) end for _, discussion in ipairs(state.DISCUSSION_DATA.unlinked_discussions) do if discussion.id == note.discussion_id then return false end end return true end) local t = { resolvable_discussions = resolvable_discussions, resolved_discussions = resolved_discussions, non_resolvable_discussions = non_resolvable_discussions, inline_draft_notes = #inline_draft_notes, unlinked_draft_notes = #unlinked_draft_notes, resolvable_notes = resolvable_notes, resolved_notes = resolved_notes, non_resolvable_notes = non_resolvable_notes, help_keymap = state.settings.keymaps.help, updated = updated, } return state.settings.discussion_tree.winbar and state.settings.discussion_tree.winbar(t) or M.make_winbar(t) end ---This function updates the winbar M.update_winbar = function() local d = require("gitlab.actions.discussions") if d.split == nil then return end local win_id = d.split.winid if win_id == nil then return end if not vim.api.nvim_win_is_valid(win_id) then return end local c = content() vim.api.nvim_set_option_value("winbar", c, { scope = "local", win = win_id }) end local function get_connector(base_title) return string.match(base_title, "%($") and "" or " " end ---Builds the title string for both sections, using the count of resolvable and draft nodes ---@param base_title string ---@param resolvable_count integer ---@param resolved_count integer ---@param drafts_count integer ---@param focused boolean ---@return string local add_drafts_and_resolvable = function( base_title, resolvable_count, resolved_count, drafts_count, non_resolvable_count, focused ) if resolvable_count == 0 and drafts_count == 0 and non_resolvable_count == 0 then return base_title end if resolvable_count ~= 0 then base_title = base_title .. M.get_resolved_text(focused, resolved_count, resolvable_count) end if non_resolvable_count ~= 0 then base_title = base_title .. M.get_nonresolveable_text(base_title, non_resolvable_count, focused) end if drafts_count ~= 0 then base_title = base_title .. M.get_drafts_text(base_title, drafts_count, focused) end return base_title end ---@param t WinbarTable M.make_winbar = function(t) local discussions_focused = M.current_view_type == "discussions" local discussion_text = add_drafts_and_resolvable( "Inline Comments:", t.resolvable_discussions, t.resolved_discussions, t.inline_draft_notes, t.non_resolvable_discussions, discussions_focused ) local notes_text = add_drafts_and_resolvable( "Notes:", t.resolvable_notes, t.resolved_notes, t.unlinked_draft_notes, t.non_resolvable_notes, not discussions_focused ) -- Colorize the active tab if discussions_focused then discussion_text = "%#Text#" .. discussion_text notes_text = "%#Comment#" .. notes_text else discussion_text = "%#Comment#" .. discussion_text notes_text = "%#Text#" .. notes_text end local sort_method = M.get_sort_method() local mode = M.get_mode() -- Join everything together and return it local separator = "%#Comment#|" local end_section = "%=" local updated = "%#Text#" .. t.updated local help = "%#Comment#Help: " .. (t.help_keymap and t.help_keymap:gsub(" ", "") .. " " or "unmapped") return string.format( " %s %s %s %s %s %s %s %s %s %s %s", discussion_text, separator, notes_text, end_section, updated, separator, sort_method, separator, mode, separator, help ) end ---Returns a string for the winbar indicating the sort method ---@return string M.get_sort_method = function() local sort_method = state.settings.discussion_tree.sort_by == "original_comment" and "↓ by thread" or "↑ by reply" return "%#GitlabSortMethod#" .. sort_method .. "%#Comment#" end M.get_resolved_text = function(focused, resolved_count, resolvable_count) local text = focused and ("%#GitlabResolved#" .. state.settings.discussion_tree.resolved .. "%#Text#") or state.settings.discussion_tree.resolved return " " .. string.format("%d%s/%d", resolved_count, text, resolvable_count) end M.get_drafts_text = function(base_title, drafts_count, focused) return get_connector(base_title) .. string.format( "%d%s", drafts_count, ( focused and ("%#GitlabDraft#" .. state.settings.discussion_tree.draft .. "%#Text#") or state.settings.discussion_tree.draft ) ) end M.get_nonresolveable_text = function(base_title, non_resolvable_count, focused) return get_connector(base_title) .. string.format( "%d%s", non_resolvable_count, ( focused and ("%#GitlabUnlinked#" .. state.settings.discussion_tree.unlinked .. "%#Text#") or state.settings.discussion_tree.unlinked ) ) end ---Returns a string for the winbar indicating the mode type, live or draft ---@return string M.get_mode = function() if state.settings.discussion_tree.draft_mode then return "%#GitlabDraftMode#Draft" else return "%#GitlabLiveMode#Live" end end ---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 else if M.current_view_type == "discussions" then M.current_view_type = "notes" elseif M.current_view_type == "notes" then M.current_view_type = "discussions" end end vim.api.nvim_set_current_buf(M.bufnr_map[M.current_view_type]) M.update_winbar() end ---Set up a timer to update the winbar periodically M.start_timer = function() M.cleanup_timer() ---@type nil|uv_timer_t M.timer = vim.uv.new_timer() M.timer:start(0, 100, vim.schedule_wrap(M.update_winbar)) end --Stop and close the timer M.cleanup_timer = function() if M.timer ~= nil then M.timer:stop() M.timer:close() M.timer = nil end end return M