⏴ Back to all articles

Published on 2024-03-07

A small trick to improve technical discussions by sharing code

Table of contents

This is a big title for a small trick that I've been using daily for years now, in every place I've worked at.

Whenever there is a technical discussion, a bug hunt, or any disagreement about the codebase, I think it really helps to look at existing code to anchor the debate in reality and make it concrete.

Copy pasting code, taking screenshots, or screen sharing may work at times but I have found a low-tech solution that's superior: Sharing a link to a region of code in the codebase. It's shorter, easier, and can be used in chats, documentation and PRs. It works for any code, be it existing code on the main branch, or experimental code on a branch:

Link in Github's web UI

Every web UI of every Version Control System (VCS) worth its salt has that feature, let's take Github for example: https://github.com/gaultier/micro-kotlin/blob/master/class_file.h#L773-L775

The hurdle is that every hosting provider has its own URL 'shape' and it's not always documented, so there is a tiny bit of reverse-engineering involved. Compare the previous URL with this one: https://gitlab.com/philigaultier/jvm-bytecode/-/blob/master/class_file.h?ref_type=heads#L125-127. It's slightly different.

So to make it easy to share a link to code with coworkers, I've written a tiny script to craft the URL for me, inside my editor. I select a few lines, hit a keystroke, and the URL is now in the clipboard for me to paste anywhere.

Since I use Neovim and Lua, this is what I'll cover, but I'm sure any editor can do that. Now that I think of it, there should be an existing extension for this? Back when I started using this trick I remember searching for one and finding nothing.

This article could also serve as a gentle introduction to using Lua in Neovim. The code is also directly mappable to Vimscript, Vim9 script or anything really.

So first thing first we need to create a user command to invoke this functionality and later map it to a keystroke:

vim.api.nvim_create_user_command('GitWebUiUrlCopy', function(arg)
end,
{force=true, range=true, nargs=0, desc='Copy to clipboard a URL to a git webui for the current line'})

We pass a callback to nvim_create_user_command which will be called when we invoke the command. For now it does nothing but we are going to implement it in a second.

arg is an object containing for our purposes the start and end line numbers:

  local line_start = arg.line1
  local line_end = arg.line2

And we also need to get the absolute path to the current file:

  local file_path_abs = vim.fn.expand('%:p')

From this point on explanations are git specific, but I'm sure other VCSes have similar features.

Note that since the current directory might be one or several directories deep relative to the root of the git repository, we need to fix this path, because the git web UI expects a path from the root of the git repository.

The easiest way to do so is using git ls-files --full-name to convert the absolute path to the path from the root of the repostory.

There are many ways in Neovim to call out to a command in a subprocess, here's one of them, to get the output of the command:

  local file_path_abs = vim.fn.expand('%:p')
  local file_path_rel_cmd = io.popen('git ls-files --full-name "' .. file_path_abs .. '"')
  local file_path_relative_to_git_root = file_path_rel_cmd:read('*a')
  file_path_rel_cmd.close()

We also need to get the git URL of the remote (assuming there is only one, but it's easy to expand the logic to handle multiple):

  local cmd_handle = io.popen('git remote get-url origin')
  local git_origin = cmd_handle:read('*a')
  cmd_handle.close()
  git_origin = string.gsub(git_origin, "%s+$", "")

And the last bit of information we need is to get the current commit. In the past, I just used the current branch name, however since this is a moving target, it meant that when opening the link, the code might be completely different than what it was when giving out the link. Using a fixed commit is thus better (assuming no one force pushes and messes with the history):

  local cmd_handle = io.popen('git rev-parse HEAD')
  local git_commit = cmd_handle:read('*a')
  cmd_handle.close()
  git_commit = string.gsub(git_commit, "%s+$", "")

Now, we can craft the URL by first extracting the interesting parts of the git remote URL and then tacking on at the end all the URL parameters precising the location. I assume the git remote URL is a ssh URL here, again it's easy to tweak to also handle https URL. Also note that this is the part that's hosting provider specific.

Since I am mainly using Azure DevOps (ADO) and Github at the moment this is what I'll show. In ADO, the git remote URL looks like this:

git@ssh.<hostname>:v3/<organization>/<directory>/<project>

And the final URL looks like:

https://<hostname>/<organization>/<directory>/_git/<project>?<params>

In Github, the git remote URL looks like this:

git@github.com:<username>/<project>.git

And the final URL looks like this:

https://github.com/<username>/<project>/blob/<commit_id>/<file_path>?<params>

We inspect the git remote url to know in which case we are:

  local url = ''
  if string.match(git_origin, 'github') then
    -- Handle Github
  elseif string.match(git_origin, 'azure.com') then
    -- End is exclusive in that case hence the `+ 1`.
    line_end = line_end + 1

    -- Handle ADO
  else
    print('hosting provider not supported')
  end

We use a Lua pattern to extract the components from the git remote URL using string.gmatch. It weirdly returns an iterator yielding only one result containing our matches, we use a for loop to do so (perhaps there is an easier way in Lua?):

Here's for Github:

    for host, user, project in string.gmatch(git_origin, 'git@([^:]+):([^/]+)/([^/]+)%.git') do
      url = 'https://' .. host .. '/' .. user .. '/' .. project .. '/blob/' .. git_commit .. '/' .. file_path_relative_to_git_root .. '#l' .. line_start .. '-l' .. line_end
      break
    end

And here's for ADO:

  for host, org, dir, project in string.gmatch(git_origin, 'git@ssh%.([^:]+):v3/([^/]+)/([^/]+)/([^\n]+)') do
    url = 'https://' .. host .. '/' .. org .. '/' .. dir .. '/_git/' .. project .. '?lineStartColumn=1&lineStyle=plain&_a=contents&version=GC' .. git_commit .. '&path=' .. file_path_relative_to_git_root .. '&line=' .. line_start .. '&lineEnd=' .. line_end
    break
  end

Finally we stick the result in the system clipboard, and we can even open the url in the default browser (I have only tested that logic on Linux but it should work on other OSes):

  -- Copy to clipboard.
  vim.fn.setreg('+', url)

  -- Open URL in the default browser.
  local os_name = vim.loop.os_uname().sysname
  if os_name == 'Linux' or os_name == 'FreeBSD' or os_name == 'OpenBSD' or os_name == 'NetBSD' then
    os.execute('xdg-open "' .. url .. '"')
  elseif os_name == 'Darwin' then
    os.execute('open "' .. url .. '"')
  elseif os_name == 'Windows' then
    os.execute('start "' .. url .. '"')
  else
    print('Unknown os: ' .. os_name)
  end

We can now map the command to our favorite keystroke, for me space + x, for both normal mode (n) and visual mode (v):

vim.keymap.set({'v', 'n'}, '<leader>x', ':GitWebUiUrlCopy<CR>')

And that's it, just 60 lines of Lua, and easy to extend to support even more hosting providers.

Addendum: the full code

The full code
vim.keymap.set({'v', 'n'}, '<leader>x', ':GitWebUiUrlCopy<CR>')
vim.api.nvim_create_user_command('GitWebUiUrlCopy', function(arg)
  local file_path_abs = vim.fn.expand('%:p')
  local file_path_rel_cmd = io.popen('git ls-files --full-name "' .. file_path_abs .. '"')
  local file_path_relative_to_git_root = file_path_rel_cmd:read('*a')
  file_path_rel_cmd.close()

  local line_start = arg.line1
  local line_end = arg.line2

  local cmd_handle = io.popen('git remote get-url origin')
  local git_origin = cmd_handle:read('*a')
  cmd_handle.close()
  git_origin = string.gsub(git_origin, "%s+$", "")

  local cmd_handle = io.popen('git rev-parse HEAD')
  local git_commit = cmd_handle:read('*a')
  cmd_handle.close()
  git_commit = string.gsub(git_commit, "%s+$", "")

  local url = ''
  if string.match(git_origin, 'github') then
    for host, user, project in string.gmatch(git_origin, 'git@([^:]+):([^/]+)/([^/]+)%.git') do
      url = 'https://' .. host .. '/' .. user .. '/' .. project .. '/blob/' .. git_commit .. '/' .. file_path_relative_to_git_root .. '#L' .. line_start .. '-L' .. line_end
      break
    end
  elseif string.match(git_origin, 'azure.com') then
    -- End is exclusive in that case hence the `+ 1`.
    line_end = line_end + 1

    for host, org, dir, project in string.gmatch(git_origin, 'git@ssh%.([^:]+):v3/([^/]+)/([^/]+)/([^\n]+)') do
      url = 'https://' .. host .. '/' .. org .. '/' .. dir .. '/_git/' .. project .. '?lineStartColumn=1&lineStyle=plain&_a=contents&version=GC' .. git_commit .. '&path=' .. file_path_relative_to_git_root .. '&line=' .. line_start .. '&lineEnd=' .. line_end
      break
    end
  else
    print('Hosting provider not supported')
  end

  -- Copy to clipboard.
  vim.fn.setreg('+', url)

  -- Open URL in the default browser.
  local os_name = vim.loop.os_uname().sysname
  if os_name == 'Linux' or os_name == 'FreeBSD' or os_name == 'OpenBSD' or os_name == 'NetBSD' then
    os.execute('xdg-open "' .. url .. '"')
  elseif os_name == 'Darwin' then
    os.execute('open "' .. url .. '"')
  elseif os_name == 'Windows' then
    os.execute('start "' .. url .. '"')
  else
    print('Unknown os: ' .. os_name)
  end
end,
{force=true, range=true, nargs=0, desc='Copy to clipboard a URL to a git webui for the current line'})

⏴ Back to all articles

This blog is open-source! If you find a problem, please open a Github issue. The content of this blog as well as the code snippets are under the BSD-3 License which I also usually use for all my personal projects. It's basically free for every use but you have to mention me as the original author.