1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-10 09:13:59 +02:00

Merge pull request #2 from GitbookIO/schema-normalize-memoize-keys

Prevent memoize cache miss by passing "node.key" instead of "node"
This commit is contained in:
Samy Pessé
2016-11-03 09:27:38 +01:00
committed by GitHub
8 changed files with 100 additions and 88 deletions

View File

@@ -83,6 +83,7 @@
"build:max": "NODE_ENV=production browserify ./src/index.js --transform babelify --transform envify --transform [ browserify-global-shim --global ] --standalone Slate > ./dist/slate.js",
"build:min": "NODE_ENV=production browserify ./src/index.js --transform babelify --transform envify --transform [ browserify-global-shim --global ] --transform uglifyify --standalone Slate | uglifyjs > ./dist/slate.min.js",
"build:npm": "babel --out-dir ./lib ./src",
"build:test": "babel --out-dir ./lib ./src --source-maps inline",
"examples": "npm-run-all examples:dev examples:prod",
"examples:dev": "browserify --debug --transform babelify ./examples/index.js > ./examples/build.dev.js",
"examples:prod": "NODE_ENV=production browserify --transform babelify ./examples/index.js > ./examples/build.prod.js",
@@ -98,7 +99,7 @@
"release": "np",
"release:next": "np --tag=next",
"start": "http-server ./examples",
"test": "npm-run-all build:npm tests",
"test": "npm-run-all build:test tests",
"tests": "mocha --compilers js:babel-core/register --reporter spec ./test/server.js",
"watch": "npm-run-all --parallel --print-label watch:lib watch:examples start",
"watch:lib": "babel --watch --out-dir ./lib ./src",

View File

@@ -202,7 +202,7 @@ const Node = {
getBlocks() {
return this
.getTexts()
.map(text => this.getClosestBlock(text))
.map(text => this.getClosestBlock(text.key))
.toOrderedSet()
.toList()
},
@@ -217,7 +217,7 @@ const Node = {
getBlocksAtRange(range) {
return this
.getTextsAtRange(range)
.map(text => this.getClosestBlock(text))
.map(text => this.getClosestBlock(text.key))
},
/**
@@ -277,6 +277,7 @@ const Node = {
*/
getClosest(key, iterator) {
key = Normalize.key(key)
let ancestors = this.getAncestors(key)
if (!ancestors) {
throw new Error(`Could not find a descendant node with key "${key}".`)
@@ -337,12 +338,12 @@ const Node = {
while (oneParent) {
ancestors = ancestors.push(oneParent)
oneParent = this.getParent(oneParent)
oneParent = this.getParent(oneParent.key)
}
while (twoParent) {
if (ancestors.includes(twoParent)) return twoParent
twoParent = this.getParent(twoParent)
twoParent = this.getParent(twoParent.key)
}
},
@@ -508,6 +509,7 @@ const Node = {
getFurthest(key, iterator) {
let ancestors = this.getAncestors(key)
if (!ancestors) {
key = Normalize.key(key)
throw new Error(`Could not find a descendant node with key "${key}".`)
}
@@ -581,7 +583,7 @@ const Node = {
getInlines() {
return this
.getTexts()
.map(text => this.getFurthestInline(text))
.map(text => this.getFurthestInline(text.key))
.filter(exists => exists)
.toOrderedSet()
.toList()
@@ -597,7 +599,7 @@ const Node = {
getInlinesAtRange(range) {
return this
.getTextsAtRange(range)
.map(text => this.getClosestInline(text))
.map(text => this.getClosestInline(text.key))
.filter(exists => exists)
.toOrderedSet()
.toList()
@@ -659,7 +661,7 @@ const Node = {
const next = this.getNextText(last)
if (!next) return null
return this.getClosestBlock(next)
return this.getClosestBlock(next.key)
},
/**
@@ -670,12 +672,16 @@ const Node = {
*/
getNextSibling(key) {
const node = this.assertDescendant(key)
return this
.getParent(node)
.nodes
.skipUntil(child => child == node)
.get(1)
key = Normalize.key(key)
const parent = this.getParent(key)
const after = parent.nodes
.skipUntil(child => child.key == key)
if (after.size == 0) {
throw new Error(`Could not find a child node with key "${key}".`)
}
return after.get(1)
},
/**
@@ -822,12 +828,16 @@ const Node = {
*/
getPreviousSibling(key) {
const node = this.assertDescendant(key)
return this
.getParent(node)
.nodes
.takeUntil(child => child == node)
.last()
key = Normalize.key(key)
const parent = this.getParent(key)
const before = parent.nodes
.takeUntil(child => child.key == key)
if (before.size == parent.nodes.size) {
throw new Error(`Could not find a child node with key "${key}".`)
}
return before.last()
},
/**
@@ -865,7 +875,7 @@ const Node = {
const previous = this.getPreviousText(first)
if (!previous) return null
return this.getClosestBlock(previous)
return this.getClosestBlock(previous.key)
},
/**
@@ -1055,7 +1065,7 @@ const Node = {
joinNode(first, second) {
let node = this
let parent = node.getParent(second)
let parent = node.getParent(second.key)
const isParent = node == parent
const index = parent.nodes.indexOf(second)
@@ -1180,7 +1190,7 @@ const Node = {
splitNode(path, offset) {
let base = this
let node = base.assertPath(path)
let parent = base.getParent(node)
let parent = base.getParent(node.key)
const isParent = base == parent
const index = parent.nodes.indexOf(node)
@@ -1194,7 +1204,7 @@ const Node = {
while (child && child != parent) {
if (child.kind == 'text') {
const i = node.kind == 'text' ? offset : offset - node.getOffset(child)
const i = node.kind == 'text' ? offset : offset - node.getOffset(child.key)
const { characters } = child
const oneChars = characters.take(i)
const twoChars = characters.skip(i)
@@ -1215,7 +1225,7 @@ const Node = {
two = child.merge({ nodes: twoNodes, key: uid() })
}
child = base.getParent(child)
child = base.getParent(child.key)
}
parent = parent.removeNode(index)
@@ -1237,14 +1247,14 @@ const Node = {
const { startKey, startOffset } = range
let base = this
let node = base.assertDescendant(startKey)
let parent = base.getClosestBlock(node)
let parent = base.getClosestBlock(node.key)
let offset = startOffset
let h = 0
while (parent && parent.kind == 'block' && h < height) {
offset += parent.getOffset(node)
offset += parent.getOffset(node.key)
node = parent
parent = base.getClosestBlock(parent)
parent = base.getClosestBlock(parent.key)
h++
}

View File

@@ -181,7 +181,7 @@ function moveNode(state, operation) {
const node = document.assertPath(path)
// Remove the node from its current parent
let parent = document.getParent(node)
let parent = document.getParent(node.key)
const isParent = document == parent
const index = parent.nodes.indexOf(node)
parent = parent.removeNode(index)
@@ -233,7 +233,7 @@ function removeNode(state, operation) {
// Update the document
const node = document.assertPath(path)
let parent = document.getParent(node)
let parent = document.getParent(node.key)
const index = parent.nodes.indexOf(node)
const isParent = document == parent
parent = parent.removeNode(index)
@@ -413,14 +413,14 @@ function splitNode(state, operation) {
: node.getTextAtOffset(offset)
const textOffset = node.kind == 'text'
? offset
: offset - node.getOffset(splittedText)
: offset - node.getOffset(splittedText.key)
// Should we update the selection ?
const shouldUpdateAnchor = splittedText.key == anchorKey && textOffset <= anchorOffset
const shouldUpdateFocus = splittedText.key == focusKey && textOffset <= focusOffset
if (shouldUpdateFocus || shouldUpdateAnchor) {
// The node next to `node`, resulting from the split
const secondNode = newDocument.getNextSibling(node)
const secondNode = newDocument.getNextSibling(node.key)
let secondText, newOffset
if (shouldUpdateAnchor) {

View File

@@ -48,10 +48,10 @@ export function _delete(transform) {
const { startText } = state
const { startKey, startOffset, endKey, endOffset } = selection
const block = document.getClosestBlock(startText)
const highest = block.getHighestChild(startText)
const previous = block.getPreviousSibling(highest)
const next = block.getNextSibling(highest)
const block = document.getClosestBlock(startText.key)
const highest = block.getHighestChild(startText.key)
const previous = block.getPreviousSibling(highest.key)
const next = block.getNextSibling(highest.key)
if (
previous &&
@@ -158,7 +158,7 @@ export function insertFragment(transform, fragment) {
if (!fragment.length) return transform
const lastText = fragment.getLastText()
const lastInline = fragment.getClosestInline(lastText)
const lastInline = fragment.getClosestInline(lastText.key)
const beforeTexts = document.getTexts()
const appending = selection.hasEdgeAtEndOf(document.getDescendant(selection.endKey))
@@ -204,7 +204,7 @@ export function insertInline(transform, inline) {
let { document, selection, startText } = state
let after
const hasVoid = document.hasVoidParent(startText)
const hasVoid = document.hasVoidParent(startText.key)
const keys = document.getTexts().map(text => text.key)
transform.unsetSelection()
@@ -219,7 +219,7 @@ export function insertInline(transform, inline) {
else {
const text = document.getTexts().find((n) => {
if (keys.includes(n.key)) return false
const parent = document.getParent(n)
const parent = document.getParent(n.key)
if (parent.kind != 'inline') return false
return true
})
@@ -314,7 +314,7 @@ export function splitBlock(transform, depth = 1) {
const { startKey } = selection
const startNode = document.getDescendant(startKey)
const nextNode = document.getNextText(startNode)
const nextNode = document.getNextText(startNode.key)
const after = selection.collapseToStartOf(nextNode)
return transform.moveTo(after)
@@ -344,7 +344,7 @@ export function splitInline(transform, depth = Infinity) {
const { startKey, startOffset } = selection
let startNode = document.assertDescendant(startKey)
const furthestInline = document.getFurthestInline(startKey)
const offset = furthestInline.getOffset(startNode)
const offset = furthestInline.getOffset(startNode.key)
// If the selection is at the start of end of the furthest inline, there isn't
// anything to split, so abort.
@@ -363,7 +363,7 @@ export function splitInline(transform, depth = Infinity) {
if (closestInline) {
startNode = document.getDescendant(startKey)
const nextNode = document.getNextText(startNode)
const nextNode = document.getNextText(startNode.key)
after = selection.collapseToStartOf(nextNode)
}
@@ -493,7 +493,7 @@ export function wrapInline(transform, properties) {
}
else if (selection.startOffset == 0) {
const text = previous ? document.getNextText(previous) : document.getFirstText()
const text = previous ? document.getNextText(previous.key) : document.getFirstText()
after = selection.moveToRangeOf(text)
}

View File

@@ -79,7 +79,7 @@ export function deleteAtRange(transform, range, options = {}) {
state = transform.state
document = state.document
const startBlock = document.getClosestBlock(startKey)
const endBlock = document.getClosestBlock(document.getNextText(endKey))
const endBlock = document.getClosestBlock(document.getNextText(endKey).key)
// remove all of the nodes between range
ancestor = document.getCommonAncestor(startKey, endKey)
@@ -103,7 +103,7 @@ export function deleteAtRange(transform, range, options = {}) {
transform.moveNodeByKey(child.key, newKey, newIndex, { normalize: false })
})
const lonely = document.getFurthest(endBlock, p => p.nodes.size == 1) || endBlock
const lonely = document.getFurthest(endBlock.key, p => p.nodes.size == 1) || endBlock
transform.removeNodeByKey(lonely.key, { normalize: false })
}
@@ -153,9 +153,9 @@ export function deleteBackwardAtRange(transform, range, n = 1, options = {}) {
const text = document.getDescendant(startKey)
if (range.isAtStartOf(text)) {
const prev = document.getPreviousText(text)
const prevBlock = document.getClosestBlock(prev)
const prevInline = document.getClosestInline(prev)
const prev = document.getPreviousText(text.key)
const prevBlock = document.getClosestBlock(prev.key)
const prevInline = document.getClosestInline(prev.key)
if (prevBlock && prevBlock.isVoid) {
return transform.removeNodeByKey(prevBlock.key, { normalize })
@@ -218,9 +218,9 @@ export function deleteForwardAtRange(transform, range, n = 1, options = {}) {
const text = document.getDescendant(startKey)
if (range.isAtEndOf(text)) {
const next = document.getNextText(text)
const nextBlock = document.getClosestBlock(next)
const nextInline = document.getClosestInline(next)
const next = document.getNextText(text.key)
const nextBlock = document.getClosestBlock(next.key)
const nextInline = document.getClosestInline(next.key)
if (nextBlock && nextBlock.isVoid) {
return transform.removeNodeByKey(nextBlock.key, { normalize })
@@ -270,7 +270,7 @@ export function insertBlockAtRange(transform, range, block, options = {}) {
const { startKey, startOffset } = range
const startText = document.assertDescendant(startKey)
const startBlock = document.getClosestBlock(startKey)
const parent = document.getParent(startBlock)
const parent = document.getParent(startBlock.key)
const index = parent.nodes.indexOf(startBlock)
if (startBlock.isVoid) {
@@ -291,7 +291,7 @@ export function insertBlockAtRange(transform, range, block, options = {}) {
}
else {
const offset = startBlock.getOffset(startText) + startOffset
const offset = startBlock.getOffset(startText.key) + startOffset
transform.splitNodeByKey(startBlock.key, offset, { normalize })
transform.insertNodeByKey(parent.key, index + 1, block, { normalize })
}
@@ -332,23 +332,23 @@ export function insertFragmentAtRange(transform, range, fragment, options = {})
let { state } = transform
let { document } = state
let startText = document.getDescendant(startKey)
let startBlock = document.getClosestBlock(startText)
let startChild = startBlock.getHighestChild(startText)
const parent = document.getParent(startBlock)
let startBlock = document.getClosestBlock(startText.key)
let startChild = startBlock.getHighestChild(startText.key)
const parent = document.getParent(startBlock.key)
const index = parent.nodes.indexOf(startBlock)
const offset = startChild == startText
? startOffset
: startChild.getOffset(startText) + startOffset
: startChild.getOffset(startText.key) + startOffset
const blocks = fragment.getBlocks()
const firstBlock = blocks.first()
const lastBlock = blocks.last()
if (firstBlock != lastBlock) {
const lonelyParent = fragment.getFurthest(firstBlock, p => p.nodes.size == 1)
const lonelyParent = fragment.getFurthest(firstBlock.key, p => p.nodes.size == 1)
const lonelyChild = lonelyParent || firstBlock
const startIndex = parent.nodes.indexOf(startBlock)
fragment = fragment.removeDescendant(lonelyChild)
fragment = fragment.removeDescendant(lonelyChild.key)
fragment.nodes.forEach((node, i) => {
const newIndex = startIndex + i + 1
@@ -364,11 +364,11 @@ export function insertFragmentAtRange(transform, range, fragment, options = {})
document = state.document
startText = document.getDescendant(startKey)
startBlock = document.getClosestBlock(startKey)
startChild = startBlock.getHighestChild(startText)
startChild = startBlock.getHighestChild(startText.key)
if (firstBlock != lastBlock) {
const nextChild = startBlock.getNextSibling(startChild)
const nextNodes = startBlock.nodes.skipUntil(n => n == nextChild)
const nextChild = startBlock.getNextSibling(startChild.key)
const nextNodes = startBlock.nodes.skipUntil(n => n.key == nextChild.key)
const lastIndex = lastBlock.nodes.size
nextNodes.forEach((node, i) => {
@@ -381,7 +381,7 @@ export function insertFragmentAtRange(transform, range, fragment, options = {})
transform.removeNodeByKey(startBlock.key, { normalize: false })
transform.insertNodeByKey(parent.key, index, firstBlock, { normalize: false })
} else {
const inlineChild = startBlock.getHighestChild(startText)
const inlineChild = startBlock.getHighestChild(startText.key)
const inlineIndex = startBlock.nodes.indexOf(inlineChild)
firstBlock.nodes.forEach((inline, i) => {
@@ -581,14 +581,14 @@ export function splitBlockAtRange(transform, range, height = 1, options = {}) {
const { state } = transform
const { document } = state
let node = document.assertDescendant(startKey)
let parent = document.getClosestBlock(node)
let parent = document.getClosestBlock(node.key)
let offset = startOffset
let h = 0
while (parent && parent.kind == 'block' && h < height) {
offset += parent.getOffset(node)
offset += parent.getOffset(node.key)
node = parent
parent = document.getClosestBlock(parent)
parent = document.getClosestBlock(parent.key)
h++
}
@@ -619,14 +619,14 @@ export function splitInlineAtRange(transform, range, height = Infinity, options
const { state } = transform
const { document } = state
let node = document.assertDescendant(startKey)
let parent = document.getClosestInline(node)
let parent = document.getClosestInline(node.key)
let offset = startOffset
let h = 0
while (parent && parent.kind == 'inline' && h < height) {
offset += parent.getOffset(node)
offset += parent.getOffset(node.key)
node = parent
parent = document.getClosestInline(parent)
parent = document.getClosestInline(parent.key)
h++
}
@@ -687,7 +687,7 @@ export function unwrapBlockAtRange(transform, range, properties, options = {}) {
const blocks = document.getBlocksAtRange(range)
const wrappers = blocks
.map((block) => {
return document.getClosest(block, (parent) => {
return document.getClosest(block.key, (parent) => {
if (parent.kind != 'block') return false
if (properties.type != null && parent.type != properties.type) return false
if (properties.isVoid != null && parent.isVoid != properties.isVoid) return false
@@ -702,11 +702,11 @@ export function unwrapBlockAtRange(transform, range, properties, options = {}) {
wrappers.forEach((block) => {
const first = block.nodes.first()
const last = block.nodes.last()
const parent = document.getParent(block)
const parent = document.getParent(block.key)
const index = parent.nodes.indexOf(block)
const children = block.nodes.filter((child) => {
return blocks.some(b => child == b || child.hasDescendant(b))
return blocks.some(b => child == b || child.hasDescendant(b.key))
})
const firstMatch = children.first()
@@ -738,12 +738,12 @@ export function unwrapBlockAtRange(transform, range, properties, options = {}) {
}
else {
const offset = block.getOffset(firstMatch)
const offset = block.getOffset(firstMatch.key)
transform.splitNodeByKey(block.key, offset, { normalize: false })
state = transform.state
document = state.document
const extra = document.getPreviousSibling(firstMatch)
const extra = document.getPreviousSibling(firstMatch.key)
children.forEach((child, i) => {
transform.moveNodeByKey(child.key, parent.key, index + 1 + i, { normalize: false })
@@ -781,7 +781,7 @@ export function unwrapInlineAtRange(transform, range, properties, options = {})
const texts = document.getTextsAtRange(range)
const inlines = texts
.map((text) => {
return document.getClosest(text, (parent) => {
return document.getClosest(text.key, (parent) => {
if (parent.kind != 'inline') return false
if (properties.type != null && parent.type != properties.type) return false
if (properties.isVoid != null && parent.isVoid != properties.isVoid) return false
@@ -794,7 +794,7 @@ export function unwrapInlineAtRange(transform, range, properties, options = {})
.toList()
inlines.forEach((inline) => {
const parent = document.getParent(inline)
const parent = document.getParent(inline.key)
const index = parent.nodes.indexOf(inline)
inline.nodes.forEach((child, i) => {
@@ -836,14 +836,14 @@ export function wrapBlockAtRange(transform, range, block, options = {}) {
// if there is only one block in the selection then we know the parent and siblings
if (blocks.length === 1) {
parent = document.getParent(firstblock)
parent = document.getParent(firstblock.key)
siblings = blocks
}
// determine closest shared parent to all blocks in selection
else {
parent = document.getClosest(firstblock, p1 => {
return !!document.getClosest(lastblock, p2 => p1 == p2)
parent = document.getClosest(firstblock.key, p1 => {
return !!document.getClosest(lastblock.key, p2 => p1 == p2)
})
}
@@ -853,8 +853,8 @@ export function wrapBlockAtRange(transform, range, block, options = {}) {
// create a list of direct children siblings of parent that fall in the selection
if (siblings == null) {
const indexes = parent.nodes.reduce((ind, node, i) => {
if (node == firstblock || node.hasDescendant(firstblock)) ind[0] = i
if (node == lastblock || node.hasDescendant(lastblock)) ind[1] = i
if (node == firstblock || node.hasDescendant(firstblock.key)) ind[0] = i
if (node == lastblock || node.hasDescendant(lastblock.key)) ind[1] = i
return ind
}, [])
@@ -935,7 +935,7 @@ export function wrapInlineAtRange(transform, range, inline, options = {}) {
const startInner = startOff == 0
? startChild
: document.getNextSibling(startChild)
: document.getNextSibling(startChild.key)
const startInnerIndex = startBlock.nodes.indexOf(startInner)

View File

@@ -381,7 +381,7 @@ export function wrapBlockByKey(transform, key, block, options) {
const { document } = transform.state
const node = document.assertDescendant(key)
const parent = document.getParent(node)
const parent = document.getParent(node.key)
const index = parent.nodes.indexOf(node)
return transform

View File

@@ -6,6 +6,7 @@ import Data from '../models/data'
import Mark from '../models/mark'
import Selection from '../models/selection'
import Text from '../models/text'
import warning from './warning'
import typeOf from 'type-of'
/**
@@ -58,13 +59,14 @@ function inline(value) {
*/
function key(value) {
if (typeOf(value) == 'string') return value
warning('Deprecation: Passing a node instead of a key to a method accepting a key can reduce performances')
if (value instanceof Block) return value.key
if (value instanceof Document) return value.key
if (value instanceof Inline) return value.key
if (value instanceof Text) return value.key
if (typeOf(value) == 'string') return value
throw new Error(`Invalid \`key\` argument! It must be either a block, an inline, a text, or a string. You passed: "${value}".`)
}
@@ -221,4 +223,3 @@ export default {
selection,
selectionProperties,
}

View File

@@ -1,10 +1,10 @@
export default function (state) {
const { document, selection } = state
const { document } = state
const block = document.nodes.get(0)
return state
.transform()
.unwrapBlockByKey(block, 'quote')
.unwrapBlockByKey(block.key, 'quote')
.apply()
}