diff --git a/README.md b/README.md index 0d34a49..ba515dc 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,7 @@ function that constructs the url and receives: ``` lua url_data = { host = "", + port = "3000" or nil, repo = "", file = "", lstart = 42, -- the line start of the selected range / current line @@ -135,6 +136,10 @@ url_data = { } ``` +`port` will always be `nil` except when the remote URI configured locally is +http(s) **and specifies a port** (e.g. `http://localhost:3000/user/repo.git`), +in which case the generated url permalink also needs the right port. + `lstart` and `lend` can be `nil` in case normal mode was used or `opts.add_current_line_on_normal_mode = false`. Do not forget to check for that in your callback. @@ -145,11 +150,11 @@ it’s already builtin**, it’s just an example): ``` lua callbacks = { ["github.com"] = function(url_data) - local url = "https://" .. url_data.host .. "/" .. url_data.repo .. "/blob/" .. - url_data.rev .. "/" .. url_data.file + local url = require"gitlinker.hosts".get_base_https_url(url_data) .. + url_data.repo .. "/blob/" .. url_data.rev .. "/" .. url_data.file if url_data.lstart then - url = url .. "#L" .. url_data.lstart - if url_data.lend then url = url .. "-L" .. url_data.lend end + url = url .. "#L" .. url_data.lstart + if url_data.lend then url = url .. "-L" .. url_data.lend end end return url end diff --git a/doc/gitlinker.txt b/doc/gitlinker.txt index 4a94995..ab2c3a6 100644 --- a/doc/gitlinker.txt +++ b/doc/gitlinker.txt @@ -121,6 +121,7 @@ function that constructs the url and receives: > url_data = { host = "", + port = "3000" -- port number or nil, repo = "", file = "", lstart = 42, -- the line start of the selected range / current line @@ -128,6 +129,10 @@ function that constructs the url and receives: } < +`port` will always be `nil` except when the remote URI configured locally is +http(s) **and specifies a port** (e.g. `http://localhost:3000/user/repo.git`), +in which case the generated url permalink also needs the right port. + `lstart` and `lend` can be `nil` in case normal mode was used or `opts.add_current_line_on_normal_mode = false`. Do not forget to check for that in your callback. @@ -138,11 +143,11 @@ it’s already builtin, it’s just an example): > callbacks = { ["github.com"] = function(url_data) - local url = "https://" .. url_data.host .. "/" .. url_data.repo .. "/blob/" .. - url_data.rev .. "/" .. url_data.file + local url = require"gitlinker.hosts".get_base_https_url(url_data) .. + url_data.repo .. "/blob/" .. url_data.rev .. "/" .. url_data.file if url_data.lstart then - url = url .. "#L" .. url_data.lstart - if url_data.lend then url = url .. "-L" .. url_data.lend end + url = url .. "#L" .. url_data.lstart + if url_data.lend then url = url .. "-L" .. url_data.lend end end return url end diff --git a/lua/gitlinker.lua b/lua/gitlinker.lua index f1f6f15..ebe0b0e 100644 --- a/lua/gitlinker.lua +++ b/lua/gitlinker.lua @@ -44,7 +44,7 @@ local function get_url_data(mode) local remote = opts.remote or git.get_branch_remote() if not remote then return nil end - local repo = git.get_repo(remote) + local repo = git.get_repo_data(remote) if not repo or vim.tbl_isempty(repo) then return nil end local buf_repo_path = buffer.get_relative_path(git.get_git_root()) @@ -57,6 +57,7 @@ local function get_url_data(mode) return { host = repo.host, repo = repo.path, + port = repo.port, rev = rev, file = buf_repo_path, lstart = range.lstart, diff --git a/lua/gitlinker/git.lua b/lua/gitlinker/git.lua index 4432ffe..d54acab 100644 --- a/lua/gitlinker/git.lua +++ b/lua/gitlinker/git.lua @@ -70,14 +70,13 @@ local function is_rev_in_remote(revspec, remote) return is_in_remote end -local function parse_uri(uri, errs) - local chars = "[_%-%w%.]+" - local pathChars = "[/_%-%w%.]+" +local chars = "[_%-%w%.]+" +-- strips the protocol (https://, git@, ssh://, etc) +local function strip_protocol(uri, errs) local protocol_schema = chars .. "://" local ssh_schema = chars .. "@" - -- strip the protocol (https://, git@, etc) local stripped_uri = uri:match(protocol_schema .. "(.+)$") or uri:match(ssh_schema .. "(.+)$") if not stripped_uri then @@ -85,28 +84,72 @@ local function parse_uri(uri, errs) ": remote uri '%s' uses an unsupported protocol format", uri)) return nil end + return stripped_uri +end - -- strip '.git' at the end if it exists - stripped_uri = stripped_uri:match("(.+)%.git$") or stripped_uri +local function strip_dot_git(uri) return uri:match("(.+)%.git$") or uri end - local host_capture = '(' .. chars .. ")[:/].+$" - local path_capture = chars .. "[:/](" .. pathChars .. ")$" - - local repo = { - host = stripped_uri:match(host_capture), - path = stripped_uri:match(path_capture) - } +local function strip_uri(uri, errs) + local stripped_uri = strip_protocol(uri, errs) + return strip_dot_git(stripped_uri) +end - if not repo.host then +local function parse_host(stripped_uri, errs) + local host_capture = '(' .. chars .. ")[:/].+$" + local host = stripped_uri:match(host_capture) + if not host then table.insert(errs, string.format( - ": cannot parse the hostname from uri '%s'", uri)) + ": cannot parse the hostname from uri '%s'", stripped_uri)) end - if not repo.path then + return host +end + +local function parse_port(stripped_uri, host) + assert(host) + local port_capture = host .. ":([0-9]+)[:/].+$" + return stripped_uri:match(port_capture) +end + +local function parse_repo_path(stripped_uri, host, port, errs) + assert(host) + + local pathChars = "[/_%-%w%.]+" + -- base of path capture + local path_capture = "[:/](" .. pathChars .. ")$" + + -- if port is specified, add it to the path capture + if port then path_capture = ':' .. port .. path_capture end + + -- add parsed host to path capture + path_capture = host .. path_capture + + -- parse repo path + local repo_path = stripped_uri:match(path_capture) + if not repo_path then table.insert(errs, string.format( - ": cannot parse the repo path from uri '%s'", uri)) + ": cannot parse the repo path from uri '%s'", stripped_uri)) + return nil end + return repo_path +end - return repo +local function parse_uri(uri, errs) + local stripped_uri = strip_uri(uri, errs) + + local host = parse_host(stripped_uri, errs) + if not host then return nil end + + local port = parse_port(stripped_uri, host) + + local repo_path = parse_repo_path(stripped_uri, host, port, errs) + if not repo_path then return nil end + + -- do not pass the port if it's NOT a http(s) uri since most likely the port + -- is just an ssh port, so it's irrelevant to the git permalink construction + -- (which is always an http url) + if not uri:match("https?://") then port = nil end + + return {host = host, port = port, path = repo_path} end local function is_file_compatible_with_revspec(buf_repo_path, revspec, errs) @@ -177,7 +220,7 @@ function M.get_closest_remote_compatible_rev(buf_repo_path, remote) return nil end -function M.get_repo(remote) +function M.get_repo_data(remote) local errs = { string.format("Failed to retrieve repo data for remote '%s'", remote) } diff --git a/lua/gitlinker/hosts.lua b/lua/gitlinker/hosts.lua index 22b0e2b..1e477fb 100644 --- a/lua/gitlinker/hosts.lua +++ b/lua/gitlinker/hosts.lua @@ -1,20 +1,14 @@ local M = {} +function M.get_base_https_url(url_data) + local url = "https://" .. url_data.host + if url_data.port then url = url .. ':' .. url_data.port end + return url .. '/' +end + --- Constructs a github style url --- --- @param url_data table containing --- { --- host = "", -- e.g. github.com --- repo = "", -- e.g. ruifm/gitlinker.nvim --- rev = "revision-sha", -- the commit revision sha --- file = "", the file path --- lstart = /nil, the line starting range --- lend = /nil, the line ending range --- } --- --- @returns The url string function M.get_github_type_url(url_data) - local url = "https://" .. url_data.host .. "/" .. url_data.repo .. "/blob/" .. + local url = M.get_base_https_url(url_data) .. url_data.repo .. "/blob/" .. url_data.rev .. "/" .. url_data.file if url_data.lstart then url = url .. "#L" .. url_data.lstart @@ -24,21 +18,10 @@ function M.get_github_type_url(url_data) end --- Constructs a gitea style url --- --- @param url_data table containing --- { --- host = "", -- e.g. gitea.com --- repo = "", -- e.g. ruifm/gitlinker.nvim --- rev = "revision-sha", -- the commit revision sha --- file = "", the file path --- lstart = /nil, the line starting range --- lend = /nil, the line ending range --- } --- --- @returns The url string function M.get_gitea_type_url(url_data) - local url = "https://" .. url_data.host .. "/" .. url_data.repo .. - "/src/commit/" .. url_data.rev .. "/" .. url_data.file + local url = + M.get_base_https_url(url_data) .. url_data.repo .. "/src/commit/" .. + url_data.rev .. "/" .. url_data.file if url_data.lstart then url = url .. "#L" .. url_data.lstart if url_data.lend then url = url .. "-L" .. url_data.lend end @@ -47,22 +30,9 @@ function M.get_gitea_type_url(url_data) end --- Constructs a gitlab style url --- --- @param url_data table containing --- { --- host = "", -- e.g. gitlab.com --- repo = "", -- e.g. ruifm/gitlinker.nvim --- rev = "revision-sha", -- the commit revision sha --- file = "", the file path --- lstart = /nil, the line starting range --- lend = /nil, the line ending range --- } --- --- @returns The url string function M.get_gitlab_type_url(url_data) - local url = - "https://" .. url_data.host .. "/" .. url_data.repo .. "-/blob/" .. - url_data.rev .. "/" .. url_data.file + local url = M.get_base_https_url(url_data) .. url_data.repo .. "-/blob/" .. + url_data.rev .. "/" .. url_data.file if url_data.lstart then url = url .. "#L" .. url_data.lstart if url_data.lend then url = url .. "-" .. url_data.lend end @@ -71,20 +41,8 @@ function M.get_gitlab_type_url(url_data) end --- Constructs a bitbucket style url --- --- @param url_data table containing --- { --- host = "", -- e.g. bitbucket.com --- repo = "", -- e.g. ruifm/gitlinker.nvim --- rev = "revision-sha", -- the commit revision sha --- file = "", the file path --- lstart = /nil, the line starting range --- lend = /nil, the line ending range --- } --- --- @returns The url string function M.get_bitbucket_type_url(url_data) - local url = "https://" .. url_data.host .. "/" .. url_data.repo .. "/src/" .. + local url = M.get_base_https_url(url_data) .. url_data.repo .. "/src/" .. url_data.rev .. "/" .. url_data.file if url_data.lstart then url = url .. "#lines-" .. url_data.lstart @@ -94,20 +52,8 @@ function M.get_bitbucket_type_url(url_data) end --- Constructs a gogs style url --- --- @param url_data table containing --- { --- host = "", -- e.g. gogs.com --- repo = "", -- e.g. ruifm/gitlinker.nvim --- rev = "revision-sha", -- the commit revision sha --- file = "", the file path --- lstart = /nil, the line starting range --- lend = /nil, the line ending range --- } --- --- @returns The url string function M.get_gogs_type_url(url_data) - local url = "https://" .. url_data.host .. "/" .. url_data.repo .. "/src/" .. + local url = M.get_base_https_url(url_data) .. url_data.repo .. "/src/" .. url_data.rev .. "/" .. url_data.file if url_data.lstart then url = url .. "#L" .. url_data.lstart @@ -117,18 +63,6 @@ function M.get_gogs_type_url(url_data) end --- Constructs a cgit style url --- --- @param url_data table containing --- { --- host = "", -- e.g. git.kernel.org --- repo = "", -- e.g. ruifm/gitlinker.nvim --- rev = "revision-sha", -- the commit revision sha --- file = "", the file path --- lstart = /nil, the line starting range --- lend = /nil, the line ending range --- } --- --- @returns The url string function M.get_cgit_type_url(url_data) local repo = "" if url_data.repo then repo = url_data.repo .. ".git/" end @@ -139,20 +73,8 @@ function M.get_cgit_type_url(url_data) end --- Constructs a sourcehut style url --- --- @param url_data table containing --- { --- host = "", -- e.g. git.sr.ht --- repo = "", -- e.g. ruifm/gitlinker.nvim --- rev = "revision-sha", -- the commit revision sha --- file = "", the file path --- lstart = /nil, the line starting range --- lend = /nil, the line ending range --- } --- --- @returns The url string function M.get_srht_type_url(url_data) - local url = "https://" .. url_data.host .. "/" .. url_data.repo .. "/tree/" .. + local url = M.get_base_https_url(url_data) .. url_data.repo .. "/tree/" .. url_data.rev .. "/item/" .. url_data.file if url_data.lstart then url = url .. "#L" .. url_data.lstart @@ -162,40 +84,16 @@ function M.get_srht_type_url(url_data) end --- Constructs a launchpad style url --- --- @param url_data table containing --- { --- host = "", -- e.g. launchpad.net --- repo = "", -- e.g. ruifm/gitlinker.nvim --- rev = "revision-sha", -- the commit revision sha --- file = "", the file path --- lstart = /nil, the line starting range --- lend = /nil, the line ending range --- } --- --- @returns The url string function M.get_launchpad_type_url(url_data) - local url = "https://" .. url_data.host .. "/" .. url_data.repo .. "/tree/" .. + local url = M.get_base_https_url(url_data) .. url_data.repo .. "/tree/" .. url_data.file .. "?id=" .. url_data.rev if url_data.lstart then url = url .. "#n" .. url_data.lstart end return url end --- Constructs a repo.or.cz style url --- --- @param url_data table containing --- { --- host = "", -- e.g. repo.or.cz --- repo = "", -- e.g. ruifm/gitlinker.nvim --- rev = "revision-sha", -- the commit revision sha --- file = "", the file path --- lstart = /nil, the line starting range --- lend = /nil, the line ending range --- } --- --- @returns The url string function M.get_repoorcz_type_url(url_data) - local url = "https://" .. url_data.host .. "/" .. url_data.repo .. "/blob/" .. + local url = M.get_base_https_url(url_data) .. url_data.repo .. "/blob/" .. url_data.rev .. ":/" .. url_data.file if url_data.lstart then url = url .. "#l" .. url_data.lstart end return url