shortcuts.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. import { EditorView, keymap } from '@codemirror/view'
  2. import { Prec } from '@codemirror/state'
  3. import { indentMore } from '../commands/indent'
  4. import {
  5. indentLess,
  6. redo,
  7. deleteLine,
  8. toggleLineComment,
  9. cursorLineBoundaryBackward,
  10. selectLineBoundaryBackward,
  11. cursorLineBoundaryForward,
  12. selectLineBoundaryForward,
  13. cursorSyntaxLeft,
  14. selectSyntaxLeft,
  15. cursorSyntaxRight,
  16. selectSyntaxRight,
  17. } from '@codemirror/commands'
  18. import { changeCase, duplicateSelection } from '../commands/ranges'
  19. import { selectNextOccurrence, selectPrevOccurrence } from '../commands/select'
  20. import { cloneSelectionVertically } from '../commands/cursor'
  21. import { dispatchEditorEvent } from './changes/change-manager'
  22. import {
  23. deleteToVisualLineEnd,
  24. deleteToVisualLineStart,
  25. } from './visual-line-selection'
  26. import { isSplitTestEnabled } from '@/utils/splitTestUtils'
  27. import { emitShortcutEvent } from '@/features/source-editor/extensions/toolbar/utils/analytics'
  28. const toggleReviewPanel = () => {
  29. if (isSplitTestEnabled('review-panel-redesign')) {
  30. window.dispatchEvent(new Event('ui.toggle-review-panel'))
  31. } else {
  32. dispatchEditorEvent('toggle-review-panel')
  33. }
  34. return true
  35. }
  36. const addNewCommentFromKbdShortcut = (view: EditorView) => {
  37. if (isSplitTestEnabled('review-panel-redesign')) {
  38. window.dispatchEvent(new Event('add-new-review-comment'))
  39. } else {
  40. dispatchEditorEvent('add-new-comment')
  41. }
  42. return true
  43. }
  44. const toggleTrackChangesFromKbdShortcut = () => {
  45. if (isSplitTestEnabled('review-panel-redesign')) {
  46. window.dispatchEvent(new Event('toggle-track-changes'))
  47. } else {
  48. dispatchEditorEvent('toggle-track-changes')
  49. }
  50. return true
  51. }
  52. /**
  53. * Custom key bindings for motion, transformation, selection, history, etc.
  54. */
  55. export const shortcuts = Prec.high(
  56. keymap.of([
  57. {
  58. key: 'Tab',
  59. run: indentMore,
  60. },
  61. {
  62. key: 'Shift-Tab',
  63. run: indentLess,
  64. },
  65. {
  66. key: 'Mod-y',
  67. preventDefault: true,
  68. run: redo,
  69. },
  70. {
  71. key: 'Mod-Shift-z',
  72. preventDefault: true,
  73. run: redo,
  74. },
  75. // defaultKeymap maps Mod-/ to toggleLineComment, but
  76. // w3c-keyname has a hard-coded mapping of Shift+key => character
  77. // which uses a US keyboard layout, so we need to add more mappings.
  78. // Mod-/, but Spanish, Portuguese, German and Swedish keyboard layouts have / at Shift+7
  79. // (keyCode 55, mapped with Shift to &)
  80. {
  81. key: 'Mod-&',
  82. preventDefault: true,
  83. run: toggleLineComment,
  84. },
  85. // Mod-/, but German keyboard layouts have / at Cmd+Shift+ß
  86. // Mod-/, but Czech keyboard layouts have / at Shift-ú
  87. // (keyCode 191, mapped with Shift to ?)
  88. {
  89. key: 'Mod-?',
  90. preventDefault: true,
  91. run: toggleLineComment,
  92. },
  93. // German keyboard layouts map 0xBF to #,
  94. // so VS Code on Windows/Linux uses Ctrl-# to toggle line comments.
  95. // This is an additional, undocumented shortcut for compatibility.
  96. {
  97. key: 'Ctrl-#',
  98. preventDefault: true,
  99. run: toggleLineComment,
  100. },
  101. {
  102. key: 'Ctrl-u',
  103. preventDefault: true,
  104. run: changeCase(true), // uppercase
  105. },
  106. {
  107. key: 'Ctrl-Shift-u',
  108. preventDefault: true,
  109. run: changeCase(false), // lowercase
  110. },
  111. {
  112. key: 'Mod-d',
  113. preventDefault: true,
  114. run(view) {
  115. emitShortcutEvent(view, 'delete-line')
  116. return deleteLine(view)
  117. },
  118. },
  119. {
  120. key: 'Mod-j',
  121. preventDefault: true,
  122. run: toggleReviewPanel,
  123. },
  124. {
  125. key: 'Mod-Shift-c',
  126. preventDefault: true,
  127. run: addNewCommentFromKbdShortcut,
  128. },
  129. {
  130. key: 'Mod-Shift-a',
  131. preventDefault: true,
  132. run: toggleTrackChangesFromKbdShortcut,
  133. },
  134. {
  135. key: 'Cmd-Alt-ArrowUp',
  136. preventDefault: true,
  137. run: cloneSelectionVertically(false, true, 'cmd'),
  138. },
  139. {
  140. key: 'Cmd-Alt-ArrowDown',
  141. preventDefault: true,
  142. run: cloneSelectionVertically(true, true, 'cmd'),
  143. },
  144. {
  145. key: 'Cmd-Alt-Shift-ArrowUp',
  146. preventDefault: true,
  147. run: cloneSelectionVertically(false, false, 'cmd'),
  148. },
  149. {
  150. key: 'Cmd-Alt-Shift-ArrowDown',
  151. preventDefault: true,
  152. run: cloneSelectionVertically(true, false, 'cmd'),
  153. },
  154. // Duplicates of the above commands,
  155. // allowing Ctrl instead of Command (but still tracking the events separately).
  156. // Note: both Ctrl and Commmand versions need to work on macOS, for backwards compatibility,
  157. // so the duplicates shouldn't simply be combined to use `Mod-`.
  158. {
  159. key: 'Ctrl-Alt-ArrowUp',
  160. preventDefault: true,
  161. run: cloneSelectionVertically(false, true, 'ctrl'),
  162. },
  163. {
  164. key: 'Ctrl-Alt-ArrowDown',
  165. preventDefault: true,
  166. run: cloneSelectionVertically(true, true, 'ctrl'),
  167. },
  168. {
  169. key: 'Ctrl-Alt-Shift-ArrowUp',
  170. preventDefault: true,
  171. run: cloneSelectionVertically(false, false, 'ctrl'),
  172. },
  173. {
  174. key: 'Ctrl-Alt-Shift-ArrowDown',
  175. preventDefault: true,
  176. run: cloneSelectionVertically(true, false, 'ctrl'),
  177. },
  178. {
  179. key: 'Ctrl-Alt-ArrowLeft',
  180. preventDefault: true,
  181. run(view) {
  182. emitShortcutEvent(view, 'select-prev-occurrence')
  183. return selectPrevOccurrence(view)
  184. },
  185. },
  186. {
  187. key: 'Ctrl-Alt-ArrowRight',
  188. preventDefault: true,
  189. run(view) {
  190. emitShortcutEvent(view, 'select-next-occurrence')
  191. return selectNextOccurrence(view)
  192. },
  193. },
  194. {
  195. key: 'Mod-Shift-d',
  196. run: duplicateSelection,
  197. },
  198. {
  199. win: 'Alt-ArrowLeft',
  200. linux: 'Alt-ArrowLeft',
  201. run: cursorLineBoundaryBackward,
  202. shift: selectLineBoundaryBackward,
  203. preventDefault: true,
  204. },
  205. {
  206. win: 'Alt-ArrowRight',
  207. linux: 'Alt-ArrowRight',
  208. run: cursorLineBoundaryForward,
  209. shift: selectLineBoundaryForward,
  210. preventDefault: true,
  211. },
  212. {
  213. mac: 'Ctrl-ArrowLeft',
  214. run: cursorSyntaxLeft,
  215. shift: selectSyntaxLeft,
  216. },
  217. {
  218. mac: 'Ctrl-ArrowRight',
  219. run: cursorSyntaxRight,
  220. shift: selectSyntaxRight,
  221. },
  222. {
  223. mac: 'Cmd-Backspace',
  224. run: deleteToVisualLineStart,
  225. },
  226. {
  227. mac: 'Cmd-Delete',
  228. run: deleteToVisualLineEnd,
  229. },
  230. ])
  231. )