GitHub Code Guides

A userscript that allows you to add one or more vertical guidelines to the code

As of 2020-07-11. See the latest version.

  1. // ==UserScript==
  2. // @name GitHub Code Guides
  3. // @version 1.1.14
  4. // @description A userscript that allows you to add one or more vertical guidelines to the code
  5. // @license MIT
  6. // @author Rob Garrison
  7. // @namespace https://github.com/Mottie
  8. // @include https://github.com/*
  9. // @run-at document-idle
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // @grant GM_registerMenuCommand
  13. // @icon https://github.githubassets.com/pinned-octocat.svg
  14. // ==/UserScript==
  15. /* copy into textarea to check the guides
  16. 1 2 3 4 5 6 7 8
  17. 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345
  18. */
  19. (() => {
  20. "use strict";
  21. const style = document.createElement("style");
  22. // eslint-disable-next-line one-var
  23. let guides = GM_getValue("ghcg-guides", [{
  24. chars: 80,
  25. color: "rgba(0, 0, 0, .3)",
  26. width: 0.2
  27. }]),
  28. font = GM_getValue("ghcg-font", "Menlo"),
  29. tabSize = GM_getValue("ghcg-tabs", 2);
  30.  
  31. function adjust(val) {
  32. return `${val.toFixed(1)}ch`;
  33. }
  34.  
  35. function addDefinition(start, end, color) {
  36. return `
  37. transparent ${start},
  38. ${color} ${start},
  39. ${color} ${end},
  40. transparent ${end},
  41. `;
  42. }
  43.  
  44. function addGuides(vals) {
  45. let css = "";
  46. // to align the guides *after* the setting, we need to subtract 0.5, then
  47. // add another 0.1 to give the guide a tiny bit of white space to the left
  48. vals.forEach(guide => {
  49. let start = parseFloat(guide.chars) - 0.5,
  50. size = parseFloat(guide.width) || 0.2;
  51. const color = guide.color || "rgba(0, 0, 0, .3)";
  52. // each line needs to be at least 0.2ch in width to be visible
  53. size = size > 0.2 ? size : 0.2;
  54. css += addDefinition(adjust(start), adjust(start + size), color);
  55. });
  56. style.textContent = `
  57. table.tab-size[data-tab-size] {
  58. tab-size: ${tabSize};
  59. -moz-tab-size: ${tabSize};
  60. }
  61. span.blob-code-inner:before,
  62. td.blob-code-inner:not(.blob-code-hunk):before,
  63. .blob-code-context .blob-code-inner:before,
  64. .blob-code-addition .blob-code-inner:before,
  65. .blob-code-deletion .blob-code-inner:before {
  66. display: block;
  67. position: absolute;
  68. top: 0;
  69. left: 1em;
  70. width: 100%;
  71. height: 100%;
  72. text-indent: -1em;
  73. }
  74. .blob-code span.blob-code-inner {
  75. display: block !important;
  76. }
  77. span.blob-code-inner,
  78. td.blob-code-inner:not(.blob-code-hunk),
  79. .blob-code-inner:before {
  80. font-family: "${font}", Consolas, "Liberation Mono", Menlo, Courier, monospace !important;
  81. }
  82. span.blob-code-inner:before,
  83. td.blob-code-inner:not(.blob-code-hunk):before {
  84. background: linear-gradient(to right, transparent 0%, ${css} transparent 100%) !important;
  85. pointer-events: none;
  86. content: '';
  87. }
  88. `;
  89. }
  90.  
  91. function validateGuides(vals) {
  92. let last = 0;
  93. const valid = [];
  94. if (!Array.isArray(vals)) {
  95. console.log("Code-Guides Userscript: Invalid guidelines", vals);
  96. return;
  97. }
  98. // Object.keys() creates an array of string values
  99. const lines = vals.sort((a, b) => parseFloat(a.chars) - parseFloat(b.chars));
  100. lines.forEach(line => {
  101. const num = parseFloat(line.chars);
  102. // 0.2 is the width of the "ch" in CSS to make it visible
  103. if (num >= last + line.width) {
  104. valid.push(line);
  105. last = num;
  106. }
  107. });
  108. if (valid.length) {
  109. guides = valid;
  110. GM_setValue("ghcg-guides", valid);
  111. GM_setValue("ghcg-font", font);
  112. GM_setValue("ghcg-tabs", tabSize);
  113. addGuides(valid);
  114. }
  115. }
  116.  
  117. document.querySelector("head").appendChild(style);
  118. validateGuides(guides);
  119.  
  120. // Add GM options
  121. GM_registerMenuCommand("Set code guideline position & color", () => {
  122. let val = prompt(
  123. `Enter valid JSON [{ "chars":80, "color":"#f00", "width":0.2 }, ...}]`,
  124. JSON.stringify(guides)
  125. );
  126. if (val !== null) {
  127. try {
  128. val = JSON.parse(val);
  129. validateGuides(val);
  130. } catch (err) {
  131. console.log(err);
  132. }
  133. }
  134. });
  135.  
  136. GM_registerMenuCommand("Set code guideline default font", () => {
  137. const val = prompt("Enter code font (monospaced)", font);
  138. if (val !== null) {
  139. font = val;
  140. validateGuides(guides);
  141. }
  142. });
  143.  
  144. GM_registerMenuCommand("Set code guideline tab size", () => {
  145. const val = prompt("Enter code guideline tab size", tabSize);
  146. if (val !== null) {
  147. tabSize = val;
  148. validateGuides(guides);
  149. }
  150. });
  151.  
  152. })();