1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-21 14:41:23 +02:00

refactor more transforms

This commit is contained in:
Ian Storm Taylor
2016-08-17 01:01:54 -07:00
parent 229cfd3e4e
commit 1d4c6db4d4
3 changed files with 200 additions and 155 deletions

View File

@@ -49,6 +49,20 @@ export function addMarkAtRange(transform, range, mark) {
*/
export function deleteAtRange(transform, range) {
// if (range.isCollapsed) return transform
// const { state } = transform
// const { document } = state
// const { startKey, startOffset, endKey, endOffset } = range
// const startText = document.assertDescendant(startKey)
// const endText = document.assertDescendant(endKey)
// if (startKey == endKey) {
// const index = startOffset
// const length = endOffset - startOffset
// return transform.removeTextByKey(startKey, index, length)
// }
let { state } = transform
let { document } = state
@@ -147,65 +161,56 @@ export function deleteAtRange(transform, range) {
*/
export function deleteBackwardAtRange(transform, range, n = 1) {
let { state } = transform
let { document } = state
const { startKey, startOffset } = range
// When the range is still expanded, just do a regular delete.
if (range.isExpanded) return deleteAtRange(transform, range)
// When collapsed at the start of the node, there's nothing to do.
if (range.isAtStartOf(document)) return transform
// When collapsed in a void node, remove that node.
const { state } = transform
const { document } = state
const { startKey, focusOffset } = range
const text = document.getDescendant(startKey)
const block = document.getClosestBlock(startKey)
if (block && block.isVoid) {
document = document.removeDescendant(block)
state = state.merge({ document })
transform.state = state
return transform
}
const inline = document.getClosestInline(startKey)
if (range.isExpanded) {
return transform.deleteAtRange(range)
}
if (block && block.isVoid) {
return transform.removeNodeByKey(block.key)
}
if (inline && inline.isVoid) {
document = document.removeDescendant(inline)
state = state.merge({ document })
transform.state = state
return transform.removeNodeByKey(inline.key)
}
if (range.isAtStartOf(document)) {
return transform
}
// When at start of a text node, merge forwards into the next text node.
const startNode = document.getDescendant(startKey)
if (range.isAtStartOf(text)) {
const prev = document.getPreviousText(text)
const prevBlock = document.getClosestBlock(prev)
const prevInline = document.getClosestInline(prev)
if (range.isAtStartOf(startNode)) {
const previous = document.getPreviousText(startNode)
// If the previous descendant is void, remove it.
const prevBlock = document.getClosestBlock(previous)
if (prevBlock && prevBlock.isVoid) {
document = document.removeDescendant(prevBlock)
state = state.merge({ document })
transform.state = state
return transform
return transform.removeNodeByKey(prevBlock.key)
}
const prevInline = document.getClosestInline(previous)
if (prevInline && prevInline.isVoid) {
document = document.removeDescendant(prevInline)
state = state.merge({ document })
transform.state = state
return transform
return transform.removeNodeByKey(prevInline.key)
}
range = range.extendToEndOf(previous)
range = range.normalize(document)
return deleteAtRange(transform, range)
range = range.merge({
anchorKey: prev.key,
anchorOffset: prev.length,
})
return transform.deleteAtRange(range)
}
// Otherwise, remove `n` characters behind of the cursor.
range = range.extendBackward(n)
range = range.normalize(document)
return deleteAtRange(transform, range)
range = range.merge({
focusOffset: focusOffset - 1,
isBackward: true,
})
return transform.deleteAtRange(range)
}
/**
@@ -218,46 +223,45 @@ export function deleteBackwardAtRange(transform, range, n = 1) {
*/
export function deleteForwardAtRange(transform, range, n = 1) {
let { state } = transform
let { document } = state
const { startKey } = range
// When the range is still expanded, just do a regular delete.
if (range.isExpanded) return deleteAtRange(transform, range)
// When collapsed at the end of the node, there's nothing to do.
if (range.isAtEndOf(document)) return transform
// When collapsed in a void node, remove that node.
const block = document.getClosestBlock(startKey)
if (block && block.isVoid) {
document = document.removeDescendant(block)
state = state.merge({ document })
transform.state = state
return transform
}
const { state } = transform
const { document } = state
const { startKey, focusOffset } = range
const text = document.getDescendant(startKey)
const inline = document.getClosestInline(startKey)
const block = document.getClosestBlock(startKey)
if (range.isExpanded) {
return transform.deleteAtRange(range)
}
if (block && block.isVoid) {
return transform.removeNodeByKey(block.key)
}
if (inline && inline.isVoid) {
document = document.removeDescendant(inline)
state = state.merge({ document })
transform.state = state
return transform.removeNodeByKey(inline.key)
}
if (range.isAtEndOf(document)) {
return transform
}
// When at end of a text node, merge forwards into the next text node.
const startNode = document.getDescendant(startKey)
if (range.isAtEndOf(startNode)) {
const next = document.getNextText(startNode)
range = range.extendToStartOf(next)
range = range.normalize(document)
return deleteAtRange(transform, range)
if (range.isAtEndOf(text)) {
const next = document.getNextText(text)
range = range.merge({
focusKey: next.key,
focusOffset: 0
})
return transform.deleteAtRange(range)
}
// Otherwise, remove `n` characters ahead of the cursor.
range = range.extendForward(n)
range = range.normalize(document)
return deleteAtRange(transform, range)
range = range.merge({
focusOffset: focusOffset + 1
})
return transform.deleteAtRange(range)
}
/**
@@ -689,58 +693,33 @@ export function splitBlockAtRange(transform, range, depth = 1) {
*/
export function splitInlineAtRange(transform, range, depth = Infinity) {
let { state } = transform
let { document } = state
// If the range is expanded, remove it first.
if (range.isExpanded) {
transform = deleteAtRange(transform, range)
state = transform.state
document = state.document
transform.deleteAtRange(range)
range = range.collapseToStart()
}
// First split the text nodes.
transform = splitTextAtRange(transform, range)
state = transform.state
document = state.document
// Find the children that were split.
const { startKey } = range
let firstChild = document.getDescendant(startKey)
let secondChild = document.getNextText(firstChild)
let parent = document.getClosestInline(firstChild)
const { startKey, startOffset } = range
const { state } = transform
const { document } = state
let node = document.assertDescendant(startKey)
let parent = document.getClosestInline(node)
let offset = startOffset
let d = 0
// While the parent is an inline parent, split the inline nodes.
while (parent && d < depth) {
firstChild = parent.merge({ nodes: Inline.createList([firstChild]) })
secondChild = Inline.create({
nodes: [secondChild],
type: parent.type,
data: parent.data
})
debugger
// Split the children.
const grandparent = document.getParent(parent)
const nodes = grandparent.nodes
.takeUntil(n => n.key == firstChild.key)
.push(firstChild)
.push(secondChild)
.concat(grandparent.nodes.skipUntil(n => n.key == firstChild.key).rest())
// Update the grandparent.
document = grandparent == document
? document.merge({ nodes })
: document.updateDescendant(grandparent.merge({ nodes }))
while (parent && parent.kind == 'inline' && d < depth) {
const index = parent.nodes.indexOf(node)
const befores = parent.nodes.take(index)
const length = befores.reduce((l, n) => n.length, 0)
offset += length
node = parent
parent = document.getClosestInline(parent)
d++
parent = document.getClosestInline(firstChild)
}
state = state.merge({ document })
transform.state = state
return transform
return transform.splitNodeByKey(node.key, offset)
}
/**

View File

@@ -1,18 +1,19 @@
import Normalize from '../utils/normalize'
import uid from '../utils/uid'
/**
* Add mark to text at `index` and `length` in node by `key`.
* Add mark to text at `offset` and `length` in node by `key`.
*
* @param {Transform} transform
* @param {String} key
* @param {Number} index
* @param {Number} offset
* @param {Number} length
* @param {Mixed} mark
* @return {Transform}
*/
export function addMarkByKey(transform, key, index, length, mark) {
export function addMarkByKey(transform, key, offset, length, mark) {
mark = Normalize.mark(mark)
let { state } = transform
@@ -20,14 +21,14 @@ export function addMarkByKey(transform, key, index, length, mark) {
let node = document.assertDescendant(key)
const path = document.getPath(node)
node = node.addMark(index, length, mark)
node = node.addMark(offset, length, mark)
document = document.updateDescendant(node)
state = state.merge({ document })
transform.state = state
transform.operations.push({
type: 'add-mark',
index,
offset,
length,
mark,
path,
@@ -70,30 +71,30 @@ export function insertNodeByKey(transform, key, index, node) {
}
/**
* Insert `text` at `index` in node by `key`.
* Insert `text` at `offset` in node by `key`.
*
* @param {Transform} transform
* @param {String} key
* @param {Number} index
* @param {Number} offset
* @param {String} text
* @param {Set} marks (optional)
* @return {Transform}
*/
export function insertTextByKey(transform, key, index, text, marks) {
export function insertTextByKey(transform, key, offset, text, marks) {
let { state } = transform
let { document } = state
let node = document.assertDescendant(key)
const path = document.getPath(node)
node = node.insertText(index, text, marks)
node = node.insertText(offset, text, marks)
document = document.updateDescendant(node)
state = state.merge({ document })
transform.state = state
transform.operations.push({
type: 'insert-text',
index,
offset,
marks,
path,
text,
@@ -145,17 +146,17 @@ export function moveNodeByKey(transform, key, newKey, newIndex) {
}
/**
* Remove mark from text at `index` and `length` in node by `key`.
* Remove mark from text at `offset` and `length` in node by `key`.
*
* @param {Transform} transform
* @param {String} key
* @param {Number} index
* @param {Number} offset
* @param {Number} length
* @param {Mark} mark
* @return {Transform}
*/
export function removeMarkByKey(transform, key, index, length, mark) {
export function removeMarkByKey(transform, key, offset, length, mark) {
mark = Normalize.mark(mark)
let { state } = transform
@@ -163,14 +164,14 @@ export function removeMarkByKey(transform, key, index, length, mark) {
let node = document.assertDescendant(key)
const path = document.getPath(node)
node = node.removeMark(index, length, mark)
node = node.removeMark(offset, length, mark)
document = document.updateDescendant(node)
state = state.merge({ document })
transform.state = state
transform.operations.push({
type: 'remove-mark',
index,
offset,
length,
mark,
path,
@@ -211,22 +212,22 @@ export function removeNodeByKey(transform, key) {
}
/**
* Remove text at `index` and `length` in node by `key`.
* Remove text at `offset` and `length` in node by `key`.
*
* @param {Transform} transform
* @param {String} key
* @param {Number} index
* @param {Number} offset
* @param {Number} length
* @return {Transform}
*/
export function removeTextByKey(transform, key, index, length) {
export function removeTextByKey(transform, key, offset, length) {
let { state } = transform
let { document } = state
let node = document.assertDescendant(key)
const path = document.getPath(node)
node = node.removeText(index, length)
node = node.removeText(offset, length)
document = document.updateDescendant(node)
document = document.normalize()
state = state.merge({ document })
@@ -234,7 +235,7 @@ export function removeTextByKey(transform, key, index, length) {
transform.state = state
transform.operations.push({
type: 'remove-text',
index,
offset,
length,
path,
})
@@ -243,17 +244,17 @@ export function removeTextByKey(transform, key, index, length) {
}
/**
* Set `properties` on mark on text at `index` and `length` in node by `key`.
* Set `properties` on mark on text at `offset` and `length` in node by `key`.
*
* @param {Transform} transform
* @param {String} key
* @param {Number} index
* @param {Number} offset
* @param {Number} length
* @param {Mark} mark
* @return {Transform}
*/
export function setMarkByKey(transform, key, index, length, mark, properties) {
export function setMarkByKey(transform, key, offset, length, mark, properties) {
mark = Normalize.mark(mark)
properties = Normalize.markProperties(properties)
@@ -262,14 +263,14 @@ export function setMarkByKey(transform, key, index, length, mark, properties) {
let node = document.assertDescendant(key)
const path = document.getPath(node)
node = node.updateMark(index, length, mark, properties)
node = node.updateMark(offset, length, mark, properties)
document = document.updateDescendant(node)
state = state.merge({ document })
transform.state = state
transform.operations.push({
type: 'set-mark',
index,
offset,
length,
mark,
path,
@@ -310,3 +311,66 @@ export function setNodeByKey(transform, key, properties) {
return transform
}
/**
* Split a node by `key` at `offset`.
*
* @param {Transform} transform
* @param {String} key
* @param {Number} offset
* @return {Transform}
*/
export function splitNodeByKey(transform, key, offset) {
let { state } = transform
let { document } = state
let node = document.assertDescendant(key)
const path = document.getPath(node)
let parent = document.getParent(node)
const isParent = document == parent
const index = parent.nodes.indexOf(node)
let child = node
let one
let two
if (node.kind != 'text') {
child = node.getTextAtOffset(offset)
}
while (child && child != parent) {
if (child.kind == 'text') {
const { characters } = child
const oneChars = characters.take(offset)
const twoChars = characters.skip(offset)
one = child.merge({ characters: oneChars })
two = child.merge({ characters: twoChars, key: uid() })
}
else {
const { nodes } = child
const oneNodes = nodes.takeUntil(n => n.key == one.key).push(one)
const twoNodes = nodes.skipUntil(n => n.key == one.key).rest().unshift(two)
one = child.merge({ nodes: oneNodes })
two = child.merge({ nodes: twoNodes, key: uid() })
}
child = document.getParent(child)
}
parent = parent.removeNode(index)
parent = parent.insertNode(index, two)
parent = parent.insertNode(index, one)
document = isParent ? parent : document.updateDescendant(parent)
state = state.merge({ document })
transform.state = state
transform.operations.push({
type: 'split-node',
offset,
path,
})
return transform
}

View File

@@ -56,15 +56,16 @@ import {
*/
import {
insertTextByKey,
removeTextByKey,
addMarkByKey,
removeMarkByKey,
setMarkByKey,
insertNodeAfterNodeByKey,
removeNodeByKey,
setNodeByKey,
insertTextByKey,
moveNodeByKey,
removeMarkByKey,
removeNodeByKey,
removeTextByKey,
setMarkByKey,
setNodeByKey,
splitNodeByKey,
} from './by-key'
/**
@@ -159,15 +160,16 @@ export default {
* By key.
*/
insertTextByKey,
removeTextByKey,
addMarkByKey,
removeMarkByKey,
setMarkByKey,
insertNodeAfterNodeByKey,
removeNodeByKey,
setNodeByKey,
insertTextByKey,
moveNodeByKey,
removeMarkByKey,
removeNodeByKey,
removeTextByKey,
setMarkByKey,
setNodeByKey,
splitNodeByKey,
/**
* On selection.