index.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import {
  2. EditorView,
  3. rectangularSelection,
  4. tooltips,
  5. crosshairCursor,
  6. dropCursor,
  7. highlightActiveLineGutter,
  8. } from '@codemirror/view'
  9. import { EditorState, Extension } from '@codemirror/state'
  10. import { foldGutter, indentOnInput, indentUnit } from '@codemirror/language'
  11. import { history } from '@codemirror/commands'
  12. import { language } from './language'
  13. import { lineWrappingIndentation } from './line-wrapping-indentation'
  14. import { theme } from './theme'
  15. import { realtime } from './realtime'
  16. import { cursorPosition } from './cursor-position'
  17. import { scrollPosition } from './scroll-position'
  18. import { annotations } from './annotations'
  19. import { cursorHighlights } from './cursor-highlights'
  20. import { autoComplete } from './auto-complete'
  21. import { editable } from './editable'
  22. import { autoPair } from './auto-pair'
  23. import { phrases } from './phrases'
  24. import { spelling } from './spelling'
  25. import { symbolPalette } from './symbol-palette'
  26. import { search } from './search'
  27. import { filterCharacters } from './filter-characters'
  28. import { keybindings } from './keybindings'
  29. import { bracketMatching, bracketSelection } from './bracket-matching'
  30. import { verticalOverflow } from './vertical-overflow'
  31. import { thirdPartyExtensions } from './third-party-extensions'
  32. import { lineNumbers } from './line-numbers'
  33. import { highlightActiveLine } from './highlight-active-line'
  34. import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
  35. import { emptyLineFiller } from './empty-line-filler'
  36. import { goToLinePanel } from './go-to-line'
  37. import { drawSelection } from './draw-selection'
  38. import { nonBlinkingCursor } from './non-blinking-cursor'
  39. import { sourceOnly, visual } from './visual/visual'
  40. import { inlineBackground } from './inline-background'
  41. import { indentationMarkers } from './indentation-markers'
  42. import { codemirrorDevTools } from '../languages/latex/codemirror-dev-tools'
  43. import { keymaps } from './keymaps'
  44. import { shortcuts } from './shortcuts'
  45. import { effectListeners } from './effect-listeners'
  46. import { highlightSpecialChars } from './highlight-special-chars'
  47. import { toolbarPanel } from './toolbar/toolbar-panel'
  48. import { breadcrumbPanel } from './breadcrumbs-panel'
  49. import { geometryChangeEvent } from './geometry-change-event'
  50. import { docName } from './doc-name'
  51. import { fileTreeItemDrop } from './file-tree-item-drop'
  52. import { mathPreview } from './math-preview'
  53. import { ranges } from './ranges'
  54. import { historyOT } from './history-ot'
  55. import { trackDetachedComments } from './track-detached-comments'
  56. import { reviewTooltip } from './review-tooltip'
  57. import { tooltipsReposition } from './tooltips-reposition'
  58. import { selectionListener } from '@/features/source-editor/extensions/selection-listener'
  59. import { contextMenu } from './context-menu'
  60. import { tabsListener } from './tabs-listener'
  61. const moduleExtensions: Array<(options: Record<string, any>) => Extension> =
  62. importOverleafModules('sourceEditorExtensions').map(
  63. (item: { import: { extension: Extension } }) => item.import.extension
  64. )
  65. export const createExtensions = (options: Record<string, any>): Extension[] => [
  66. lineNumbers(),
  67. highlightSpecialChars(options.visual.visual),
  68. // The built-in extension that manages the history stack,
  69. // configured to increase the maximum delay between adjacent grouped edits
  70. history({ newGroupDelay: 250 }),
  71. // The built-in extension that displays buttons for folding code in a gutter element,
  72. // configured with custom openText and closeText symbols.
  73. foldGutter({
  74. openText: '▾',
  75. closedText: '▸',
  76. }),
  77. drawSelection(),
  78. nonBlinkingCursor(),
  79. // A built-in facet that is set to true to allow multiple selections.
  80. // This makes the editor more like a code editor than Google Docs or Microsoft Word,
  81. // which only have single selections.
  82. EditorState.allowMultipleSelections.of(true),
  83. // A built-in extension that enables soft line wrapping.
  84. EditorView.lineWrapping,
  85. sourceOnly(
  86. options.visual.visual,
  87. EditorView.contentAttributes.of({ 'aria-label': 'Source Editor editing' })
  88. ),
  89. // A built-in extension that re-indents input if the language defines an indentOnInput field in its language data.
  90. indentOnInput(),
  91. lineWrappingIndentation(options.visual.visual),
  92. indentationMarkers(options.visual.visual),
  93. bracketMatching(),
  94. bracketSelection(),
  95. // A built-in extension that enables rectangular selections, created by dragging a new selection while holding down Alt.
  96. rectangularSelection(),
  97. // A built-in extension that turns the pointer into a crosshair while Alt is pressed.
  98. crosshairCursor(),
  99. // A built-in extension that shows where dragged content will be dropped.
  100. dropCursor(),
  101. // A built-in extension that is used for configuring tooltip behaviour,
  102. // configured so that the tooltip parent is the document body,
  103. // to avoid cutting off tooltips which overflow the editor.
  104. tooltips({
  105. parent: document.body,
  106. tooltipSpace(view) {
  107. const { top, bottom } = view.scrollDOM.getBoundingClientRect()
  108. return {
  109. top,
  110. left: 0,
  111. bottom,
  112. right: window.innerWidth,
  113. }
  114. },
  115. }),
  116. keymaps,
  117. goToLinePanel(),
  118. filterCharacters(),
  119. // NOTE: `autoComplete` needs to be before `keybindings` so that arrow key handling
  120. // in the autocomplete pop-up takes precedence over Vim/Emacs key bindings
  121. autoComplete({
  122. enabled: options.settings.autoComplete,
  123. projectFeatures: options.projectFeatures,
  124. referencesSearchMode: options.settings.referencesSearchMode,
  125. }),
  126. // NOTE: `keybindings` needs to be before `language` so that Vim/Emacs bindings take
  127. // precedence over language-specific keyboard shortcuts
  128. keybindings(),
  129. docName(options.docName),
  130. // NOTE: `annotations` needs to be before `language`
  131. annotations(),
  132. language(options.docName, options.metadata, options.settings),
  133. indentUnit.of(' '), // 4 spaces
  134. theme(options.theme),
  135. realtime(options.currentDoc, options.handleError),
  136. cursorPosition(options.currentDoc),
  137. scrollPosition(options.currentDoc, options.visual),
  138. cursorHighlights(),
  139. autoPair(options.settings),
  140. editable(),
  141. search(options.initialSearchQuery),
  142. phrases(options.phrases),
  143. spelling(options.spelling),
  144. shortcuts,
  145. symbolPalette(),
  146. // NOTE: `emptyLineFiller` needs to be before `trackChanges`,
  147. // so the decorations are added in the correct order.
  148. emptyLineFiller(),
  149. options.currentDoc.currentDocument.getType() === 'history-ot'
  150. ? historyOT(options.currentDoc.currentDocument)
  151. : ranges(),
  152. trackDetachedComments(options.currentDoc),
  153. visual(options.docName, options.visual),
  154. mathPreview(options.settings.mathPreview),
  155. reviewTooltip(
  156. options.settings.floatingMenu,
  157. options.editorContextMenuEnabled
  158. ),
  159. contextMenu(options.editorContextMenuEnabled),
  160. toolbarPanel(),
  161. breadcrumbPanel(),
  162. verticalOverflow(),
  163. highlightActiveLine(options.visual.visual),
  164. // The built-in extension that highlights the active line in the gutter.
  165. highlightActiveLineGutter(),
  166. inlineBackground(options.visual.visual),
  167. codemirrorDevTools(),
  168. // Send exceptions to Sentry
  169. EditorView.exceptionSink.of(options.handleException),
  170. // CodeMirror extensions provided by modules
  171. moduleExtensions.map(extension => extension(options)),
  172. thirdPartyExtensions(),
  173. effectListeners(),
  174. geometryChangeEvent(),
  175. fileTreeItemDrop(),
  176. tooltipsReposition(),
  177. selectionListener(options.setEditorSelection),
  178. tabsListener(options.settings.editorTabs),
  179. ]