Overleaf Custom VIM Keybindings (Code Mirror v6) (aik2 modified)

Configure a list of shortcuts for Vim-mode + user-defined :commands for toggling panels on Overleaf.

  1. // ==UserScript==
  2. // @name Overleaf Custom VIM Keybindings (Code Mirror v6) (aik2 modified)
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.0.1
  5. // @match https://www.overleaf.com/project/*
  6. // @grant none
  7. // @description Configure a list of shortcuts for Vim-mode + user-defined :commands for toggling panels on Overleaf.
  8. // @license MIT
  9. // ==/UserScript==
  10.  
  11. /* globals jQuery, $, waitForKeyElements */
  12.  
  13. (function() {
  14. 'use strict';
  15. // Interact with Writefull suggestion card.
  16. setTimeout(function(){
  17. var card;
  18. var editor = window.editor
  19. // Use ESC to dismiss the suggestion card
  20. $(document).keyup(function(event){
  21. switch(event.keyCode) {
  22. case 27:
  23. esc()
  24. case 96:
  25. esc()
  26. }
  27. });
  28. function esc(){
  29. card = $('.writefull-suggest-card');
  30. if(card.css("display")=='flex'){
  31. card.css("display",'none')
  32. // Restore the focus back to ACE editor
  33. editor.focus() // side-effect - a side box is drawn
  34. }
  35. };
  36.  
  37. var KeycodeMap = {49:1, 50:2, 82:'r', 65:"a", 57:9, 48:0}; // scancode to literal keys
  38. $(document).keyup(function(event){
  39. card = $('.writefull-suggest-card');
  40. if(card.css("display")=='flex'){
  41.  
  42. // If 1 is pressed - accept the current suggestion
  43. if (KeycodeMap[event.which] == 1){
  44. console.log('1 is pressed -> accepting the current instance')
  45. card.find('li.replacement').eq(0)[0].click();
  46. editor.focus()
  47. }
  48.  
  49. // If 2 is pressed - reject the current suggestion
  50. if (KeycodeMap[event.which] == 2){
  51. console.log('2 is pressed -> rejecting the current instance')
  52. card.find('li.replacement').eq(1)[0].click();
  53. editor.focus()
  54. }
  55.  
  56. // If 9 is pressed - accept all
  57. if (KeycodeMap[event.which] == 9){
  58. console.log('9 is pressed -> accept all is pressed')
  59. card.find('span.accept-all').click();
  60. editor.focus()
  61. }
  62. // If 0 is pressed - reject all
  63. if (KeycodeMap[event.which] == 0){
  64. console.log('0 is pressed -> reject all is pressed')
  65. card.find('span.reject-all').click();
  66. editor.focus()
  67. }
  68.  
  69. // TODO-1: use a and r to accept and reject.
  70. // Note, refer to TDOD-2 as well - need to block a/r from CodeMirror's vim.
  71. }
  72. });
  73. },2000);
  74.  
  75. window.addEventListener("UNSTABLE_editor:extensions", (event) => {
  76. const { CodeMirror, CodeMirrorVim, extensions } = event.detail;
  77.  
  78. // Nullify keys used for choosing Writefull card (TODO-2)
  79. // Reference: https://stackoverflow.com/a/24490849
  80. // Task: condition the execution of key "a" and "r" based on whether the Writefull card is available.
  81.  
  82. // Mappings in insert mode
  83. CodeMirrorVim.Vim.map("<c-]>", "<Esc>", "insert");
  84. CodeMirrorVim.Vim.map("jk", "<Esc>", "insert");
  85.  
  86. // Remap in normal mode
  87. /// To navigate through wrapped long lines
  88. CodeMirrorVim.Vim.map("$", "g$", "normal");
  89. CodeMirrorVim.Vim.map("0", "g0", "normal");
  90. CodeMirrorVim.Vim.map("j", "gj", "normal");
  91. CodeMirrorVim.Vim.map("k", "gk", "normal");
  92. CodeMirrorVim.Vim.map("H", "g0", "normal");
  93. CodeMirrorVim.Vim.map("H", "g0", "visual");
  94. CodeMirrorVim.Vim.map("L", "g$", "normal");
  95. CodeMirrorVim.Vim.map("L", "g$", "visual");
  96. CodeMirrorVim.Vim.map("J", "5gj", "normal");
  97. CodeMirrorVim.Vim.map("J", "5gj", "visual");
  98. CodeMirrorVim.Vim.map("K", "5gk", "normal");
  99. CodeMirrorVim.Vim.map("K", "5gk", "visual");
  100. CodeMirrorVim.Vim.map("Y", "y$", "normal");
  101. CodeMirrorVim.Vim.map("U", "<C-r>", "normal");
  102.  
  103.  
  104.  
  105. // User-defined commands
  106. CodeMirrorVim.Vim.defineEx("home", undefined, buctton_click_ProjectList);
  107. CodeMirrorVim.Vim.defineEx("back", undefined, buctton_click_ProjectList);
  108. CodeMirrorVim.Vim.defineEx("forward", undefined, buctton_click_forward_search); // ref: https://discuss.codemirror.net/t/vim-how-to-use-defineex/738/2
  109. CodeMirrorVim.Vim.defineEx("pdf", undefined, buctton_click_toggle_pdf_panel);
  110. CodeMirrorVim.Vim.defineEx("p", undefined, buctton_click_toggle_pdf_panel);
  111. CodeMirrorVim.Vim.defineEx("toc", undefined, buctton_click_toggle_TOC_left_panel);
  112. CodeMirrorVim.Vim.defineEx("only", undefined, buctton_click_toggle_editor_only);
  113. CodeMirrorVim.Vim.defineEx("o", undefined, buctton_click_toggle_editor_only); // :o as :o[nly]
  114. CodeMirrorVim.Vim.defineEx("re", undefined, writefull_rephrase); // :re[phrase]
  115. // Access the functions by defining them as actions first
  116. CodeMirrorVim.Vim.defineAction('jumpToPdf', buctton_click_forward_search);
  117. CodeMirrorVim.Vim.defineAction('only', buctton_click_toggle_editor_only);
  118. CodeMirrorVim.Vim.defineAction('toc', buctton_click_toggle_TOC_left_panel);
  119. CodeMirrorVim.Vim.defineAction('WritefullPrevious', writefull_previous_error);
  120. CodeMirrorVim.Vim.defineAction('WritefullNext', writefull_next_error);
  121. CodeMirrorVim.Vim.defineAction('WritefullRephrase', writefull_rephrase);
  122.  
  123. // forward search: from VimTeX, <localleader>lv
  124. CodeMirrorVim.Vim.unmap(';');
  125. CodeMirrorVim.Vim.mapCommand("\\lv", "action", "jumpToPdf");
  126. CodeMirrorVim.Vim.mapCommand(";lv", 'action', 'jumpToPdf');
  127. // Alternatively: use , as leader key
  128. CodeMirrorVim.Vim.unmap(',');
  129. CodeMirrorVim.Vim.mapCommand(",lv", 'action', 'jumpToPdf');
  130. // quick toggles
  131. CodeMirrorVim.Vim.mapCommand(",v", 'action', 'toc'); // <leader>v is from VimTeX
  132. CodeMirrorVim.Vim.mapCommand(",o", 'action', 'only'); // <leader>o for :o[nly]
  133.  
  134.  
  135. // Writefull shortcuts:
  136. // Jump to suggested typo using Writefull, with [s and ]s
  137. CodeMirrorVim.Vim.mapCommand("[s", 'action', 'WritefullPrevious');
  138. CodeMirrorVim.Vim.mapCommand("]s", 'action', 'WritefullNext');
  139. // Remap in visual mode - select a paragraph, press gr to trigger the rephrase tool
  140. CodeMirrorVim.Vim.mapCommand("gr", "action", "WritefullRephrase", "visual");
  141. // Next step:
  142. // It would be nice to accept/reject the suggestion card using 1/a or 2/r, for example. Leave it for Writefull to build this for now (2023-03-02, 15:52)
  143.  
  144.  
  145. // 2. Restore Ctrl+C to copy into system clipboard
  146. // Solution: Undo mappings for Ctrl+C completely.
  147. // Ref: https://discuss.codemirror.net/t/vim-how-to-copy-selected-text-in-visual-mode-with-ctrl-c-on-windows/5855/6
  148. // Caveat: upon unmapping <C-c>, copying with Ctrl+C works. Yet, if one were to copy from Visual mode, an extract j/k movement is needed to "escape" from Visual mode.
  149. CodeMirrorVim.Vim.unmap('<C-c>');
  150. CodeMirrorVim.Vim.map("<C-c>", "<C-c><Esc>");
  151. });
  152. })();
  153.  
  154. // Collection of functions
  155.  
  156. // Navigation releated functions
  157. function buctton_click_forward_search(cm) {
  158. document.querySelector('.fa-arrow-right').click()
  159. };
  160. function buctton_click_toggle_pdf_panel(cm) {
  161. document.querySelector('a[class*="custom-toggler-east"]').click()
  162. };
  163. function buctton_click_toggle_TOC_left_panel(cm) {
  164. document.querySelector('a[tooltip*="the file-tree"]').click()
  165. };
  166. function buctton_click_toggle_editor_only(cm) {
  167. // This is just the sum of the two: toggle toc and toggle pdf
  168. document.querySelector('a[class*="custom-toggler-east"]').click()
  169. document.querySelector('a[tooltip*="the file-tree"]').click()
  170. };
  171. function buctton_click_ProjectList(cm) {
  172. document.querySelector('i[class*="fa-home"]').click()
  173. };
  174.  
  175. // Spell-checker specific functions
  176. function writefull_next_error(cm) {
  177. document.querySelector('mwc-icon-button[id="writefull-next"]').click()
  178. }
  179. function writefull_previous_error(cm) {
  180. document.querySelector('mwc-icon-button[id="writefull-previous"]').click()
  181. }
  182. function writefull_rephrase(cm) {
  183. document.querySelector('mwc-icon-button[id="writefull-rephrase"]').click()
  184. }