mark.ts (3948B)
1 import {Mark, MarkType, Slice, Fragment, NodeType} from "prosemirror-model" 2 3 import {Step} from "./step" 4 import {Transform} from "./transform" 5 import {AddMarkStep, RemoveMarkStep} from "./mark_step" 6 import {ReplaceStep} from "./replace_step" 7 8 export function addMark(tr: Transform, from: number, to: number, mark: Mark) { 9 let removed: Step[] = [], added: Step[] = [] 10 let removing: RemoveMarkStep | undefined, adding: AddMarkStep | undefined 11 tr.doc.nodesBetween(from, to, (node, pos, parent) => { 12 if (!node.isInline) return 13 let marks = node.marks 14 if (!mark.isInSet(marks) && parent!.type.allowsMarkType(mark.type)) { 15 let start = Math.max(pos, from), end = Math.min(pos + node.nodeSize, to) 16 let newSet = mark.addToSet(marks) 17 18 for (let i = 0; i < marks.length; i++) { 19 if (!marks[i].isInSet(newSet)) { 20 if (removing && removing.to == start && removing.mark.eq(marks[i])) 21 (removing as any).to = end 22 else 23 removed.push(removing = new RemoveMarkStep(start, end, marks[i])) 24 } 25 } 26 27 if (adding && adding.to == start) 28 (adding as any).to = end 29 else 30 added.push(adding = new AddMarkStep(start, end, mark)) 31 } 32 }) 33 34 removed.forEach(s => tr.step(s)) 35 added.forEach(s => tr.step(s)) 36 } 37 38 export function removeMark(tr: Transform, from: number, to: number, mark?: Mark | MarkType | null) { 39 let matched: {style: Mark, from: number, to: number, step: number}[] = [], step = 0 40 tr.doc.nodesBetween(from, to, (node, pos) => { 41 if (!node.isInline) return 42 step++ 43 let toRemove = null 44 if (mark instanceof MarkType) { 45 let set = node.marks, found 46 while (found = mark.isInSet(set)) { 47 ;(toRemove || (toRemove = [])).push(found) 48 set = found.removeFromSet(set) 49 } 50 } else if (mark) { 51 if (mark.isInSet(node.marks)) toRemove = [mark] 52 } else { 53 toRemove = node.marks 54 } 55 if (toRemove && toRemove.length) { 56 let end = Math.min(pos + node.nodeSize, to) 57 for (let i = 0; i < toRemove.length; i++) { 58 let style = toRemove[i], found 59 for (let j = 0; j < matched.length; j++) { 60 let m = matched[j] 61 if (m.step == step - 1 && style.eq(matched[j].style)) found = m 62 } 63 if (found) { 64 found.to = end 65 found.step = step 66 } else { 67 matched.push({style, from: Math.max(pos, from), to: end, step}) 68 } 69 } 70 } 71 }) 72 matched.forEach(m => tr.step(new RemoveMarkStep(m.from, m.to, m.style))) 73 } 74 75 export function clearIncompatible(tr: Transform, pos: number, parentType: NodeType, 76 match = parentType.contentMatch, 77 clearNewlines = true) { 78 let node = tr.doc.nodeAt(pos)! 79 let replSteps: Step[] = [], cur = pos + 1 80 for (let i = 0; i < node.childCount; i++) { 81 let child = node.child(i), end = cur + child.nodeSize 82 let allowed = match.matchType(child.type) 83 if (!allowed) { 84 replSteps.push(new ReplaceStep(cur, end, Slice.empty)) 85 } else { 86 match = allowed 87 for (let j = 0; j < child.marks.length; j++) if (!parentType.allowsMarkType(child.marks[j].type)) 88 tr.step(new RemoveMarkStep(cur, end, child.marks[j])) 89 90 if (clearNewlines && child.isText && parentType.whitespace != "pre") { 91 let m, newline = /\r?\n|\r/g, slice 92 while (m = newline.exec(child.text!)) { 93 if (!slice) slice = new Slice(Fragment.from(parentType.schema.text(" ", parentType.allowedMarks(child.marks))), 94 0, 0) 95 replSteps.push(new ReplaceStep(cur + m.index, cur + m.index + m[0].length, slice)) 96 } 97 } 98 } 99 cur = end 100 } 101 if (!match.validEnd) { 102 let fill = match.fillBefore(Fragment.empty, true) 103 tr.replace(cur, cur, new Slice(fill!, 0, 0)) 104 } 105 for (let i = replSteps.length - 1; i >= 0; i--) tr.step(replSteps[i]) 106 }