Bitbucket : commit links in PRs

Adds convenience links in PRs of Bitbucket v7.6.+

当前为 2022-12-16 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Bitbucket : commit links in PRs
  3. // @namespace http://tampermonkey.net/
  4. // @version 12
  5. // @license MIT
  6. // @description Adds convenience links in PRs of Bitbucket v7.6.+
  7. // @author Andrei Rybak
  8. // @match https://bitbucket.example.com/*/repos/*/pull-requests/*
  9. // @icon https://bitbucket.org/favicon.ico
  10. // @homepageURL https://github.com/rybak/atlassian-tweaks
  11. // @grant none
  12. // ==/UserScript==
  13.  
  14. /*
  15. * Copyright (c) 2021-2022 Andrei Rybak
  16. *
  17. * Permission is hereby granted, free of charge, to any person obtaining a copy
  18. * of this software and associated documentation files (the "Software"), to deal
  19. * in the Software without restriction, including without limitation the rights
  20. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  21. * copies of the Software, and to permit persons to whom the Software is
  22. * furnished to do so, subject to the following conditions:
  23. *
  24. * The above copyright notice and this permission notice shall be included in all
  25. * copies or substantial portions of the Software.
  26. *
  27. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  28. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  29. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  30. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  31. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  32. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  33. * SOFTWARE.
  34. */
  35.  
  36. (function() {
  37. 'use strict';
  38.  
  39. function log(msg) {
  40. console.log("[PR commit links] " + msg);
  41. }
  42.  
  43. const ABBREV_LEN = 8; // abbreviate commit hashes to this number of characters
  44. const BLOCK_ID = 'RybakCommitLinkDiv';
  45. const URL_ID = 'RybakCommitLinkA';
  46. const TOOLTIP_BLOCK_ID = 'RybakCommitMessageDiv';
  47. const TOOLTIP_MSG_ID = 'RybakCommitMessagePre';
  48. const parsePath = /[/](projects|users)[/]([^/]*)[/]repos[/]([^/]*)[/].*[/]commits[/]([0-9a-f]+)/
  49.  
  50. function createTooltip(message) {
  51. $('#' + BLOCK_ID).hover((e) => {
  52. $('#' + TOOLTIP_BLOCK_ID).remove(); // delete previous tooltip
  53. const tooltipHtml = $('<div id="' + TOOLTIP_BLOCK_ID + '" class="Tooltip sc-jnlKLf ghcsui sc-bZQynM WXFrO sc-EHOje cffcMV"' +
  54. 'style="z-index:800; opacity: 1; position: fixed; top: 0px; left: 0px;' + // tweaked original element.style
  55. 'max-width: 600px; width: auto;' + // override of .ghcsui for better fitting of text
  56. 'background-color: rgb(23, 43, 77); border-radius: 3px; box-sizing: border-box; color: rgb(255, 255, 255); font-size: 12px; ' + // from .WXFrO
  57. 'line-height: 1.3; padding: 2px 6px; overflow-wrap: break-word;' + // from .WXFrO
  58. 'pointer-events: none;' + // from .cffcMV
  59. '">' +
  60. '<pre id="' + TOOLTIP_MSG_ID + '" class="commit-message-tooltip" style="' +
  61. 'white-space: pre-wrap; word-break: break-word;' + // from .commit-message-tooltip
  62. '"></pre>' +
  63. '</div>');
  64. $($('.atlaskit-portal-container')[0]).append(tooltipHtml);
  65. $('#' + TOOLTIP_MSG_ID).text(message); // text added early to calculate height correctly
  66.  
  67. const width = $('#' + TOOLTIP_BLOCK_ID).outerWidth();
  68. const height = $('#' + TOOLTIP_BLOCK_ID).height();
  69. const block = $('#' + BLOCK_ID);
  70. const blockOffset = block.offset();
  71. var x = blockOffset.left;
  72. var y = blockOffset.top + block.height() + 8; // 8 is from CSS rule ".changes-scope-actions > *"
  73. const maxX = $(window).width() + window.pageXOffset;
  74. const maxY = $(window).height() + window.pageYOffset;
  75. if (x + width > maxX) {
  76. x = Math.max(maxX - width, 0);
  77. }
  78. if (y + height > maxY) {
  79. y = Math.max(maxY - height, 0);
  80. }
  81. $('#' + TOOLTIP_BLOCK_ID).css({left: x, top: y}).show();
  82. }, (e) => {
  83. $('#' + TOOLTIP_BLOCK_ID).hide();
  84. });
  85. }
  86.  
  87. function ensureCommitLink() {
  88. const matching = document.location.pathname.match(parsePath);
  89. if (!matching) {
  90. log("No commit in the URL: " + document.location.pathname);
  91. return;
  92. }
  93. const origin = document.location.origin;
  94. const hash = document.location.hash; // add hash in case the user clicked to a different file
  95. const projectOrUser = matching[1];
  96. const project = matching[2];
  97. const repository = matching[3];
  98. const commit = matching[4];
  99. log("Parsed " + project + "/" + repository + "/" + commit);
  100.  
  101. const url = origin + '/' + projectOrUser + '/' + project + '/repos/' + repository + '/commits/' + commit + document.location.hash;
  102. const linkText = commit.substring(0, ABBREV_LEN);
  103. log("Link: " + url);
  104. log("Text: " + linkText);
  105.  
  106. const prevBlock = $('#' + BLOCK_ID);
  107. if (prevBlock.length) {
  108. log("Updating the link...");
  109. } else {
  110. // css-18u3ks8 is for Bitbucket Server ~v7.6 (aui ~ 8.1.*)
  111. // css-7svmop is for Bitbucket Server v7.21+ (aui ~ 9.3.*)
  112. const html = '<div id="' + BLOCK_ID + '"><div class="css-18u3ks8 css-7svmop">' + '<a id="' + URL_ID + '"></a>' + '</div></div>';
  113. $(".changes-scope-actions").append(html);
  114. log("Creating the link...");
  115. }
  116. $('#' + URL_ID)
  117. .attr('href', url)
  118. .text(linkText);
  119. log("Ajax...: " + document.location.origin + "/rest/api/1.0/" + projectOrUser + "/" + project + '/repos/' + repository + '/commits/' + commit);
  120.  
  121. $.ajax({
  122. // https://docs.atlassian.com/bitbucket-server/rest/7.6.0/bitbucket-rest.html#idp224
  123. url: (document.location.origin + "/rest/api/1.0/" + projectOrUser + "/" + project + '/repos/' + repository + '/commits/' + commit)
  124. }).then(data => {
  125. log("Ajax response received");
  126. createTooltip(data.message);
  127. });
  128. log("Done");
  129. }
  130.  
  131. $(document).ready(function() {
  132. ensureCommitLink();
  133. window.onpopstate = function(event) {
  134. ensureCommitLink();
  135. };
  136. });
  137.  
  138. })();