GitHub Code Guides

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

As of 2018-01-18. See the latest version.

  1. // ==UserScript==
  2. // @name GitHub Code Guides
  3. // @version 1.1.10
  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://assets-cdn.github.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 add 1, then add
  47. // 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) + 1,
  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:after,
  62. td.blob-code-inner:not(.blob-code-hunk):after,
  63. .blob-code-context .blob-code-inner:after,
  64. .blob-code-addition .blob-code-inner:after,
  65. .blob-code-deletion .blob-code-inner:after {
  66. content: " ";
  67. display: block;
  68. position: absolute;
  69. top: 0;
  70. left: 1em;
  71. width: 100%;
  72. height: 100%;
  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:after {
  80. font-family: "${font}", Consolas, "Liberation Mono", Menlo, Courier, monospace !important;
  81. }
  82. span.blob-code-inner:after,
  83. td.blob-code-inner:not(.blob-code-hunk):after {
  84. background: linear-gradient(to right, transparent 0%, ${css} transparent 100%) !important;
  85. pointer-events: none;
  86. }
  87. `;
  88. }
  89.  
  90. function validateGuides(vals) {
  91. let last = 0;
  92. const valid = [];
  93. if (!Array.isArray(vals)) {
  94. console.log("Code-Guides Userscript: Invalid guidelines", vals);
  95. return;
  96. }
  97. // Object.keys() creates an array of string values
  98. const lines = vals.sort((a, b) => parseFloat(a.chars) - parseFloat(b.chars));
  99. lines.forEach(line => {
  100. const num = parseFloat(line.chars);
  101. // 0.2 is the width of the "ch" in CSS to make it visible
  102. if (num >= last + line.width) {
  103. valid.push(line);
  104. last = num;
  105. }
  106. });
  107. if (valid.length) {
  108. guides = valid;
  109. GM_setValue("ghcg-guides", valid);
  110. GM_setValue("ghcg-font", font);
  111. GM_setValue("ghcg-tabs", tabSize);
  112. addGuides(valid);
  113. }
  114. }
  115.  
  116. document.querySelector("head").appendChild(style);
  117. validateGuides(guides);
  118.  
  119. // Add GM options
  120. GM_registerMenuCommand("Set code guideline position & color", () => {
  121. let val = prompt(
  122. `Enter valid JSON [{ "chars":80, "color":"#f00", "width":0.2 }, ...}]`,
  123. JSON.stringify(guides)
  124. );
  125. if (val !== null) {
  126. try {
  127. val = JSON.parse(val);
  128. validateGuides(val);
  129. } catch (err) {
  130. console.log(err);
  131. }
  132. }
  133. });
  134.  
  135. GM_registerMenuCommand("Set code guideline default font", () => {
  136. const val = prompt("Enter code font (monospaced)", font);
  137. if (val !== null) {
  138. font = val;
  139. validateGuides(guides);
  140. }
  141. });
  142.  
  143. GM_registerMenuCommand("Set code guideline tab size", () => {
  144. const val = prompt("Enter code guideline tab size", tabSize);
  145. if (val !== null) {
  146. tabSize = val;
  147. validateGuides(guides);
  148. }
  149. });
  150.  
  151. })();