UBERF-4317 Apply mappings to table of contents decorations (#4006)

Signed-off-by: Alexander Onnikov <alexander.onnikov@xored.com>
This commit is contained in:
Alexander Onnikov 2023-11-17 13:49:41 +07:00 committed by GitHub
parent b5f261318f
commit f0ba202f28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -15,7 +15,7 @@
import { Extension } from '@tiptap/core' import { Extension } from '@tiptap/core'
import { Node as ProseMirrorNode } from '@tiptap/pm/model' import { Node as ProseMirrorNode } from '@tiptap/pm/model'
import { EditorState, Plugin, PluginKey } from '@tiptap/pm/state' import { EditorState, Plugin, PluginKey, Transaction } from '@tiptap/pm/state'
import { Decoration, DecorationSet } from '@tiptap/pm/view' import { Decoration, DecorationSet } from '@tiptap/pm/view'
import slugify from 'slugify' import slugify from 'slugify'
import { Heading } from '../../types' import { Heading } from '../../types'
@ -59,42 +59,15 @@ export const HeadingsExtension: Extension<HeadingsOptions, HeadingsStorage> = Ex
options.onChange?.(headings) options.onChange?.(headings)
return { decorations } return { decorations: DecorationSet.create(state.doc, decorations) }
}, },
apply (tr, value, oldState, newState) { apply (tr, value, oldState, newState) {
if (!tr.docChanged) { const headingUpdate = hasHeadingUpdate(tr)
return value
}
let headingUpdate = false
tr.mapping.maps.forEach((map, index) =>
map.forEach((oldStart, oldEnd, newStart, newEnd) => {
const oldDoc = tr.docs[index]
const newDoc = tr.docs[index + 1] ?? tr.doc
oldDoc.nodesBetween(oldStart, oldEnd, (node) => {
if (headingUpdate) {
return false
} else if (node.type.name === 'heading') {
headingUpdate = true
}
return true
})
newDoc.nodesBetween(newStart, newEnd, (node) => {
if (headingUpdate) {
return false
} else if (node.type.name === 'heading') {
headingUpdate = true
}
return true
})
})
)
if (!headingUpdate) { if (!headingUpdate) {
return value const { decorations } = value
return { decorations: decorations.map(tr.mapping, tr.doc) }
} }
const decorations = getHeadingDecorations(newState, prefixId) const decorations = getHeadingDecorations(newState, prefixId)
@ -103,7 +76,7 @@ export const HeadingsExtension: Extension<HeadingsOptions, HeadingsStorage> = Ex
options.onChange?.(headings) options.onChange?.(headings)
storage.headings = headings storage.headings = headings
return { decorations } return { decorations: DecorationSet.create(tr.doc, decorations) }
} }
}, },
props: { props: {
@ -111,7 +84,7 @@ export const HeadingsExtension: Extension<HeadingsOptions, HeadingsStorage> = Ex
const pluginState = this.getState(state) const pluginState = this.getState(state)
if (pluginState !== undefined) { if (pluginState !== undefined) {
const { decorations } = pluginState const { decorations } = pluginState
return DecorationSet.create(state.doc, decorations) return decorations
} }
return DecorationSet.empty return DecorationSet.empty
@ -124,6 +97,40 @@ export const HeadingsExtension: Extension<HeadingsOptions, HeadingsStorage> = Ex
} }
}) })
function hasHeadingUpdate (tr: Transaction): boolean {
if (!tr.docChanged) {
return false
}
let found = false
tr.mapping.maps.forEach((map, index) =>
map.forEach((oldStart, oldEnd, newStart, newEnd) => {
const oldDoc = tr.docs[index]
const newDoc = tr.docs[index + 1] ?? tr.doc
oldDoc.nodesBetween(oldStart, oldEnd, (node) => {
if (found) {
return false
} else if (node.type.name === 'heading') {
found = true
}
return true
})
newDoc.nodesBetween(newStart, newEnd, (node) => {
if (found) {
return false
} else if (node.type.name === 'heading') {
found = true
}
return true
})
})
)
return found
}
function getHeadingDecorations (state: EditorState, idPrefix: string): Decoration[] { function getHeadingDecorations (state: EditorState, idPrefix: string): Decoration[] {
const decorations: Decoration[] = [] const decorations: Decoration[] = []
const alreadySeen: Map<string, number> = new Map<string, number>() const alreadySeen: Map<string, number> = new Map<string, number>()