Three backticks will break the discussion tree, this MR specifically adds custom UUIDs to each text node to avoid this issue.
248 lines
7.4 KiB
Lua
248 lines
7.4 KiB
Lua
local u = require("gitlab.utils")
|
|
local NuiTree = require("nui.tree")
|
|
local NuiSplit = require("nui.split")
|
|
local job = require("gitlab.job")
|
|
local state = require("gitlab.state")
|
|
local Popup = require("nui.popup")
|
|
local keymaps = require("gitlab.keymaps")
|
|
|
|
local M = {}
|
|
|
|
local replyPopup = Popup(u.create_popup_state("Reply", "80%", "80%"))
|
|
|
|
M.reply = function(discussion_id)
|
|
if u.base_invalid() then return end
|
|
replyPopup:mount()
|
|
keymaps.set_popup_keymaps(replyPopup, M.send_reply(discussion_id))
|
|
end
|
|
|
|
M.send_reply = function(discussion_id)
|
|
return function(text)
|
|
local jsonTable = { discussion_id = discussion_id, reply = text }
|
|
local json = vim.json.encode(jsonTable)
|
|
job.run_job("reply", "POST", json, function(data)
|
|
M.add_note_to_tree(data.note, discussion_id)
|
|
end)
|
|
end
|
|
end
|
|
|
|
-- Places all of the discussions into a readable list
|
|
M.list_discussions = function()
|
|
if u.base_invalid() then return end
|
|
job.run_job("discussions", "GET", nil, function(data)
|
|
if type(data.discussions) ~= "table" then
|
|
vim.notify("No discussions for this MR")
|
|
return
|
|
end
|
|
|
|
local splitState = state.DISCUSSION_SPLIT
|
|
splitState.buf_options = { modifiable = false }
|
|
local split = NuiSplit(splitState)
|
|
split:mount()
|
|
|
|
local buf = split.bufnr
|
|
state.SPLIT_BUF = buf
|
|
|
|
local tree_nodes = M.add_discussions_to_table(data.discussions)
|
|
|
|
state.tree = NuiTree({ nodes = tree_nodes, bufnr = buf })
|
|
M.set_tree_keymaps(buf)
|
|
|
|
state.tree:render()
|
|
vim.api.nvim_buf_set_option(buf, 'filetype', 'markdown')
|
|
u.darken_metadata(buf, '')
|
|
end)
|
|
end
|
|
|
|
M.jump_to_file = function()
|
|
local node = state.tree:get_node()
|
|
if node == nil then return end
|
|
|
|
local wins = vim.api.nvim_list_wins()
|
|
local discussion_win = vim.api.nvim_get_current_win()
|
|
for _, winId in ipairs(wins) do
|
|
if winId ~= discussion_win then
|
|
vim.api.nvim_set_current_win(winId)
|
|
end
|
|
end
|
|
|
|
local discussion_node = M.get_root_node(node)
|
|
u.jump_to_file(discussion_node.file_name, discussion_node.line_number)
|
|
end
|
|
|
|
M.set_tree_keymaps = function(buf)
|
|
-- Jump to file location where comment was left
|
|
vim.keymap.set('n', state.keymaps.discussion_tree.jump_to_location, function()
|
|
M.jump_to_file()
|
|
end, { buffer = true })
|
|
|
|
vim.keymap.set('n', state.keymaps.discussion_tree.edit_comment, function()
|
|
require("gitlab.comment").edit_comment()
|
|
end, { buffer = true })
|
|
|
|
vim.keymap.set('n', state.keymaps.discussion_tree.delete_comment, function()
|
|
require("gitlab.comment").delete_comment()
|
|
end, { buffer = true })
|
|
|
|
-- Expand/collapse the current node
|
|
vim.keymap.set('n', state.keymaps.discussion_tree.toggle_node, function()
|
|
local node = state.tree:get_node()
|
|
if node == nil then return end
|
|
local children = node:get_child_ids()
|
|
if node == nil then return end
|
|
if node:is_expanded() then
|
|
node:collapse()
|
|
for _, child in ipairs(children) do
|
|
state.tree:get_node(child):collapse()
|
|
end
|
|
else
|
|
for _, child in ipairs(children) do
|
|
state.tree:get_node(child):expand()
|
|
end
|
|
node:expand()
|
|
end
|
|
|
|
state.tree:render()
|
|
u.darken_metadata(buf, '')
|
|
end,
|
|
{ buffer = true })
|
|
|
|
vim.keymap.set('n', 'r', function()
|
|
local node = state.tree:get_node()
|
|
if node == nil then return end
|
|
local discussion_node = M.get_root_node(node)
|
|
M.reply(tostring(discussion_node.id))
|
|
end, { buffer = true })
|
|
end
|
|
|
|
M.get_root_node = function(node)
|
|
if (not node.is_root) then
|
|
local parent_id = node:get_parent_id()
|
|
return M.get_root_node(state.tree:get_node(parent_id))
|
|
else
|
|
return node
|
|
end
|
|
end
|
|
|
|
M.get_note_node = function(node)
|
|
if (not node.is_note) then
|
|
local parent_id = node:get_parent_id()
|
|
if parent_id == nil then return node end
|
|
return M.get_note_node(state.tree:get_node(parent_id))
|
|
else
|
|
return node
|
|
end
|
|
end
|
|
|
|
M.build_note_body = function(note)
|
|
local text_nodes = {}
|
|
for bodyLine in note.body:gmatch("[^\n]+") do
|
|
local line = u.attach_uuid(bodyLine)
|
|
table.insert(text_nodes, NuiTree.Node({
|
|
text = line.text,
|
|
id = line.id,
|
|
is_body = true
|
|
}, {}))
|
|
end
|
|
|
|
local noteHeader = "@" ..
|
|
note.author.username .. " " .. u.format_date(note.created_at)
|
|
|
|
return noteHeader, text_nodes
|
|
end
|
|
|
|
M.build_note = function(note)
|
|
local text, text_nodes = M.build_note_body(note)
|
|
local line_number = note.position.new_line or note.position.old_line
|
|
local note_node = NuiTree.Node(
|
|
{
|
|
text = text,
|
|
id = note.id,
|
|
file_name = note.position.new_path,
|
|
line_number = line_number,
|
|
is_note = true
|
|
}, text_nodes)
|
|
|
|
return note_node, text, text_nodes
|
|
end
|
|
|
|
M.add_note_to_tree = function(note, discussion_id)
|
|
local note_node = M.build_note(note)
|
|
note_node:expand()
|
|
state.tree:add_node(note_node, discussion_id and ("-" .. discussion_id) or nil)
|
|
state.tree:render()
|
|
local buf = vim.api.nvim_get_current_buf()
|
|
u.darken_metadata(buf, '')
|
|
vim.notify("Sent reply!", vim.log.levels.INFO)
|
|
end
|
|
|
|
M.refresh_tree = function()
|
|
job.run_job("discussions", "GET", nil, function(data)
|
|
if type(data.discussions) ~= "table" then
|
|
vim.notify("No discussions for this MR")
|
|
return
|
|
end
|
|
|
|
if not state.SPLIT_BUF then return end
|
|
|
|
vim.api.nvim_buf_set_option(state.SPLIT_BUF, 'modifiable', true)
|
|
vim.api.nvim_buf_set_option(state.SPLIT_BUF, 'readonly', false)
|
|
vim.api.nvim_buf_set_lines(state.SPLIT_BUF, 0, -1, false, {})
|
|
vim.api.nvim_buf_set_option(state.SPLIT_BUF, 'readonly', true)
|
|
vim.api.nvim_buf_set_option(state.SPLIT_BUF, 'modifiable', false)
|
|
|
|
local tree_nodes = M.add_discussions_to_table(data.discussions)
|
|
state.tree = NuiTree({ nodes = tree_nodes, bufnr = state.SPLIT_BUF })
|
|
M.set_tree_keymaps(state.SPLIT_BUF)
|
|
state.tree:render()
|
|
vim.api.nvim_buf_set_option(state.SPLIT_BUF, 'filetype', 'markdown')
|
|
u.darken_metadata(state.SPLIT_BUF, '')
|
|
end)
|
|
end
|
|
|
|
M.add_discussions_to_table = function(discussions)
|
|
local t = {}
|
|
for _, discussion in ipairs(discussions) do
|
|
local discussion_children = {}
|
|
|
|
-- These properties are filled in by the first note
|
|
local root_text = ''
|
|
local root_note_id = ''
|
|
local root_line_number = 0
|
|
local root_file_name = ''
|
|
local root_id = 0
|
|
local root_text_nodes = {}
|
|
|
|
for j, note in ipairs(discussion.notes) do
|
|
if j == 1 then
|
|
__, root_text, root_text_nodes = M.build_note(note)
|
|
root_file_name = note.position.new_path
|
|
root_line_number = note.position.new_line or note.position.old_line
|
|
root_id = discussion.id
|
|
root_note_id = note.id
|
|
else -- Otherwise insert it as a child node...
|
|
local note_node = M.build_note(note)
|
|
table.insert(discussion_children, note_node)
|
|
end
|
|
end
|
|
|
|
-- Creates the first node in the discussion, and attaches children
|
|
local body = u.join_tables(root_text_nodes, discussion_children)
|
|
local root_node = NuiTree.Node({
|
|
text = root_text,
|
|
is_note = true,
|
|
is_root = true,
|
|
id = root_id,
|
|
root_note_id = root_note_id,
|
|
file_name = root_file_name,
|
|
line_number = root_line_number,
|
|
}, body)
|
|
|
|
table.insert(t, root_node)
|
|
end
|
|
|
|
return t
|
|
end
|
|
|
|
return M
|