🏠 Home 

GitHub Diff Links

A userscript that adds links to diff and pull request headers to jump back & forth between files

// ==UserScript==
// @name        GitHub Diff Links
// @version     1.2.18
// @description A userscript that adds links to diff and pull request headers to jump back & forth between files
// @license     MIT
// @author      Rob Garrison
// @namespace   https://github.com/Mottie
// @match       https://github.com/*
// @run-at      document-idle
// @grant       GM_addStyle
// @require     https://greasyfork.org/scripts/28721-mutations/code/mutations.js?version=1108163
// @require     https://greasyfork.org/scripts/398877-utils-js/code/utilsjs.js?version=1079637
// @icon        https://github.githubassets.com/pinned-octocat.svg
// @supportURL  https://github.com/Mottie/GitHub-userscripts/issues
// ==/UserScript==
/* global $ $$ on */
(() => {
"use strict";
// sometimes tooltips are too narrow...
// and move diff anchors below sticky header
GM_addStyle(`
.gh-diff-links:after { min-width: 120px; }
a[name*="diff-"]:before {
padding-top: 60px !important;
margin-top: -60px !important;
content: "";
position: absolute;
}
div.file-actions > div {
display: inline;
}`
);
const button = document.createElement("a"),
// button [ InnerHTML, tooltip ]
nextBtn = ["Next", "Jump to next file\n("],
prevBtn = ["Prev", "Jump to previous file\n(" ];
button.className = "btn btn-sm tooltipped tooltipped-s tooltipped-multiline gh-diff-links";
button.setAttribute("rel", "nofollow");
function addButton(el, content, link) {
let btn = button.cloneNode(),
txt = el.classList.contains("select-menu-item") ?
$(".description", el).textContent :
link.textContent || "";
// clean up whitespace
txt = txt.replace(/\s+/g, " ").trim();
// only add file name to tooltip
txt = txt.substring(txt.lastIndexOf("/") + 1, txt.length);
btn.innerHTML = content[0];
btn.setAttribute("aria-label", content[1] + txt + ")" );
btn.href = link.hash;
// prepend button
el.insertBefore(btn, el.childNodes[0]);
}
function addSpace(el, content) {
let btn = button.cloneNode();
btn.disabled = true;
btn.className = "btn btn-sm gh-diff-links disabled";
btn.innerHTML = content[0];
el.insertBefore(btn, el.childNodes[0]);
}
function addLinks() {
let last, temp,
links = $$(".file-header .file-info a");
if (links.length) {
// links & file-actions "should" be the same length
last = links.length - 1;
$$(".file-actions").forEach((wrap, indx) => {
const el = wrap.firstElementChild;
// remove disabled buttons added before progressive
// content has completed loading
temp = $(".gh-diff-links.disabled", el);
if (temp) {
temp.parentNode.removeChild(temp);
// remove both buttons to allow updating
temp = $(".gh-diff-links", el);
temp.parentNode.removeChild(temp);
}
if (!$(".gh-diff-links", el)) {
if (indx === 0) {
addButton(el, nextBtn, links[indx + 1]);
addSpace(el, prevBtn);
} else if (indx === last) {
// add dummy "next" button to keep spacing
addSpace(el, nextBtn);
addButton(el, prevBtn, links[indx - 1]);
} else {
addButton(el, nextBtn, links[indx + 1]);
addButton(el, prevBtn, links[indx - 1]);
}
}
});
}
}
function init() {
if ($("#files.diff-view") || $(".pr-toolbar")) {
addLinks();
}
}
// DOM targets - to detect GitHub dynamic ajax page loading
on(document, "ghmo:container", init);
on(document, "ghmo:diff", addLinks);
init();
})();