local function file_exists(file_path) local file = io.open(file_path, "rb") if file then file:close() end return file ~= nil end local remote_prefix_patterns = {'ssh://', 'http[s]?://', '%w+@'} local function transform_url(remote) for _, pattern in pairs(remote_prefix_patterns) do local regex_pattern = string.format('^%s(.*)', pattern) if remote:match(regex_pattern) then local _url = remote:match(regex_pattern, 1) _url = _url:gsub('(:)(%a+)', '/%2') _url = _url:gsub('.git$', '') if not _url:match('^http[s]?://') then return string.format('%s%s', 'https://', _url) end return _url end end end -- TODO: add more providers -- TODO: add feature to specify provider local provider_mapping = { ['github'] = '%s/blob/%s/%s#L%i', ['bitbucket'] = '%s/src/%s/%s#lines-%i', ['forgejo'] = '%s/src/commit/%s/%s#L%i', } local function get_provider_template(remote) local transformed_url = transform_url(remote) if not transformed_url then return end for domain, template_string in pairs(provider_mapping) do if transformed_url:match(domain) then return transformed_url, template_string end end end local function source_open() local git_conf_path = '.git/config' -- TODO: determine based on recursively traversing upwards in path if not file_exists(git_conf_path) then return end local _remote_output = io.popen('git remote get-url origin', 'r') -- TODO: test with other refs local _commit_output = io.popen('git show --pretty=%H HEAD', 'r') if _remote_output == nil or _commit_output == nil then return end local line_number = unpack(vim.api.nvim_win_get_cursor(0)) local filepath = vim.api.nvim_buf_get_name(0) local relative_filepath = vim.fn.fnamemodify(filepath, ':.') local remote = _remote_output:read() local branch = _commit_output:read() local transformed_url, template_string = get_provider_template(remote) for _, variable in pairs({ filepath, transformed_url, template_string }) do if variable == nil then return end end local template_args = { transformed_url, branch, relative_filepath, line_number } local url = template_string:format(unpack(template_args)) os.execute(('xdg-open %s'):format(url)) end local function setup(opts) vim.api.nvim_create_user_command('SourceOpen', source_open, {}) end return { setup = setup }