GitHub Indent Comments

A userscript that allows you to indent & outdent blocks in the comment editor

当前为 2023-07-01 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name GitHub Indent Comments
  3. // @version 1.0.18
  4. // @description A userscript that allows you to indent & outdent blocks in the comment editor
  5. // @license MIT
  6. // @author Rob Garrison
  7. // @namespace https://github.com/Mottie
  8. // @match https://github.com/*
  9. // @match https://gist.github.com/*
  10. // @run-at document-idle
  11. // @grant GM_addStyle
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @grant GM_registerMenuCommand
  15. // @connect github.com
  16. // @require https://greasyfork.org/scripts/28721-mutations/code/mutations.js?version=1108163
  17. // @require https://greasyfork.org/scripts/398877-utils-js/code/utilsjs.js?version=1079637
  18. // @require https://greasyfork.org/scripts/28239-rangy-inputs-mod-js/code/rangy-inputs-modjs.js?version=181769
  19. // @icon https://github.githubassets.com/pinned-octocat.svg
  20. // @supportURL https://github.com/Mottie/GitHub-userscripts/issues
  21. // ==/UserScript==
  22. /* global $ $$ on make */
  23. (() => {
  24. "use strict";
  25.  
  26. let spaceSize = GM_getValue("space-size", 2);
  27.  
  28. const icons = {
  29. indent: `
  30. <svg class="octicon" xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16">
  31. <path d="M12 13c0 .6 0 1-.9 1H.9c-.9 0-.9-.4-.9-1s0-1 .9-1h10.2c.88 0 .88.4.88 1zM.92 4h10.2C12 4 12 3.6 12 3s0-1-.9-1H.92c-.9 0-.9.4-.9 1s0 1 .9 1zM11.5 7h-5C6 7 6 7.4 6 8s0 1 .5 1h5c.5 0 .5-.4.5-1s0-1-.5-1zm-7 1L0 5v6z"/>
  32. </svg>`,
  33. outdent: `
  34. <svg class="octicon" xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16">
  35. <path d="M12 13c0 .6 0 1-.9 1H.9c-.9 0-.9-.4-.9-1s0-1 .9-1h10.2c.88 0 .88.4.88 1zM.92 4h10.2C12 4 12 3.6 12 3s0-1-.9-1H.92c-.9 0-.9.4-.9 1s0 1 .9 1zm10.7 3H6.4c-.46 0-.4.4-.4 1s-.06 1 .4 1h5.2c.47 0 .4-.4.4-1s.07-1-.4-1zM0 8l4.5-3v6z"/>
  36. </svg>`
  37. };
  38.  
  39. GM_addStyle(".ghio-in-outdent * { pointer-events:none; }");
  40.  
  41. // Add indent & outdent buttons
  42. function addButtons() {
  43. createButton("Outdent");
  44. createButton("Indent");
  45. }
  46.  
  47. function createButton(name) {
  48. const nam = name.toLowerCase();
  49. const button = make({
  50. className: `ghio-${nam.toLowerCase()} ghio-in-outdent btn-link toolbar-item btn-octicon no-underline tooltipped tooltipped-n`,
  51. attrs: {
  52. "aria-label": `${name} Selected Text`,
  53. tabindex: "-1",
  54. type: "button"
  55. },
  56. html: icons[nam.toLowerCase()]
  57. });
  58. $$(".toolbar-commenting").forEach(el => {
  59. if (el && !$(`.ghio-${nam.toLowerCase()}`, el)) {
  60. el.insertBefore(button.cloneNode(true), el.childNodes[0]);
  61. }
  62. });
  63. }
  64.  
  65. function indent(text) {
  66. let result = [];
  67. let block = new Array(parseInt(spaceSize, 10) + 1).join(" ");
  68. (text || "").split(/\r*\n/).forEach(line => {
  69. result.push(block + line);
  70. });
  71. return result.join("\n");
  72. }
  73.  
  74. function outdent(text) {
  75. let regex = new RegExp(`^(\x20{1,${spaceSize}}|\xA0{1,${spaceSize}}|\x09)`);
  76. let result = [];
  77. (text || "").split(/\r*\n/).forEach(line => {
  78. result.push(line.replace(regex, ""));
  79. });
  80. return result.join("\n");
  81. }
  82.  
  83. function addBindings() {
  84. window.rangyInput.init();
  85. saveTabSize();
  86. on($("body"), "click", ({ target }) => {
  87. if (target?.classList.contains("ghio-in-outdent")) {
  88. const form = target.closest(".previewable-comment-form");
  89. const textarea = $(".comment-form-textarea", form);
  90. textarea.focus();
  91. setTimeout(() => {
  92. window.rangyInput.indentSelectedText(
  93. textarea,
  94. target.classList.contains("ghio-indent") ? indent : outdent
  95. );
  96. }, 100);
  97. return false;
  98. }
  99. });
  100. // Add Tab & Shift + Tab
  101. on($("body"), "keydown", event => {
  102. const { target, key } = event;
  103. if (key === "Tab") {
  104. if (target?.classList.contains("comment-form-textarea")) {
  105. event.preventDefault();
  106. target.focus();
  107. setTimeout(() => {
  108. window.rangyInput.indentSelectedText(
  109. target,
  110. // shift + tab = outdent
  111. event.getModifierState("Shift") ? outdent : indent
  112. );
  113. }, 100);
  114. }
  115. }
  116. });
  117. }
  118.  
  119. function saveTabSize() {
  120. let $el = $(".gh-indent-size");
  121. if (!$el) {
  122. $el = document.createElement("style");
  123. $el.setAttribute("rel", "stylesheet");
  124. $el.className = "gh-indent-size";
  125. document.querySelector("head").appendChild($el);
  126. }
  127. $el.innerHTML = `.comment-form-textarea { tab-size:${spaceSize}; }`;
  128. }
  129.  
  130. // Add GM options
  131. GM_registerMenuCommand(
  132. "Indent or outdent size",
  133. () => {
  134. const spaces = GM_getValue("indentOutdentSize", spaceSize);
  135. let val = prompt("Enter number of spaces to indent or outdent:", spaces);
  136. if (val !== null && typeof val === "string") {
  137. spaceSize = val;
  138. GM_setValue("space-size", val);
  139. saveTabSize();
  140. }
  141. }
  142. );
  143.  
  144. on(document, "ghmo:container", addButtons);
  145. addBindings();
  146. addButtons();
  147. })();