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

refactor transforms to apply operations immediately

This commit is contained in:
Ian Storm Taylor
2016-08-16 17:36:17 -07:00
parent 4162bb7a2a
commit c297972539
3 changed files with 68 additions and 50 deletions

View File

@@ -383,9 +383,7 @@ class State extends new Record(DEFAULTS) {
transform = rule.normalize(transform, document, value)
}
return transform.steps.size
? transform.apply({ snapshot: false })
: state
return transform.apply({ snapshot: false })
}
/**

View File

@@ -11,14 +11,14 @@ import { List, Record } from 'immutable'
const Snapshot = new Record({
document: null,
selection: null,
steps: new List()
operations: new List()
})
/**
* Step.
* Operation.
*/
const Step = new Record({
const Operation = new Record({
type: null,
args: null
})
@@ -29,7 +29,7 @@ const Step = new Record({
const DEFAULT_PROPERTIES = {
state: null,
steps: new List()
operations: new List()
}
/**
@@ -91,7 +91,7 @@ class Transform extends new Record(DEFAULT_PROPERTIES) {
apply(options = {}) {
const transform = this
let { state, steps } = transform
let { state, operations } = transform
let { cursorMarks, history, selection } = state
let { undos, redos } = history
@@ -110,9 +110,6 @@ class Transform extends new Record(DEFAULT_PROPERTIES) {
state = state.merge({ history })
}
// Apply each of the steps in the transform, arriving at a new state.
state = steps.reduce((memo, step) => this.applyStep(memo, step), state)
// If there are cursor marks and they haven't changed, remove them.
if (state.cursorMarks && state.cursorMarks == cursorMarks) {
state = state.merge({
@@ -130,56 +127,36 @@ class Transform extends new Record(DEFAULT_PROPERTIES) {
}
/**
* Apply a single `step` to a `state`, differentiating between types.
*
* @param {State} state
* @param {Step} step
* @return {State} state
*/
applyStep(state, step) {
const { type, args } = step
const transform = Transforms[type]
if (!transform) {
throw new Error(`Unknown transform type: "${type}".`)
}
state = transform(state, ...args)
return state
}
/**
* Check whether the current transform steps should create a snapshot.
* Check whether the current transform operations should create a snapshot.
*
* @return {Boolean}
*/
shouldSnapshot() {
const transform = this
const { state, steps } = transform
const { state, operations } = transform
const { cursorMarks, history, selection } = state
const { undos, redos } = history
const previous = undos.peek()
// If the only steps applied are selection transforms, don't snapshot.
const onlySelections = steps.every(step => includes(SELECTION_TRANSFORMS, step.type))
// If the only operations applied are selection transforms, don't snapshot.
const onlySelections = operations.every(operation => includes(SELECTION_TRANSFORMS, operation.type))
if (onlySelections) return false
// If there isn't a previous state, snapshot.
if (!previous) return true
// If there is a previous state but the steps are different, snapshot.
const types = steps.map(step => step.type)
const prevTypes = previous.steps.map(step => step.type)
// If there is a previous state but the operations are different, snapshot.
const types = operations.map(operation => operation.type)
const prevTypes = previous.operations.map(operation => operation.type)
const diff = xor(types.toArray(), prevTypes.toArray())
if (diff.length) return true
// If the current steps aren't one of the "combinable" types, snapshot.
// If the current operations aren't one of the "combinable" types, snapshot.
const allCombinable = (
steps.every(step => step.type == 'insertText') ||
steps.every(step => step.type == 'deleteForward') ||
steps.every(step => step.type == 'deleteBackward')
operations.every(operation => operation.type == 'insertText') ||
operations.every(operation => operation.type == 'deleteForward') ||
operations.every(operation => operation.type == 'deleteBackward')
)
if (!allCombinable) return true
@@ -195,9 +172,9 @@ class Transform extends new Record(DEFAULT_PROPERTIES) {
*/
snapshot() {
let { state, steps } = this
let { state, operations } = this
let { document, selection } = state
return new Snapshot({ document, selection, steps })
return new Snapshot({ document, selection, operations })
}
/**
@@ -275,15 +252,16 @@ class Transform extends new Record(DEFAULT_PROPERTIES) {
}
/**
* Add a step-creating method for each of the transforms.
* Add a operation-creating method for each of the transforms.
*/
Object.keys(Transforms).forEach((type) => {
Transform.prototype[type] = function (...args) {
let transform = this
let { steps } = transform
steps = steps.push(new Step({ type, args }))
transform = transform.merge({ steps })
let { operations, state } = transform
operations = operations.push(new Operation({ type, args }))
state = Transforms[type](state, ...args)
transform = transform.merge({ operations, state })
return transform
}
})

View File

@@ -6,7 +6,7 @@ import Normalize from '../utils/normalize'
*
* @param {State} state
* @param {String} key
* @return {State} state
* @return {State}
*/
export function removeNodeByKey(state, key) {
@@ -23,7 +23,7 @@ export function removeNodeByKey(state, key) {
* @param {State} state
* @param {String} key
* @param {Object or String} properties
* @return {State} state
* @return {State}
*/
export function setNodeByKey(state, key, properties) {
@@ -35,3 +35,45 @@ export function setNodeByKey(state, key, properties) {
state = state.merge({ document })
return state
}
/**
* Insert a `node` after a node by `key`.
*
* @param {State} state
* @param {String} key
* @param {Node} node
* @return {State}
*/
export function insertNodeAfterNodeByKey(state, key, node) {
let { document } = state
let descendant = document.assertDescendant(key)
let parent = document.getParent(key)
let index = parent.nodes.indexOf(descendant)
let nodes = parent.nodes.splice(index + 1, 0, node)
parent = parent.merge({ nodes })
document = document.updateDescendant(parent)
state = state.merge({ document })
return state
}
/**
* Insert a `node` before a node by `key`.
*
* @param {State} state
* @param {String} key
* @param {Node} node
* @return {State}
*/
export function insertNodeBeforeNodeByKey(state, key, node) {
let { document } = state
let descendant = document.assertDescendant(key)
let parent = document.getParent(key)
let index = parent.nodes.indexOf(descendant)
let nodes = parent.nodes.splice(index, 0, node)
parent = parent.merge({ nodes })
document = document.updateDescendant(parent)
state = state.merge({ document })
return state
}