trackDetachedComments as separate extension

GitOrigin-RevId: 8039de3f9766b072e9bb2170b50e683073105748
This commit is contained in:
Domagoj Kriskovic 2024-08-21 11:23:10 +02:00 committed by Copybot
parent ec6b17eb01
commit dc70054caf
4 changed files with 109 additions and 175 deletions

View file

@ -51,6 +51,7 @@ import { fileTreeItemDrop } from './file-tree-item-drop'
import { mathPreview } from './math-preview'
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
import { ranges } from './ranges'
import { trackDetachedComments } from './track-detached-comments'
const moduleExtensions: Array<() => Extension> = importOverleafModules(
'sourceEditorExtensions'
@ -129,6 +130,7 @@ export const createExtensions = (options: Record<string, any>): Extension[] => [
isSplitTestEnabled('review-panel-redesign')
? ranges(options.currentDoc, options.changeManager)
: trackChanges(options.currentDoc, options.changeManager),
trackDetachedComments(options.currentDoc),
visual(options.visual),
mathPreview(options.settings.mathPreview),
toolbarPanel(),

View file

@ -1,11 +1,4 @@
import {
EditorState,
RangeSet,
StateEffect,
StateField,
Transaction,
TransactionSpec,
} from '@codemirror/state'
import { EditorState, StateEffect, TransactionSpec } from '@codemirror/state'
import {
Decoration,
type DecorationSet,
@ -14,14 +7,6 @@ import {
ViewPlugin,
WidgetType,
} from '@codemirror/view'
import {
findCommentsInCut,
findDetachedCommentsInChanges,
restoreCommentsOnPaste,
restoreDetachedComments,
StoredComment,
} from './changes/comments'
import { invertedEffects } from '@codemirror/commands'
import {
Change,
DeleteOperation,
@ -58,17 +43,6 @@ export const updateRanges = (data: RangesData): TransactionSpec => {
const clearChangesEffect = StateEffect.define()
const buildChangesEffect = StateEffect.define()
const restoreDetachedCommentsEffect = StateEffect.define<RangeSet<any>>({
map: (value, mapping) => {
return value
.update({
filter: (from, to) => {
return from <= mapping.length && to <= mapping.length
},
})
.map(mapping)
},
})
type Options = {
currentDoc: DocumentContainer
@ -85,68 +59,7 @@ export const ranges = (
{ currentDoc, loadingThreads, ranges, threads }: Options,
changeManager?: ChangeManager
) => {
// A state field that stored any comments found within the ranges of a "cut" transaction,
// to be restored when pasting matching text.
const cutCommentsState = StateField.define<StoredComment[]>({
create: () => {
return []
},
update: (value, transaction) => {
if (transaction.annotation(Transaction.remote)) {
return value
}
if (!transaction.docChanged) {
return value
}
if (transaction.isUserEvent('delete.cut')) {
return findCommentsInCut(currentDoc, transaction)
}
if (transaction.isUserEvent('input.paste')) {
restoreCommentsOnPaste(currentDoc, transaction, value)
return []
}
return value
},
})
return [
// attach any comments detached by the transaction as an inverted effect, to be applied on undo
invertedEffects.of(transaction => {
if (
transaction.docChanged &&
!transaction.annotation(Transaction.remote)
) {
const detachedComments = findDetachedCommentsInChanges(
currentDoc,
transaction
)
if (detachedComments.size) {
return [restoreDetachedCommentsEffect.of(detachedComments)]
}
}
return []
}),
// restore any detached comments on undo
EditorState.transactionExtender.of(transaction => {
for (const effect of transaction.effects) {
if (effect.is(restoreDetachedCommentsEffect)) {
// send the comments to the ShareJS doc
restoreDetachedComments(currentDoc, transaction, effect.value)
// return a transaction spec to rebuild the change markers
return buildChangeMarkers()
}
}
return null
}),
cutCommentsState,
// initialize/destroy the change manager, and handle any updates
changeManager
? ViewPlugin.define(() => {

View file

@ -1,10 +1,4 @@
import {
EditorState,
RangeSet,
StateEffect,
StateField,
Transaction,
} from '@codemirror/state'
import { StateEffect } from '@codemirror/state'
import {
Decoration,
type DecorationSet,
@ -13,14 +7,6 @@ import {
ViewPlugin,
WidgetType,
} from '@codemirror/view'
import {
findCommentsInCut,
findDetachedCommentsInChanges,
restoreCommentsOnPaste,
restoreDetachedComments,
StoredComment,
} from './changes/comments'
import { invertedEffects } from '@codemirror/commands'
import { Change, DeleteOperation } from '../../../../../types/change'
import { ChangeManager } from './changes/change-manager'
import { debugConsole } from '@/utils/debugging'
@ -32,17 +18,6 @@ import {
const clearChangesEffect = StateEffect.define()
const buildChangesEffect = StateEffect.define()
const restoreDetachedCommentsEffect = StateEffect.define<RangeSet<any>>({
map: (value, mapping) => {
return value
.update({
filter: (from, to) => {
return from <= mapping.length && to <= mapping.length
},
})
.map(mapping)
},
})
type Options = {
currentDoc: DocumentContainer
@ -57,68 +32,7 @@ export const trackChanges = (
{ currentDoc, loadingThreads }: Options,
changeManager: ChangeManager
) => {
// A state field that stored any comments found within the ranges of a "cut" transaction,
// to be restored when pasting matching text.
const cutCommentsState = StateField.define<StoredComment[]>({
create: () => {
return []
},
update: (value, transaction) => {
if (transaction.annotation(Transaction.remote)) {
return value
}
if (!transaction.docChanged) {
return value
}
if (transaction.isUserEvent('delete.cut')) {
return findCommentsInCut(currentDoc, transaction)
}
if (transaction.isUserEvent('input.paste')) {
restoreCommentsOnPaste(currentDoc, transaction, value)
return []
}
return value
},
})
return [
// attach any comments detached by the transaction as an inverted effect, to be applied on undo
invertedEffects.of(transaction => {
if (
transaction.docChanged &&
!transaction.annotation(Transaction.remote)
) {
const detachedComments = findDetachedCommentsInChanges(
currentDoc,
transaction
)
if (detachedComments.size) {
return [restoreDetachedCommentsEffect.of(detachedComments)]
}
}
return []
}),
// restore any detached comments on undo
EditorState.transactionExtender.of(transaction => {
for (const effect of transaction.effects) {
if (effect.is(restoreDetachedCommentsEffect)) {
// send the comments to the ShareJS doc
restoreDetachedComments(currentDoc, transaction, effect.value)
// return a transaction spec to rebuild the change markers
return buildChangeMarkers()
}
}
return null
}),
cutCommentsState,
// initialize/destroy the change manager, and handle any updates
ViewPlugin.define(() => {
changeManager.initialize()

View file

@ -0,0 +1,105 @@
import {
EditorState,
RangeSet,
StateEffect,
StateField,
Transaction,
} from '@codemirror/state'
import {
findCommentsInCut,
findDetachedCommentsInChanges,
restoreCommentsOnPaste,
restoreDetachedComments,
StoredComment,
} from './changes/comments'
import { invertedEffects } from '@codemirror/commands'
import { DocumentContainer } from '@/features/ide-react/editor/document-container'
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
import { buildChangeMarkers } from './track-changes'
const restoreDetachedCommentsEffect = StateEffect.define<RangeSet<any>>({
map: (value, mapping) => {
return value
.update({
filter: (from, to) => {
return from <= mapping.length && to <= mapping.length
},
})
.map(mapping)
},
})
/**
* A custom extension that detects detached comments when a comment is cut and pasted,
* or when a deleted comment is undone
*/
export const trackDetachedComments = ({
currentDoc,
}: {
currentDoc: DocumentContainer
}) => {
// A state field that stored any comments found within the ranges of a "cut" transaction,
// to be restored when pasting matching text.
const cutCommentsState = StateField.define<StoredComment[]>({
create: () => {
return []
},
update: (value, transaction) => {
if (transaction.annotation(Transaction.remote)) {
return value
}
if (!transaction.docChanged) {
return value
}
if (transaction.isUserEvent('delete.cut')) {
return findCommentsInCut(currentDoc, transaction)
}
if (transaction.isUserEvent('input.paste')) {
restoreCommentsOnPaste(currentDoc, transaction, value)
return []
}
return value
},
})
return [
// attach any comments detached by the transaction as an inverted effect, to be applied on undo
invertedEffects.of(transaction => {
if (
transaction.docChanged &&
!transaction.annotation(Transaction.remote)
) {
const detachedComments = findDetachedCommentsInChanges(
currentDoc,
transaction
)
if (detachedComments.size) {
return [restoreDetachedCommentsEffect.of(detachedComments)]
}
}
return []
}),
// restore any detached comments on undo
EditorState.transactionExtender.of(transaction => {
for (const effect of transaction.effects) {
if (effect.is(restoreDetachedCommentsEffect)) {
// send the comments to the ShareJS doc
restoreDetachedComments(currentDoc, transaction, effect.value)
if (isSplitTestEnabled('review-panel-redesign')) {
// return a transaction spec to rebuild the change markers
return buildChangeMarkers()
}
}
}
return null
}),
cutCommentsState,
]
}