local u = require("gitlab.utils") local NuiTree = require("nui.tree") local notify = require("notify") local state = require("gitlab.state") local Job = require("plenary.job") 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() if u.base_invalid() then return end replyPopup:mount() keymaps.set_popup_keymaps(replyPopup, M.send_reply) end M.send_reply = function(text) Job:new({ command = state.BIN, args = { "reply", state.PROJECT_ID, state.ACTIVE_DISCUSSION, text, }, on_stdout = function(_, line) local note = vim.json.decode(line) if note == nil then notify("There was an issue creating the note", "error") return end local note_node = M.build_note(note) note_node:expand() state.tree:add_node(note_node, "-" .. state.ACTIVE_DISCUSSION) vim.schedule(function() state.tree:render() local buf = vim.api.nvim_get_current_buf() u.darken_metadata(buf, '') notify("Sent reply!") end) end, on_stderr = u.print_error }):start() end -- Places all of the discussions into a readable list M.list_discussions = function() if u.base_invalid() then return end Job:new({ command = state.BIN, args = { "listDiscussions", state.PROJECT_ID }, on_stdout = function(_, line) local discussions = vim.json.decode(line) if (type(discussions) == 'userdata') then notify("No discussions found for this MR", "warn") return end M.discussions = discussions vim.schedule(function() vim.cmd.tabnew() local buf = vim.api.nvim_create_buf(false, true) vim.api.nvim_command("vsplit") vim.api.nvim_buf_set_option(buf, 'filetype', 'markdown') vim.api.nvim_set_current_buf(buf) local allDiscussions = {} for i, discussion in ipairs(discussions) do local discussionChildren = {} for _, note in ipairs(discussion.notes) do local note_node = M.build_note(note) if i == 1 then note_node:expand() end table.insert(discussionChildren, note_node) end local discussionNode = NuiTree.Node({ text = discussion.id, id = discussion.id, is_discussion = true }, discussionChildren) if i == 1 then discussionNode:expand() end table.insert(allDiscussions, discussionNode) end state.tree = NuiTree({ nodes = allDiscussions, bufnr = buf }) M.set_tree_keymaps(buf) state.tree:render() vim.api.nvim_buf_set_option(buf, 'filetype', 'markdown') u.darken_metadata(buf, '') M.jump_to_file() end) end, on_stderr = u.print_error, }):start() end M.jump_to_file = function() local node = state.tree:get_node() if node == nil then return end local childrenIds = node:get_child_ids() -- We have selected a note node if node.file_name ~= nil then u.jump_to_file(node.file_name, node.line_number) elseif node.is_body then local parentId = node:get_parent_id() local parent = state.tree:get_node(parentId) if parent == nil then return end u.jump_to_file(parent.file_name, parent.line_number) else local firstChild = state.tree:get_node(childrenIds[1]) if firstChild == nil then return end u.jump_to_file(firstChild.file_name, firstChild.line_number) end 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) -- 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 -- Get closest discussion parent if node.is_body then local parentId = node:get_parent_id() local parent = state.tree:get_node(parentId) if parent == nil then return end parentId = parent:get_parent_id() parent = state.tree:get_node(parentId) if parent == nil then return end node = parent elseif node.is_note then local parentId = node:get_parent_id() local parent = state.tree:get_node(parentId) if parent == nil then return end node = parent end state.ACTIVE_DISCUSSION = node.id M.reply() end, { buffer = true }) end M.build_note = function(note) local noteTextNodes = {} for bodyLine in note.body:gmatch("[^\n]+") do table.insert(noteTextNodes, NuiTree.Node({ text = bodyLine, is_body = true }, {})) end local noteHeader = "@" .. note.author.username .. " on " .. u.format_date(note.created_at) local line_number = note.position.new_line or note.position.old_line local note_node = NuiTree.Node( { text = noteHeader, id = note.id, file_name = note.position.new_path, line_number = line_number, is_note = true }, noteTextNodes) return note_node end return M