Rebased all commits, v0.0.1
added a new file First major commit Successful POST of new comment 🚀 Updated README Updated README 📕 Added more infrastructure Creating async job More setup Getting arguments from Neovim -> Lua -> Golang Moved commands Added getProjectInfo command Adding make comment command Setting up arguments for MakeComment command Removed extraneous comments Setup basic function for adding comments Lint fixes Handling bad requests correctly Better formatting Printing success message Adding utility table print Set comment from popup UI Added mappings for closing and sending text Moved popup into separate file Added comment Cleaned up code and added approve command Initialize project information Removed extraneous import Don't initialize project in non-gitlab directories Setup approve command Set up revoke and approve commands correctly Cleaned up redundant code Moved get current branch command Reorganization of the code First attempt to add step installing Go binary Adjusted path to binary Added install bin check Do Lua method Fixed install step Tweaked binPath + bin Added basic readme information 📗 Removed .luarc.json file Adding diffview command Added string_starts function Made base branch configurable Added note to readme Fixed readme Added diffview dep to readme Update README.md Update README.md Update README.md Update README.md Renamed files Set up developer workflow Updated README Removed dev note Refactor and moving around files Fixed ft/after mappings Setup read command Added read summary command Got rid of filetype bindings and set up commands Set correct filetype for comment buffer Added read() command to README Updated review -> summary Fixed issue with diffview buffers Added command for getting and showing all comments (out of order) Better error message Adding more code to handle showing comments Added ability to jump from comment to specific changed buffer line Initial refactor Added simple comment action Fixed error message More cleanup Fixed bug with M.PROJECT_ID Leaving comment refactor Fixed comment Cleaned up old code Added missing exit command Check gitlab repo status before initialization Better help strings Added ListDiscussions command Added Go code Darkened metadata, filtered out non-real discussions Removed dummy log Sort the discussions by most recent activity Grab hash of current discussion Wired up reply action in Lua code Moved to NUI Table Adding basic jump-to-file ability More tweaks Allow jump anywhere in the tree Ability to reply directly in the buffer window Jump to location in file Don't jump if no refresh is set Cleaned up mappings + other code Get rid of gitlab CLI dependency Fixed discussions bug Don't initialize client on main/master branches Moved comment into separate module Moved lua modules into separate files Modularized library and state Slightly better error/exception handling Added license file Updated readme Moved into todo.md file Added todo file Standardized naming conventions (snake_case in Lua, camelCase in Go) Moved common popup state into utils folder Cleaned up keymapping functions Note on install Changing bin path Updated README Chnaged from success to info Redirect output to /dev/null on build Checking install code Removed print statement Slight reorganization Setting up delete comment Set up confirmation modal Passing in node ID to delete_comment Functioning comment deletion Added delete_comment command Updated README Furhter modularized discussion code Cleaned up and refactored reply code Update README.md Added ability to edit comments Updated todos Fixed main/master base branch issue Set up keybinding rules Updated todo.md Removed diffview dependency Slight cleanup 🧹 Trying something out... Trying something for the binary... Trying again Fixed install for non-lazy users Update README.md Update README.md Update README.md Update README.md Update README.md
This commit is contained in:
18
lua/gitlab/approve.lua
Normal file
18
lua/gitlab/approve.lua
Normal file
@@ -0,0 +1,18 @@
|
||||
local u = require("gitlab.utils")
|
||||
local state = require("gitlab.state")
|
||||
local Job = require("plenary.job")
|
||||
local M = {}
|
||||
|
||||
-- Approves the current merge request
|
||||
M.approve = function()
|
||||
if u.base_invalid() then return end
|
||||
Job:new({
|
||||
command = state.BIN,
|
||||
args = { "approve", state.PROJECT_ID },
|
||||
on_stdout = u.print_success,
|
||||
on_stderr = u.print_error
|
||||
}):start()
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
187
lua/gitlab/comment.lua
Normal file
187
lua/gitlab/comment.lua
Normal file
@@ -0,0 +1,187 @@
|
||||
local Menu = require("nui.menu")
|
||||
local NuiTree = require("nui.tree")
|
||||
local Job = require("plenary.job")
|
||||
local state = require("gitlab.state")
|
||||
local u = require("gitlab.utils")
|
||||
local keymaps = require("gitlab.keymaps")
|
||||
local Popup = require("nui.popup")
|
||||
local M = {}
|
||||
|
||||
local commentPopup = Popup(u.create_popup_state("Comment", "40%", "60%"))
|
||||
local editPopup = Popup(u.create_popup_state("Edit Comment", "80%", "80%"))
|
||||
|
||||
M.create_comment = function()
|
||||
if u.base_invalid() then return end
|
||||
commentPopup:mount()
|
||||
keymaps.set_popup_keymaps(commentPopup, M.confirm_create_comment)
|
||||
end
|
||||
|
||||
-- Sends the comment to Gitlab
|
||||
M.confirm_create_comment = function(text)
|
||||
if u.base_invalid() then return end
|
||||
local relative_file_path = u.get_relative_file_path()
|
||||
local current_line_number = u.get_current_line_number()
|
||||
Job:new({
|
||||
command = state.BIN,
|
||||
args = {
|
||||
"comment",
|
||||
state.PROJECT_ID,
|
||||
current_line_number,
|
||||
relative_file_path,
|
||||
text,
|
||||
},
|
||||
-- TODO: Render the tree after comment creation. Refresh?
|
||||
on_stdout = u.print_success,
|
||||
on_stderr = u.print_error
|
||||
}):start()
|
||||
end
|
||||
|
||||
M.delete_comment = function()
|
||||
local menu = Menu({
|
||||
position = "50%",
|
||||
size = {
|
||||
width = 25,
|
||||
},
|
||||
border = {
|
||||
style = "single",
|
||||
text = {
|
||||
top = "Delete Comment?",
|
||||
top_align = "center",
|
||||
},
|
||||
},
|
||||
win_options = {
|
||||
winhighlight = "Normal:Normal,FloatBorder:Normal",
|
||||
},
|
||||
}, {
|
||||
lines = {
|
||||
Menu.item("Confirm"),
|
||||
Menu.item("Cancel"),
|
||||
},
|
||||
max_width = 20,
|
||||
keymap = {
|
||||
focus_next = state.keymaps.dialogue.focus_next,
|
||||
focus_prev = state.keymaps.dialogue.focus_prev,
|
||||
close = state.keymaps.dialogue.close,
|
||||
submit = state.keymaps.dialogue.submit,
|
||||
},
|
||||
on_submit = function(item)
|
||||
if item.text == "Confirm" then
|
||||
local note_id
|
||||
local node = state.tree:get_node()
|
||||
if node.is_note then
|
||||
note_id = node:get_id()
|
||||
end
|
||||
local parentId = node:get_parent_id()
|
||||
while (parentId ~= nil) do
|
||||
node = state.tree:get_node(parentId)
|
||||
parentId = node:get_parent_id()
|
||||
if node.is_note then
|
||||
note_id = node:get_id()
|
||||
end
|
||||
end
|
||||
local discussion_id = node:get_id()
|
||||
discussion_id = string.sub(discussion_id, 2) -- Remove the "-" at the start
|
||||
note_id = string.sub(note_id, 2) -- Remove the "-" at the start
|
||||
Job:new({
|
||||
command = state.BIN,
|
||||
args = {
|
||||
"deleteComment",
|
||||
state.PROJECT_ID,
|
||||
discussion_id,
|
||||
note_id,
|
||||
},
|
||||
on_stdout = function(_, line)
|
||||
vim.schedule(function()
|
||||
if line ~= nil and line ~= "" then
|
||||
require("notify")(line, "info")
|
||||
state.tree:remove_node("-" .. note_id)
|
||||
local discussion_node = state.tree:get_node("-" .. discussion_id)
|
||||
if not discussion_node:has_children() then
|
||||
state.tree:remove_node("-" .. discussion_id)
|
||||
end
|
||||
state.tree:render()
|
||||
end
|
||||
end)
|
||||
end,
|
||||
on_stderr = u.print_error
|
||||
}):start()
|
||||
end
|
||||
end,
|
||||
})
|
||||
menu:mount()
|
||||
end
|
||||
|
||||
|
||||
M.edit_comment = function()
|
||||
if u.base_invalid() then return end
|
||||
local node = state.tree:get_node()
|
||||
if node.is_discussion then return end
|
||||
if node.is_body then
|
||||
local parentId = node:get_parent_id()
|
||||
node = state.tree:get_node(parentId) -- Get the node for the comment
|
||||
end
|
||||
|
||||
editPopup:mount()
|
||||
|
||||
local note_id = string.sub(node:get_id(), 2) -- Remove the "-" at the start
|
||||
local discussion_id = node:get_parent_id()
|
||||
discussion_id = string.sub(discussion_id, 2) -- Remove the "-" at the start
|
||||
|
||||
state.ACTIVE_DISCUSSION = discussion_id
|
||||
state.ACTIVE_NOTE = note_id
|
||||
|
||||
local lines = {}
|
||||
local childrenIds = node:get_child_ids()
|
||||
for _, value in ipairs(childrenIds) do
|
||||
local line = state.tree:get_node(value).text
|
||||
table.insert(lines, line)
|
||||
end
|
||||
|
||||
local currentBuffer = vim.api.nvim_get_current_buf()
|
||||
vim.api.nvim_buf_set_lines(currentBuffer, 0, -1, false, lines)
|
||||
keymaps.set_popup_keymaps(editPopup, M.send_edits)
|
||||
end
|
||||
|
||||
M.send_edits = function(text)
|
||||
Job:new({
|
||||
command = state.BIN,
|
||||
args = {
|
||||
"editComment",
|
||||
state.PROJECT_ID,
|
||||
state.ACTIVE_DISCUSSION,
|
||||
state.ACTIVE_NOTE,
|
||||
text,
|
||||
},
|
||||
on_stdout = function(_, line)
|
||||
local note = vim.json.decode(line)
|
||||
if note == nil then
|
||||
require("notify")("There was an issue editing the note", "error")
|
||||
return
|
||||
end
|
||||
|
||||
vim.schedule(function()
|
||||
local node = state.tree:get_node("-" .. state.ACTIVE_NOTE)
|
||||
|
||||
local childrenIds = node:get_child_ids()
|
||||
for _, value in ipairs(childrenIds) do
|
||||
state.tree:remove_node(value)
|
||||
end
|
||||
|
||||
local newNoteTextNodes = {}
|
||||
for bodyLine in note.body:gmatch("[^\n]+") do
|
||||
table.insert(newNoteTextNodes, NuiTree.Node({ text = bodyLine, is_body = true }, {}))
|
||||
end
|
||||
|
||||
state.tree:set_nodes(newNoteTextNodes, "-" .. state.ACTIVE_NOTE)
|
||||
|
||||
state.tree:render()
|
||||
local buf = vim.api.nvim_get_current_buf()
|
||||
u.darken_metadata(buf, '')
|
||||
require("notify")("Edited comment!")
|
||||
end)
|
||||
end,
|
||||
on_stderr = u.print_error
|
||||
}):start()
|
||||
end
|
||||
|
||||
return M
|
||||
207
lua/gitlab/discussions.lua
Normal file
207
lua/gitlab/discussions.lua
Normal file
@@ -0,0 +1,207 @@
|
||||
local u = require("gitlab.utils")
|
||||
local NuiTree = require("nui.tree")
|
||||
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
|
||||
require("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, '')
|
||||
require("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)
|
||||
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)
|
||||
if discussions == nil then
|
||||
require("notify")("No discussions found for this MR", "warn")
|
||||
else
|
||||
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, '')
|
||||
if not is_refresh then
|
||||
M.jump_to_file()
|
||||
end
|
||||
end
|
||||
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 note_node = NuiTree.Node(
|
||||
{
|
||||
text = noteHeader,
|
||||
id = note.id,
|
||||
file_name = note.position.new_path,
|
||||
line_number = note.position.new_line,
|
||||
is_note = true
|
||||
}, noteTextNodes)
|
||||
|
||||
return note_node
|
||||
end
|
||||
|
||||
return M
|
||||
79
lua/gitlab/init.lua
Normal file
79
lua/gitlab/init.lua
Normal file
@@ -0,0 +1,79 @@
|
||||
local Job = require("plenary.job")
|
||||
local state = require("gitlab.state")
|
||||
local discussions = require("gitlab.discussions")
|
||||
local summary = require("gitlab.summary")
|
||||
local keymaps = require("gitlab.keymaps")
|
||||
local comment = require("gitlab.comment")
|
||||
local approve = require("gitlab.approve")
|
||||
local revoke = require("gitlab.revoke")
|
||||
local u = require("gitlab.utils")
|
||||
|
||||
-- Root Module Scope
|
||||
local M = {}
|
||||
M.summary = summary.summary
|
||||
M.approve = approve.approve
|
||||
M.revoke = revoke.revoke
|
||||
M.create_comment = comment.create_comment
|
||||
M.list_discussions = discussions.list_discussions
|
||||
M.edit_comment = comment.edit_comment
|
||||
M.delete_comment = comment.delete_comment
|
||||
M.reply = discussions.reply
|
||||
|
||||
-- Builds the Go binary, initializes the plugin, fetches MR info
|
||||
local projectData = {}
|
||||
local function current_file_path()
|
||||
local path = debug.getinfo(1, 'S').source:sub(2)
|
||||
return vim.fn.fnamemodify(path, ':p')
|
||||
end
|
||||
|
||||
M.setup = function(args)
|
||||
local file_path = current_file_path()
|
||||
local parent_dir = vim.fn.fnamemodify(file_path, ":h:h:h")
|
||||
|
||||
state.BIN_PATH = parent_dir
|
||||
state.BIN = parent_dir .. "/bin"
|
||||
|
||||
local binExists = io.open(state.BIN, "r")
|
||||
if not binExists or args.dev == true then
|
||||
local command = string.format("cd %s && make", state.BIN_PATH)
|
||||
local installCode = os.execute(command .. "> /dev/null")
|
||||
if installCode ~= 0 then
|
||||
require("notify")("Could not install gitlab.nvim! Do you have Go installed?", "error")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if args.project_id == nil then
|
||||
error("No project ID provided!")
|
||||
end
|
||||
state.PROJECT_ID = args.project_id
|
||||
|
||||
if args.base_branch ~= nil then
|
||||
state.BASE_BRANCH = args.base_branch
|
||||
end
|
||||
|
||||
if u.is_gitlab_repo() then
|
||||
Job:new({
|
||||
command = state.BIN,
|
||||
args = { "info", state.PROJECT_ID },
|
||||
on_stdout = function(_, line)
|
||||
table.insert(projectData, line)
|
||||
end,
|
||||
on_stderr = u.print_error,
|
||||
on_exit = function()
|
||||
if projectData[1] ~= nil then
|
||||
local parsed_ok, data = pcall(vim.json.decode, projectData[1])
|
||||
if parsed_ok ~= true then
|
||||
require("notify")("Failed calling setup. Could not get project data.", "error")
|
||||
else
|
||||
state.INFO = data
|
||||
end
|
||||
end
|
||||
end,
|
||||
}):start()
|
||||
end
|
||||
|
||||
keymaps.set_keymap_keys(args.keymaps)
|
||||
end
|
||||
|
||||
return M
|
||||
22
lua/gitlab/keymaps.lua
Normal file
22
lua/gitlab/keymaps.lua
Normal file
@@ -0,0 +1,22 @@
|
||||
local u = require("gitlab.utils")
|
||||
local state = require("gitlab.state")
|
||||
local M = {}
|
||||
|
||||
M.set_popup_keymaps = function(popup, action)
|
||||
vim.keymap.set('n', state.keymaps.popup.exit, function() u.exit(popup) end, { buffer = true })
|
||||
vim.keymap.set('n', ':', '', { buffer = true })
|
||||
if action ~= nil then
|
||||
vim.keymap.set('n', state.keymaps.popup.perform_action, function()
|
||||
local text = u.get_buffer_text(popup.bufnr)
|
||||
popup:unmount()
|
||||
action(text)
|
||||
end, { buffer = true })
|
||||
end
|
||||
end
|
||||
|
||||
M.set_keymap_keys = function(keyTable)
|
||||
if keyTable == nil then return end
|
||||
state.keymaps = u.merge_tables(state.keymaps, keyTable)
|
||||
end
|
||||
|
||||
return M
|
||||
17
lua/gitlab/revoke.lua
Normal file
17
lua/gitlab/revoke.lua
Normal file
@@ -0,0 +1,17 @@
|
||||
local u = require("gitlab.utils")
|
||||
local state = require("gitlab.state")
|
||||
local M = {}
|
||||
local Job = require("plenary.job")
|
||||
|
||||
-- Revokes approval for the current merge request
|
||||
M.revoke = function()
|
||||
if u.base_invalid() then return end
|
||||
Job:new({
|
||||
command = state.BIN,
|
||||
args = { "revoke", state.PROJECT_ID },
|
||||
on_stdout = u.print_success,
|
||||
on_stderr = u.print_error
|
||||
}):start()
|
||||
end
|
||||
|
||||
return M
|
||||
29
lua/gitlab/state.lua
Normal file
29
lua/gitlab/state.lua
Normal file
@@ -0,0 +1,29 @@
|
||||
local M = {}
|
||||
|
||||
M.BIN_PATH = nil
|
||||
M.BIN = nil
|
||||
M.PROJECT_ID = nil
|
||||
M.ACTIVE_DISCUSSION = nil
|
||||
M.ACTIVE_NOTE = nil
|
||||
M.BASE_BRANCH = "main"
|
||||
M.keymaps = {
|
||||
popup = {
|
||||
exit = "<Esc>",
|
||||
perform_action = "<leader>s",
|
||||
},
|
||||
discussion_tree = {
|
||||
jump_to_location = "o",
|
||||
edit_comment = "e",
|
||||
delete_comment = "dd",
|
||||
reply_to_comment = "r",
|
||||
toggle_node = "t",
|
||||
},
|
||||
dialogue = {
|
||||
focus_next = { "j", "<Down>", "<Tab>" },
|
||||
focus_prev = { "k", "<Up>", "<S-Tab>" },
|
||||
close = { "<Esc>", "<C-c>" },
|
||||
submit = { "<CR>", "<Space>" },
|
||||
}
|
||||
}
|
||||
|
||||
return M
|
||||
27
lua/gitlab/summary.lua
Normal file
27
lua/gitlab/summary.lua
Normal file
@@ -0,0 +1,27 @@
|
||||
local state = require("gitlab.state")
|
||||
local Popup = require("nui.popup")
|
||||
local u = require("gitlab.utils")
|
||||
local keymaps = require("gitlab.keymaps")
|
||||
local summaryPopup = Popup(u.create_popup_state("Loading Summary...", "80%", "80%"))
|
||||
local M = {}
|
||||
|
||||
M.summary = function()
|
||||
if u.base_invalid() then return end
|
||||
summaryPopup:mount()
|
||||
local currentBuffer = vim.api.nvim_get_current_buf()
|
||||
local title = state.INFO.title
|
||||
local description = state.INFO.description
|
||||
local lines = {}
|
||||
for line in description:gmatch("[^\n]+") do
|
||||
table.insert(lines, line)
|
||||
table.insert(lines, "")
|
||||
end
|
||||
vim.schedule(function()
|
||||
vim.api.nvim_buf_set_lines(currentBuffer, 0, -1, false, lines)
|
||||
vim.api.nvim_buf_set_option(currentBuffer, "modifiable", false)
|
||||
summaryPopup.border:set_text("top", title, "center")
|
||||
keymaps.set_popup_keymaps(summaryPopup)
|
||||
end)
|
||||
end
|
||||
|
||||
return M
|
||||
226
lua/gitlab/utils/init.lua
Normal file
226
lua/gitlab/utils/init.lua
Normal file
@@ -0,0 +1,226 @@
|
||||
local state = require("gitlab.state")
|
||||
|
||||
local function get_git_root()
|
||||
local output = vim.fn.system('git rev-parse --show-toplevel 2>/dev/null')
|
||||
if vim.v.shell_error == 0 then
|
||||
return vim.fn.substitute(output, '\n', '', '')
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function get_relative_file_path()
|
||||
local git_root = get_git_root()
|
||||
if git_root ~= nil then
|
||||
local current_file = vim.fn.expand('%:p')
|
||||
return vim.fn.substitute(current_file, git_root .. '/', '', '')
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local get_current_line_number = function()
|
||||
return vim.api.nvim_call_function('line', { '.' })
|
||||
end
|
||||
|
||||
function P(...)
|
||||
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
|
||||
|
||||
local function get_buffer_text(bufnr)
|
||||
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
|
||||
local text = table.concat(lines, "\n")
|
||||
return text
|
||||
end
|
||||
|
||||
local feature_branch_exists = function(base_branch)
|
||||
local is_git_branch = io.popen("git rev-parse --is-inside-work-tree 2>/dev/null"):read("*a")
|
||||
if is_git_branch == "true\n" then
|
||||
for line in io.popen("git branch 2>/dev/null"):lines() do
|
||||
line = line:gsub("%s+", "")
|
||||
if line == base_branch then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local string_starts = function(str, start)
|
||||
return str:sub(1, #start) == start
|
||||
end
|
||||
|
||||
local press_enter = function()
|
||||
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<CR>", false, true, true), "n", false)
|
||||
end
|
||||
|
||||
local base_invalid = function()
|
||||
local current_branch_raw = io.popen("git rev-parse --abbrev-ref HEAD"):read("*a")
|
||||
local current_branch = string.gsub(current_branch_raw, "\n", "")
|
||||
|
||||
if current_branch == "main" or current_branch == "master" then
|
||||
require("notify")('On ' .. current_branch .. ' branch, no MRs available', "error")
|
||||
return true
|
||||
end
|
||||
|
||||
local base = state.BASE_BRANCH
|
||||
local hasBaseBranch = feature_branch_exists(base)
|
||||
if not hasBaseBranch then
|
||||
require("notify")('No base branch. If this is a Gitlab repository, please check your setup function!', "error")
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local format_date = function(date_string)
|
||||
local year, month, day, hour, min, sec = date_string:match("(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)")
|
||||
local date = os.time({ year = year, month = month, day = day, hour = hour, min = min, sec = sec })
|
||||
|
||||
-- Format date into human-readable string without leading zeros
|
||||
local formatted_date = os.date("%A, %B %e at %l:%M %p", date)
|
||||
return formatted_date
|
||||
end
|
||||
|
||||
local add_comment_sign = function(line_number)
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
vim.cmd("sign define piet text= texthl=Substitute")
|
||||
vim.fn.sign_place(0, "piet", "piet", bufnr, { lnum = line_number })
|
||||
end
|
||||
|
||||
local function is_gitlab_repo()
|
||||
local current_dir = vim.fn.getcwd()
|
||||
|
||||
-- check if it contains a .git folder
|
||||
local git_dir = current_dir .. "/.git"
|
||||
if vim.fn.isdirectory(git_dir) == 0 then
|
||||
return false
|
||||
end
|
||||
|
||||
local git_cmd = 'git remote get-url origin'
|
||||
local handle = io.popen(git_cmd)
|
||||
local result = handle:read("*a")
|
||||
handle:close()
|
||||
|
||||
-- check if the remote URL is a Gitlab URL
|
||||
if string.match(result, "gitlab%.com") then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function jump_to_file(filename, line_number)
|
||||
vim.api.nvim_command("wincmd l")
|
||||
local bufnr = vim.fn.bufnr(filename)
|
||||
if bufnr ~= -1 then
|
||||
-- Buffer is already open, switch to it
|
||||
vim.cmd("buffer " .. bufnr)
|
||||
vim.api.nvim_win_set_cursor(0, { line_number, 0 })
|
||||
return
|
||||
end
|
||||
|
||||
-- If buffer is not already open, open it
|
||||
vim.cmd("edit " .. filename)
|
||||
vim.api.nvim_win_set_cursor(0, { line_number, 0 })
|
||||
end
|
||||
|
||||
local function find_value_by_id(tbl, id)
|
||||
for i = 1, #tbl do
|
||||
if tbl[i].id == tonumber(id) then
|
||||
return tbl[i]
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
vim.cmd("highlight Gray guifg=#888888")
|
||||
local function darken_metadata(bufnr, regex)
|
||||
local num_lines = vim.api.nvim_buf_line_count(bufnr)
|
||||
for i = 0, num_lines - 1 do
|
||||
local line = vim.api.nvim_buf_get_lines(bufnr, i, i + 1, false)[1]
|
||||
if string.match(line, regex) then
|
||||
vim.api.nvim_buf_add_highlight(bufnr, -1, 'Gray', i, 0, -1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function print_success(_, line)
|
||||
if line ~= nil and line ~= "" then
|
||||
require("notify")(line, "info")
|
||||
end
|
||||
end
|
||||
|
||||
local function print_error(_, line)
|
||||
if line ~= nil and line ~= "" then
|
||||
require("notify")(line, "error")
|
||||
end
|
||||
end
|
||||
|
||||
local function exit(popup)
|
||||
popup:unmount()
|
||||
end
|
||||
|
||||
local create_popup_state = function(title, width, height)
|
||||
return {
|
||||
buf_options = {
|
||||
filetype = 'markdown'
|
||||
},
|
||||
enter = true,
|
||||
focusable = true,
|
||||
border = {
|
||||
style = "rounded",
|
||||
text = {
|
||||
top = title
|
||||
},
|
||||
},
|
||||
position = "50%",
|
||||
size = {
|
||||
width = width,
|
||||
height = height,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local M = {}
|
||||
M.merge_tables = function(defaults, overrides)
|
||||
local result = {}
|
||||
|
||||
for key, value in pairs(defaults) do
|
||||
if type(value) == "table" then
|
||||
result[key] = M.merge_tables(value, overrides[key] or {})
|
||||
else
|
||||
result[key] = overrides[key] or value
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
|
||||
M.get_relative_file_path = get_relative_file_path
|
||||
M.get_current_line_number = get_current_line_number
|
||||
M.get_buffer_text = get_buffer_text
|
||||
M.feature_branch_exists = feature_branch_exists
|
||||
M.press_enter = press_enter
|
||||
M.string_starts = string_starts
|
||||
M.base_invalid = base_invalid
|
||||
M.format_date = format_date
|
||||
M.add_comment_sign = add_comment_sign
|
||||
M.jump_to_file = jump_to_file
|
||||
M.find_value_by_id = find_value_by_id
|
||||
M.is_gitlab_repo = is_gitlab_repo
|
||||
M.darken_metadata = darken_metadata
|
||||
M.print_success = print_success
|
||||
M.print_error = print_error
|
||||
M.create_popup_state = create_popup_state
|
||||
M.exit = exit
|
||||
M.P = P
|
||||
return M
|
||||
Reference in New Issue
Block a user