Merge Atlantis Comments

Merges concurrent comments by atlantisvoxel on GitHub pull requests

  1. // ==UserScript==
  2. // @name Merge Atlantis Comments
  3. // @namespace http://tampermonkey.net/
  4. // @version 2023-12-04
  5. // @description Merges concurrent comments by atlantisvoxel on GitHub pull requests
  6. // @author Walker Hildebrand (wbhildeb)
  7. // @match https://github.com/*/pull/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=github.com
  9. // @license MIT
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. const noisyLineRegexes = [
  17. /*
  18.  
  19. /\[id=.+\]$/,
  20. /: Reading\.\.\.$/,
  21.  
  22. */
  23. ]
  24.  
  25. function getAllCommentBlocks(parent) {
  26. return parent.querySelectorAll('.timeline-comment');
  27. }
  28.  
  29. function getAuthorOfCommentBlock(commentBlock) {
  30. return commentBlock.querySelector('.author').innerText.trim();
  31. }
  32.  
  33. function isAtlantisComment(commentBlock) {
  34. return getAuthorOfCommentBlock(commentBlock).startsWith('atlantis')
  35. }
  36.  
  37. function getTruncationTextElement(commentBlock) {
  38. const elem = commentBlock.querySelector('.comment-body p:last-child');
  39. if (elem != null && elem.innerText.trim() !== 'Warning: Output length greater than max comment size. Continued in next comment.') {
  40. return null
  41. }
  42.  
  43. return elem
  44. }
  45.  
  46. function isTruncatedCommentBlock(commentBlock) {
  47. return getTruncationTextElement(commentBlock) != null
  48. }
  49.  
  50. function getContinuationTextElement(commentBlock) {
  51. const elem = commentBlock.querySelector('.comment-body p:first-child')
  52. if (elem != null && elem.innerText.trim() !== 'Continued plan output from previous comment.') {
  53. return null
  54. }
  55.  
  56. return elem
  57. }
  58.  
  59. function isContiuationCommentBlock(commentBlock) {
  60. return getContinuationTextElement(commentBlock) != null
  61. }
  62.  
  63. function getOutputElements(commentBlock) {
  64. return commentBlock.querySelectorAll('pre')
  65. }
  66.  
  67. function removeNoisyLines(outputElement) {
  68. const lines = outputElement.innerHTML.split('\n');
  69.  
  70. const filteredLines = lines.filter(line => {
  71. return !noisyLineRegexes.some(regex => regex.test(line.trim()));
  72. });
  73.  
  74. outputElement.innerHTML = filteredLines.join('\n');
  75. }
  76.  
  77. function mergeCommentBlocks(curBlock, nxtBlock) {
  78. // Remove "Warning: Output length greater than max comment size. Continued in next comment."
  79. const truncationElement = getTruncationTextElement(curBlock)
  80. truncationElement.previousElementSibling.remove() // remove the break
  81. truncationElement.remove()
  82.  
  83. // Remove "Continued plan output from previous comment."
  84. getContinuationTextElement(nxtBlock).remove()
  85.  
  86. // Merge continued outputs
  87. const curBlockOutputs = getOutputElements(curBlock)
  88. const nxtBlockOutputs = getOutputElements(nxtBlock)
  89. if (curBlockOutputs.length > 0 && nxtBlockOutputs.length > 0) {
  90. const curOutputElem = curBlockOutputs[curBlockOutputs.length-1]
  91. const nxtOutputElem = nxtBlockOutputs[0]
  92.  
  93. curOutputElem.innerHTML = curOutputElem.innerHTML.trim() + nxtOutputElem.innerHTML.trim()
  94.  
  95. nxtBlockOutputs[0].closest('details').remove()
  96. }
  97.  
  98. // Merge the rest of the comment
  99. const curBodyElem = curBlock.querySelector('.comment-body');
  100. const nxtBodyElem = nxtBlock.querySelector('.comment-body');
  101. curBodyElem.innerHTML = curBodyElem.innerHTML.trim() + nxtBodyElem.innerHTML.trim()
  102. nxtBlock.closest('.js-timeline-item').remove();
  103. }
  104.  
  105. function concatenateCommentBodies(commentBlocks) {
  106. for (let i = 0; i < commentBlocks.length -1; ) {
  107. const curBlock = commentBlocks[i];
  108. const nxtBlock = commentBlocks[i+1];
  109.  
  110. const curIsTruncated = isTruncatedCommentBlock(curBlock);
  111. const nxtIsContinuation = isContiuationCommentBlock(nxtBlock);
  112.  
  113. // Check if we should merge
  114. if (!curIsTruncated || !nxtIsContinuation) {
  115. i++
  116. continue
  117. }
  118.  
  119. // Merge the output blocks
  120. mergeCommentBlocks(curBlock, nxtBlock)
  121.  
  122. getOutputElements(curBlock).forEach(elem => {
  123. removeNoisyLines(elem)
  124. })
  125.  
  126. commentBlocks.splice(i+1,1)
  127. }
  128. }
  129.  
  130. function onLoad() {
  131. const commentBlocks = Array.from(getAllCommentBlocks(document)).filter(isAtlantisComment);
  132.  
  133. concatenateCommentBodies(commentBlocks)
  134. }
  135.  
  136. window.addEventListener('load', onLoad);
  137. })();