Maintained fork of ruifm's gitlinker, refactored with bug fixes, ssh host alias,
/blame
url support and other improvements.
A lua plugin for Neovim to generate sharable file permalinks (with line ranges) for git host websites. Inspired by tpope/vim-fugitive's :GBrowse
.
Here's an example of git permalink: https://github.com/neovim/neovim/blob/2e156a3b7d7e25e56b03683cc6228c531f4c91ef/src/nvim/main.c#L137-L156.
For now supported platforms are:
PRs are welcomed for other git host websites!
- Break Changes:
- Provide
GitLink
command instead of default key mappings.
- Provide
- New Features:
- Windows (+wsl) support.
- Respect ssh host alias.
- Add
?plain=1
for markdown files. - Support
/blame
(by default is/blob
).
- Improvements:
- Use stderr from git command as error message.
- Performant child process IO via
uv.spawn
. - Drop off
plenary
dependency.
Requirement:
return require('packer').startup(function(use)
use {
'linrongbin16/gitlinker.nvim',
config = function()
require('gitlinker').setup()
end,
}
end)
call plug#begin()
Plug 'linrongbin16/gitlinker.nvim'
call plug#end()
lua require('gitlinker').setup()
require("lazy").setup({
{
'linrongbin16/gitlinker.nvim',
config = function()
require('gitlinker').setup()
end,
},
})
You could use below command:
GitLink
: copy the/blob
url to clipboard.GitLink!
: open the/blob
url in browser.GitLink blame
: copy the/blame
url to clipboard.GitLink! blame
: open the/blame
url in browser.
There're two routers provided:
browse
: generate the/blob
urls (default router inGitLink
), also work for other git host websites, e.g. generate/src
for bitbucket.org.blame
: generate the/blame
urls, also work for other git host websites, e.g. generate/annotate
for bitbucket.org.
Click here to see recommended key mappings
-- browse
vim.keymap.set(
{"n", 'v'},
"<leader>gl",
"<cmd>GitLink<cr>",
{ silent = true, noremap = true, desc = "Copy git permlink to clipboard" }
)
vim.keymap.set(
{"n", 'v'},
"<leader>gL",
"<cmd>GitLink!<cr>",
{ silent = true, noremap = true, desc = "Open git permlink in browser" }
)
-- blame
vim.keymap.set(
{"n", 'v'},
"<leader>gb",
"<cmd>GitLink blame<cr>",
{ silent = true, noremap = true, desc = "Copy git blame link to clipboard" }
)
vim.keymap.set(
{"n", 'v'},
"<leader>gB",
"<cmd>GitLink! blame<cr>",
{ silent = true, noremap = true, desc = "Open git blame link in browser" }
)
require('gitlinker').setup({
-- print message in command line
message = true,
-- highlights the linked line(s) by the time in ms
-- disable highlight by setting a value equal or less than 0
highlight_duration = 500,
-- user command
command = {
-- to copy link to clipboard, use: 'GitLink'
-- to open link in browser, use bang: 'GitLink!'
-- to use blame router, use: 'GitLink blame' and 'GitLink! blame'
name = "GitLink",
desc = "Generate git permanent link",
},
-- router bindings
router = {
browse = {
-- example: https://github.com/linrongbin16/gitlinker.nvim/blob/9679445c7a24783d27063cd65f525f02def5f128/lua/gitlinker.lua#L3-L4
["^github%.com"] = "https://github.com/"
.. "{_A.USER}/"
.. "{_A.REPO}/blob/"
.. "{_A.REV}/"
.. "{_A.FILE}"
.. "{(string.len(_A.FILE) >= 3 and _A.FILE:sub(#_A.FILE-2) == '.md') and '?plain=1' or ''}" -- '?plain=1'
.. "#L{_A.LSTART}"
.. "{(_A.LEND > _A.LSTART and ('-L' .. _A.LEND) or '')}",
-- example: https://gitlab.com/linrongbin16/gitlinker.nvim/blob/9679445c7a24783d27063cd65f525f02def5f128/lua/gitlinker.lua#L3-L4
["^gitlab%.com"] = "https://gitlab.com/"
.. "{_A.USER}/"
.. "{_A.REPO}/blob/"
.. "{_A.REV}/"
.. "{_A.FILE}"
.. "#L{_A.LSTART}"
.. "{(_A.LEND > _A.LSTART and ('-L' .. _A.LEND) or '')}",
-- example: https://bitbucket.org/linrongbin16/gitlinker.nvim/src/9679445c7a24783d27063cd65f525f02def5f128/lua/gitlinker.lua#lines-3:4
["^bitbucket%.org"] = "https://bitbucket.org/"
.. "{_A.USER}/"
.. "{_A.REPO}/src/"
.. "{_A.REV}/"
.. "{_A.FILE}"
.. "#lines-{_A.LSTART}"
.. "{(_A.LEND > _A.LSTART and (':' .. _A.LEND) or '')}",
},
blame = {
-- example: https://github.com/linrongbin16/gitlinker.nvim/blame/9679445c7a24783d27063cd65f525f02def5f128/lua/gitlinker.lua#L3-L4
["^github%.com"] = "https://github.com/"
.. "{_A.USER}/"
.. "{_A.REPO}/blame/"
.. "{_A.REV}/"
.. "{_A.FILE}"
.. "{(string.len(_A.FILE) >= 3 and _A.FILE:sub(#_A.FILE-2) == '.md') and '?plain=1' or ''}"
.. "#L{_A.LSTART}"
.. "{(_A.LEND > _A.LSTART and ('-L' .. _A.LEND) or '')}",
-- example: https://gitlab.com/linrongbin16/gitlinker.nvim/blame/9679445c7a24783d27063cd65f525f02def5f128/lua/gitlinker.lua#L3-L4
["^gitlab%.com"] = "https://gitlab.com/"
.. "{_A.USER}/"
.. "{_A.REPO}/blame/"
.. "{_A.REV}/"
.. "{_A.FILE}"
.. "#L{_A.LSTART}"
.. "{(_A.LEND > _A.LSTART and ('-L' .. _A.LEND) or '')}",
-- example: https://bitbucket.org/linrongbin16/gitlinker.nvim/annotate/9679445c7a24783d27063cd65f525f02def5f128/lua/gitlinker.lua#lines-3:4
["^bitbucket%.org"] = "https://bitbucket.org/"
.. "{_A.USER}/"
.. "{_A.REPO}/annotate/"
.. "{_A.REV}/"
.. "{_A.FILE}"
.. "#lines-{_A.LSTART}"
.. "{(_A.LEND > _A.LSTART and (':' .. _A.LEND) or '')}",
},
},
-- enable debug
debug = false,
-- write logs to console(command line)
console_log = true,
-- write logs to file
file_log = false,
})
To create your own highlighting, please use below config before setup this plugin:
-- lua
vim.api.nvim_set_hl( 0, "NvimGitLinkerHighlightTextObject", { link = "Constant" })
" vimscript
hi link NvimGitLinkerHighlightTextObject Constant
Also see Highlight Group.
For self-host git host websites, please add more bindings in router
option.
Below example shows how to apply the github style routers to a self-host github websites, e.g. github.your.host
:
require('gitlinker').setup({
router = {
browse = {
-- add your host here
["^github%.your%.host"] = require('gitlinker.routers').github_browse,
},
blame = {
-- add your host here
["^github%.your%.host"] = require('gitlinker.routers').github_blame,
},
},
})
There're 3 groups of builtin APIs you can directly use:
github_browse
/github_blame
: for github.com.gitlab_browse
/gitlab_blame
: for gitlab.com.bitbucket_browse
/bitbucket_blame
: for bitbucket.org.
To fully customize url generation, please refer to the implementation of routers.lua, a router is simply construct the url string from below components:
protocol
:git@
,ssh://git@
,https
, etc.host
:github.com
,gitlab.com
,bitbucket.org
, etc.user
:linrongbin16
(for this plugin),neovim
(for neovim), etc.repo
:gitlinker.nvim.git
,neovim.git
, etc.rev
: git commit, e.g.dbf3922382576391fbe50b36c55066c1768b08b6
.file
: file name, e.g.lua/gitlinker/routers.lua
.lstart
/lend
: start/end line numbers, e.g.#L37-L156
.
For example you can customize the line numbers in form ?&line=1&lines-count=2
like this:
--- @param s string
--- @param t string
local function string_endswith(s, t)
return string.len(s) >= string.len(t) and string.sub(s, #s - #t + 1) == t
end
--- @param lk gitlinker.Linker
local function your_router(lk)
local builder = "https://"
-- host: 'github.com', 'gitlab.com', 'bitbucket.org'
builder = builder .. lk.host .. "/"
-- user: 'linrongbin16', 'neovim'
builder = builder .. lk.user .. "/"
-- repo: 'gitlinker.nvim.git', 'neovim'
builder = builder
.. (string_endswith(lk.repo, ".git") and lk.repo:sub(1, #lk.repo - 4) or lk.repo)
.. "/"
-- rev: git commit, e.g. 'e605210941057849491cca4d7f44c0e09f363a69'
builder = lk.rev .. "/"
-- file: 'lua/gitlinker/logger.lua'
builder = builder
.. lk.file
.. (string_endswith(lk.file, ".md") and "?plain=1" or "")
-- line range: start line number, end line number
builder = builder .. string.format("&lines=%d", lk.lstart)
if lk.lend > lk.lstart then
builder = builder
.. string.format("&lines-count=%d", lk.lend - lk.lstart + 1)
end
return builder
end
require("gitlinker").setup({
router = {
browse = {
["^github%.your%.host"] = your_router,
},
},
})
Quite a lot of engineering effort, isn't it? You can also use the url template, which should be easier to define the url schema:
The url template is also the default implementation of builtin routers (see
router
option in Configuration), but the error message could be confusing if there's any syntax issue.
require("gitlinker").setup({
router = {
browse = {
["^github%.your%.host"] = "https://github.your.host/"
.. "{_A.USER}/"
.. "{_A.REPO}/blob/"
.. "{_A.REV}/"
.. "{_A.FILE}"
.. "?&lines={_A.LSTART}"
.. "{_A.LEND > _A.LSTART and ('&lines-count=' .. _A.LEND - _A.LSTART + 1) or ''}",
},
},
})
The template string use curly braces {}
to contains lua scripts, and evaluate via luaeval().
The available variables are the same with the lk
parameter passing to hook functions, but in upper case, and with the _A.
prefix:
_A.PROTOCOL
:git@
,ssh://git@
,https
, etc._A.HOST
:github.com
,gitlab.com
,bitbucket.org
, etc._A.USER
:linrongbin16
(for this plugin),neovim
(for neovim), etc._A.REPO
:gitlinker.nvim
,neovim
, etc.- Note: for easier writing, the
.git
suffix has been removed.
- Note: for easier writing, the
_A.REV
: git commit, e.g.dbf3922382576391fbe50b36c55066c1768b08b6
._A.FILE
: file name, e.g.lua/gitlinker/routers.lua
._A.LSTART
/_A.LEND
: start/end line numbers, e.g.#L37-L156
.
Highlight Group | Default Group | Description |
---|---|---|
NvimGitLinkerHighlightTextObject | Search | highlight line ranges when copy/open |
To develop the project and make PR, please setup with:
To run unit tests, please install below dependencies:
Then test with vusted ./test
.
Please also open issue/PR for anything about gitlinker.nvim.
Like gitlinker.nvim? Consider