[attribution manager] returns null instead of empty array to reduce gc allocations

This commit is contained in:
Kevin Jahns
2025-04-27 22:50:48 +02:00
parent 5992126eb5
commit c54ceebed4
2 changed files with 77 additions and 75 deletions

View File

@@ -1008,82 +1008,84 @@ export class YText extends AbstractType {
const d = delta.create()
for (let item = this._start; item !== null; item = item.right) {
const cs = am.getContent(item)
for (let i = 0; i < cs.length; i++) {
const { content, deleted, attrs } = cs[i]
/**
* @type {import('../utils/Delta.js').Attribution?}
*/
let attributions = null
if (attrs != null) {
attributions = {}
if (deleted) {
attributions.delete = []
} else {
attributions.insert = []
}
attrs.forEach(attr => {
switch (attr.name) {
case 'insert':
case 'delete':
case 'suggest': {
const as = /** @type {import('../utils/Delta.js').Attribution} */ (attributions)
const ls = as[attr.name] = as[attr.name] ?? []
ls.push(attr.val)
break
}
default: {
if (attr.name[0] !== '_') {
/** @type {any} */ (attributions)[attr.name] = attr.val
if (cs != null) {
for (let i = 0; i < cs.length; i++) {
const { content, deleted, attrs } = cs[i]
/**
* @type {import('../utils/Delta.js').Attribution?}
*/
let attributions = null
if (attrs != null) {
attributions = {}
if (deleted) {
attributions.delete = []
} else {
attributions.insert = []
}
attrs.forEach(attr => {
switch (attr.name) {
case 'insert':
case 'delete':
case 'suggest': {
const as = /** @type {import('../utils/Delta.js').Attribution} */ (attributions)
const ls = as[attr.name] = as[attr.name] ?? []
ls.push(attr.val)
break
}
default: {
if (attr.name[0] !== '_') {
/** @type {any} */ (attributions)[attr.name] = attr.val
}
}
}
}
})
}
switch (content.constructor) {
case ContentString: {
d.insert(/** @type {ContentString} */ (content).str, null, attributions)
break
})
}
case ContentType:
case ContentEmbed: {
d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attributions)
break
switch (content.constructor) {
case ContentString: {
d.insert(/** @type {ContentString} */ (content).str, null, attributions)
break
}
case ContentType:
case ContentEmbed: {
d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attributions)
break
}
case ContentFormat:
const contentFormat = /** @type {ContentFormat} */ (content)
if (attributions != null) {
/**
* @type {import('../utils/Delta.js').Attribution}
*/
const formattingAttributions = object.assign({}, d.usedAttribution)
const attributesChanged = /** @type {{ [key: string]: Array<any> }} */ (formattingAttributions.attributes = object.assign({}, formattingAttributions.attributes ?? {}))
if (contentFormat.value === null) {
delete attributesChanged[contentFormat.key]
} else {
const by = attributesChanged[contentFormat.key] = attributesChanged[contentFormat.key]?.slice() ?? []
by.push(...((deleted ? attributions.delete : attributions.insert) ?? []))
const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt)
if (attributedAt) formattingAttributions.attributedAt = attributedAt
}
if (object.isEmpty(attributesChanged)) {
d.useAttribution(null)
} else {
const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt)
if (attributedAt != null) formattingAttributions.attributedAt = attributedAt
d.useAttribution(formattingAttributions)
}
}
if (!deleted) {
const currAttrs = d.usedAttributes
if (contentFormat.value == null) {
const nextAttrs = object.assign({}, currAttrs)
delete nextAttrs[contentFormat.key]
d.useAttributes(nextAttrs)
} else {
d.useAttributes(object.assign({}, currAttrs, { [contentFormat.key]: contentFormat.value }))
}
}
break
}
case ContentFormat:
const contentFormat = /** @type {ContentFormat} */ (content)
if (attributions != null) {
/**
* @type {import('../utils/Delta.js').Attribution}
*/
const formattingAttributions = object.assign({}, d.usedAttribution)
const attributesChanged = /** @type {{ [key: string]: Array<any> }} */ (formattingAttributions.attributes = object.assign({}, formattingAttributions.attributes ?? {}))
if (contentFormat.value === null) {
delete attributesChanged[contentFormat.key]
} else {
const by = attributesChanged[contentFormat.key] = attributesChanged[contentFormat.key]?.slice() ?? []
by.push(...((deleted ? attributions.delete : attributions.insert) ?? []))
const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt)
if (attributedAt) formattingAttributions.attributedAt = attributedAt
}
if (object.isEmpty(attributesChanged)) {
d.useAttribution(null)
} else {
const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt)
if (attributedAt != null) formattingAttributions.attributedAt = attributedAt
d.useAttribution(formattingAttributions)
}
}
if (!deleted) {
const currAttrs = d.usedAttributes
if (contentFormat.value == null) {
const nextAttrs = object.assign({}, currAttrs)
delete nextAttrs[contentFormat.key]
d.useAttributes(nextAttrs)
} else {
d.useAttributes(object.assign({}, currAttrs, { [contentFormat.key]: contentFormat.value }))
}
}
break
}
}
}

View File

@@ -26,7 +26,7 @@ export class AttributedContent {
export class AbstractAttributionManager {
/**
* @param {Item} _item
* @return {Array<AttributedContent<any>>}
* @return {Array<AttributedContent<any>>?}
*/
getContent (_item) {
error.methodUnimplemented()
@@ -78,10 +78,10 @@ export class TwosetAttributionManager {
export class NoAttributionsManager {
/**
* @param {Item} item
* @return {Array<AttributedContent<any>>}
* @return {Array<AttributedContent<any>>?}
*/
getContent (item) {
return item.deleted ? [] : [new AttributedContent(item.content, false, null)]
return item.deleted ? null : [new AttributedContent(item.content, false, null)]
}
}