selection.ts 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. import { EditorSelection, StateEffect, Line, Text } from '@codemirror/state'
  2. import { EditorView } from '@codemirror/view'
  3. import { hasEffect, updateHasEffect } from '../../utils/effects'
  4. export const selectionIntersects = (
  5. selection: EditorSelection,
  6. extents: { from: number; to: number }
  7. ) =>
  8. selection.ranges.some(
  9. range =>
  10. // Case 1: from is inside node
  11. (extents.from <= range.from && extents.to >= range.from) ||
  12. // Case 2: to is inside node
  13. (extents.from <= range.to && extents.to >= range.to)
  14. )
  15. export const placeSelectionInsideBlock = (
  16. view: EditorView,
  17. event: MouseEvent
  18. ) => {
  19. const line = view.lineBlockAtHeight(event.pageY - view.documentTop)
  20. const selectionRange = EditorSelection.cursor(line.to)
  21. const selection = event.ctrlKey
  22. ? view.state.selection.addRange(selectionRange)
  23. : selectionRange
  24. return { selection, effects: EditorView.scrollIntoView(line.to) }
  25. }
  26. export const extendBackwardsOverEmptyLines = (doc: Text, line: Line) => {
  27. let { number, from } = line
  28. for (let lineNumber = number - 1; lineNumber > 0; lineNumber--) {
  29. const line = doc.line(lineNumber)
  30. if (line.text.trim().length > 0) {
  31. break
  32. }
  33. from = line.from
  34. }
  35. return from
  36. }
  37. export const extendForwardsOverEmptyLines = (doc: Text, line: Line) => {
  38. let { number, to } = line
  39. for (let lineNumber = number + 1; lineNumber <= doc.lines; lineNumber++) {
  40. const line = doc.line(lineNumber)
  41. if (line.text.trim().length > 0) {
  42. break
  43. }
  44. to = line.to
  45. }
  46. return to
  47. }
  48. export const mouseDownEffect = StateEffect.define<boolean>()
  49. export const hasMouseDownEffect = hasEffect(mouseDownEffect)
  50. export const updateHasMouseDownEffect = updateHasEffect(mouseDownEffect)
  51. export const mouseDownListener = EditorView.domEventHandlers({
  52. mousedown: (event, view) => {
  53. // not wrapped in a timeout, so update listeners know that the mouse is down before they process the selection
  54. view.dispatch({
  55. effects: mouseDownEffect.of(true),
  56. })
  57. },
  58. mouseup: (event, view) => {
  59. // wrap in a timeout, so update listeners receive this effect after the new selection has finished being handled
  60. window.setTimeout(() => {
  61. view.dispatch({
  62. effects: mouseDownEffect.of(false),
  63. })
  64. })
  65. },
  66. })