Getting the permalink URL to a line of code¶
When writing responses to issues on GitHub, I often find myself wanting to grab the permalink to a specific piece of code. Up until now I’ve always just clicked around the UI until I’ve found the piece of code I’m looking for.
However, surely I can convince Emacs to generate the correct URL for me?
Permalinks on GitHub have the following structure:
https://github.com/<owner>/<repo>/blob/<commit>/<filepath>#L<start>(-L<end>)
So building the URL should be “just” a case of looking up the relevant info and joining it all together!
emacs/lisp/alc-git.el(defun alc-git-permalink-to-kill-ring ()
  "Construct a permalink URL for the current line or region and place it on
the kill-ring"
  (interactive)
  (if-let* ((repo (alc-git-get-remote-url))
            (commit (alc-git-get-commit))
            (filepath (alc-git-get-filepath))
            (pos (alc-git-get-position))
            (path-start (format "%s#L%s" filepath (nth 0 pos)))
            (url (s-join "/" `(,repo "blob" ,commit ,path-start))))
      (kill-new
       (if (nth 1 pos)
           (format "%s-L%s" url (nth 1 pos))
         url))
    (message "No permalink found")))
Owner and Repo¶
It’s easy enough to get this information from the configured remotes of the git repository.
I was a bit surprised not to find a function that already did this in either vc or magit, though it did finally give me an excuse to learn about condition-case
emacs/lisp/alc-git.el(defun alc-git-get-remote-url ()
  "Return the url of the remote associated with the current buffer.
Prioritizes 'upstream' but will fall back to 'origin' if it is unavailable."
  (condition-case err
      (vc-git-repository-url buffer-file-name "upstream")
    (error err
           (condition-case err
               (vc-git-repository-url buffer-file-name "origin")
             (error err nil)))))
Commit¶
Getting the commit of the current file is easy enough with vc-working-revision however, it would be nice to figure out how
- to adapt this to also work when viewing an older version of a file with magit 
- to check if the current commit exists on the remote 
emacs/lisp/alc-git.el(defun alc-git-get-commit ()
  "Return the commit hash for the current file"
  (vc-working-revision buffer-file-truename))
Filepath¶
The filepath of course needs to be relative to the root of the repository
emacs/lisp/alc-git.el(defun alc-git-get-filepath ()
  "Return the filepath of the current buffer relative to the root of the
repository"
  (string-remove-prefix (vc-git-root buffer-file-truename)
                        buffer-file-truename))
Start and End¶
emacs/lisp/alc-git.el(defun alc-git-get-position ()
  "Return the start and end line number"
  (if (region-active-p)
      `(,(line-number-at-pos (region-beginning))
        ,(line-number-at-pos (region-end)))
    `(,(line-number-at-pos) nil)))
emacs/lisp/alc-git.el(provide 'alc-git)