projection-state-field.ts 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. import { ChangeSet, StateField } from '@codemirror/state'
  2. import {
  3. ProjectionItem,
  4. ProjectionResult,
  5. getUpdatedProjection,
  6. EnterNodeFn,
  7. ProjectionStatus,
  8. } from './tree-operations/projection'
  9. export function mergeChangeRanges(changes: ChangeSet) {
  10. let fromA = Number.MAX_VALUE
  11. let fromB = Number.MAX_VALUE
  12. let toA = Number.MIN_VALUE
  13. let toB = Number.MIN_VALUE
  14. changes.iterChangedRanges(
  15. (changeFromA, changeToA, changeFromB, changeToB) => {
  16. fromA = Math.min(changeFromA, fromA)
  17. fromB = Math.min(changeFromB, fromB)
  18. toA = Math.max(changeToA, toA)
  19. toB = Math.max(changeToB, toB)
  20. }
  21. )
  22. return { fromA, toA, fromB, toB }
  23. }
  24. /**
  25. * Creates a StateField to manage a 'projection' of the document. Type T is the subclass of
  26. * ProjectionItem that we will extract from the document.
  27. *
  28. * @param enterNode A function to call when 'enter'ing a node while traversing the syntax tree,
  29. * Used to identify nodes we are interested in, and create instances of T.
  30. */
  31. export function makeProjectionStateField<T extends ProjectionItem>(
  32. enterNode: EnterNodeFn<T>
  33. ): StateField<ProjectionResult<T>> {
  34. const field = StateField.define<ProjectionResult<T>>({
  35. create(state) {
  36. const projection = getUpdatedProjection<T>(
  37. state,
  38. 0,
  39. state.doc.length,
  40. 0,
  41. state.doc.length,
  42. true,
  43. enterNode
  44. )
  45. return projection
  46. },
  47. update(currentProjection, transaction) {
  48. if (
  49. transaction.docChanged ||
  50. currentProjection.status !== ProjectionStatus.Complete
  51. ) {
  52. const { fromA, toA, fromB, toB } = mergeChangeRanges(
  53. transaction.changes
  54. )
  55. const list = getUpdatedProjection<T>(
  56. transaction.state,
  57. fromA,
  58. toA,
  59. fromB,
  60. toB,
  61. false,
  62. enterNode,
  63. transaction,
  64. currentProjection
  65. )
  66. return list
  67. }
  68. return currentProjection
  69. },
  70. })
  71. return field
  72. }