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

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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%