mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-15 14:43:46 +01:00
[overleaf-editor-core] Restructure TextOperation hierachy (#16582)
* [overleaf-editor-core] Restructure TextOperation hierachy Restructures the hierachy of TextOperations to include a superclass EditOperation. This superclass will later on contain other classes used for tracked changes and comments. * [overleaf-editor-core] Update json format of LazyStringFileData * [history-v1+project-history] Fix TextOperation.fromJSON calls * [overleaf-editor-core] Change EditOperationBuilder.fromRaw to fromJSON * [overleaf-editor-core] Update apply and invert functions to accept FileData * [overleaf-editor-core] Pass missing argument to store method * [overleaf-editor-core] Remove unused method * [overleaf-editor-core] User EditOperationTransformer * [overleaf-editor-core] Clean up JSDoc comments * [overleaf-editor-core] Add tests for EditOperation * [overleaf-editor-core] Update JSDoc types GitOrigin-RevId: 9c22a3a89b8483bdb87b43f329ddbdd887ffed42
This commit is contained in:
committed by
Copybot
parent
fc90db231c
commit
43b2fe4a3a
@@ -1,5 +1,9 @@
|
||||
// @ts-check
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* @typedef {import('../operation/edit_operation')} EditOperation
|
||||
*/
|
||||
const assert = require('check-types').assert
|
||||
|
||||
const FileData = require('./')
|
||||
@@ -44,9 +48,12 @@ class HollowStringFileData extends FileData {
|
||||
return this
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
edit(textOperation) {
|
||||
this.stringLength = textOperation.applyToLength(this.stringLength)
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @param {EditOperation} operation
|
||||
*/
|
||||
edit(operation) {
|
||||
this.stringLength = operation.applyToLength(this.stringLength)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ let StringFileData = null
|
||||
|
||||
/**
|
||||
* @typedef {import("../types").BlobStore} BlobStore
|
||||
* @typedef {import("../operation/edit_operation")} EditOperation
|
||||
* @typedef {import("../types").CommentRawData} CommentRawData
|
||||
*/
|
||||
|
||||
@@ -62,33 +63,52 @@ class FileData {
|
||||
throw new Error('FileData: toRaw not implemented')
|
||||
}
|
||||
|
||||
/** @see File#getHash */
|
||||
/**
|
||||
* @see File#getHash
|
||||
* @return {string | null | undefined}
|
||||
*/
|
||||
|
||||
getHash() {
|
||||
return null
|
||||
}
|
||||
|
||||
/** @see File#getContent */
|
||||
/**
|
||||
* @see File#getContent
|
||||
* @return {string | null | undefined}
|
||||
*/
|
||||
getContent() {
|
||||
return null
|
||||
}
|
||||
|
||||
/** @see File#isEditable */
|
||||
/**
|
||||
* @see File#isEditable
|
||||
* @return {boolean | null | undefined} null if it is not currently known
|
||||
*/
|
||||
isEditable() {
|
||||
return null
|
||||
}
|
||||
|
||||
/** @see File#getByteLength */
|
||||
/**
|
||||
* @see File#getByteLength
|
||||
* @return {number | null | undefined}
|
||||
*/
|
||||
getByteLength() {
|
||||
return null
|
||||
}
|
||||
|
||||
/** @see File#getStringLength */
|
||||
/**
|
||||
* @see File#getStringLength
|
||||
* @return {number | null | undefined}
|
||||
*/
|
||||
getStringLength() {
|
||||
return null
|
||||
}
|
||||
|
||||
/** @see File#edit */
|
||||
edit(textOperation) {
|
||||
/**
|
||||
* @see File#edit
|
||||
* @param {EditOperation} editOperation
|
||||
*/
|
||||
edit(editOperation) {
|
||||
throw new Error('edit not implemented for ' + JSON.stringify(this))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// @ts-check
|
||||
'use strict'
|
||||
|
||||
const _ = require('lodash')
|
||||
@@ -6,40 +7,41 @@ const assert = require('check-types').assert
|
||||
const Blob = require('../blob')
|
||||
const FileData = require('./')
|
||||
const EagerStringFileData = require('./string_file_data')
|
||||
const TextOperation = require('../operation/text_operation')
|
||||
const EditOperation = require('../operation/edit_operation')
|
||||
const EditOperationBuilder = require('../operation/edit_operation_builder')
|
||||
|
||||
class LazyStringFileData extends FileData {
|
||||
/**
|
||||
* @param {string} hash
|
||||
* @param {number} stringLength
|
||||
* @param {Array.<TextOperation>} [textOperations]
|
||||
* @param {Array.<EditOperation>} [operations]
|
||||
* @see FileData
|
||||
*/
|
||||
constructor(hash, stringLength, textOperations) {
|
||||
constructor(hash, stringLength, operations) {
|
||||
super()
|
||||
assert.match(hash, Blob.HEX_HASH_RX)
|
||||
assert.greaterOrEqual(stringLength, 0)
|
||||
assert.maybe.array.of.instance(textOperations, TextOperation)
|
||||
assert.maybe.array.of.instance(operations, EditOperation)
|
||||
|
||||
this.hash = hash
|
||||
this.stringLength = stringLength
|
||||
this.textOperations = textOperations || []
|
||||
this.operations = operations || []
|
||||
}
|
||||
|
||||
static fromRaw(raw) {
|
||||
return new LazyStringFileData(
|
||||
raw.hash,
|
||||
raw.stringLength,
|
||||
raw.textOperations && _.map(raw.textOperations, TextOperation.fromJSON)
|
||||
raw.operations && _.map(raw.operations, EditOperationBuilder.fromJSON)
|
||||
)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
toRaw() {
|
||||
const raw = { hash: this.hash, stringLength: this.stringLength }
|
||||
if (this.textOperations.length) {
|
||||
raw.textOperations = _.map(this.textOperations, function (textOperation) {
|
||||
return textOperation.toJSON()
|
||||
if (this.operations.length) {
|
||||
raw.operations = _.map(this.operations, function (operation) {
|
||||
return operation.toJSON()
|
||||
})
|
||||
}
|
||||
return raw
|
||||
@@ -47,7 +49,7 @@ class LazyStringFileData extends FileData {
|
||||
|
||||
/** @inheritdoc */
|
||||
getHash() {
|
||||
if (this.textOperations.length) return null
|
||||
if (this.operations.length) return null
|
||||
return this.hash
|
||||
}
|
||||
|
||||
@@ -76,16 +78,21 @@ class LazyStringFileData extends FileData {
|
||||
* Get the cached text operations that are to be applied to this file to get
|
||||
* from the content with its last known hash to its latest content.
|
||||
*
|
||||
* @return {Array.<TextOperation>}
|
||||
* @return {Array.<EditOperation>}
|
||||
*/
|
||||
getTextOperations() {
|
||||
return this.textOperations
|
||||
getOperations() {
|
||||
return this.operations
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {Promise<EagerStringFileData>}
|
||||
*/
|
||||
async toEager(blobStore) {
|
||||
const content = await blobStore.getString(this.hash)
|
||||
return new EagerStringFileData(computeContent(this.textOperations, content))
|
||||
const file = new EagerStringFileData(content)
|
||||
applyOperations(this.operations, file)
|
||||
return file
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
@@ -99,33 +106,30 @@ class LazyStringFileData extends FileData {
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
edit(textOperation) {
|
||||
this.stringLength = textOperation.applyToLength(this.stringLength)
|
||||
this.textOperations.push(textOperation)
|
||||
edit(operation) {
|
||||
this.stringLength = operation.applyToLength(this.stringLength)
|
||||
this.operations.push(operation)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
async store(blobStore) {
|
||||
if (this.textOperations.length === 0) {
|
||||
if (this.operations.length === 0) {
|
||||
return { hash: this.hash }
|
||||
}
|
||||
|
||||
const content = await blobStore.getString(this.hash)
|
||||
const blob = await blobStore.putString(
|
||||
computeContent(this.textOperations, content)
|
||||
)
|
||||
this.hash = blob.getHash()
|
||||
this.stringLength = blob.getStringLength()
|
||||
this.textOperations.length = 0
|
||||
return { hash: this.hash }
|
||||
const eager = await this.toEager(blobStore)
|
||||
this.operations.length = 0
|
||||
return eager.store(blobStore)
|
||||
}
|
||||
}
|
||||
|
||||
function computeContent(textOperations, initialFile) {
|
||||
function applyTextOperation(content, textOperation) {
|
||||
return textOperation.apply(content)
|
||||
}
|
||||
return _.reduce(textOperations, applyTextOperation, initialFile)
|
||||
/**
|
||||
*
|
||||
* @param {EditOperation[]} operations
|
||||
* @param {EagerStringFileData} file
|
||||
* @returns {void}
|
||||
*/
|
||||
function applyOperations(operations, file) {
|
||||
_.each(operations, operation => operation.apply(file))
|
||||
}
|
||||
|
||||
module.exports = LazyStringFileData
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// @ts-check
|
||||
'use strict'
|
||||
|
||||
const assert = require('check-types').assert
|
||||
@@ -7,6 +8,8 @@ const CommentList = require('./comment_list')
|
||||
|
||||
/**
|
||||
* @typedef {import("../types").StringFileRawData} StringFileRawData
|
||||
* @typedef {import("../operation/edit_operation")} EditOperation
|
||||
* @typedef {import("../types").BlobStore} BlobStore
|
||||
* @typedef {import("../types").CommentRawData} CommentRawData
|
||||
*/
|
||||
|
||||
@@ -65,9 +68,11 @@ class StringFileData extends FileData {
|
||||
return this.content.length
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
edit(textOperation) {
|
||||
this.content = textOperation.apply(this.content)
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @param {EditOperation} operation */
|
||||
edit(operation) {
|
||||
operation.apply(this)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
@@ -75,7 +80,10 @@ class StringFileData extends FileData {
|
||||
return this.comments.getComments()
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @returns {Promise<StringFileData>}
|
||||
*/
|
||||
async toEager() {
|
||||
return this
|
||||
}
|
||||
@@ -85,7 +93,10 @@ class StringFileData extends FileData {
|
||||
return FileData.createHollow(this.getByteLength(), this.getStringLength())
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @param {BlobStore} blobStore
|
||||
*/
|
||||
async store(blobStore) {
|
||||
const blob = await blobStore.putString(this.content)
|
||||
return { hash: blob.getHash() }
|
||||
|
||||
Reference in New Issue
Block a user