---------------------------------------------------------------------------- -- 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