Winbar Support + Notes and Discussions; Help Popup + Auto-Open (#133)
- Adds support for toggling between discussions and notes views - Deprecates the split view shared with both discussions and notes at the same time - Adds winbar to discussion split, with metadata about resolved and unresolved discussions - Adds help popups with information about keybindings for all views - Modifies highlights in discussion tree and default symbol for unresolved discussions This is a MINOR version bump as the default behavior of the discussion tree is changed slightly. Existing configurations should still function.
This commit is contained in:
committed by
GitHub
parent
d5038d63ca
commit
d5510f9d9a
@@ -117,6 +117,7 @@ require("gitlab").setup({
|
|||||||
config_path = nil, -- Custom path for `.gitlab.nvim` file, please read the "Connecting to Gitlab" section
|
config_path = nil, -- Custom path for `.gitlab.nvim` file, please read the "Connecting to Gitlab" section
|
||||||
debug = { go_request = false, go_response = false }, -- Which values to log
|
debug = { go_request = false, go_response = false }, -- Which values to log
|
||||||
attachment_dir = nil, -- The local directory for files (see the "summary" section)
|
attachment_dir = nil, -- The local directory for files (see the "summary" section)
|
||||||
|
help = "?", -- Opens a help popup for local keymaps when a relevant view is focused (popup, discussion panel, etc)
|
||||||
popup = { -- The popup for comment creation, editing, and replying
|
popup = { -- The popup for comment creation, editing, and replying
|
||||||
exit = "<Esc>",
|
exit = "<Esc>",
|
||||||
perform_action = "<leader>s", -- Once in normal mode, does action (like saving comment or editing description, etc)
|
perform_action = "<leader>s", -- Once in normal mode, does action (like saving comment or editing description, etc)
|
||||||
@@ -132,6 +133,9 @@ require("gitlab").setup({
|
|||||||
reply = nil,
|
reply = nil,
|
||||||
},
|
},
|
||||||
discussion_tree = { -- The discussion tree that holds all comments
|
discussion_tree = { -- The discussion tree that holds all comments
|
||||||
|
auto_open = true, -- Automatically open when the reviewer is opened
|
||||||
|
switch_view = "T", -- Toggles between the notes and discussions views
|
||||||
|
default_view = "discussions" -- Show "discussions" or "notes" by default
|
||||||
blacklist = {}, -- List of usernames to remove from tree (bots, CI, etc)
|
blacklist = {}, -- List of usernames to remove from tree (bots, CI, etc)
|
||||||
jump_to_file = "o", -- Jump to comment location in file
|
jump_to_file = "o", -- Jump to comment location in file
|
||||||
jump_to_reviewer = "m", -- Jump to the location in the reviewer window
|
jump_to_reviewer = "m", -- Jump to the location in the reviewer window
|
||||||
@@ -146,6 +150,8 @@ 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
|
||||||
tree_type = "simple", -- Type of discussion tree - "simple" means just list of discussions, "by_file_name" means file tree with discussions under file
|
tree_type = "simple", -- Type of discussion tree - "simple" means just list of discussions, "by_file_name" means file tree with discussions under file
|
||||||
|
winbar = nil -- Custom function to return winbar title, should return a string. Provided with WinbarTable (defined in annotations.lua)
|
||||||
|
-- If using lualine, please add "gitlab" to disabled file types, otherwise you will not see the winbar.
|
||||||
},
|
},
|
||||||
info = { -- Show additional fields in the summary pane
|
info = { -- Show additional fields in the summary pane
|
||||||
enabled = true,
|
enabled = true,
|
||||||
|
|||||||
@@ -6,10 +6,14 @@ syntax match Username "@\S*"
|
|||||||
syntax match Date "\v\d+\s+\w+\s+ago"
|
syntax match Date "\v\d+\s+\w+\s+ago"
|
||||||
syntax match ChevronDown ""
|
syntax match ChevronDown ""
|
||||||
syntax match ChevronRight ""
|
syntax match ChevronRight ""
|
||||||
|
syntax match Resolved "✓$"
|
||||||
|
syntax match Unresolved "-$"
|
||||||
|
|
||||||
highlight link Username GitlabUsername
|
highlight link Username GitlabUsername
|
||||||
highlight link Date GitlabDate
|
highlight link Date GitlabDate
|
||||||
highlight link ChevronDown GitlabChevron
|
highlight link ChevronDown GitlabChevron
|
||||||
highlight link ChevronRight GitlabChevron
|
highlight link ChevronRight GitlabChevron
|
||||||
|
highlight link Resolved GitlabResolved
|
||||||
|
highlight link Unresolved GitlabUnresolved
|
||||||
|
|
||||||
let b:current_syntax = "gitlab"
|
let b:current_syntax = "gitlab"
|
||||||
|
|||||||
@@ -129,6 +129,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)
|
||||||
u.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 })
|
||||||
|
discussions.refresh_discussion_data()
|
||||||
end)
|
end)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -64,3 +64,10 @@
|
|||||||
---@class DiscussionData
|
---@class DiscussionData
|
||||||
---@field discussions Discussion[]
|
---@field discussions Discussion[]
|
||||||
---@field unlinked_discussions UnlinkedDiscussion[]
|
---@field unlinked_discussions UnlinkedDiscussion[]
|
||||||
|
|
||||||
|
---@class WinbarTable
|
||||||
|
---@field name string
|
||||||
|
---@field resolvable_discussions number
|
||||||
|
---@field resolved_discussions number
|
||||||
|
---@field resolvable_notes number
|
||||||
|
---@field resolved_notes number
|
||||||
|
|||||||
@@ -5,417 +5,146 @@ local Split = require("nui.split")
|
|||||||
local Popup = require("nui.popup")
|
local Popup = require("nui.popup")
|
||||||
local NuiTree = require("nui.tree")
|
local NuiTree = require("nui.tree")
|
||||||
local NuiLine = require("nui.line")
|
local NuiLine = require("nui.line")
|
||||||
local Layout = require("nui.layout")
|
|
||||||
local job = require("gitlab.job")
|
local job = require("gitlab.job")
|
||||||
local u = require("gitlab.utils")
|
local u = require("gitlab.utils")
|
||||||
local state = require("gitlab.state")
|
local state = require("gitlab.state")
|
||||||
local reviewer = require("gitlab.reviewer")
|
local reviewer = require("gitlab.reviewer")
|
||||||
local miscellaneous = require("gitlab.actions.miscellaneous")
|
local miscellaneous = require("gitlab.actions.miscellaneous")
|
||||||
local discussions_tree = require("gitlab.actions.discussions.tree")
|
local discussions_tree = require("gitlab.actions.discussions.tree")
|
||||||
|
local signs = require("gitlab.actions.discussions.signs")
|
||||||
local discussion_sign_name = "gitlab_discussion"
|
local winbar = require("gitlab.actions.discussions.winbar")
|
||||||
local discussion_helper_sign_start = "gitlab_discussion_helper_start"
|
local help = require("gitlab.actions.help")
|
||||||
local discussion_helper_sign_mid = "gitlab_discussion_helper_mid"
|
|
||||||
local discussion_helper_sign_end = "gitlab_discussion_helper_end"
|
|
||||||
local diagnostics_namespace = vim.api.nvim_create_namespace(discussion_sign_name)
|
|
||||||
|
|
||||||
local M = {
|
local M = {
|
||||||
layout_visible = false,
|
split_visible = false,
|
||||||
layout = nil,
|
split = nil,
|
||||||
layout_buf = nil,
|
---@type number
|
||||||
|
split_bufnr = nil,
|
||||||
---@type Discussion[]
|
---@type Discussion[]
|
||||||
discussions = {},
|
discussions = {},
|
||||||
---@type UnlinkedDiscussion[]
|
---@type UnlinkedDiscussion[]
|
||||||
unlinked_discussions = {},
|
unlinked_discussions = {},
|
||||||
linked_section = nil,
|
---@type number
|
||||||
unlinked_section = nil,
|
linked_bufnr = nil,
|
||||||
|
---@type number
|
||||||
|
unlinked_bufnr = nil,
|
||||||
|
---@type number
|
||||||
|
focused_bufnr = nil,
|
||||||
discussion_tree = nil,
|
discussion_tree = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
---Load the discussion data, storage them in M.discussions and M.unlinked_discussions and call
|
---Makes API call to get the discussion data, store it in M.discussions and M.unlinked_discussions and call
|
||||||
---callback with data
|
---callback with data
|
||||||
---@param callback (fun(data: DiscussionData): nil)?
|
---@param callback (fun(data: DiscussionData): nil)?
|
||||||
M.load_discussions = function(callback)
|
M.load_discussions = function(callback)
|
||||||
job.run_job("/discussions/list", "POST", { blacklist = state.settings.discussion_tree.blacklist }, function(data)
|
job.run_job("/discussions/list", "POST", { blacklist = state.settings.discussion_tree.blacklist }, function(data)
|
||||||
M.discussions = data.discussions
|
M.discussions = data.discussions ~= vim.NIL and data.discussions or {}
|
||||||
M.unlinked_discussions = data.unlinked_discussions
|
M.unlinked_discussions = data.unlinked_discussions ~= vim.NIL and data.unlinked_discussions or {}
|
||||||
if type(callback) == "function" then
|
if type(callback) == "function" then
|
||||||
callback(data)
|
callback(data)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Parse line code and return old and new line numbers
|
---Initialize everything for discussions like setup of signs, callbacks for reviewer, etc.
|
||||||
---@param line_code string gitlab line code -> 588440f66559714280628a4f9799f0c4eb880a4a_10_10
|
M.initialize_discussions = function()
|
||||||
---@return number?
|
signs.setup_signs()
|
||||||
---@return number?
|
-- Setup callback to refresh discussion data, discussion signs and diagnostics whenever the reviewed file changes.
|
||||||
local function _parse_line_code(line_code)
|
reviewer.set_callback_for_file_changed(M.refresh_discussion_data)
|
||||||
local line_code_regex = "%w+_(%d+)_(%d+)"
|
-- Setup callback to clear signs and diagnostics whenever reviewer is left.
|
||||||
local old_line, new_line = line_code:match(line_code_regex)
|
reviewer.set_callback_for_reviewer_leave(signs.clear_signs_and_discussions)
|
||||||
return tonumber(old_line), tonumber(new_line)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---Filter all discussions which are relevant for currently visible signs and diagnostscs.
|
---Refresh discussion data, signs, diagnostics, and winbar with new data from API
|
||||||
---@return Discussion[]?
|
|
||||||
M.filter_discussions_for_signs_and_diagnostics = function()
|
|
||||||
if type(M.discussions) ~= "table" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local file = reviewer.get_current_file()
|
|
||||||
if not file then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local discussions = {}
|
|
||||||
for _, discussion in ipairs(M.discussions) do
|
|
||||||
local first_note = discussion.notes[1]
|
|
||||||
if
|
|
||||||
type(first_note.position) == "table"
|
|
||||||
and (first_note.position.new_path == file or first_note.position.old_path == file)
|
|
||||||
then
|
|
||||||
if
|
|
||||||
--Skip resolved discussions
|
|
||||||
not (
|
|
||||||
state.settings.discussion_sign_and_diagnostic.skip_resolved_discussion
|
|
||||||
and first_note.resolvable
|
|
||||||
and first_note.resolved
|
|
||||||
)
|
|
||||||
--Skip discussions from old revisions
|
|
||||||
and not (
|
|
||||||
state.settings.discussion_sign_and_diagnostic.skip_old_revision_discussion
|
|
||||||
and u.from_iso_format_date_to_timestamp(first_note.created_at)
|
|
||||||
<= u.from_iso_format_date_to_timestamp(state.MR_REVISIONS[1].created_at)
|
|
||||||
)
|
|
||||||
then
|
|
||||||
table.insert(discussions, discussion)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return discussions
|
|
||||||
end
|
|
||||||
|
|
||||||
---Refresh the discussion signs for currently loaded file in reviewer For convinience we use same
|
|
||||||
---string for sign name and sign group ( currently there is only one sign needed)
|
|
||||||
M.refresh_signs = function()
|
|
||||||
local diagnostics = M.filter_discussions_for_signs_and_diagnostics()
|
|
||||||
if diagnostics == nil then
|
|
||||||
vim.diagnostic.reset(diagnostics_namespace)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local new_signs = {}
|
|
||||||
local old_signs = {}
|
|
||||||
for _, discussion in ipairs(diagnostics) do
|
|
||||||
local first_note = discussion.notes[1]
|
|
||||||
local base_sign = {
|
|
||||||
name = discussion_sign_name,
|
|
||||||
group = discussion_sign_name,
|
|
||||||
priority = state.settings.discussion_sign.priority,
|
|
||||||
}
|
|
||||||
local base_helper_sign = {
|
|
||||||
name = discussion_sign_name,
|
|
||||||
group = discussion_sign_name,
|
|
||||||
priority = state.settings.discussion_sign.priority - 1,
|
|
||||||
}
|
|
||||||
if first_note.position.line_range ~= nil then
|
|
||||||
local start_old_line, start_new_line = _parse_line_code(first_note.position.line_range.start.line_code)
|
|
||||||
local end_old_line, end_new_line = _parse_line_code(first_note.position.line_range["end"].line_code)
|
|
||||||
local discussion_line, start_line, end_line
|
|
||||||
if first_note.position.line_range.start.type == "new" then
|
|
||||||
table.insert(
|
|
||||||
new_signs,
|
|
||||||
vim.tbl_deep_extend("force", {
|
|
||||||
id = first_note.id,
|
|
||||||
lnum = first_note.position.new_line,
|
|
||||||
}, base_sign)
|
|
||||||
)
|
|
||||||
discussion_line = first_note.position.new_line
|
|
||||||
start_line = start_new_line
|
|
||||||
end_line = end_new_line
|
|
||||||
elseif first_note.position.line_range.start.type == "old" then
|
|
||||||
table.insert(
|
|
||||||
old_signs,
|
|
||||||
vim.tbl_deep_extend("force", {
|
|
||||||
id = first_note.id,
|
|
||||||
lnum = first_note.position.old_line,
|
|
||||||
}, base_sign)
|
|
||||||
)
|
|
||||||
discussion_line = first_note.position.old_line
|
|
||||||
start_line = start_old_line
|
|
||||||
end_line = end_old_line
|
|
||||||
end
|
|
||||||
-- Helper signs does not have specific ids currently.
|
|
||||||
if state.settings.discussion_sign.helper_signs.enabled then
|
|
||||||
local helper_signs = {}
|
|
||||||
if start_line > end_line then
|
|
||||||
start_line, end_line = end_line, start_line
|
|
||||||
end
|
|
||||||
for i = start_line, end_line do
|
|
||||||
if i ~= discussion_line then
|
|
||||||
local sign_name
|
|
||||||
if i == start_line then
|
|
||||||
sign_name = discussion_helper_sign_start
|
|
||||||
elseif i == end_line then
|
|
||||||
sign_name = discussion_helper_sign_end
|
|
||||||
else
|
|
||||||
sign_name = discussion_helper_sign_mid
|
|
||||||
end
|
|
||||||
table.insert(
|
|
||||||
helper_signs,
|
|
||||||
vim.tbl_deep_extend("keep", {
|
|
||||||
name = sign_name,
|
|
||||||
lnum = i,
|
|
||||||
}, base_helper_sign)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if first_note.position.line_range.start.type == "new" then
|
|
||||||
vim.list_extend(new_signs, helper_signs)
|
|
||||||
elseif first_note.position.line_range.start.type == "old" then
|
|
||||||
vim.list_extend(old_signs, helper_signs)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local sign = vim.tbl_deep_extend("force", {
|
|
||||||
id = first_note.id,
|
|
||||||
}, base_sign)
|
|
||||||
if first_note.position.new_line ~= nil then
|
|
||||||
table.insert(new_signs, vim.tbl_deep_extend("force", { lnum = first_note.position.new_line }, sign))
|
|
||||||
end
|
|
||||||
if first_note.position.old_line ~= nil then
|
|
||||||
table.insert(old_signs, vim.tbl_deep_extend("force", { lnum = first_note.position.old_line }, sign))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
vim.fn.sign_unplace(discussion_sign_name)
|
|
||||||
reviewer.place_sign(old_signs, "old")
|
|
||||||
reviewer.place_sign(new_signs, "new")
|
|
||||||
end
|
|
||||||
|
|
||||||
---Build note header from note.
|
|
||||||
---@param note Note
|
|
||||||
---@return string
|
|
||||||
M.build_note_header = function(note)
|
|
||||||
return "@" .. note.author.username .. " " .. u.time_since(note.created_at)
|
|
||||||
end
|
|
||||||
|
|
||||||
---Refresh the diagnostics for the currently reviewed file
|
|
||||||
M.refresh_diagnostics = function()
|
|
||||||
-- Keep in mind that diagnostic line numbers use 0-based indexing while line numbers use
|
|
||||||
-- 1-based indexing
|
|
||||||
local diagnostics = M.filter_discussions_for_signs_and_diagnostics()
|
|
||||||
if diagnostics == nil then
|
|
||||||
vim.diagnostic.reset(diagnostics_namespace)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local new_diagnostics = {}
|
|
||||||
local old_diagnostics = {}
|
|
||||||
for _, discussion in ipairs(diagnostics) do
|
|
||||||
local first_note = discussion.notes[1]
|
|
||||||
local message = ""
|
|
||||||
for _, note in ipairs(discussion.notes) do
|
|
||||||
message = message .. M.build_note_header(note) .. "\n" .. note.body .. "\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
local diagnostic = {
|
|
||||||
message = message,
|
|
||||||
col = 0,
|
|
||||||
severity = state.settings.discussion_diagnostic.severity,
|
|
||||||
user_data = { discussion_id = discussion.id, header = M.build_note_header(discussion.notes[1]) },
|
|
||||||
source = "gitlab",
|
|
||||||
code = state.settings.discussion_diagnostic.code,
|
|
||||||
}
|
|
||||||
if first_note.position.line_range ~= nil then
|
|
||||||
-- Diagnostics for line range discussions are tricky - you need to set lnum to
|
|
||||||
-- line number equal to note.position.new_line or note.position.old_line because that is
|
|
||||||
-- only line where you can trigger the diagnostic show. This also need to be in sinc
|
|
||||||
-- with the sign placement.
|
|
||||||
local start_old_line, start_new_line = _parse_line_code(first_note.position.line_range.start.line_code)
|
|
||||||
local end_old_line, end_new_line = _parse_line_code(first_note.position.line_range["end"].line_code)
|
|
||||||
if first_note.position.line_range.start.type == "new" then
|
|
||||||
local new_diagnostic
|
|
||||||
if first_note.position.new_line == start_new_line then
|
|
||||||
new_diagnostic = {
|
|
||||||
lnum = start_new_line - 1,
|
|
||||||
end_lnum = end_new_line - 1,
|
|
||||||
}
|
|
||||||
else
|
|
||||||
new_diagnostic = {
|
|
||||||
lnum = end_new_line - 1,
|
|
||||||
end_lnum = start_new_line - 1,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
new_diagnostic = vim.tbl_deep_extend("force", new_diagnostic, diagnostic)
|
|
||||||
table.insert(new_diagnostics, new_diagnostic)
|
|
||||||
elseif first_note.position.line_range.start.type == "old" then
|
|
||||||
local old_diagnostic
|
|
||||||
if first_note.position.old_line == start_old_line then
|
|
||||||
old_diagnostic = {
|
|
||||||
lnum = start_old_line - 1,
|
|
||||||
end_lnum = end_old_line - 1,
|
|
||||||
}
|
|
||||||
else
|
|
||||||
old_diagnostic = {
|
|
||||||
lnum = end_old_line - 1,
|
|
||||||
end_lnum = start_old_line - 1,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
old_diagnostic = vim.tbl_deep_extend("force", old_diagnostic, diagnostic)
|
|
||||||
table.insert(old_diagnostics, old_diagnostic)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Diagnostics for single line discussions.
|
|
||||||
if first_note.position.new_line ~= nil then
|
|
||||||
local new_diagnostic = {
|
|
||||||
lnum = first_note.position.new_line - 1,
|
|
||||||
}
|
|
||||||
new_diagnostic = vim.tbl_deep_extend("force", new_diagnostic, diagnostic)
|
|
||||||
table.insert(new_diagnostics, new_diagnostic)
|
|
||||||
end
|
|
||||||
if first_note.position.old_line ~= nil then
|
|
||||||
local old_diagnostic = {
|
|
||||||
lnum = first_note.position.old_line - 1,
|
|
||||||
}
|
|
||||||
old_diagnostic = vim.tbl_deep_extend("force", old_diagnostic, diagnostic)
|
|
||||||
table.insert(old_diagnostics, old_diagnostic)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
vim.diagnostic.reset(diagnostics_namespace)
|
|
||||||
reviewer.set_diagnostics(
|
|
||||||
diagnostics_namespace,
|
|
||||||
new_diagnostics,
|
|
||||||
"new",
|
|
||||||
state.settings.discussion_diagnostic.display_opts
|
|
||||||
)
|
|
||||||
reviewer.set_diagnostics(
|
|
||||||
diagnostics_namespace,
|
|
||||||
old_diagnostics,
|
|
||||||
"old",
|
|
||||||
state.settings.discussion_diagnostic.display_opts
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
---Refresh discussion data, discussion signs and diagnostics
|
|
||||||
M.refresh_discussion_data = function()
|
M.refresh_discussion_data = function()
|
||||||
M.load_discussions(function()
|
M.load_discussions(function()
|
||||||
if state.settings.discussion_sign.enabled then
|
if state.settings.discussion_sign.enabled then
|
||||||
M.refresh_signs()
|
signs.refresh_signs(M.discussions)
|
||||||
end
|
end
|
||||||
if state.settings.discussion_diagnostic.enabled then
|
if state.settings.discussion_diagnostic.enabled then
|
||||||
M.refresh_diagnostics()
|
signs.refresh_diagnostics(M.discussions)
|
||||||
|
end
|
||||||
|
if M.split_visible then
|
||||||
|
local linked_is_focused = M.linked_bufnr == M.focused_bufnr
|
||||||
|
winbar.update_winbar(M.discussions, M.unlinked_discussions, linked_is_focused and "Discussions" or "Notes")
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Define signs for discussions if not already defined
|
|
||||||
M.setup_signs = function()
|
|
||||||
local discussion_sign = state.settings.discussion_sign
|
|
||||||
local signs = {
|
|
||||||
[discussion_sign_name] = discussion_sign.text,
|
|
||||||
[discussion_helper_sign_start] = discussion_sign.helper_signs.start,
|
|
||||||
[discussion_helper_sign_mid] = discussion_sign.helper_signs.mid,
|
|
||||||
[discussion_helper_sign_end] = discussion_sign.helper_signs["end"],
|
|
||||||
}
|
|
||||||
for sign_name, sign_text in pairs(signs) do
|
|
||||||
if #vim.fn.sign_getdefined(sign_name) == 0 then
|
|
||||||
vim.fn.sign_define(sign_name, {
|
|
||||||
text = sign_text,
|
|
||||||
linehl = discussion_sign.linehl,
|
|
||||||
texthl = discussion_sign.texthl,
|
|
||||||
culhl = discussion_sign.culhl,
|
|
||||||
numhl = discussion_sign.numhl,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---Initialize everything for discussions like setup of signs, callbacks for reviewer, etc.
|
|
||||||
M.initialize_discussions = function()
|
|
||||||
M.setup_signs()
|
|
||||||
M.setup_refresh_discussion_data_callback()
|
|
||||||
M.setup_leave_reviewer_callback()
|
|
||||||
end
|
|
||||||
|
|
||||||
---Setup callback to refresh discussion data, discussion signs and diagnostics whenever the
|
|
||||||
---reviewed file changes.
|
|
||||||
M.setup_refresh_discussion_data_callback = function()
|
|
||||||
reviewer.set_callback_for_file_changed(M.refresh_discussion_data)
|
|
||||||
end
|
|
||||||
|
|
||||||
---Clear all signs and diagnostics
|
|
||||||
M.clear_signs_and_discussions = function()
|
|
||||||
vim.fn.sign_unplace(discussion_sign_name)
|
|
||||||
vim.diagnostic.reset(diagnostics_namespace)
|
|
||||||
end
|
|
||||||
|
|
||||||
---Setup callback to clear signs and diagnostics whenever reviewer is left.
|
|
||||||
M.setup_leave_reviewer_callback = function()
|
|
||||||
reviewer.set_callback_for_reviewer_leave(M.clear_signs_and_discussions)
|
|
||||||
end
|
|
||||||
|
|
||||||
M.refresh_discussion_tree = function()
|
|
||||||
if M.layout_visible == false then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(M.discussions) == "table" then
|
|
||||||
M.rebuild_discussion_tree()
|
|
||||||
end
|
|
||||||
if type(M.unlinked_discussions) == "table" then
|
|
||||||
M.rebuild_unlinked_discussion_tree()
|
|
||||||
end
|
|
||||||
|
|
||||||
M.switch_can_edit_bufs(true)
|
|
||||||
M.add_empty_titles({
|
|
||||||
{ 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
|
|
||||||
|
|
||||||
---Opens the discussion tree, sets the keybindings. It also
|
---Opens the discussion tree, sets the keybindings. It also
|
||||||
---creates the tree for notes (which are not linked to specific lines of code)
|
---creates the tree for notes (which are not linked to specific lines of code)
|
||||||
---@param callback function?
|
---@param callback function?
|
||||||
M.toggle = function(callback)
|
M.toggle = function(callback)
|
||||||
if M.layout_visible then
|
if M.split_visible then
|
||||||
M.layout:unmount()
|
M.close()
|
||||||
M.layout_visible = false
|
|
||||||
M.discussion_tree = nil
|
|
||||||
M.linked_section = nil
|
|
||||||
M.unlinked_section = nil
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local linked_section, unlinked_section, layout = M.create_layout()
|
local split, linked_bufnr, unlinked_bufnr = M.create_split_and_bufs()
|
||||||
M.linked_section = linked_section
|
M.linked_bufnr = linked_bufnr
|
||||||
M.unlinked_section = unlinked_section
|
M.unlinked_bufnr = unlinked_bufnr
|
||||||
|
|
||||||
|
M.split = split
|
||||||
|
M.split_visible = true
|
||||||
|
M.split_bufnr = split.bufnr
|
||||||
|
split:mount()
|
||||||
|
M.switch_can_edit_bufs(true)
|
||||||
|
|
||||||
|
vim.api.nvim_buf_set_lines(split.bufnr, 0, -1, false, { "Loading data..." })
|
||||||
|
vim.api.nvim_set_option_value("filetype", "gitlab", { buf = M.split_bufnr })
|
||||||
|
vim.api.nvim_set_option_value("filetype", "gitlab", { buf = M.unlinked_bufnr })
|
||||||
|
vim.api.nvim_set_option_value("filetype", "gitlab", { buf = M.linked_bufnr })
|
||||||
|
|
||||||
|
local default_discussions = state.settings.discussion_tree.default_view == "discussions"
|
||||||
|
winbar.update_winbar({}, {}, default_discussions and "Discussions" or "Notes")
|
||||||
|
|
||||||
M.load_discussions(function()
|
M.load_discussions(function()
|
||||||
if type(M.discussions) ~= "table" and type(M.unlinked_discussions) ~= "table" then
|
if type(M.discussions) ~= "table" and type(M.unlinked_discussions) ~= "table" then
|
||||||
vim.notify("No discussions or notes for this MR", vim.log.levels.WARN)
|
vim.notify("No discussions or notes for this MR", vim.log.levels.WARN)
|
||||||
|
vim.api.nvim_buf_set_lines(split.bufnr, 0, -1, false, { "" })
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
layout:mount()
|
M.rebuild_discussion_tree()
|
||||||
layout:show()
|
M.rebuild_unlinked_discussion_tree()
|
||||||
|
M.add_empty_titles({
|
||||||
|
{ M.linked_bufnr, M.discussions, "No Discussions for this MR" },
|
||||||
|
{ M.unlinked_bufnr, M.unlinked_discussions, "No Notes (Unlinked Discussions) for this MR" },
|
||||||
|
})
|
||||||
|
|
||||||
M.layout = layout
|
local default_buffer = default_discussions and M.linked_bufnr or M.unlinked_bufnr
|
||||||
M.layout_visible = true
|
vim.api.nvim_set_current_buf(default_buffer)
|
||||||
M.layout_buf = layout.bufnr
|
M.focused_bufnr = default_buffer
|
||||||
state.discussion_buf = layout.bufnr
|
|
||||||
M.refresh_discussion_tree()
|
M.switch_can_edit_bufs(false)
|
||||||
|
winbar.update_winbar(M.discussions, M.unlinked_discussions, default_discussions and "Discussions" or "Notes")
|
||||||
if type(callback) == "function" then
|
if type(callback) == "function" then
|
||||||
callback()
|
callback()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local switch_view_type = function()
|
||||||
|
local change_to_unlinked = M.linked_bufnr == M.focused_bufnr
|
||||||
|
local new_bufnr = change_to_unlinked and M.unlinked_bufnr or M.linked_bufnr
|
||||||
|
vim.api.nvim_set_current_buf(new_bufnr)
|
||||||
|
winbar.update_winbar(M.discussions, M.unlinked_discussions, change_to_unlinked and "Notes" or "Discussions")
|
||||||
|
M.focused_bufnr = new_bufnr
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Clears the discussion state and unmounts the split
|
||||||
|
M.close = function()
|
||||||
|
if M.split then
|
||||||
|
M.split:unmount()
|
||||||
|
end
|
||||||
|
M.split_visible = false
|
||||||
|
M.discussion_tree = nil
|
||||||
|
end
|
||||||
|
|
||||||
---Move to the discussion tree at the discussion from diagnostic on current line.
|
---Move to the discussion tree at the discussion from diagnostic on current line.
|
||||||
M.move_to_discussion_tree = function()
|
M.move_to_discussion_tree = function()
|
||||||
local current_line = vim.api.nvim_win_get_cursor(0)[1]
|
local current_line = vim.api.nvim_win_get_cursor(0)[1]
|
||||||
local diagnostics = vim.diagnostic.get(0, { namespace = diagnostics_namespace, lnum = current_line - 1 })
|
local diagnostics = vim.diagnostic.get(0, { namespace = signs.diagnostics_namespace, lnum = current_line - 1 })
|
||||||
|
|
||||||
---Function used to jump to the discussion tree after the menu selection.
|
---Function used to jump to the discussion tree after the menu selection.
|
||||||
local jump_after_menu_selection = function(diagnostic)
|
local jump_after_menu_selection = function(diagnostic)
|
||||||
@@ -435,11 +164,11 @@ M.move_to_discussion_tree = function()
|
|||||||
discussion_node:expand()
|
discussion_node:expand()
|
||||||
end
|
end
|
||||||
M.discussion_tree:render()
|
M.discussion_tree:render()
|
||||||
vim.api.nvim_win_set_cursor(M.linked_section.winid, { line_number, 0 })
|
vim.api.nvim_win_set_cursor(M.split.winid, { line_number, 0 })
|
||||||
vim.api.nvim_set_current_win(M.linked_section.winid)
|
vim.api.nvim_set_current_win(M.split.winid)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not M.layout_visible then
|
if not M.split_visible then
|
||||||
M.toggle(jump_after_tree_opened)
|
M.toggle(jump_after_tree_opened)
|
||||||
else
|
else
|
||||||
jump_after_tree_opened()
|
jump_after_tree_opened()
|
||||||
@@ -523,13 +252,14 @@ M.send_deletion = function(tree, unlinked)
|
|||||||
M.discussions = u.remove_first_value(M.discussions)
|
M.discussions = u.remove_first_value(M.discussions)
|
||||||
M.rebuild_discussion_tree()
|
M.rebuild_discussion_tree()
|
||||||
end
|
end
|
||||||
M.switch_can_edit_bufs(true)
|
|
||||||
M.add_empty_titles({
|
M.add_empty_titles({
|
||||||
{ M.linked_section.bufnr, M.discussions, "No Discussions for this MR" },
|
{ M.linked_bufnr, M.discussions, "No Discussions for this MR" },
|
||||||
{ M.unlinked_section.bufnr, M.unlinked_discussions, "No Notes (Unlinked Discussions) for this MR" },
|
{ M.unlinked_bufnr, M.unlinked_discussions, "No Notes (Unlinked Discussions) for this MR" },
|
||||||
})
|
})
|
||||||
M.switch_can_edit_bufs(false)
|
M.switch_can_edit_bufs(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
M.refresh_discussion_data()
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -573,6 +303,7 @@ M.send_edits = function(discussion_id, note_id, unlinked)
|
|||||||
}
|
}
|
||||||
job.run_job("/comment", "PATCH", body, function(data)
|
job.run_job("/comment", "PATCH", body, function(data)
|
||||||
u.notify(data.message, vim.log.levels.INFO)
|
u.notify(data.message, vim.log.levels.INFO)
|
||||||
|
M.rebuild_discussion_tree()
|
||||||
if unlinked then
|
if unlinked then
|
||||||
M.replace_text(M.unlinked_discussions, discussion_id, note_id, text)
|
M.replace_text(M.unlinked_discussions, discussion_id, note_id, text)
|
||||||
M.rebuild_unlinked_discussion_tree()
|
M.rebuild_unlinked_discussion_tree()
|
||||||
@@ -599,6 +330,7 @@ M.toggle_discussion_resolved = function(tree)
|
|||||||
job.run_job("/discussions/resolve", "PUT", body, function(data)
|
job.run_job("/discussions/resolve", "PUT", body, function(data)
|
||||||
u.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)
|
||||||
|
M.refresh_discussion_data()
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -693,36 +425,37 @@ end
|
|||||||
|
|
||||||
M.rebuild_discussion_tree = function()
|
M.rebuild_discussion_tree = function()
|
||||||
M.switch_can_edit_bufs(true)
|
M.switch_can_edit_bufs(true)
|
||||||
vim.api.nvim_buf_set_lines(M.linked_section.bufnr, 0, -1, false, {})
|
vim.api.nvim_buf_set_lines(M.linked_bufnr, 0, -1, false, {})
|
||||||
local discussion_tree_nodes = discussions_tree.add_discussions_to_table(M.discussions, false)
|
local discussion_tree_nodes = discussions_tree.add_discussions_to_table(M.discussions, false)
|
||||||
local discussion_tree =
|
local discussion_tree =
|
||||||
NuiTree({ nodes = discussion_tree_nodes, bufnr = M.linked_section.bufnr, prepare_node = nui_tree_prepare_node })
|
NuiTree({ nodes = discussion_tree_nodes, bufnr = M.linked_bufnr, prepare_node = nui_tree_prepare_node })
|
||||||
discussion_tree:render()
|
discussion_tree:render()
|
||||||
M.set_tree_keymaps(discussion_tree, M.linked_section.bufnr, false)
|
M.set_tree_keymaps(discussion_tree, M.linked_bufnr, false)
|
||||||
M.discussion_tree = discussion_tree
|
M.discussion_tree = discussion_tree
|
||||||
M.switch_can_edit_bufs(false)
|
M.switch_can_edit_bufs(false)
|
||||||
vim.api.nvim_set_option_value("filetype", "gitlab", { buf = M.linked_section.bufnr })
|
vim.api.nvim_set_option_value("filetype", "gitlab", { buf = M.linked_bufnr })
|
||||||
end
|
end
|
||||||
|
|
||||||
M.rebuild_unlinked_discussion_tree = function()
|
M.rebuild_unlinked_discussion_tree = function()
|
||||||
M.switch_can_edit_bufs(true)
|
M.switch_can_edit_bufs(true)
|
||||||
vim.api.nvim_buf_set_lines(M.unlinked_section.bufnr, 0, -1, false, {})
|
vim.api.nvim_buf_set_lines(M.unlinked_bufnr, 0, -1, false, {})
|
||||||
local unlinked_discussion_tree_nodes = discussions_tree.add_discussions_to_table(M.unlinked_discussions, true)
|
local unlinked_discussion_tree_nodes = discussions_tree.add_discussions_to_table(M.unlinked_discussions, true)
|
||||||
local unlinked_discussion_tree = NuiTree({
|
local unlinked_discussion_tree = NuiTree({
|
||||||
nodes = unlinked_discussion_tree_nodes,
|
nodes = unlinked_discussion_tree_nodes,
|
||||||
bufnr = M.unlinked_section.bufnr,
|
bufnr = M.unlinked_bufnr,
|
||||||
prepare_node = nui_tree_prepare_node,
|
prepare_node = nui_tree_prepare_node,
|
||||||
})
|
})
|
||||||
unlinked_discussion_tree:render()
|
unlinked_discussion_tree:render()
|
||||||
M.set_tree_keymaps(unlinked_discussion_tree, M.unlinked_section.bufnr, true)
|
M.set_tree_keymaps(unlinked_discussion_tree, M.unlinked_bufnr, true)
|
||||||
M.unlinked_discussion_tree = unlinked_discussion_tree
|
M.unlinked_discussion_tree = unlinked_discussion_tree
|
||||||
M.switch_can_edit_bufs(false)
|
M.switch_can_edit_bufs(false)
|
||||||
vim.api.nvim_set_option_value("filetype", "gitlab", { buf = M.unlinked_section.bufnr })
|
|
||||||
end
|
end
|
||||||
|
|
||||||
M.switch_can_edit_bufs = function(bool)
|
M.switch_can_edit_bufs = function(bool)
|
||||||
u.switch_can_edit_buf(M.unlinked_section.bufnr, bool)
|
u.switch_can_edit_buf(M.unlinked_bufnr, bool)
|
||||||
u.switch_can_edit_buf(M.linked_section.bufnr, bool)
|
u.switch_can_edit_buf(M.linked_bufnr, bool)
|
||||||
|
vim.api.nvim_set_option_value("filetype", "gitlab", { buf = M.unlinked_bufnr })
|
||||||
|
vim.api.nvim_set_option_value("filetype", "gitlab", { buf = M.linked_bufnr })
|
||||||
end
|
end
|
||||||
|
|
||||||
M.add_discussion = function(arg)
|
M.add_discussion = function(arg)
|
||||||
@@ -732,44 +465,35 @@ M.add_discussion = function(arg)
|
|||||||
M.unlinked_discussions = {}
|
M.unlinked_discussions = {}
|
||||||
end
|
end
|
||||||
table.insert(M.unlinked_discussions, 1, discussion)
|
table.insert(M.unlinked_discussions, 1, discussion)
|
||||||
if M.unlinked_section ~= nil then
|
|
||||||
M.rebuild_unlinked_discussion_tree()
|
M.rebuild_unlinked_discussion_tree()
|
||||||
end
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if type(M.discussions) ~= "table" then
|
if type(M.discussions) ~= "table" then
|
||||||
M.discussions = {}
|
M.discussions = {}
|
||||||
end
|
end
|
||||||
table.insert(M.discussions, 1, discussion)
|
table.insert(M.discussions, 1, discussion)
|
||||||
if M.linked_section ~= nil then
|
|
||||||
M.rebuild_discussion_tree()
|
M.rebuild_discussion_tree()
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
M.create_layout = function()
|
|
||||||
local linked_section = Split({ enter = true })
|
|
||||||
local unlinked_section = Split({})
|
|
||||||
|
|
||||||
|
M.create_split_and_bufs = function()
|
||||||
local position = state.settings.discussion_tree.position
|
local position = state.settings.discussion_tree.position
|
||||||
local size = state.settings.discussion_tree.size
|
local size = state.settings.discussion_tree.size
|
||||||
local relative = state.settings.discussion_tree.relative
|
local relative = state.settings.discussion_tree.relative
|
||||||
|
|
||||||
local layout = Layout(
|
local split = Split({
|
||||||
{
|
relative = relative,
|
||||||
position = position,
|
position = position,
|
||||||
size = size,
|
size = size,
|
||||||
relative = relative,
|
})
|
||||||
},
|
|
||||||
Layout.Box({
|
|
||||||
Layout.Box(linked_section, { size = "50%" }),
|
|
||||||
Layout.Box(unlinked_section, { size = "50%" }),
|
|
||||||
}, { dir = (position == "left" and "col" or "row") })
|
|
||||||
)
|
|
||||||
|
|
||||||
return linked_section, unlinked_section, layout
|
local linked_bufnr = vim.api.nvim_create_buf(true, false)
|
||||||
|
local unlinked_bufnr = vim.api.nvim_create_buf(true, false)
|
||||||
|
|
||||||
|
return split, linked_bufnr, unlinked_bufnr
|
||||||
end
|
end
|
||||||
|
|
||||||
M.add_empty_titles = function(args)
|
M.add_empty_titles = function(args)
|
||||||
|
M.switch_can_edit_bufs(true)
|
||||||
local ns_id = vim.api.nvim_create_namespace("GitlabNamespace")
|
local ns_id = vim.api.nvim_create_namespace("GitlabNamespace")
|
||||||
vim.cmd("highlight default TitleHighlight guifg=#787878")
|
vim.cmd("highlight default TitleHighlight guifg=#787878")
|
||||||
for _, section in ipairs(args) do
|
for _, section in ipairs(args) do
|
||||||
@@ -830,7 +554,12 @@ M.set_tree_keymaps = function(tree, bufnr, unlinked)
|
|||||||
M.reply(tree)
|
M.reply(tree)
|
||||||
end
|
end
|
||||||
end, { buffer = bufnr, desc = "Reply" })
|
end, { buffer = bufnr, desc = "Reply" })
|
||||||
|
vim.keymap.set("n", state.settings.discussion_tree.switch_view, function()
|
||||||
|
switch_view_type()
|
||||||
|
end, { buffer = bufnr, desc = "Switch view type" })
|
||||||
|
vim.keymap.set("n", state.settings.help, function()
|
||||||
|
help.open()
|
||||||
|
end, { buffer = bufnr, desc = "Open help popup" })
|
||||||
if not unlinked then
|
if not unlinked then
|
||||||
vim.keymap.set("n", state.settings.discussion_tree.jump_to_file, function()
|
vim.keymap.set("n", state.settings.discussion_tree.jump_to_file, function()
|
||||||
if M.is_current_node_note(tree) then
|
if M.is_current_node_note(tree) then
|
||||||
|
|||||||
293
lua/gitlab/actions/discussions/signs.lua
Normal file
293
lua/gitlab/actions/discussions/signs.lua
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
local state = require("gitlab.state")
|
||||||
|
local u = require("gitlab.utils")
|
||||||
|
local reviewer = require("gitlab.reviewer")
|
||||||
|
local discussion_sign_name = "gitlab_discussion"
|
||||||
|
local discussion_helper_sign_start = "gitlab_discussion_helper_start"
|
||||||
|
local discussion_helper_sign_mid = "gitlab_discussion_helper_mid"
|
||||||
|
local discussion_helper_sign_end = "gitlab_discussion_helper_end"
|
||||||
|
local diagnostics_namespace = vim.api.nvim_create_namespace(discussion_sign_name)
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
M.diagnostics_namespace = diagnostics_namespace
|
||||||
|
|
||||||
|
---Parse line code and return old and new line numbers
|
||||||
|
---@param line_code string gitlab line code -> 588440f66559714280628a4f9799f0c4eb880a4a_10_10
|
||||||
|
---@return number?
|
||||||
|
---@return number?
|
||||||
|
local function _parse_line_code(line_code)
|
||||||
|
local line_code_regex = "%w+_(%d+)_(%d+)"
|
||||||
|
local old_line, new_line = line_code:match(line_code_regex)
|
||||||
|
return tonumber(old_line), tonumber(new_line)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Filter all discussions which are relevant for currently visible signs and diagnostscs.
|
||||||
|
---@return Discussion[]?
|
||||||
|
local filter_discussions_for_signs_and_diagnostics = function(all_discussions)
|
||||||
|
if type(all_discussions) ~= "table" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local file = reviewer.get_current_file()
|
||||||
|
if not file then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local discussions = {}
|
||||||
|
for _, discussion in ipairs(all_discussions) do
|
||||||
|
local first_note = discussion.notes[1]
|
||||||
|
if
|
||||||
|
type(first_note.position) == "table"
|
||||||
|
and (first_note.position.new_path == file or first_note.position.old_path == file)
|
||||||
|
then
|
||||||
|
if
|
||||||
|
--Skip resolved discussions
|
||||||
|
not (
|
||||||
|
state.settings.discussion_sign_and_diagnostic.skip_resolved_discussion
|
||||||
|
and first_note.resolvable
|
||||||
|
and first_note.resolved
|
||||||
|
)
|
||||||
|
--Skip discussions from old revisions
|
||||||
|
and not (
|
||||||
|
state.settings.discussion_sign_and_diagnostic.skip_old_revision_discussion
|
||||||
|
and u.from_iso_format_date_to_timestamp(first_note.created_at)
|
||||||
|
<= u.from_iso_format_date_to_timestamp(state.MR_REVISIONS[1].created_at)
|
||||||
|
)
|
||||||
|
then
|
||||||
|
table.insert(discussions, discussion)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return discussions
|
||||||
|
end
|
||||||
|
|
||||||
|
---Build note header from note.
|
||||||
|
---@param note Note
|
||||||
|
---@return string
|
||||||
|
local build_note_header = function(note)
|
||||||
|
return "@" .. note.author.username .. " " .. u.time_since(note.created_at)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Define signs for discussions if not already defined
|
||||||
|
M.setup_signs = function()
|
||||||
|
local discussion_sign = state.settings.discussion_sign
|
||||||
|
local signs = {
|
||||||
|
[discussion_sign_name] = discussion_sign.text,
|
||||||
|
[discussion_helper_sign_start] = discussion_sign.helper_signs.start,
|
||||||
|
[discussion_helper_sign_mid] = discussion_sign.helper_signs.mid,
|
||||||
|
[discussion_helper_sign_end] = discussion_sign.helper_signs["end"],
|
||||||
|
}
|
||||||
|
for sign_name, sign_text in pairs(signs) do
|
||||||
|
if #vim.fn.sign_getdefined(sign_name) == 0 then
|
||||||
|
vim.fn.sign_define(sign_name, {
|
||||||
|
text = sign_text,
|
||||||
|
linehl = discussion_sign.linehl,
|
||||||
|
texthl = discussion_sign.texthl,
|
||||||
|
culhl = discussion_sign.culhl,
|
||||||
|
numhl = discussion_sign.numhl,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---Refresh the discussion signs for currently loaded file in reviewer For convinience we use same
|
||||||
|
---string for sign name and sign group ( currently there is only one sign needed)
|
||||||
|
M.refresh_signs = function(discussions)
|
||||||
|
local diagnostics = filter_discussions_for_signs_and_diagnostics(discussions)
|
||||||
|
if diagnostics == nil then
|
||||||
|
vim.diagnostic.reset(diagnostics_namespace)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local new_signs = {}
|
||||||
|
local old_signs = {}
|
||||||
|
for _, discussion in ipairs(diagnostics) do
|
||||||
|
local first_note = discussion.notes[1]
|
||||||
|
local base_sign = {
|
||||||
|
name = discussion_sign_name,
|
||||||
|
group = discussion_sign_name,
|
||||||
|
priority = state.settings.discussion_sign.priority,
|
||||||
|
}
|
||||||
|
local base_helper_sign = {
|
||||||
|
name = discussion_sign_name,
|
||||||
|
group = discussion_sign_name,
|
||||||
|
priority = state.settings.discussion_sign.priority - 1,
|
||||||
|
}
|
||||||
|
if first_note.position.line_range ~= nil then
|
||||||
|
local start_old_line, start_new_line = _parse_line_code(first_note.position.line_range.start.line_code)
|
||||||
|
local end_old_line, end_new_line = _parse_line_code(first_note.position.line_range["end"].line_code)
|
||||||
|
local discussion_line, start_line, end_line
|
||||||
|
if first_note.position.line_range.start.type == "new" then
|
||||||
|
table.insert(
|
||||||
|
new_signs,
|
||||||
|
vim.tbl_deep_extend("force", {
|
||||||
|
id = first_note.id,
|
||||||
|
lnum = first_note.position.new_line,
|
||||||
|
}, base_sign)
|
||||||
|
)
|
||||||
|
discussion_line = first_note.position.new_line
|
||||||
|
start_line = start_new_line
|
||||||
|
end_line = end_new_line
|
||||||
|
elseif first_note.position.line_range.start.type == "old" then
|
||||||
|
table.insert(
|
||||||
|
old_signs,
|
||||||
|
vim.tbl_deep_extend("force", {
|
||||||
|
id = first_note.id,
|
||||||
|
lnum = first_note.position.old_line,
|
||||||
|
}, base_sign)
|
||||||
|
)
|
||||||
|
discussion_line = first_note.position.old_line
|
||||||
|
start_line = start_old_line
|
||||||
|
end_line = end_old_line
|
||||||
|
end
|
||||||
|
-- Helper signs does not have specific ids currently.
|
||||||
|
if state.settings.discussion_sign.helper_signs.enabled then
|
||||||
|
local helper_signs = {}
|
||||||
|
if start_line > end_line then
|
||||||
|
start_line, end_line = end_line, start_line
|
||||||
|
end
|
||||||
|
for i = start_line, end_line do
|
||||||
|
if i ~= discussion_line then
|
||||||
|
local sign_name
|
||||||
|
if i == start_line then
|
||||||
|
sign_name = discussion_helper_sign_start
|
||||||
|
elseif i == end_line then
|
||||||
|
sign_name = discussion_helper_sign_end
|
||||||
|
else
|
||||||
|
sign_name = discussion_helper_sign_mid
|
||||||
|
end
|
||||||
|
table.insert(
|
||||||
|
helper_signs,
|
||||||
|
vim.tbl_deep_extend("keep", {
|
||||||
|
name = sign_name,
|
||||||
|
lnum = i,
|
||||||
|
}, base_helper_sign)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if first_note.position.line_range.start.type == "new" then
|
||||||
|
vim.list_extend(new_signs, helper_signs)
|
||||||
|
elseif first_note.position.line_range.start.type == "old" then
|
||||||
|
vim.list_extend(old_signs, helper_signs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local sign = vim.tbl_deep_extend("force", {
|
||||||
|
id = first_note.id,
|
||||||
|
}, base_sign)
|
||||||
|
if first_note.position.new_line ~= nil then
|
||||||
|
table.insert(new_signs, vim.tbl_deep_extend("force", { lnum = first_note.position.new_line }, sign))
|
||||||
|
end
|
||||||
|
if first_note.position.old_line ~= nil then
|
||||||
|
table.insert(old_signs, vim.tbl_deep_extend("force", { lnum = first_note.position.old_line }, sign))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
vim.fn.sign_unplace(discussion_sign_name)
|
||||||
|
reviewer.place_sign(old_signs, "old")
|
||||||
|
reviewer.place_sign(new_signs, "new")
|
||||||
|
end
|
||||||
|
|
||||||
|
---Refresh the diagnostics for the currently reviewed file
|
||||||
|
M.refresh_diagnostics = function(discussions)
|
||||||
|
-- Keep in mind that diagnostic line numbers use 0-based indexing while line numbers use
|
||||||
|
-- 1-based indexing
|
||||||
|
local diagnostics = filter_discussions_for_signs_and_diagnostics(discussions)
|
||||||
|
if diagnostics == nil then
|
||||||
|
vim.diagnostic.reset(diagnostics_namespace)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local new_diagnostics = {}
|
||||||
|
local old_diagnostics = {}
|
||||||
|
for _, discussion in ipairs(diagnostics) do
|
||||||
|
local first_note = discussion.notes[1]
|
||||||
|
local message = ""
|
||||||
|
for _, note in ipairs(discussion.notes) do
|
||||||
|
message = message .. build_note_header(note) .. "\n" .. note.body .. "\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
local diagnostic = {
|
||||||
|
message = message,
|
||||||
|
col = 0,
|
||||||
|
severity = state.settings.discussion_diagnostic.severity,
|
||||||
|
user_data = { discussion_id = discussion.id, header = build_note_header(discussion.notes[1]) },
|
||||||
|
source = "gitlab",
|
||||||
|
code = state.settings.discussion_diagnostic.code,
|
||||||
|
}
|
||||||
|
if first_note.position.line_range ~= nil then
|
||||||
|
-- Diagnostics for line range discussions are tricky - you need to set lnum to
|
||||||
|
-- line number equal to note.position.new_line or note.position.old_line because that is
|
||||||
|
-- only line where you can trigger the diagnostic show. This also need to be in sinc
|
||||||
|
-- with the sign placement.
|
||||||
|
local start_old_line, start_new_line = _parse_line_code(first_note.position.line_range.start.line_code)
|
||||||
|
local end_old_line, end_new_line = _parse_line_code(first_note.position.line_range["end"].line_code)
|
||||||
|
if first_note.position.line_range.start.type == "new" then
|
||||||
|
local new_diagnostic
|
||||||
|
if first_note.position.new_line == start_new_line then
|
||||||
|
new_diagnostic = {
|
||||||
|
lnum = start_new_line - 1,
|
||||||
|
end_lnum = end_new_line - 1,
|
||||||
|
}
|
||||||
|
else
|
||||||
|
new_diagnostic = {
|
||||||
|
lnum = end_new_line - 1,
|
||||||
|
end_lnum = start_new_line - 1,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
new_diagnostic = vim.tbl_deep_extend("force", new_diagnostic, diagnostic)
|
||||||
|
table.insert(new_diagnostics, new_diagnostic)
|
||||||
|
elseif first_note.position.line_range.start.type == "old" then
|
||||||
|
local old_diagnostic
|
||||||
|
if first_note.position.old_line == start_old_line then
|
||||||
|
old_diagnostic = {
|
||||||
|
lnum = start_old_line - 1,
|
||||||
|
end_lnum = end_old_line - 1,
|
||||||
|
}
|
||||||
|
else
|
||||||
|
old_diagnostic = {
|
||||||
|
lnum = end_old_line - 1,
|
||||||
|
end_lnum = start_old_line - 1,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
old_diagnostic = vim.tbl_deep_extend("force", old_diagnostic, diagnostic)
|
||||||
|
table.insert(old_diagnostics, old_diagnostic)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Diagnostics for single line discussions.
|
||||||
|
if first_note.position.new_line ~= nil then
|
||||||
|
local new_diagnostic = {
|
||||||
|
lnum = first_note.position.new_line - 1,
|
||||||
|
}
|
||||||
|
new_diagnostic = vim.tbl_deep_extend("force", new_diagnostic, diagnostic)
|
||||||
|
table.insert(new_diagnostics, new_diagnostic)
|
||||||
|
end
|
||||||
|
if first_note.position.old_line ~= nil then
|
||||||
|
local old_diagnostic = {
|
||||||
|
lnum = first_note.position.old_line - 1,
|
||||||
|
}
|
||||||
|
old_diagnostic = vim.tbl_deep_extend("force", old_diagnostic, diagnostic)
|
||||||
|
table.insert(old_diagnostics, old_diagnostic)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.diagnostic.reset(diagnostics_namespace)
|
||||||
|
reviewer.set_diagnostics(
|
||||||
|
diagnostics_namespace,
|
||||||
|
new_diagnostics,
|
||||||
|
"new",
|
||||||
|
state.settings.discussion_diagnostic.display_opts
|
||||||
|
)
|
||||||
|
reviewer.set_diagnostics(
|
||||||
|
diagnostics_namespace,
|
||||||
|
old_diagnostics,
|
||||||
|
"old",
|
||||||
|
state.settings.discussion_diagnostic.display_opts
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Clear all signs and diagnostics
|
||||||
|
M.clear_signs_and_discussions = function()
|
||||||
|
vim.fn.sign_unplace(discussion_sign_name)
|
||||||
|
vim.diagnostic.reset(diagnostics_namespace)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
58
lua/gitlab/actions/discussions/winbar.lua
Normal file
58
lua/gitlab/actions/discussions/winbar.lua
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
local M = {}
|
||||||
|
local state = require("gitlab.state")
|
||||||
|
|
||||||
|
---@param nodes Discussion[]|UnlinkedDiscussion[]|nil
|
||||||
|
local get_data = function(nodes)
|
||||||
|
if nodes == nil then
|
||||||
|
return 0, 0
|
||||||
|
end
|
||||||
|
local total_resolvable = 0
|
||||||
|
local total_resolved = 0
|
||||||
|
if nodes == vim.NIL then
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, d in ipairs(nodes) do
|
||||||
|
local first_child = d.notes[1]
|
||||||
|
if first_child ~= nil then
|
||||||
|
if first_child.resolvable then
|
||||||
|
total_resolvable = total_resolvable + 1
|
||||||
|
end
|
||||||
|
if first_child.resolved then
|
||||||
|
total_resolved = total_resolved + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return total_resolvable, total_resolved
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param discussions Discussion[]|nil
|
||||||
|
---@param unlinked_discussions UnlinkedDiscussion[]|nil
|
||||||
|
---@param file_name string
|
||||||
|
local function content(discussions, unlinked_discussions, file_name)
|
||||||
|
local resolvable_discussions, resolved_discussions = get_data(discussions)
|
||||||
|
local resolvable_notes, resolved_notes = get_data(unlinked_discussions)
|
||||||
|
|
||||||
|
local t = {
|
||||||
|
name = file_name,
|
||||||
|
resolvable_discussions = resolvable_discussions,
|
||||||
|
resolved_discussions = resolved_discussions,
|
||||||
|
resolvable_notes = resolvable_notes,
|
||||||
|
resolved_notes = resolved_notes,
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.settings.discussion_tree.winbar(t)
|
||||||
|
end
|
||||||
|
|
||||||
|
---This function sends the edited comment to the Go server
|
||||||
|
---@param discussions Discussion[]
|
||||||
|
---@param unlinked_discussions UnlinkedDiscussion[]
|
||||||
|
---@param base_title string
|
||||||
|
M.update_winbar = function(discussions, unlinked_discussions, base_title)
|
||||||
|
local d = require("gitlab.actions.discussions")
|
||||||
|
local winId = d.split.winid
|
||||||
|
vim.wo[winId].winbar = content(discussions, unlinked_discussions, base_title)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
27
lua/gitlab/actions/help.lua
Normal file
27
lua/gitlab/actions/help.lua
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
local u = require("gitlab.utils")
|
||||||
|
local state = require("gitlab.state")
|
||||||
|
local Popup = require("nui.popup")
|
||||||
|
|
||||||
|
M.open = function()
|
||||||
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
|
local keymaps = vim.api.nvim_buf_get_keymap(bufnr, "n")
|
||||||
|
local help_content_lines = {}
|
||||||
|
for _, keymap in ipairs(keymaps) do
|
||||||
|
if keymap.desc ~= nil then
|
||||||
|
local new_line = string.format("%s: %s", keymap.lhs, keymap.desc)
|
||||||
|
table.insert(help_content_lines, new_line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local longest_line = u.get_longest_string(help_content_lines)
|
||||||
|
local help_popup =
|
||||||
|
Popup(u.create_popup_state("Help", state.settings.popup.help, longest_line + 3, #help_content_lines + 3, 60))
|
||||||
|
help_popup:mount()
|
||||||
|
|
||||||
|
state.set_popup_keymaps(help_popup, "Help", nil)
|
||||||
|
local currentBuffer = vim.api.nvim_get_current_buf()
|
||||||
|
vim.api.nvim_buf_set_lines(currentBuffer, 0, #help_content_lines, false, help_content_lines)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -10,3 +10,5 @@ vim.api.nvim_set_hl(0, "GitlabChevron", u.get_colors_for_group(discussion.chevro
|
|||||||
vim.api.nvim_set_hl(0, "GitlabDirectory", u.get_colors_for_group(discussion.directory))
|
vim.api.nvim_set_hl(0, "GitlabDirectory", u.get_colors_for_group(discussion.directory))
|
||||||
vim.api.nvim_set_hl(0, "GitlabDirectoryIcon", u.get_colors_for_group(discussion.directory_icon))
|
vim.api.nvim_set_hl(0, "GitlabDirectoryIcon", u.get_colors_for_group(discussion.directory_icon))
|
||||||
vim.api.nvim_set_hl(0, "GitlabFileName", u.get_colors_for_group(discussion.file_name))
|
vim.api.nvim_set_hl(0, "GitlabFileName", u.get_colors_for_group(discussion.file_name))
|
||||||
|
vim.api.nvim_set_hl(0, "GitlabResolved", u.get_colors_for_group(discussion.resolved))
|
||||||
|
vim.api.nvim_set_hl(0, "GitlabUnresolved", u.get_colors_for_group(discussion.unresolved))
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ M.open = function()
|
|||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if state.settings.discussion_tree.auto_open then
|
||||||
|
local discussions = require("gitlab.actions.discussions")
|
||||||
|
discussions.close()
|
||||||
|
discussions.toggle()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
M.jump = function(file_name, new_line, old_line)
|
M.jump = function(file_name, new_line, old_line)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ M.settings = {
|
|||||||
config_path = nil,
|
config_path = nil,
|
||||||
reviewer = "diffview",
|
reviewer = "diffview",
|
||||||
attachment_dir = "",
|
attachment_dir = "",
|
||||||
|
help = "?",
|
||||||
popup = {
|
popup = {
|
||||||
exit = "<Esc>",
|
exit = "<Esc>",
|
||||||
perform_action = "<leader>s",
|
perform_action = "<leader>s",
|
||||||
@@ -26,9 +27,11 @@ M.settings = {
|
|||||||
reply = nil,
|
reply = nil,
|
||||||
comment = nil,
|
comment = nil,
|
||||||
note = nil,
|
note = nil,
|
||||||
|
help = nil,
|
||||||
pipeline = nil,
|
pipeline = nil,
|
||||||
},
|
},
|
||||||
discussion_tree = {
|
discussion_tree = {
|
||||||
|
auto_open = true,
|
||||||
blacklist = {},
|
blacklist = {},
|
||||||
jump_to_file = "o",
|
jump_to_file = "o",
|
||||||
jump_to_reviewer = "m",
|
jump_to_reviewer = "m",
|
||||||
@@ -41,8 +44,27 @@ M.settings = {
|
|||||||
position = "left",
|
position = "left",
|
||||||
size = "20%",
|
size = "20%",
|
||||||
resolved = "✓",
|
resolved = "✓",
|
||||||
unresolved = "",
|
unresolved = "-",
|
||||||
tree_type = "simple",
|
tree_type = "simple",
|
||||||
|
switch_view = "T",
|
||||||
|
default_view = "discussions",
|
||||||
|
---@param t WinbarTable
|
||||||
|
winbar = function(t)
|
||||||
|
local discussions_content = t.resolvable_discussions ~= 0
|
||||||
|
and string.format("Discussions (%d/%d)", t.resolved_discussions, t.resolvable_discussions)
|
||||||
|
or "Discussions"
|
||||||
|
local notes_content = t.resolvable_notes ~= 0
|
||||||
|
and string.format("Notes (%d/%d)", t.resolved_notes, t.resolvable_notes)
|
||||||
|
or "Notes"
|
||||||
|
if t.name == "Discussions" then
|
||||||
|
notes_content = "%#Comment#" .. notes_content
|
||||||
|
discussions_content = "%#Text#" .. discussions_content
|
||||||
|
else
|
||||||
|
discussions_content = "%#Comment#" .. discussions_content
|
||||||
|
notes_content = "%#Text#" .. notes_content
|
||||||
|
end
|
||||||
|
return " " .. discussions_content .. " %#Comment#| " .. notes_content
|
||||||
|
end,
|
||||||
},
|
},
|
||||||
info = {
|
info = {
|
||||||
enabled = true,
|
enabled = true,
|
||||||
@@ -91,13 +113,13 @@ M.settings = {
|
|||||||
display_opts = {}, -- this is dirrectly used as opts in vim.diagnostic.set, see :h vim.diagnostic.config.
|
display_opts = {}, -- this is dirrectly used as opts in vim.diagnostic.set, see :h vim.diagnostic.config.
|
||||||
},
|
},
|
||||||
pipeline = {
|
pipeline = {
|
||||||
created = "",
|
created = "",
|
||||||
pending = "",
|
pending = "",
|
||||||
preparing = "",
|
preparing = "",
|
||||||
scheduled = "",
|
scheduled = "",
|
||||||
running = "ﰌ",
|
running = "",
|
||||||
canceled = "ﰸ",
|
canceled = "",
|
||||||
skipped = "ﰸ",
|
skipped = "",
|
||||||
success = "✓",
|
success = "✓",
|
||||||
failed = "",
|
failed = "",
|
||||||
},
|
},
|
||||||
@@ -111,6 +133,8 @@ M.settings = {
|
|||||||
directory = "Directory",
|
directory = "Directory",
|
||||||
directory_icon = "DiffviewFolderSign",
|
directory_icon = "DiffviewFolderSign",
|
||||||
file_name = "Normal",
|
file_name = "Normal",
|
||||||
|
resolved = "DiagnosticSignOk",
|
||||||
|
unresolved = "DiagnosticSignWarn",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -217,6 +241,13 @@ M.set_popup_keymaps = function(popup, action, linewise_action, opts)
|
|||||||
vim.keymap.set("n", M.settings.popup.exit, function()
|
vim.keymap.set("n", M.settings.popup.exit, function()
|
||||||
exit(popup, opts.cb)
|
exit(popup, opts.cb)
|
||||||
end, { buffer = popup.bufnr, desc = "Exit popup" })
|
end, { buffer = popup.bufnr, desc = "Exit popup" })
|
||||||
|
|
||||||
|
if action ~= "Help" then -- Don't show help on the help popup
|
||||||
|
vim.keymap.set("n", M.settings.help, function()
|
||||||
|
local help = require("gitlab.actions.help")
|
||||||
|
help.open()
|
||||||
|
end, { buffer = popup.bufnr, desc = "Open help" })
|
||||||
|
end
|
||||||
if action ~= nil then
|
if action ~= nil then
|
||||||
vim.keymap.set("n", M.settings.popup.perform_action, function()
|
vim.keymap.set("n", M.settings.popup.perform_action, function()
|
||||||
local text = u.get_buffer_text(popup.bufnr)
|
local text = u.get_buffer_text(popup.bufnr)
|
||||||
|
|||||||
@@ -262,17 +262,6 @@ M.split_path = function(path)
|
|||||||
return path_parts
|
return path_parts
|
||||||
end
|
end
|
||||||
|
|
||||||
M.P = function(...)
|
|
||||||
local objects = {}
|
|
||||||
for i = 1, select("#", ...) do
|
|
||||||
local v = select(i, ...)
|
|
||||||
table.insert(objects, vim.inspect(v))
|
|
||||||
end
|
|
||||||
|
|
||||||
print(table.concat(objects, "\n"))
|
|
||||||
return ...
|
|
||||||
end
|
|
||||||
|
|
||||||
M.get_buffer_text = function(bufnr)
|
M.get_buffer_text = function(bufnr)
|
||||||
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||||
local text = table.concat(lines, "\n")
|
local text = table.concat(lines, "\n")
|
||||||
@@ -347,10 +336,10 @@ end
|
|||||||
---Get the popup view_opts
|
---Get the popup view_opts
|
||||||
---@param title string The string to appear on top of the popup
|
---@param title string The string to appear on top of the popup
|
||||||
---@param settings table User defined popup settings
|
---@param settings table User defined popup settings
|
||||||
---@param width string Override default width
|
---@param width number? Override default width
|
||||||
---@param height string Override default height
|
---@param height number? Override default height
|
||||||
---@return table
|
---@return table
|
||||||
M.create_popup_state = function(title, settings, width, height)
|
M.create_popup_state = function(title, settings, width, height, zindex)
|
||||||
local default_settings = require("gitlab.state").settings.popup
|
local default_settings = require("gitlab.state").settings.popup
|
||||||
local user_settings = settings or {}
|
local user_settings = settings or {}
|
||||||
local view_opts = {
|
local view_opts = {
|
||||||
@@ -360,6 +349,7 @@ M.create_popup_state = function(title, settings, width, height)
|
|||||||
relative = "editor",
|
relative = "editor",
|
||||||
enter = true,
|
enter = true,
|
||||||
focusable = true,
|
focusable = true,
|
||||||
|
zindex = zindex or 50,
|
||||||
border = {
|
border = {
|
||||||
style = user_settings.border or default_settings.border,
|
style = user_settings.border or default_settings.border,
|
||||||
text = {
|
text = {
|
||||||
@@ -373,6 +363,7 @@ M.create_popup_state = function(title, settings, width, height)
|
|||||||
},
|
},
|
||||||
opacity = user_settings.opacity or default_settings.opacity,
|
opacity = user_settings.opacity or default_settings.opacity,
|
||||||
}
|
}
|
||||||
|
|
||||||
return view_opts
|
return view_opts
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -627,4 +618,9 @@ M.get_icon = function(filename)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
M.basename = function(str)
|
||||||
|
local name = string.gsub(str, "(.*/)(.*)", "%2")
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
local nodes = tree.add_discussions_to_table(discussions)
|
local nodes = tree.add_discussions_to_table(discussions)
|
||||||
assert.are.same(tree_nodes_to_table(nodes, { note = true, note_body = true }), {
|
assert.are.same(tree_nodes_to_table(nodes, { note = true, note_body = true }), {
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -267,7 +267,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -293,7 +293,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
type = "file_name",
|
type = "file_name",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -312,7 +312,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
type = "file_name",
|
type = "file_name",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -339,7 +339,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
type = "file_name",
|
type = "file_name",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -377,7 +377,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
type = "file_name",
|
type = "file_name",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -396,7 +396,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
type = "file_name",
|
type = "file_name",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -415,7 +415,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
type = "file_name",
|
type = "file_name",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -464,7 +464,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
type = "file_name",
|
type = "file_name",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -491,7 +491,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
type = "file_name",
|
type = "file_name",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -514,7 +514,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
type = "file_name",
|
type = "file_name",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -550,7 +550,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
type = "file_name",
|
type = "file_name",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -561,7 +561,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -591,7 +591,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
type = "file_name",
|
type = "file_name",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -602,7 +602,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -626,7 +626,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
local nodes = tree.add_discussions_to_table(unlinked_discussions, true)
|
local nodes = tree.add_discussions_to_table(unlinked_discussions, true)
|
||||||
assert.are.same(tree_nodes_to_table(nodes, { note = true, note_body = true }), {
|
assert.are.same(tree_nodes_to_table(nodes, { note = true, note_body = true }), {
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -637,7 +637,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -669,7 +669,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
local nodes = tree.add_discussions_to_table(unlinked_discussions, true)
|
local nodes = tree.add_discussions_to_table(unlinked_discussions, true)
|
||||||
assert.are.same(tree_nodes_to_table(nodes, { note = true, note_body = true }), {
|
assert.are.same(tree_nodes_to_table(nodes, { note = true, note_body = true }), {
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
@@ -680,7 +680,7 @@ describe("gitlab/actions/discussions/tree.lua", function()
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text = "@gitlab.username 5 days ago ",
|
text = "@gitlab.username 5 days ago -",
|
||||||
type = "note",
|
type = "note",
|
||||||
children = {
|
children = {
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user