Files
gitea.nvim/lua/gitea/init.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