track-detached-comments.ts 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import {
  2. EditorState,
  3. RangeSet,
  4. StateEffect,
  5. StateField,
  6. Transaction,
  7. } from '@codemirror/state'
  8. import {
  9. findCommentsInCut,
  10. findDetachedCommentsInChanges,
  11. restoreCommentsOnPaste,
  12. restoreDetachedComments,
  13. StoredComment,
  14. } from './changes/comments'
  15. import { invertedEffects } from '@codemirror/commands'
  16. import { DocumentContainer } from '@/features/ide-react/editor/document-container'
  17. const restoreDetachedCommentsEffect = StateEffect.define<RangeSet<any>>({
  18. map: (value, mapping) => {
  19. return value
  20. .update({
  21. filter: (from, to) => {
  22. return from <= mapping.length && to <= mapping.length
  23. },
  24. })
  25. .map(mapping)
  26. },
  27. })
  28. /**
  29. * A custom extension that detects detached comments when a comment is cut and pasted,
  30. * or when a deleted comment is undone
  31. */
  32. export const trackDetachedComments = ({
  33. currentDoc,
  34. }: {
  35. currentDoc: DocumentContainer
  36. }) => {
  37. // A state field that stored any comments found within the ranges of a "cut" transaction,
  38. // to be restored when pasting matching text.
  39. const cutCommentsState = StateField.define<StoredComment[]>({
  40. create: () => {
  41. return []
  42. },
  43. update: (value, transaction) => {
  44. if (transaction.annotation(Transaction.remote)) {
  45. return value
  46. }
  47. if (!transaction.docChanged) {
  48. return value
  49. }
  50. if (transaction.isUserEvent('delete.cut')) {
  51. return findCommentsInCut(currentDoc, transaction)
  52. }
  53. if (transaction.isUserEvent('input.paste')) {
  54. restoreCommentsOnPaste(currentDoc, transaction, value)
  55. return []
  56. }
  57. return value
  58. },
  59. })
  60. return [
  61. // attach any comments detached by the transaction as an inverted effect, to be applied on undo
  62. invertedEffects.of(transaction => {
  63. if (
  64. transaction.docChanged &&
  65. !transaction.annotation(Transaction.remote)
  66. ) {
  67. const detachedComments = findDetachedCommentsInChanges(
  68. currentDoc,
  69. transaction
  70. )
  71. if (detachedComments.size) {
  72. return [restoreDetachedCommentsEffect.of(detachedComments)]
  73. }
  74. }
  75. return []
  76. }),
  77. // restore any detached comments on undo
  78. EditorState.transactionExtender.of(transaction => {
  79. for (const effect of transaction.effects) {
  80. if (effect.is(restoreDetachedCommentsEffect)) {
  81. // send the comments to the ShareJS doc
  82. restoreDetachedComments(currentDoc, transaction, effect.value)
  83. }
  84. }
  85. return null
  86. }),
  87. cutCommentsState,
  88. ]
  89. }