feat: first implementation for issues and pull requests
This commit is contained in:
261
lua/gitea/pulls.lua
Normal file
261
lua/gitea/pulls.lua
Normal file
@@ -0,0 +1,261 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- lua/gitea/pulls.lua
|
||||
--
|
||||
-- Provides functions specifically for Gitea pull requests:
|
||||
-- list, create, merge, comment, etc.
|
||||
-- We add a key binding to merge the PR in the PR buffer.
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
local M = {}
|
||||
local core = require('gitea')
|
||||
local Job = require('plenary.job')
|
||||
local lyaml = require("lyaml")
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
-- fallback for owner/repo
|
||||
----------------------------------------------------------------------------
|
||||
local function fallback_owner_repo(owner, repo)
|
||||
if (not owner or owner == "") or (not repo or repo == "") then
|
||||
owner = core._cached_owner
|
||||
repo = core._cached_repo
|
||||
end
|
||||
if not owner or not repo then
|
||||
vim.schedule(function()
|
||||
vim.cmd([[echoerr "No owner/repo provided and none found in .git/config"]])
|
||||
end)
|
||||
return nil, nil
|
||||
end
|
||||
return owner, repo
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
-- M.list_pulls
|
||||
----------------------------------------------------------------------------
|
||||
function M.list_pulls(owner, repo, on_done)
|
||||
owner, repo = fallback_owner_repo(owner, repo)
|
||||
if not owner or not repo then return end
|
||||
|
||||
local ep = string.format("/api/v1/repos/%s/%s/pulls", owner, repo)
|
||||
core.request("GET", ep, nil, function(data, err)
|
||||
if err then
|
||||
vim.schedule(function()
|
||||
vim.cmd(('echoerr "Failed to list PRs: %s"'):format(err))
|
||||
end)
|
||||
return
|
||||
end
|
||||
if on_done then
|
||||
on_done(data)
|
||||
else
|
||||
print(string.format("Got %d PRs for %s/%s", #data, owner, repo))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
-- M.merge_pull
|
||||
----------------------------------------------------------------------------
|
||||
function M.merge_pull(owner, repo, pr_number)
|
||||
owner, repo = fallback_owner_repo(owner, repo)
|
||||
if not owner or not repo or not pr_number then
|
||||
vim.schedule(function()
|
||||
vim.cmd([[echoerr "Usage: :Gitea pr merge <owner> <repo> <pr_number>"]])
|
||||
end)
|
||||
return
|
||||
end
|
||||
local ep = string.format("/api/v1/repos/%s/%s/pulls/%d/merge", owner, repo, pr_number)
|
||||
local data = { Do = "merge" }
|
||||
core.request("POST", ep, data, function(_, err)
|
||||
if err then
|
||||
vim.schedule(function()
|
||||
vim.cmd(('echoerr "Failed to merge PR: %s"'):format(err))
|
||||
end)
|
||||
return
|
||||
end
|
||||
print(("Merged PR #%d"):format(pr_number))
|
||||
end)
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
-- M.create_pull
|
||||
----------------------------------------------------------------------------
|
||||
function M.create_pull(owner, repo, head, base, title, body, cb)
|
||||
owner, repo = fallback_owner_repo(owner, repo)
|
||||
if not owner or not repo then
|
||||
if cb then cb(nil, "No owner/repo provided") end
|
||||
return
|
||||
end
|
||||
if not head or head == "" or not base or base == "" then
|
||||
if cb then cb(nil, "Head or base branch not specified") end
|
||||
return
|
||||
end
|
||||
local ep = string.format("/api/v1/repos/%s/%s/pulls", owner, repo)
|
||||
local data = {
|
||||
head = head,
|
||||
base = base,
|
||||
title = title or ("PR from " .. head),
|
||||
body = body or ""
|
||||
}
|
||||
core.request("POST", ep, data, function(resp, err)
|
||||
if err then
|
||||
if cb then cb(nil, err) end
|
||||
return
|
||||
end
|
||||
if cb then
|
||||
cb(resp, nil)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
-- Buffer approach for the PR (like issues)
|
||||
----------------------------------------------------------------------------
|
||||
local function set_pr_buffer_options(buf)
|
||||
vim.api.nvim_buf_set_option(buf, "buftype", "acwrite") -- allow saving
|
||||
vim.api.nvim_buf_set_option(buf, "bufhidden", "wipe")
|
||||
end
|
||||
|
||||
local function attach_pr_autocmds(buf)
|
||||
vim.api.nvim_create_autocmd("BufWriteCmd", {
|
||||
buffer = buf,
|
||||
callback = function()
|
||||
local owner = vim.api.nvim_buf_get_var(buf, "gitea_pr_owner")
|
||||
local repo = vim.api.nvim_buf_get_var(buf, "gitea_pr_repo")
|
||||
local number = vim.api.nvim_buf_get_var(buf, "gitea_pr_number")
|
||||
|
||||
local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
|
||||
local doc_str = table.concat(lines, "\n")
|
||||
|
||||
-- Parse as YAML
|
||||
local ok, docs = pcall(lyaml.load, doc_str)
|
||||
if not ok or type(docs) ~= "table" then
|
||||
vim.schedule(function()
|
||||
vim.cmd([[echoerr "Failed to parse PR buffer as YAML!"]])
|
||||
end)
|
||||
return
|
||||
end
|
||||
|
||||
local pull_data = docs.pull or {}
|
||||
local patch_data = {}
|
||||
|
||||
if type(pull_data.title) == "string" and pull_data.title ~= "" then
|
||||
patch_data.title = pull_data.title
|
||||
end
|
||||
if type(pull_data.body) == "string" then
|
||||
patch_data.body = pull_data.body
|
||||
end
|
||||
|
||||
if vim.tbl_count(patch_data) > 0 then
|
||||
local ep = string.format("/api/v1/repos/%s/%s/pulls/%d", owner, repo, number)
|
||||
core.request("PATCH", ep, patch_data, function(_, err)
|
||||
if err then
|
||||
vim.schedule(function()
|
||||
vim.cmd(('echoerr "Failed to update pull request: %s"'):format(err))
|
||||
end)
|
||||
return
|
||||
end
|
||||
print("[gitea.nvim] Pull request updated.")
|
||||
end)
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
-- Keymap callback to merge the current PR
|
||||
----------------------------------------------------------------------------
|
||||
function M.merge_current_pr()
|
||||
local buf = vim.api.nvim_get_current_buf()
|
||||
local owner = vim.api.nvim_buf_get_var(buf, "gitea_pr_owner")
|
||||
local repo = vim.api.nvim_buf_get_var(buf, "gitea_pr_repo")
|
||||
local number = vim.api.nvim_buf_get_var(buf, "gitea_pr_number")
|
||||
M.merge_pull(owner, repo, number)
|
||||
end
|
||||
|
||||
function M.open_pull_in_buffer(owner, repo, pr_number)
|
||||
owner, repo = fallback_owner_repo(owner, repo)
|
||||
if not owner or not repo or not pr_number then
|
||||
vim.schedule(function()
|
||||
vim.cmd([[echoerr "Usage: open_pull_in_buffer <owner> <repo> <pr_number>"]])
|
||||
end)
|
||||
return
|
||||
end
|
||||
|
||||
-- 1) Get PR details
|
||||
local pr_ep = string.format("/api/v1/repos/%s/%s/pulls/%d", owner, repo, pr_number)
|
||||
core.request("GET", pr_ep, nil, function(pr, err)
|
||||
if err then
|
||||
vim.schedule(function()
|
||||
vim.cmd(('echoerr "Failed to load PR: %s"'):format(err))
|
||||
end)
|
||||
return
|
||||
end
|
||||
|
||||
-- 2) Create buffer, fill it
|
||||
vim.schedule(function()
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_set_name(buf, string.format("gitea://pr_%d.yaml", pr_number))
|
||||
vim.api.nvim_buf_set_option(buf, "filetype", "yaml")
|
||||
|
||||
set_pr_buffer_options(buf)
|
||||
attach_pr_autocmds(buf)
|
||||
|
||||
vim.api.nvim_buf_set_var(buf, "gitea_pr_owner", owner)
|
||||
vim.api.nvim_buf_set_var(buf, "gitea_pr_repo", repo)
|
||||
vim.api.nvim_buf_set_var(buf, "gitea_pr_number", pr_number)
|
||||
|
||||
local lines = {}
|
||||
table.insert(lines, "pull:")
|
||||
table.insert(lines, (" number: %d"):format(pr.number or pr_number))
|
||||
table.insert(lines, (" title: %s"):format(pr.title or ""))
|
||||
table.insert(lines, (" state: %s"):format(pr.state or ""))
|
||||
table.insert(lines, " body: |")
|
||||
if type(pr.body) == "string" and #pr.body > 0 then
|
||||
for line in string.gmatch(pr.body, "[^\r\n]+") do
|
||||
table.insert(lines, " " .. line)
|
||||
end
|
||||
end
|
||||
|
||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
||||
|
||||
-- Keymap to merge the current PR from the buffer
|
||||
vim.api.nvim_buf_set_keymap(
|
||||
buf,
|
||||
"n",
|
||||
"<leader>pm",
|
||||
":lua require('gitea.pulls').merge_current_pr()<CR>",
|
||||
{ noremap = true, silent = true }
|
||||
)
|
||||
|
||||
-- Keymap to close the PR buffer quickly
|
||||
vim.api.nvim_buf_set_keymap(
|
||||
buf,
|
||||
"n",
|
||||
"<leader>pq",
|
||||
":bd!<CR>",
|
||||
{ noremap = true, silent = true }
|
||||
)
|
||||
|
||||
-- Show the buffer
|
||||
if core.preview_style == "split" then
|
||||
vim.cmd("vsplit")
|
||||
vim.api.nvim_set_current_buf(buf)
|
||||
else
|
||||
local width = math.floor(vim.o.columns * 0.7)
|
||||
local height = math.floor(vim.o.lines * 0.7)
|
||||
local row = math.floor((vim.o.lines - height) / 2)
|
||||
local col = math.floor((vim.o.columns - width) / 2)
|
||||
vim.api.nvim_open_win(buf, true, {
|
||||
relative = "editor",
|
||||
width = width,
|
||||
height = height,
|
||||
row = row,
|
||||
col = col,
|
||||
style = "minimal",
|
||||
border = "rounded"
|
||||
})
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
return M
|
||||
Reference in New Issue
Block a user