Files
gitea.nvim/lua/gitea/commands.lua
2024-12-23 01:46:04 +01:00

444 lines
14 KiB
Lua

local M = {}
local config = require("gitea.config")
local api = require("gitea.api")
local auth = require("gitea.auth")
-- Utility: parse "owner/repo" from the remote URL
local function detect_owner_repo()
local cmd = "git config remote.origin.url"
local url = vim.fn.systemlist(cmd)[1]
if vim.v.shell_error ~= 0 or not url or url == "" then
return nil, nil
end
-- Remove trailing ".git"
url = url:gsub("%.git$", "")
-- Try to parse: e.g. https://my.gitea.com/owner/repo
local _, after = url:match("^(https?://)(.*)$")
if after then
-- after might be "my.gitea.com/owner/repo"
-- let's capture the part after the first slash
local path = after:match("[^/]+/(.*)$") -- skip domain
if not path then
return nil, nil
end
local owner, repo = path:match("^(.-)/(.*)$")
return owner, repo
end
-- SSH style: git@my.gitea.com:owner/repo
local user_host, path = url:match("^(.-):(.*)$")
if user_host and path then
-- user_host might be "git@my.gitea.com"
local owner, repo = path:match("^(.-)/(.*)$")
return owner, repo
end
return nil, nil
end
-------------------------------
-- Subcommands
-------------------------------
local subcommands = {}
subcommands.issue = {
list = function(args)
local owner, repo = detect_owner_repo()
if not owner or not repo then
vim.notify("[gitea.nvim] Could not detect owner/repo.", vim.log.levels.ERROR)
return
end
local issues, status = api.list_issues(owner, repo, {})
if not issues then
vim.notify(string.format("[gitea.nvim] Error fetching issues (HTTP %s).", status or "?"), vim.log.levels.ERROR)
return
end
-- For now, print to message area
print("Issues for " .. owner .. "/" .. repo .. ":")
for _, issue in ipairs(issues) do
print(string.format("#%d %s", issue.number, issue.title))
end
end,
create = function(args)
local owner, repo = detect_owner_repo()
if not owner or not repo then
vim.notify("[gitea.nvim] Could not detect owner/repo.", vim.log.levels.ERROR)
return
end
-- We'll open a scratch buffer for user to fill in Title/Body
-- Then upon writing, we create the issue.
-- For brevity, let's do a simpler approach: ask input in command line
local title = vim.fn.input("Issue title: ")
local body = vim.fn.input("Issue body: ")
local created, status = api.create_issue(owner, repo, {
title = title,
body = body,
})
if created then
vim.notify("[gitea.nvim] Created issue #" .. created.number .. "!")
else
vim.notify(string.format("[gitea.nvim] Failed to create issue (HTTP %s).", status or "?"), vim.log.levels.ERROR)
end
end,
edit = function(args)
local number = tonumber(args[1])
if not number then
vim.notify("[gitea.nvim] Usage: :Gitea issue edit <number>", vim.log.levels.ERROR)
return
end
local owner, repo = detect_owner_repo()
if not owner or not repo then
vim.notify("[gitea.nvim] Could not detect owner/repo.", vim.log.levels.ERROR)
return
end
-- For a minimal approach: fetch issue, then open in a scratch buffer
-- with title/body. On write, update it. We'll do a simple approach:
local issue_data, status = api.get_issue(owner, repo, number)
if not issue_data then
vim.notify(string.format("[gitea.nvim] Error retrieving issue (HTTP %s).", status or "?"), vim.log.levels.ERROR)
return
end
-- Create a new scratch buffer
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_option(buf, "filetype", "markdown")
vim.api.nvim_buf_set_name(buf, "gitea_issue_"..number)
local lines = {
"# Issue #"..issue_data.number,
"Title: "..(issue_data.title or ""),
"",
issue_data.body or "",
}
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
-- Attach an autocmd on BufWriteCmd to push changes
vim.api.nvim_create_autocmd("BufWriteCmd", {
buffer = buf,
callback = function()
local new_lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
if #new_lines < 2 then return end
local new_title = new_lines[1]:gsub("^Title:%s*", "")
local new_body = table.concat({unpack(new_lines, 3)}, "\n")
local updated, st = api.edit_issue(owner, repo, number, {
title = new_title,
body = new_body,
})
if updated then
vim.notify("[gitea.nvim] Issue updated successfully!")
else
vim.notify(string.format("[gitea.nvim] Failed to update issue (HTTP %s).", st or "?"), vim.log.levels.ERROR)
end
end
})
vim.api.nvim_set_current_buf(buf)
end,
close = function(args)
local number = tonumber(args[1])
if not number then
vim.notify("[gitea.nvim] Usage: :Gitea issue close <number>", vim.log.levels.ERROR)
return
end
local owner, repo = detect_owner_repo()
local res, status = api.close_issue(owner, repo, number)
if res then
vim.notify("[gitea.nvim] Issue #"..number.." closed.")
else
vim.notify(string.format("[gitea.nvim] Failed to close issue (HTTP %s).", status or "?"), vim.log.levels.ERROR)
end
end,
reopen = function(args)
local number = tonumber(args[1])
if not number then
vim.notify("[gitea.nvim] Usage: :Gitea issue reopen <number>", vim.log.levels.ERROR)
return
end
local owner, repo = detect_owner_repo()
local res, status = api.reopen_issue(owner, repo, number)
if res then
vim.notify("[gitea.nvim] Issue #"..number.." reopened.")
else
vim.notify(string.format("[gitea.nvim] Failed to reopen issue (HTTP %s).", status or "?"), vim.log.levels.ERROR)
end
end,
comment = function(args)
local number = tonumber(args[1])
if not number then
vim.notify("[gitea.nvim] Usage: :Gitea issue comment <number>", vim.log.levels.ERROR)
return
end
local owner, repo = detect_owner_repo()
local body = vim.fn.input("Comment: ")
if not body or body == "" then
return
end
local res, status = api.comment_issue(owner, repo, number, body)
if res then
vim.notify("[gitea.nvim] Comment added to issue #"..number..".")
else
vim.notify(string.format("[gitea.nvim] Failed to add comment (HTTP %s).", status or "?"), vim.log.levels.ERROR)
end
end,
}
subcommands.pr = {
list = function(args)
local owner, repo = detect_owner_repo()
if not owner or not repo then
vim.notify("[gitea.nvim] Could not detect owner/repo.", vim.log.levels.ERROR)
return
end
local prs, status = api.list_pull_requests(owner, repo, {})
if not prs then
vim.notify(string.format("[gitea.nvim] Error fetching PRs (HTTP %s).", status or "?"), vim.log.levels.ERROR)
return
end
print("Pull Requests for " .. owner .. "/" .. repo .. ":")
for _, pr in ipairs(prs) do
print(string.format("#%d %s (state=%s)", pr.number, pr.title, pr.state))
end
end,
create = function(args)
local owner, repo = detect_owner_repo()
if not owner or not repo then
vim.notify("[gitea.nvim] Could not detect owner/repo.", vim.log.levels.ERROR)
return
end
local title = vim.fn.input("PR title: ")
local body = vim.fn.input("PR body: ")
local base = vim.fn.input("Base branch (e.g. main): ")
local head = vim.fn.input("Head branch (e.g. feature/my-branch): ")
local created, status = api.create_pull_request(owner, repo, {
title = title,
body = body,
base = base,
head = head,
})
if created then
vim.notify("[gitea.nvim] Created PR #" .. created.number .. ".")
else
vim.notify(string.format("[gitea.nvim] Failed to create PR (HTTP %s).", status or "?"), vim.log.levels.ERROR)
end
end,
edit = function(args)
local number = tonumber(args[1])
if not number then
vim.notify("[gitea.nvim] Usage: :Gitea pr edit <number>", vim.log.levels.ERROR)
return
end
local owner, repo = detect_owner_repo()
local pr_data, status = api.get_pull_request(owner, repo, number)
if not pr_data then
vim.notify(string.format("[gitea.nvim] Could not get PR (HTTP %s).", status or "?"), vim.log.levels.ERROR)
return
end
-- Similar scratch buffer approach
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_option(buf, "filetype", "markdown")
vim.api.nvim_buf_set_name(buf, "gitea_pr_"..number)
local lines = {
"# PR #"..pr_data.number,
"Title: "..(pr_data.title or ""),
"",
pr_data.body or "",
}
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
vim.api.nvim_create_autocmd("BufWriteCmd", {
buffer = buf,
callback = function()
local new_lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
if #new_lines < 2 then return end
local new_title = new_lines[1]:gsub("^Title:%s*", "")
local new_body = table.concat({unpack(new_lines, 3)}, "\n")
local updated, st = api.edit_pull_request(owner, repo, number, {
title = new_title,
body = new_body,
})
if updated then
vim.notify("[gitea.nvim] PR updated successfully!")
else
vim.notify(string.format("[gitea.nvim] Failed to update PR (HTTP %s).", st or "?"), vim.log.levels.ERROR)
end
end
})
vim.api.nvim_set_current_buf(buf)
end,
merge = function(args)
local number = tonumber(args[1])
if not number then
vim.notify("[gitea.nvim] Usage: :Gitea pr merge <number> [merge|rebase|squash] [delete|nodelete]", vim.log.levels.ERROR)
return
end
local merge_style = args[2] or "merge" -- "merge", "rebase", "squash"
local delete_arg = args[3] or "nodelete"
local owner, repo = detect_owner_repo()
local merged, status = api.merge_pull_request(owner, repo, number, merge_style)
if merged then
vim.notify(string.format("[gitea.nvim] PR #%d merged via %s.", number, merge_style))
if delete_arg == "delete" then
-- The Gitea API doesn't directly delete the branch here,
-- but you can do so with the Git CLI or Gitea API call.
-- Deleting the branch example:
-- os.execute(string.format("git push origin --delete <branch>"))
vim.notify("[gitea.nvim] Branch deletion not implemented. Use `git push origin --delete <branch>`.")
end
else
vim.notify(string.format("[gitea.nvim] Failed to merge PR #%d (HTTP %s).", number, status or "?"), vim.log.levels.ERROR)
end
end,
close = function(args)
local number = tonumber(args[1])
if not number then
vim.notify("[gitea.nvim] Usage: :Gitea pr close <number>", vim.log.levels.ERROR)
return
end
local owner, repo = detect_owner_repo()
local res, status = api.close_pull_request(owner, repo, number)
if res then
vim.notify("[gitea.nvim] PR #"..number.." closed.")
else
vim.notify(string.format("[gitea.nvim] Failed to close PR (HTTP %s).", status or "?"), vim.log.levels.ERROR)
end
end,
reopen = function(args)
local number = tonumber(args[1])
if not number then
vim.notify("[gitea.nvim] Usage: :Gitea pr reopen <number>", vim.log.levels.ERROR)
return
end
local owner, repo = detect_owner_repo()
local res, status = api.reopen_pull_request(owner, repo, number)
if res then
vim.notify("[gitea.nvim] PR #"..number.." reopened.")
else
vim.notify(string.format("[gitea.nvim] Failed to reopen PR (HTTP %s).", status or "?"), vim.log.levels.ERROR)
end
end,
comment = function(args)
local number = tonumber(args[1])
if not number then
vim.notify("[gitea.nvim] Usage: :Gitea pr comment <number>", vim.log.levels.ERROR)
return
end
local owner, repo = detect_owner_repo()
local body = vim.fn.input("Comment: ")
if not body or body == "" then
return
end
local res, status = api.comment_pull_request(owner, repo, number, body)
if res then
vim.notify("[gitea.nvim] Comment added to PR #"..number..".")
else
vim.notify(string.format("[gitea.nvim] Failed to add comment (HTTP %s).", status or "?"), vim.log.levels.ERROR)
end
end,
}
-- Expand with other objects (e.g. 'repo', 'review', etc.) if needed.
-------------------------------
-- Command dispatcher
-------------------------------
function M.register()
vim.api.nvim_create_user_command("Gitea", function(cmd_opts)
-- Ensure token is loaded (in case user runs a command after startup)
local _ = require("gitea.auth").ensure_token()
local args = {}
for w in string.gmatch(cmd_opts.args, "%S+") do
table.insert(args, w)
end
local object = args[1] -- "issue", "pr", etc.
local action = args[2] -- "list", "create", etc.
if not object then
print("Usage: :Gitea <object> <action> [args]")
print("Examples:")
print(" :Gitea issue list")
print(" :Gitea pr list")
return
end
if not subcommands[object] then
vim.notify("[gitea.nvim] Unknown object: "..object, vim.log.levels.ERROR)
return
end
if not action then
vim.notify("[gitea.nvim] Please specify an action for "..object, vim.log.levels.ERROR)
return
end
local fn = subcommands[object][action]
if not fn then
vim.notify(string.format("[gitea.nvim] Unknown action '%s' for object '%s'", action, object), vim.log.levels.ERROR)
return
end
-- remove first two tokens
table.remove(args, 1)
table.remove(args, 1)
fn(args)
end, {
nargs = "*",
complete = function(arg_lead, cmd_line, cursor_pos)
-- Very basic completion
local parts = vim.split(cmd_line, "%s+")
if #parts == 2 then
-- user typed: :Gitea <object-partial>
local objs = {}
for k, _ in pairs(subcommands) do
if k:match("^"..arg_lead) then
table.insert(objs, k)
end
end
return objs
elseif #parts == 3 then
local object = parts[2]
local acts = {}
if subcommands[object] then
for k, _ in pairs(subcommands[object]) do
if k:match("^"..arg_lead) then
table.insert(acts, k)
end
end
end
return acts
else
return {}
end
end
})
end
return M