From 8b4b454b978cff21cb0d262e65d95f5bb0028af3 Mon Sep 17 00:00:00 2001 From: Eric Mc Sween <5454374+emcsween@users.noreply.github.com> Date: Mon, 26 Aug 2024 07:50:34 -0400 Subject: [PATCH] Merge pull request #20052 from overleaf/em-tracked-deletes-limit Include tracked deletes when limiting document size GitOrigin-RevId: 9d7b2870001fb942eaa92db78993fee1ddb3937c --- libraries/ranges-tracker/index.cjs | 10 ++++++++ .../web/frontend/extracted-translations.json | 1 + .../context/editor-manager-context.tsx | 12 ++++++---- .../ide-react/editor/document-container.ts | 4 +--- .../source-editor/extensions/realtime.ts | 23 ++++++++++++++----- services/web/locales/en.json | 3 ++- .../source-editor/helpers/mock-doc.ts | 1 + 7 files changed, 40 insertions(+), 14 deletions(-) diff --git a/libraries/ranges-tracker/index.cjs b/libraries/ranges-tracker/index.cjs index 3f3f35aff6..30860e6f56 100644 --- a/libraries/ranges-tracker/index.cjs +++ b/libraries/ranges-tracker/index.cjs @@ -768,6 +768,16 @@ class RangesTracker { return this._dirtyState } + getTrackedDeletesLength() { + let length = 0 + for (const change of this.changes) { + if (change.op.d != null) { + length += change.op.d.length + } + } + return length + } + _markAsDirty(object, type, action) { this._dirtyState[type][action][object.id] = object } diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 971b53a52a..03a543c77a 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -326,6 +326,7 @@ "do_you_want_to_overwrite_them": "", "document_too_long": "", "document_too_long_detail": "", + "document_too_long_tracked_deletes": "", "document_updated_externally": "", "document_updated_externally_detail": "", "documentation": "", diff --git a/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx b/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx index b6cca40558..82c822f773 100644 --- a/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx @@ -548,10 +548,14 @@ export const EditorManagerProvider: FC = ({ children }) => { if (message.includes('maxDocLength')) { openDoc(doc, { forceReopen: true }) - showGenericMessageModal( - t('document_too_long'), - t('document_too_long_detail') - ) + const hasTrackedDeletes = + document.ranges != null && + document.ranges.changes.some(change => 'd' in change.op) + const explanation = hasTrackedDeletes + ? `${t('document_too_long_detail')} ${t('document_too_long_tracked_deletes')}` + : t('document_too_long_detail') + + showGenericMessageModal(t('document_too_long'), explanation) setDocTooLongErrorShown(true) } else if (/too many comments or tracked changes/.test(message)) { showGenericMessageModal( diff --git a/services/web/frontend/js/features/ide-react/editor/document-container.ts b/services/web/frontend/js/features/ide-react/editor/document-container.ts index 6bc7322a1b..e35dc4be9d 100644 --- a/services/web/frontend/js/features/ide-react/editor/document-container.ts +++ b/services/web/frontend/js/features/ide-react/editor/document-container.ts @@ -127,9 +127,7 @@ export class DocumentContainer extends EventEmitter { if (this.doc) { this.doc.attachToCM6(this.cm6) } - if (this.cm6) { - this.cm6.on('change', this.checkConsistency) - } + this.cm6.on('change', this.checkConsistency) } detachFromCM6() { diff --git a/services/web/frontend/js/features/source-editor/extensions/realtime.ts b/services/web/frontend/js/features/source-editor/extensions/realtime.ts index f71dedba18..36d9956a76 100644 --- a/services/web/frontend/js/features/source-editor/extensions/realtime.ts +++ b/services/web/frontend/js/features/source-editor/extensions/realtime.ts @@ -1,6 +1,7 @@ import { Prec, Transaction, Annotation, ChangeSpec } from '@codemirror/state' import { EditorView, ViewPlugin } from '@codemirror/view' import { EventEmitter } from 'events' +import RangesTracker from '@overleaf/ranges-tracker' import { ShareDoc } from '../../../../../types/share-doc' import { debugConsole } from '@/utils/debugging' import { DocumentContainer } from '@/features/ide-react/editor/document-container' @@ -45,7 +46,7 @@ export const realtime = ( return { update(update) { if (update.docChanged) { - editor.handleUpdateFromCM(update.transactions) + editor.handleUpdateFromCM(update.transactions, currentDoc.ranges) } }, destroy() { @@ -166,8 +167,13 @@ export class EditorFacade extends EventEmitter { // Process an update from CodeMirror, applying changes to the // ShareJs doc if appropriate - handleUpdateFromCM(transactions: readonly Transaction[]) { + handleUpdateFromCM( + transactions: readonly Transaction[], + ranges?: RangesTracker + ) { const shareDoc = this.shareDoc + const trackedDeletesLength = + ranges != null ? ranges.getTrackedDeletesLength() : 0 if (!shareDoc) { throw new Error('Trying to process updates with no shareDoc') @@ -181,10 +187,15 @@ export class EditorFacade extends EventEmitter { return } - if ( - this.maxDocLength && - transaction.changes.desc.newLength >= this.maxDocLength - ) { + // This is an approximation. Some deletes could have generated new + // tracked deletes since we measured trackedDeletesLength at the top of + // the function. Unfortunately, the ranges tracker is only updated + // after all transactions are processed, so it's not easy to get an + // exact number. + const fullDocLength = + transaction.changes.desc.newLength + trackedDeletesLength + + if (this.maxDocLength && fullDocLength >= this.maxDocLength) { shareDoc.emit( 'error', new Error('document length is greater than maxDocLength') diff --git a/services/web/locales/en.json b/services/web/locales/en.json index 6c30af4dfe..3bd2b5bfbf 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -461,7 +461,8 @@ "do_you_want_to_overwrite_it_plural": "Do you want to overwrite them?", "do_you_want_to_overwrite_them": "Do you want to overwrite them?", "document_too_long": "Document Too Long", - "document_too_long_detail": "Sorry, this file is too long to be edited manually. Please upload it directly.", + "document_too_long_detail": "Sorry, this file is too long to be edited manually. Please try to split it into smaller files.", + "document_too_long_tracked_deletes": "You can also accept pending deletions to reduce the size of the file.", "document_updated_externally": "Document Updated Externally", "document_updated_externally_detail": "This document was just updated externally. Any recent changes you have made may have been overwritten. To see previous versions, please look in the history.", "documentation": "Documentation", diff --git a/services/web/test/frontend/features/source-editor/helpers/mock-doc.ts b/services/web/test/frontend/features/source-editor/helpers/mock-doc.ts index 5f2a6aad8d..f7511807d6 100644 --- a/services/web/test/frontend/features/source-editor/helpers/mock-doc.ts +++ b/services/web/test/frontend/features/source-editor/helpers/mock-doc.ts @@ -78,6 +78,7 @@ export const mockDoc = (content = defaultContent) => { comments: [], getIdSeed: () => '123', setIdSeed: () => {}, + getTrackedDeletesLength: () => 0, }, setTrackChangesIdSeeds: () => {}, getTrackingChanges: () => true,