mirror of
https://github.com/yjs/yjs.git
synced 2025-12-16 19:57:45 +01:00
fix suggestion issues with formatting by introducing an option to disable automattic formatting cleanups
This commit is contained in:
@@ -387,6 +387,7 @@ const insertText = (transaction, parent, currPos, text, attributes) => {
|
|||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAttributes) => {
|
const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAttributes) => {
|
||||||
|
if (!transaction.doc.cleanupFormatting) return 0
|
||||||
/**
|
/**
|
||||||
* @type {Item|null}
|
* @type {Item|null}
|
||||||
*/
|
*/
|
||||||
@@ -417,6 +418,7 @@ const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAtt
|
|||||||
if (endFormats.get(key) !== content || startAttrValue === value) {
|
if (endFormats.get(key) !== content || startAttrValue === value) {
|
||||||
// Either this format is overwritten or it is not necessary because the attribute already existed.
|
// Either this format is overwritten or it is not necessary because the attribute already existed.
|
||||||
start.delete(transaction)
|
start.delete(transaction)
|
||||||
|
transaction.cleanUps.add(start.id.client, start.id.clock, start.length)
|
||||||
cleanups++
|
cleanups++
|
||||||
if (!reachedCurr && (currAttributes.get(key) ?? null) === value && startAttrValue !== value) {
|
if (!reachedCurr && (currAttributes.get(key) ?? null) === value && startAttrValue !== value) {
|
||||||
if (startAttrValue === null) {
|
if (startAttrValue === null) {
|
||||||
@@ -443,6 +445,7 @@ const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAtt
|
|||||||
* @param {Item | null} item
|
* @param {Item | null} item
|
||||||
*/
|
*/
|
||||||
const cleanupContextlessFormattingGap = (transaction, item) => {
|
const cleanupContextlessFormattingGap = (transaction, item) => {
|
||||||
|
if (!transaction.doc.cleanupFormatting) return 0
|
||||||
// iterate until item.right is null or content
|
// iterate until item.right is null or content
|
||||||
while (item && item.right && (item.right.deleted || !item.right.countable)) {
|
while (item && item.right && (item.right.deleted || !item.right.countable)) {
|
||||||
item = item.right
|
item = item.right
|
||||||
@@ -454,6 +457,7 @@ const cleanupContextlessFormattingGap = (transaction, item) => {
|
|||||||
const key = /** @type {ContentFormat} */ (item.content).key
|
const key = /** @type {ContentFormat} */ (item.content).key
|
||||||
if (attrs.has(key)) {
|
if (attrs.has(key)) {
|
||||||
item.delete(transaction)
|
item.delete(transaction)
|
||||||
|
transaction.cleanUps.add(item.id.client, item.id.clock, item.length)
|
||||||
} else {
|
} else {
|
||||||
attrs.add(key)
|
attrs.add(key)
|
||||||
}
|
}
|
||||||
@@ -475,6 +479,7 @@ const cleanupContextlessFormattingGap = (transaction, item) => {
|
|||||||
* @return {number} How many formatting attributes have been cleaned up.
|
* @return {number} How many formatting attributes have been cleaned up.
|
||||||
*/
|
*/
|
||||||
export const cleanupYTextFormatting = type => {
|
export const cleanupYTextFormatting = type => {
|
||||||
|
if (!type.doc?.cleanupFormatting) return 0
|
||||||
let res = 0
|
let res = 0
|
||||||
transact(/** @type {Doc} */ (type.doc), transaction => {
|
transact(/** @type {Doc} */ (type.doc), transaction => {
|
||||||
let start = /** @type {Item} */ (type._start)
|
let start = /** @type {Item} */ (type._start)
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ export const generateNewClientId = random.uint32
|
|||||||
* @property {any} [DocOpts.meta] Any kind of meta information you want to associate with this document. If this is a subdocument, remote peers will store the meta information as well.
|
* @property {any} [DocOpts.meta] Any kind of meta information you want to associate with this document. If this is a subdocument, remote peers will store the meta information as well.
|
||||||
* @property {boolean} [DocOpts.autoLoad] If a subdocument, automatically load document. If this is a subdocument, remote peers will load the document as well automatically.
|
* @property {boolean} [DocOpts.autoLoad] If a subdocument, automatically load document. If this is a subdocument, remote peers will load the document as well automatically.
|
||||||
* @property {boolean} [DocOpts.shouldLoad] Whether the document should be synced by the provider now. This is toggled to true when you call ydoc.load()
|
* @property {boolean} [DocOpts.shouldLoad] Whether the document should be synced by the provider now. This is toggled to true when you call ydoc.load()
|
||||||
|
* @property {boolean} [DocOpts.isSuggestionDoc] Set to true if this document merely suggests
|
||||||
|
* changes. If this flag is not set in a suggestion document, automatic formatting changes will be
|
||||||
|
* displayed as suggestions, which might not be intended.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,13 +62,15 @@ export class Doc extends ObservableV2 {
|
|||||||
/**
|
/**
|
||||||
* @param {DocOpts} opts configuration
|
* @param {DocOpts} opts configuration
|
||||||
*/
|
*/
|
||||||
constructor ({ guid = random.uuidv4(), collectionid = null, gc = true, gcFilter = () => true, meta = null, autoLoad = false, shouldLoad = true } = {}) {
|
constructor ({ guid = random.uuidv4(), collectionid = null, gc = true, gcFilter = () => true, meta = null, autoLoad = false, shouldLoad = true, isSuggestionDoc = true } = {}) {
|
||||||
super()
|
super()
|
||||||
this.gc = gc
|
this.gc = gc
|
||||||
this.gcFilter = gcFilter
|
this.gcFilter = gcFilter
|
||||||
this.clientID = generateNewClientId()
|
this.clientID = generateNewClientId()
|
||||||
this.guid = guid
|
this.guid = guid
|
||||||
this.collectionid = collectionid
|
this.collectionid = collectionid
|
||||||
|
this.isSuggestionDoc = isSuggestionDoc
|
||||||
|
this.cleanupFormatting = !isSuggestionDoc
|
||||||
/**
|
/**
|
||||||
* @type {Map<string, AbstractType<YEvent<any>>>}
|
* @type {Map<string, AbstractType<YEvent<any>>>}
|
||||||
*/
|
*/
|
||||||
@@ -350,9 +355,10 @@ export class Doc extends ObservableV2 {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Doc} ydoc
|
* @param {Doc} ydoc
|
||||||
|
* @param {DocOpts} [opts]
|
||||||
*/
|
*/
|
||||||
export const cloneDoc = ydoc => {
|
export const cloneDoc = (ydoc, opts) => {
|
||||||
const clone = new Doc()
|
const clone = new Doc(opts)
|
||||||
applyUpdate(clone, encodeStateAsUpdate(ydoc))
|
applyUpdate(clone, encodeStateAsUpdate(ydoc))
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,11 @@ export class Transaction {
|
|||||||
* Describes the set of deleted items by ids
|
* Describes the set of deleted items by ids
|
||||||
*/
|
*/
|
||||||
this.deleteSet = createIdSet()
|
this.deleteSet = createIdSet()
|
||||||
|
/**
|
||||||
|
* Describes the set of items that are cleaned up / deleted by ids. It is a subset of
|
||||||
|
* this.deleteSet
|
||||||
|
*/
|
||||||
|
this.cleanUps = createIdSet()
|
||||||
/**
|
/**
|
||||||
* Describes the set of inserted items by ids
|
* Describes the set of inserted items by ids
|
||||||
*/
|
*/
|
||||||
@@ -354,7 +359,7 @@ const cleanupTransactions = (transactionCleanups, i) => {
|
|||||||
})
|
})
|
||||||
fs.push(() => doc.emit('afterTransaction', [transaction, doc]))
|
fs.push(() => doc.emit('afterTransaction', [transaction, doc]))
|
||||||
callAll(fs, [])
|
callAll(fs, [])
|
||||||
if (transaction._needFormattingCleanup) {
|
if (transaction._needFormattingCleanup && doc.cleanupFormatting) {
|
||||||
cleanupYTextAfterTransaction(transaction)
|
cleanupYTextAfterTransaction(transaction)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user