Gitlab, open file in phpstorm

add a link to phpstorm in gitlab ci merge requests files

As of 2021-10-29. See the latest version.

  1. // ==UserScript==
  2. // @name Gitlab, open file in phpstorm
  3. // @namespace micoli.phpstorm.openlinks
  4. // @version 0.7
  5. // @description add a link to phpstorm in gitlab ci merge requests files
  6. // @author You
  7. // @match https://gitlab.com/*
  8. // ==/UserScript==
  9.  
  10. (function () {
  11. // defaults write com.google.Chrome URLWhitelist -array "phpstorm://*
  12. 'use strict';
  13. let config = {};
  14. let linkCounter = 0;
  15.  
  16. document.querySelectorAll('a[href*="redirectFromReferer"]').forEach((link)=> {
  17. link.href = link.href+'?referer='+window.location.href
  18. });
  19.  
  20. const project = $('nav.breadcrumbs ul.js-breadcrumbs-list li a')[1].pathname;
  21.  
  22. const loadPreferences = function () {
  23. if (!window.localStorage.getItem('openInPhpStormSettings')) {
  24. return {};
  25. }
  26. config = JSON.parse(window.localStorage.getItem('openInPhpStormSettings'));
  27. }
  28.  
  29. const savePreferences = function () {
  30. window.localStorage.setItem('openInPhpStormSettings', JSON.stringify(config))
  31. removeLinks();
  32. setLinks();
  33. }
  34.  
  35. const icon = function (color) {
  36. return `
  37. <svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" viewBox="0 0 128 128" width="16" height="16">
  38. <path d="M61,87H35a2,2,0,0,0,0,4H61a2,2,0,0,0,0-4Z"/>
  39. <path fill="${color || '#A12681'}" d="M52.32,42.24a5.52,5.52,0,0,0-2-2.14,5.82,5.82,0,0,0-3.09-.82H41V50.57h6.88A4.71,4.71,0,0,0,51.64,49,5.81,5.81,0,0,0,53,45,5.91,5.91,0,0,0,52.32,42.24Zm0,0a5.52,5.52,0,0,0-2-2.14,5.82,5.82,0,0,0-3.09-.82H41V50.57h6.88A4.71,4.71,0,0,0,51.64,49,5.81,5.81,0,0,0,53,45,5.91,5.91,0,0,0,52.32,42.24ZM114.15,70.4l11.77-11.59a7.87,7.87,0,0,0,1.93-6.73L114.23,17.82a7.78,7.78,0,0,0-4.75-5.09L85.06,8.27A7.08,7.08,0,0,0,83,8a8.92,8.92,0,0,0-5,1.48l-9.51,7-1.35-4A7.73,7.73,0,0,0,62,7.87L26,.12A5.62,5.62,0,0,0,24.77,0a7.84,7.84,0,0,0-5.41,2.2L2.08,20.91a9,9,0,0,0-2,6.83L11.87,92.1a5,5,0,0,0,4.7,3.9H25v3a4,4,0,0,0,4,4h8.37l.76,6.32a7.18,7.18,0,0,0,4.24,5.35l34.85,12.9a7.87,7.87,0,0,0,2.69.43,9.17,9.17,0,0,0,4.5-1.1l41.53-25a4.33,4.33,0,0,0,1.6-5.62ZM80.4,12.7A5.08,5.08,0,0,1,83,12c.47,0,25.41,4.61,25.41,4.61a4,4,0,0,1,1.88,2.14l.07.28.1.27L124,53.21a4.12,4.12,0,0,1-.87,2.75L112.23,66.68,103,48.83V29a4,4,0,0,0-4-4H63.8ZM73.73,51.21l-3.14-.67a27.09,27.09,0,0,1-4.89-1.22,8.8,8.8,0,0,1-3.24-2.12,5.26,5.26,0,0,1-1.46-4,6,6,0,0,1,2.5-5.34,12,12,0,0,1,7-1.87,15.4,15.4,0,0,1,4.76.68,15.86,15.86,0,0,1,4.14,2.1l.11.09-2,2.82a.45.45,0,0,0,0,.09.62.62,0,0,1-.22-.11A14.76,14.76,0,0,0,73.77,40a9.55,9.55,0,0,0-7.1.16A3.05,3.05,0,0,0,64.84,43a2.66,2.66,0,0,0,1,2.11,5.17,5.17,0,0,0,2,1c.51.16,1.48.4,2.9.72L72,47a29.44,29.44,0,0,1,5,1.36,7,7,0,0,1,2.79,2.07A6.18,6.18,0,0,1,81,54.57,6.45,6.45,0,0,1,78.59,60a11.2,11.2,0,0,1-7.07,2,16.38,16.38,0,0,1-10.41-3.35L61,58.56s1.91-2.93,1.91-2.93a.57.57,0,0,1,.19.1,17,17,0,0,0,4.08,2.08c3.48,1.26,6.41,1,8.24-.17a3.41,3.41,0,0,0,1.74-2.92C77.16,53.56,76.58,52,73.73,51.21ZM25,29V92H16.57a1,1,0,0,1-.76-.62L4,27a5.08,5.08,0,0,1,1-3.39L22.29,4.92A3.91,3.91,0,0,1,24.77,4c.18,0,36.4,7.78,36.4,7.78a3.86,3.86,0,0,1,2.2,2L65.14,19l-8.06,6H29A4,4,0,0,0,25,29ZM41,61.91s0,.08-.09.09H37.1a.13.13,0,0,1-.1-.09V36.17A.38.38,0,0,1,37,36a.41.41,0,0,1,.16,0H47a11.27,11.27,0,0,1,5.31,1.2,8.57,8.57,0,0,1,3.46,3.26,9.56,9.56,0,0,1,.07,9.11,7.82,7.82,0,0,1-3.31,3.09A12,12,0,0,1,47,53.85H41Zm82.91,36.56-41.53,25a5.22,5.22,0,0,1-2.44.53,3.9,3.9,0,0,1-1.3-.18l-34.85-12.9a3.35,3.35,0,0,1-1.66-2.08L41.4,103H99a4,4,0,0,0,4-4V57.54l21,40.58A.37.37,0,0,1,123.88,98.47ZM51.64,49A5.81,5.81,0,0,0,53,45a5.91,5.91,0,0,0-.67-2.8,5.52,5.52,0,0,0-2-2.14,5.82,5.82,0,0,0-3.09-.82H41V50.57h6.88A4.71,4.71,0,0,0,51.64,49Z"/>
  40. </svg>`
  41. };
  42.  
  43. const phpStormMimeLink = function(link){
  44. const prefix = config[project] || '';
  45. document.location = `phpstorm://open?file=${prefix}${link}`;
  46. };
  47.  
  48. document.phpStormRestApiLink = function (link) {
  49. const prefix = config[project] || '';
  50. const hrefLink = `http://localhost:63342/api/file?file=${prefix}${link}`;
  51. const linkWindow = window.open(hrefLink,'autoOpen');
  52. setTimeout(function(){
  53. linkWindow.close();
  54. },1000);
  55. };
  56.  
  57. const openLinkInHiddenForm = function (action,parameters) {
  58. let hiddenDiv = document.getElementById('phpstorm-autoform');
  59.  
  60. if (hiddenDiv){
  61. hiddenDiv.parentNode.removeChild(hiddenDiv);
  62. }
  63. console.log(action);
  64. document.querySelector('body').insertAdjacentHTML('beforeEnd',
  65. `<form id='phpstorm-autoform' action="${action}" target="_hiddenTarget" method='GET'>
  66. <input name="file" value="${parameters.file}"/>
  67. <iframe name="_hiddenTarget" width="0" height="0"></iframe>
  68. </form>
  69. `);
  70.  
  71. document.getElementById('phpstorm-autoform').submit();
  72. };
  73.  
  74. const setLinks = function () {
  75. document.querySelectorAll('.diff-file').forEach(function (divDiffFile) {
  76. let jsEditInPhpStormButtonFound = false;
  77. let linkCounter = 0;
  78. divDiffFile.querySelectorAll('button[data-clipboard-text]').forEach( function (buttonClipboard) {
  79. const fileUrl = JSON.parse(buttonClipboard.getAttribute('data-clipboard-text'))['text'];
  80.  
  81. divDiffFile.querySelectorAll('.js-edit-in-phpstorm').forEach(function (buttonEditInPhpStorm) {
  82. if (buttonEditInPhpStorm.getAttribute('data-link') === fileUrl) {
  83. jsEditInPhpStormButtonFound = true;
  84. }
  85. });
  86.  
  87. if (jsEditInPhpStormButtonFound) {
  88. return;
  89. }
  90.  
  91. linkCounter++;
  92. const jsEditInPhpStormId = `js-edit-in-phpstorm-${divDiffFile.id}-${linkCounter}`;
  93. buttonClipboard
  94. .insertAdjacentHTML('afterend', `
  95. <div
  96. id="${jsEditInPhpStormId}"
  97. title="Edit file in phpStorm"
  98. data-link="${fileUrl}"
  99. class="btn btn-transparent js-edit-in-phpstorm"
  100. style="margin-top: -6px;"
  101. >
  102. ${icon()}
  103. </div>
  104. `);
  105.  
  106. document.getElementById(jsEditInPhpStormId).addEventListener('click',function(event){
  107. document.phpStormRestApiLink(event.currentTarget.dataset.link);
  108. });
  109. });
  110. });
  111.  
  112. document.querySelectorAll('.js-edit-in-phpstorm').forEach(function (buttonEditInPhpStorm) {
  113. let buttonClipboardFound = false;
  114. buttonEditInPhpStorm.parentNode.querySelectorAll('.diff-file button[data-clipboard-text]').forEach( function (buttonClipboard) {
  115. const fileUrl = JSON.parse(buttonClipboard.getAttribute('data-clipboard-text'))['text'];
  116. if(buttonEditInPhpStorm.getAttribute('data-link') === fileUrl) {
  117. buttonClipboardFound = true;
  118. }
  119. });
  120. if(!buttonClipboardFound) {
  121. buttonEditInPhpStorm.remove();
  122. }
  123. });
  124. }
  125.  
  126. const removeLinks = function () {
  127. document.querySelectorAll('.js-edit-in-phpstorm').forEach(function (v) {
  128. v.parentElement.removeChild(v);
  129. });
  130. document.querySelectorAll('.file-header-content .file-title-name').forEach(function (v) {
  131. v.class = v.class.replace(/rgx-add-phpstorm-done/, '');
  132. });
  133. }
  134.  
  135. const isConfigurationValid = function () {
  136. try {
  137. JSON.parse(document.getElementById("open-in-phpstorm-map").value);
  138. return true;
  139. } catch (e) {
  140. return false;
  141. }
  142. };
  143.  
  144. const togglePref = function () {
  145. var element = document.getElementById('open-in-phpstorm-settings');
  146. element.style.display = element.style.display == 'none' ? 'inline-block' : 'none'
  147. if (element.style.display == 'inline-block') {
  148. document.getElementById("open-in-phpstorm-map").value = JSON.stringify(config);
  149. }
  150. };
  151.  
  152. const initPreferenceButton = function () {
  153. document.querySelector('.alert-wrapper').insertAdjacentHTML('afterEnd', `
  154.  
  155. <div class="container-fluid">
  156. <div
  157. class="row"
  158. id="open-in-phpstorm-settings"
  159. style="display:none;width:600px;"
  160. >
  161. <div class="form-group col-md-9">
  162. <label class="label-bold" for="open-in-phpstorm-map">
  163. "Open in phpstorm" project association
  164. </label>
  165. <textarea class="form-control" rows="3" maxlength="650" id="open-in-phpstorm-map" name="open-in-phpstorm-settings"></textarea>
  166. </div>
  167. <div class="form-group col-md-9">
  168. <pre>{"${project}":"/home/src/localpath/"}</pre>
  169. <button
  170. id="js-btn-save-in-phpstorm-settings"
  171. class="btn btn-secondary"
  172. style="float:left;"
  173. >
  174. Save ${icon('#919191')} Preferences
  175. </button>
  176. </div>
  177. </div>
  178. </div>
  179. `);
  180.  
  181. document.querySelector('.dropdown-menu.dropdown-menu-right .current-user').parentNode.insertAdjacentHTML('afterend', `
  182. <li >
  183. <a title="PhpStorm Settings" id="js-btn-open-in-phpstorm-settings" data-qa-selector="open_in_phpstorm_settings_link" href="#">${icon('#919191')} "Open in PhpStorm" Settings</a>
  184. </li>
  185. `);
  186.  
  187. document.getElementById('js-btn-open-in-phpstorm-settings').addEventListener('click', function () {
  188. togglePref();
  189. return false;
  190. });
  191.  
  192. document.getElementById('js-btn-save-in-phpstorm-settings').addEventListener('click', function () {
  193. if (isConfigurationValid()) {
  194. togglePref();
  195. config = JSON.parse(document.getElementById("open-in-phpstorm-map").value);
  196. savePreferences();
  197. }
  198. return false;
  199. });
  200. };
  201.  
  202.  
  203. console.log('started');
  204. loadPreferences();
  205. setLinks();
  206. window.setTimeout(initPreferenceButton, 50);
  207. window.setInterval(setLinks, 1500);
  208. })();