Skip to content

Maintained fork of ruifm's gitlinker, refactored with bug fixes, git alias host, `/blame` url support and other improvements.

License

Notifications You must be signed in to change notification settings

bagnaram/gitlinker.nvim

 
 

Repository files navigation

gitlinker.nvim

Neovim Language ci.yml codecov

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.

gitlinker

For now supported platforms are:

PRs are welcomed for other git host websites!

Table of Contents

Break Changes & Updates

  1. Break Changes:
    • Provide GitLink command instead of default key mappings.
  2. New Features:
    • Windows (+wsl) support.
    • Respect ssh host alias.
    • Add ?plain=1 for markdown files.
    • Support /blame (by default is /blob).
  3. Improvements:
    • Use stderr from git command as error message.
    • Performant child process IO via uv.spawn.
    • Drop off plenary dependency.

Installation

Requirement:

  • neovim ≥ v0.7.
  • git.
  • ssh (optional for resolve ssh host alias).
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,
  },
})

Usage

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 in GitLink), 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" }
)

Configuration

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,
})

Highlighting

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.

Self-host Git Hosts

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:

Fully Customize Urls

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.
  • _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

Highlight Group Default Group Description
NvimGitLinkerHighlightTextObject Search highlight line ranges when copy/open

Development

To develop the project and make PR, please setup with:

To run unit tests, please install below dependencies:

Then test with vusted ./test.

Contribute

Please also open issue/PR for anything about gitlinker.nvim.

Like gitlinker.nvim? Consider

Github Sponsor Wechat Pay Alipay

About

Maintained fork of ruifm's gitlinker, refactored with bug fixes, git alias host, `/blame` url support and other improvements.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Lua 100.0%