Files
gitlab.nvim/lua/gitlab/discussions.lua
Harrison (Harry) Cramer 6bb7575c6d Attach UUIDs to Discussion Text (#37)
Three backticks will break the discussion tree, this MR specifically adds custom UUIDs to each text node to avoid this issue.
2023-08-15 23:33:52 -04:00

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