243 lines
6.9 KiB
Lua
243 lines
6.9 KiB
Lua
----------------------------------------------------------------------------
|
|
-- lua/gitea/init.lua
|
|
--
|
|
-- Core plugin logic: reading .git/config, handling tokens, domain detection,
|
|
-- and an async request() function. Also has the top-level setup().
|
|
----------------------------------------------------------------------------
|
|
|
|
local Job = require("plenary.job")
|
|
local commands = require("gitea.commands")
|
|
|
|
local M = {}
|
|
|
|
----------------------------------------------------------------------------
|
|
-- Configuration / State
|
|
----------------------------------------------------------------------------
|
|
M.token_folder = vim.fn.stdpath("data") .. "/gitea_tokens"
|
|
M.preview_style = "floating"
|
|
M._cached_base_url = nil
|
|
M._cached_token = nil
|
|
M._cached_owner = nil
|
|
M._cached_repo = nil
|
|
|
|
----------------------------------------------------------------------------
|
|
-- Internal Utils
|
|
----------------------------------------------------------------------------
|
|
local function ensure_dir(dirpath)
|
|
if vim.fn.isdirectory(dirpath) == 0 then
|
|
vim.fn.mkdir(dirpath, "p")
|
|
end
|
|
end
|
|
|
|
local function read_git_config_lines()
|
|
local gitconfig_path = vim.fn.getcwd() .. "/.git/config"
|
|
local f = io.open(gitconfig_path, "r")
|
|
if not f then
|
|
return {}
|
|
end
|
|
local lines = {}
|
|
for line in f:lines() do
|
|
table.insert(lines, line)
|
|
end
|
|
f:close()
|
|
return lines
|
|
end
|
|
|
|
local function strip_git_suffix(str)
|
|
return (str:gsub("%.git$", ""))
|
|
end
|
|
|
|
local function parse_git_config_for_domain()
|
|
local lines = read_git_config_lines()
|
|
for _, line in ipairs(lines) do
|
|
local url = line:match("^%s*url%s*=%s*(.+)")
|
|
if url then
|
|
-- SSH style: user@host:owner/repo.git
|
|
local ssh_host = url:match("^[^@]+@([^:]+):")
|
|
if ssh_host then
|
|
return ssh_host
|
|
end
|
|
-- HTTPS style: https://host/owner/repo(.git)
|
|
local https_host = url:match("^https?://([^/]+)")
|
|
if https_host then
|
|
return https_host
|
|
end
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function M.parse_owner_repo()
|
|
local lines = read_git_config_lines()
|
|
for _, line in ipairs(lines) do
|
|
local url = line:match("^%s*url%s*=%s*(.+)")
|
|
if url then
|
|
-- SSH
|
|
local ssh_host, ssh_path = url:match("^[^@]+@([^:]+):(.+)")
|
|
if ssh_host and ssh_path then
|
|
ssh_path = strip_git_suffix(ssh_path)
|
|
local slash_idx = ssh_path:find("/")
|
|
if slash_idx then
|
|
return ssh_path:sub(1, slash_idx - 1),
|
|
ssh_path:sub(slash_idx + 1)
|
|
end
|
|
end
|
|
|
|
-- HTTPS
|
|
local https_path = url:match("^https?://[^/]+/(.+)")
|
|
if https_path then
|
|
https_path = strip_git_suffix(https_path)
|
|
local slash_idx = https_path:find("/")
|
|
if slash_idx then
|
|
return https_path:sub(1, slash_idx - 1),
|
|
https_path:sub(slash_idx + 1)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return nil, nil
|
|
end
|
|
|
|
----------------------------------------------------------------------------
|
|
-- Token Logic
|
|
----------------------------------------------------------------------------
|
|
local function token_file_for_domain(domain)
|
|
return M.token_folder .. "/" .. domain .. ".token"
|
|
end
|
|
|
|
local function load_token(domain)
|
|
if not domain or domain == "" then
|
|
domain = "default"
|
|
end
|
|
local token_path = token_file_for_domain(domain)
|
|
local f = io.open(token_path, "r")
|
|
if f then
|
|
local token = f:read("*a")
|
|
f:close()
|
|
if token and token ~= "" then
|
|
return token
|
|
end
|
|
end
|
|
|
|
vim.schedule(function()
|
|
vim.cmd(('echo "No Gitea token found for %s"'):format(domain))
|
|
end)
|
|
|
|
local user_input = vim.fn.inputsecret("Enter your Gitea token for " .. domain .. ": ")
|
|
if not (user_input and user_input ~= "") then
|
|
return nil
|
|
end
|
|
|
|
ensure_dir(M.token_folder)
|
|
local fh, err = io.open(token_path, "w")
|
|
if not fh then
|
|
error(string.format("[gitea.nvim] Could not open token file for writing (%s). Error: %s",
|
|
token_path, err or "unknown"))
|
|
end
|
|
fh:write(user_input)
|
|
fh:close()
|
|
print(string.format("[gitea.nvim] Token written to %s", token_path))
|
|
return user_input
|
|
end
|
|
|
|
local function ensure_base_url()
|
|
if M._cached_base_url and M._cached_base_url ~= "" then
|
|
return M._cached_base_url
|
|
end
|
|
local domain = parse_git_config_for_domain()
|
|
if not domain then
|
|
error("[gitea.nvim] Could not parse a domain from .git/config.")
|
|
end
|
|
if not domain:match("^https?://") then
|
|
domain = "https://" .. domain
|
|
end
|
|
M._cached_base_url = domain
|
|
return domain
|
|
end
|
|
|
|
local function ensure_token()
|
|
if M._cached_token and M._cached_token ~= "" then
|
|
return M._cached_token
|
|
end
|
|
local raw_domain = parse_git_config_for_domain() or "default"
|
|
raw_domain = raw_domain:gsub("^https://", "")
|
|
raw_domain = raw_domain:gsub("^http://", "")
|
|
local tok = load_token(raw_domain)
|
|
if not tok or tok == "" then
|
|
error("[gitea.nvim] Could not load/create a token for domain '" .. raw_domain .. "'.")
|
|
end
|
|
M._cached_token = tok
|
|
return tok
|
|
end
|
|
|
|
----------------------------------------------------------------------------
|
|
-- Async Requests (Plenary)
|
|
----------------------------------------------------------------------------
|
|
local function do_request(method, endpoint, data, callback)
|
|
local base_url = ensure_base_url()
|
|
local token = ensure_token()
|
|
|
|
local headers = {
|
|
"Content-Type: application/json",
|
|
"Authorization: token " .. token
|
|
}
|
|
local args = { "-s", "-X", method, base_url .. endpoint, "-H", headers[1], "-H", headers[2] }
|
|
if data then
|
|
local json_data = vim.fn.json_encode(data)
|
|
table.insert(args, "-d")
|
|
table.insert(args, json_data)
|
|
end
|
|
|
|
Job:new({
|
|
command = "curl",
|
|
args = args,
|
|
on_exit = function(j, return_val)
|
|
vim.schedule(function()
|
|
if return_val == 0 then
|
|
local result = table.concat(j:result(), "\n")
|
|
local ok, decoded = pcall(vim.fn.json_decode, result)
|
|
if ok then
|
|
callback(decoded, nil)
|
|
else
|
|
callback(nil, "Failed to parse JSON: " .. result)
|
|
end
|
|
else
|
|
callback(nil, "curl failed (code=" .. return_val .. ")")
|
|
end
|
|
end)
|
|
end
|
|
}):start()
|
|
end
|
|
|
|
----------------------------------------------------------------------------
|
|
-- Expose to other modules
|
|
----------------------------------------------------------------------------
|
|
M.ensure_base_url = ensure_base_url
|
|
M.ensure_token = ensure_token
|
|
M.request = do_request
|
|
|
|
----------------------------------------------------------------------------
|
|
-- setup()
|
|
----------------------------------------------------------------------------
|
|
function M.setup(opts)
|
|
if opts and opts.token_folder then
|
|
M.token_folder = opts.token_folder
|
|
end
|
|
if opts and opts.preview_style then
|
|
M.preview_style = opts.preview_style
|
|
end
|
|
|
|
local domain = parse_git_config_for_domain()
|
|
if domain and not domain:match("^https?://") then
|
|
domain = "https://" .. domain
|
|
end
|
|
M._cached_base_url = domain
|
|
|
|
local o, r = M.parse_owner_repo()
|
|
M._cached_owner = o
|
|
M._cached_repo = r
|
|
|
|
commands.setup_commands(M)
|
|
end
|
|
|
|
return M |