This MR is a #MAJOR breaking change to the plugin. While the plugin will continue to work for users with their existing settings, they will be informed of outdated configuration (diagnostics and signs have been simplified) the next time they open the reviewer. Fix: Trim trailing slash from custom URLs Update: .github/CONTRIBUTING.md, .github/ISSUE_TEMPLATE/bug_report.md Feat: Improve discussion tree toggling (#192) Fix: Toggle modified notes (#188) Fix: Toggle discussion nodes correctly Feat: Show Help keymap in discussion tree winbar Fix: Enable toggling nodes from the note body Fix: Enable toggling resolved status from child nodes Fix: Only try to show emoji popup on note nodes Feat: Add keymap for toggling tree type Fix: Disable tree type toggling in Notes Fix Multi Line Issues (Large Refactor) (#197) Fix: Multi-line discussions. The calculation of a range for a multiline comment has been consolidated and moved into the location.lua file. This does not attempt to fix diagnostics. Refactor: It refactors the discussions code to split hunk parsing and management into a separate module Fix: Don't allow comments on modified buffers #194 by preventing comments on the reviewer when using --imply-local and when the working tree is dirty entirely. Refactor: It introduces a new List class for data aggregation, filtering, etc. Fix: It removes redundant API calls and refreshes from the discussion pane Fix: Location provider (#198) Fix: add nil check for Diffview performance issue (#199) Fix: Switch Tabs During Comment Creation (#200) Fix: Check if file is modified (#201) Fix: Off-By-One Issue in Old SHA (#202) Fix: Rebuild Diagnostics + Signs (#203) Fix: Off-By-One Issue in New SHA (#205) Fix: Reviewer Jumps to wrong location (#206) BREAKING CHANGE: Changes configuration of diagnostics and signs in the setup call.
322 lines
7.9 KiB
Lua
322 lines
7.9 KiB
Lua
-- This module is responsible for creating am MR
|
|
-- for the current branch
|
|
local Layout = require("nui.layout")
|
|
local Input = require("nui.input")
|
|
local Popup = require("nui.popup")
|
|
local job = require("gitlab.job")
|
|
local u = require("gitlab.utils")
|
|
local git = require("gitlab.git")
|
|
local state = require("gitlab.state")
|
|
local miscellaneous = require("gitlab.actions.miscellaneous")
|
|
|
|
---@class Mr
|
|
---@field target? string
|
|
---@field title? string
|
|
---@field description? string
|
|
|
|
---@class Args
|
|
---@field target? string
|
|
---@field template_file? string
|
|
|
|
local M = {
|
|
started = false,
|
|
layout_visible = false,
|
|
layout = nil,
|
|
layout_buf = nil,
|
|
title_bufnr = nil,
|
|
description_bufnr = nil,
|
|
mr = {
|
|
target = "",
|
|
title = "",
|
|
description = "",
|
|
},
|
|
}
|
|
|
|
M.reset_state = function()
|
|
M.started = false
|
|
M.mr.title = ""
|
|
M.mr.target = ""
|
|
M.mr.description = ""
|
|
end
|
|
|
|
local title_popup_settings = {
|
|
buf_options = {
|
|
filetype = "markdown",
|
|
},
|
|
focusable = true,
|
|
border = {
|
|
style = "rounded",
|
|
text = {
|
|
top = "Title",
|
|
},
|
|
},
|
|
}
|
|
|
|
local target_popup_settings = {
|
|
buf_options = {
|
|
filetype = "markdown",
|
|
},
|
|
focusable = true,
|
|
border = {
|
|
style = "rounded",
|
|
text = {
|
|
top = "Target branch",
|
|
},
|
|
},
|
|
}
|
|
|
|
local description_popup_settings = {
|
|
buf_options = {
|
|
filetype = "markdown",
|
|
},
|
|
enter = true,
|
|
focusable = true,
|
|
border = {
|
|
style = "rounded",
|
|
text = {
|
|
top = "Description",
|
|
},
|
|
},
|
|
}
|
|
|
|
---1. If the user has already begun writing an MR, prompt them to
|
|
--- continue working on it.
|
|
---@param args? Args
|
|
M.start = function(args)
|
|
if M.started then
|
|
vim.ui.select({ "Yes", "No" }, { prompt = "Continue your previous MR?" }, function(choice)
|
|
if choice == "Yes" then
|
|
M.open_confirmation_popup(M.mr)
|
|
return
|
|
else
|
|
M.reset_state()
|
|
M.pick_target(args)
|
|
end
|
|
end)
|
|
else
|
|
M.pick_target(args)
|
|
end
|
|
end
|
|
|
|
---2. Pick the target branch
|
|
---@param args? Args
|
|
M.pick_target = function(args)
|
|
if not args then
|
|
args = {}
|
|
end
|
|
if args.target ~= nil then
|
|
M.pick_template({ target = args.target }, args)
|
|
return
|
|
end
|
|
|
|
if state.settings.create_mr.target ~= nil then
|
|
M.pick_template({ target = state.settings.create_mr.target }, args)
|
|
return
|
|
end
|
|
|
|
local all_branch_names = u.get_all_git_branches(true)
|
|
vim.ui.select(all_branch_names, {
|
|
prompt = "Choose target branch for merge",
|
|
}, function(choice)
|
|
if choice then
|
|
M.pick_template({ target = choice }, args)
|
|
end
|
|
end)
|
|
end
|
|
|
|
local function make_template_path(t)
|
|
local base_dir = git.base_dir()
|
|
return base_dir
|
|
.. state.settings.file_separator
|
|
.. ".gitlab"
|
|
.. state.settings.file_separator
|
|
.. "merge_request_templates"
|
|
.. state.settings.file_separator
|
|
.. t
|
|
end
|
|
|
|
---3. Pick template (if applicable). This is used as the description
|
|
---@param mr Mr
|
|
---@param args Args
|
|
M.pick_template = function(mr, args)
|
|
if not args then
|
|
args = {}
|
|
end
|
|
|
|
local template_file = args.template_file or state.settings.create_mr.template_file
|
|
if template_file ~= nil then
|
|
local description = u.read_file(make_template_path(template_file))
|
|
M.add_title({ target = mr.target, description = description })
|
|
return
|
|
end
|
|
|
|
local all_templates = u.list_files_in_folder(".gitlab" .. state.settings.file_separator .. "merge_request_templates")
|
|
if all_templates == nil then
|
|
M.add_title({ target = mr.target })
|
|
return
|
|
end
|
|
|
|
local opts = { "Blank Template" }
|
|
for _, v in ipairs(all_templates) do
|
|
table.insert(opts, v)
|
|
end
|
|
vim.ui.select(opts, {
|
|
prompt = "Choose Template",
|
|
}, function(choice)
|
|
if choice then
|
|
local description = u.read_file(make_template_path(choice))
|
|
M.add_title({ target = mr.target, description = description })
|
|
elseif choice == "Blank Template" then
|
|
M.add_title({ target = mr.target })
|
|
end
|
|
end)
|
|
end
|
|
|
|
---4. Prompts the user for the title of the MR
|
|
---@param mr Mr
|
|
M.add_title = function(mr)
|
|
local input = Input({
|
|
position = "50%",
|
|
relative = "editor",
|
|
size = state.settings.create_mr.title_input.width,
|
|
border = {
|
|
style = state.settings.create_mr.title_input.border,
|
|
text = {
|
|
top = "Title",
|
|
},
|
|
},
|
|
}, {
|
|
prompt = "",
|
|
default_value = "",
|
|
on_close = function() end,
|
|
on_submit = function(_value)
|
|
M.open_confirmation_popup(mr)
|
|
end,
|
|
on_change = function(value)
|
|
mr.title = value
|
|
end,
|
|
})
|
|
input:map("n", "<Esc>", function()
|
|
input:unmount()
|
|
end, { noremap = true })
|
|
|
|
input:mount()
|
|
end
|
|
|
|
---5. Show the final popup.
|
|
---The function will render a popup containing the MR title and MR description, and
|
|
---target branch. The title and description are editable.
|
|
---@param mr Mr
|
|
M.open_confirmation_popup = function(mr)
|
|
M.started = true
|
|
if M.layout_visible then
|
|
M.layout:unmount()
|
|
M.layout_visible = false
|
|
return
|
|
end
|
|
|
|
local layout, title_popup, description_popup, target_popup = M.create_layout()
|
|
|
|
M.layout = layout
|
|
M.layout_buf = layout.bufnr
|
|
M.layout_visible = true
|
|
|
|
local function exit()
|
|
local title = vim.fn.trim(u.get_buffer_text(M.title_bufnr))
|
|
local description = u.get_buffer_text(M.description_bufnr)
|
|
local target = vim.fn.trim(u.get_buffer_text(target_popup.bufnr))
|
|
M.mr = {
|
|
title = title,
|
|
description = description,
|
|
target = target,
|
|
}
|
|
layout:unmount()
|
|
M.layout_visible = false
|
|
end
|
|
|
|
local description_lines = mr.description and M.build_description_lines(mr.description) or { "" }
|
|
|
|
vim.schedule(function()
|
|
vim.api.nvim_buf_set_lines(description_popup.bufnr, 0, -1, false, description_lines)
|
|
vim.api.nvim_buf_set_lines(title_popup.bufnr, 0, -1, false, { mr.title })
|
|
vim.api.nvim_buf_set_lines(target_popup.bufnr, 0, -1, false, { mr.target })
|
|
|
|
local popup_opts = {
|
|
cb = exit,
|
|
action_before_close = true,
|
|
action_before_exit = true,
|
|
}
|
|
|
|
state.set_popup_keymaps(description_popup, M.create_mr, miscellaneous.attach_file, popup_opts)
|
|
state.set_popup_keymaps(title_popup, M.create_mr, nil, popup_opts)
|
|
state.set_popup_keymaps(target_popup, M.create_mr, nil, popup_opts)
|
|
|
|
vim.api.nvim_set_current_buf(description_popup.bufnr)
|
|
end)
|
|
end
|
|
|
|
---Builds a lua list of strings that contain the MR description
|
|
M.build_description_lines = function(template_content)
|
|
local description_lines = {}
|
|
for line in u.split_by_new_lines(template_content) do
|
|
table.insert(description_lines, line)
|
|
end
|
|
-- TODO: @harrisoncramer Same as in lua/gitlab/actions/summary.lua:114
|
|
table.insert(description_lines, "")
|
|
|
|
return description_lines
|
|
end
|
|
|
|
---This function will POST the new MR to create it
|
|
M.create_mr = function()
|
|
local description = u.get_buffer_text(M.description_bufnr)
|
|
local title = u.get_buffer_text(M.title_bufnr):gsub("\n", " ")
|
|
local target = u.get_buffer_text(M.target_bufnr):gsub("\n", " ")
|
|
|
|
local body = {
|
|
title = title,
|
|
description = description,
|
|
target_branch = target,
|
|
}
|
|
|
|
job.run_job("/create_mr", "POST", body, function(data)
|
|
u.notify(data.message, vim.log.levels.INFO)
|
|
M.reset_state()
|
|
M.layout:unmount()
|
|
M.layout_visible = false
|
|
end)
|
|
end
|
|
|
|
M.create_layout = function()
|
|
local title_popup = Popup(title_popup_settings)
|
|
M.title_bufnr = title_popup.bufnr
|
|
local description_popup = Popup(description_popup_settings)
|
|
M.description_bufnr = description_popup.bufnr
|
|
local target_branch_popup = Popup(target_popup_settings)
|
|
M.target_bufnr = target_branch_popup.bufnr
|
|
|
|
local internal_layout
|
|
internal_layout = Layout.Box({
|
|
Layout.Box({
|
|
Layout.Box(title_popup, { grow = 1 }),
|
|
Layout.Box(target_branch_popup, { grow = 1 }),
|
|
}, { size = 3 }),
|
|
Layout.Box(description_popup, { grow = 1 }),
|
|
}, { dir = "col" })
|
|
|
|
local layout = Layout({
|
|
position = "50%",
|
|
relative = "editor",
|
|
size = {
|
|
width = "95%",
|
|
height = "95%",
|
|
},
|
|
}, internal_layout)
|
|
|
|
layout:mount()
|
|
|
|
return layout, title_popup, description_popup, target_branch_popup
|
|
end
|
|
|
|
return M
|