GitHub Diff Filename

A userscript that highlights filename & permission alterations

  1. // ==UserScript==
  2. // @name GitHub Diff Filename
  3. // @version 1.1.6
  4. // @description A userscript that highlights filename & permission alterations
  5. // @license MIT
  6. // @author Rob Garrison
  7. // @namespace https://github.com/Mottie
  8. // @match https://github.com/*
  9. // @run-at document-end
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // @require https://gf.qytechs.cn/scripts/28721-mutations/code/mutations.js?version=1108163
  13. // @require https://gf.qytechs.cn/scripts/398877-utils-js/code/utilsjs.js?version=1079637
  14. // @icon https://github.githubassets.com/pinned-octocat.svg
  15. // @supportURL https://github.com/Mottie/GitHub-userscripts/issues
  16. // ==/UserScript==
  17.  
  18. /* global $ $$ on */
  19. (() => {
  20. "use strict";
  21.  
  22. const arrow = "\u2192"; // "→"
  23. const regex = new RegExp(`\\s${arrow}\\s`);
  24.  
  25. function processFileInfo(el) {
  26. if (!$(".ghdfn", el)) {
  27. // A file can be moved AND include permission changes
  28. // e.g. main.js → scripts/main.js 100755 → 100644
  29. // see https://github.com/openstyles/stylus/pull/110/files#diff-5186ece9a52b5e8b0d2e221fdf139ae963ae774267b2f52653c7e45e2a0bda52
  30.  
  31. const link = $("a[title]", el);
  32. // file name/location changes are inside the link
  33. if (link && regex.test(link.textContent)) {
  34. modifyLinkText(link);
  35. }
  36. // permission changes in a text node as a direct child of the wrapper
  37. // process permission change (if it exists)
  38. const node = findTextNode(el)[0];
  39. processNode(node);
  40. }
  41. }
  42.  
  43. function modifyLinkText(link) {
  44. if (link) {
  45. const [oldFile, newFile] = (link.title || "").split(regex);
  46. link.innerHTML = `
  47. <span class="ghdfn color-fg-danger">${oldFile}</span> ${arrow}
  48. <span class="ghdfn color-fg-success">${newFile}</span>`;
  49. }
  50. }
  51.  
  52. function processNode(node) {
  53. if (node) {
  54. let txt = node.textContent,
  55. // modify right node first to maintain node text indexing
  56. middle = txt.indexOf(arrow);
  57. if (middle > -1) {
  58. wrapParts({
  59. start: middle + 2,
  60. end: txt.length,
  61. name: "ghdfn color-fg-success",
  62. node
  63. });
  64. }
  65. middle = node.textContent.indexOf(arrow);
  66. if (middle > -1) {
  67. wrapParts({
  68. start: 0,
  69. end: middle - 1,
  70. name: "ghdfn color-fg-danger",
  71. node
  72. });
  73. }
  74. }
  75. }
  76.  
  77. function findTextNode(el) {
  78. return [...el.childNodes].filter(
  79. node => regex.test(node.textContent) && node.nodeType === 3
  80. );
  81. }
  82.  
  83. function wrapParts(data) {
  84. let newNode, tmpNode;
  85. const {start, end, name, node} = data;
  86. if (node && node.nodeType === 3) {
  87. tmpNode = node.splitText(start);
  88. tmpNode.splitText(end - start);
  89. newNode = document.createElement("span");
  90. newNode.className = name;
  91. newNode.textContent = tmpNode.textContent;
  92. tmpNode.parentNode.replaceChild(newNode, tmpNode);
  93. }
  94. }
  95.  
  96. function init() {
  97. if ($("#files")) {
  98. $$("#files .file-info").forEach(processFileInfo);
  99. }
  100. }
  101.  
  102. on(document, "ghmo:container ghmo:diff", init);
  103. init();
  104.  
  105. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址