1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-08-28 09:29:49 +02:00

Merge branch 'fix-word-char-deletes'

This commit is contained in:
Ian Storm Taylor
2017-02-07 12:26:04 -08:00
56 changed files with 810 additions and 31 deletions

View File

@@ -131,9 +131,13 @@ export function deleteAtRange(transform, range, options = {}) {
export function deleteCharBackwardAtRange(transform, range, options) {
const { state } = transform
const { startOffset, startBlock } = state
const { document } = state
const { startKey, startOffset } = range
const startBlock = document.getClosestBlock(startKey)
const offset = startBlock.getOffset(startKey)
const o = offset + startOffset
const { text } = startBlock
const n = String.getCharOffsetBackward(text, startOffset)
const n = String.getCharOffsetBackward(text, o)
transform.deleteBackwardAtRange(range, n, options)
}
@@ -148,8 +152,12 @@ export function deleteCharBackwardAtRange(transform, range, options) {
export function deleteLineBackwardAtRange(transform, range, options) {
const { state } = transform
const { startOffset } = state
transform.deleteBackwardAtRange(range, startOffset, options)
const { document } = state
const { startKey, startOffset } = range
const startBlock = document.getClosestBlock(startKey)
const offset = startBlock.getOffset(startKey)
const o = offset + startOffset
transform.deleteBackwardAtRange(range, o, options)
}
/**
@@ -163,9 +171,13 @@ export function deleteLineBackwardAtRange(transform, range, options) {
export function deleteWordBackwardAtRange(transform, range, options) {
const { state } = transform
const { startOffset, startBlock } = state
const { document } = state
const { startKey, startOffset } = range
const startBlock = document.getClosestBlock(startKey)
const offset = startBlock.getOffset(startKey)
const o = offset + startOffset
const { text } = startBlock
const n = String.getWordOffsetBackward(text, startOffset)
const n = String.getWordOffsetBackward(text, o)
transform.deleteBackwardAtRange(range, n, options)
}
@@ -230,22 +242,58 @@ export function deleteBackwardAtRange(transform, range, n = 1, options = {}) {
return
}
// If the previous text's block is inside the current block, then we need
// to remove a character when deleteing. Otherwise, we just want to join
// the two blocks together.
// If we're deleting by one character and the previous text node is not
// inside the current block, we need to join the two blocks together.
if (n == 1 && prevBlock != block) {
range = range.merge({
anchorKey: prev.key,
anchorOffset: prev.length,
})
transform.deleteAtRange(range, { normalize })
return
}
}
// If the focus offset is farther than the number of characters to delete,
// just remove the characters backwards inside the current node.
if (n < focusOffset) {
range = range.merge({
anchorKey: prev.key,
anchorOffset: prevBlock == block ? prev.length - 1 : prev.length,
focusOffset: focusOffset - n,
isBackward: true,
})
transform.deleteAtRange(range, { normalize })
return
}
// Otherwise, just remove a character backwards.
// Otherwise, we need to see how many nodes backwards to go.
let node = text
let offset = 0
let traversed = focusOffset
while (n > traversed) {
node = document.getPreviousText(node.key)
const next = traversed + node.length
if (n <= next) {
offset = next - n
break
} else {
traversed = next
}
}
// If the focus node is inside a void, go up until right after it.
if (document.hasVoidParent(node.key)) {
const parent = document.getClosest(node.key, p => p.isVoid)
node = document.getNextText(parent.key)
offset = 0
}
range = range.merge({
focusOffset: focusOffset - n,
isBackward: true,
focusKey: node.key,
focusOffset: offset,
isBackward: true
})
transform.deleteAtRange(range, { normalize })
@@ -262,9 +310,13 @@ export function deleteBackwardAtRange(transform, range, n = 1, options = {}) {
export function deleteCharForwardAtRange(transform, range, options) {
const { state } = transform
const { startOffset, startBlock } = state
const { document } = state
const { startKey, startOffset } = range
const startBlock = document.getClosestBlock(startKey)
const offset = startBlock.getOffset(startKey)
const o = offset + startOffset
const { text } = startBlock
const n = String.getCharOffsetForward(text, startOffset)
const n = String.getCharOffsetForward(text, o)
transform.deleteForwardAtRange(range, n, options)
}
@@ -279,8 +331,12 @@ export function deleteCharForwardAtRange(transform, range, options) {
export function deleteLineForwardAtRange(transform, range, options) {
const { state } = transform
const { startOffset, startBlock } = state
transform.deleteForwardAtRange(range, startBlock.length - startOffset, options)
const { document } = state
const { startKey, startOffset } = range
const startBlock = document.getClosestBlock(startKey)
const offset = startBlock.getOffset(startKey)
const o = offset + startOffset
transform.deleteForwardAtRange(range, o, options)
}
/**
@@ -294,9 +350,13 @@ export function deleteLineForwardAtRange(transform, range, options) {
export function deleteWordForwardAtRange(transform, range, options) {
const { state } = transform
const { startOffset, startBlock } = state
const { document } = state
const { startKey, startOffset } = range
const startBlock = document.getClosestBlock(startKey)
const offset = startBlock.getOffset(startKey)
const o = offset + startOffset
const { text } = startBlock
const n = String.getWordOffsetForward(text, startOffset)
const n = String.getWordOffsetForward(text, o)
transform.deleteForwardAtRange(range, n, options)
}
@@ -316,57 +376,102 @@ export function deleteForwardAtRange(transform, range, n = 1, options = {}) {
const { document } = state
const { startKey, focusOffset } = range
// If the range is expanded, perform a regular delete instead.
if (range.isExpanded) {
transform.deleteAtRange(range, { normalize })
return
}
// If the closest block is void, delete it.
const block = document.getClosestBlock(startKey)
if (block && block.isVoid) {
transform.removeNodeByKey(block.key, { normalize })
return
}
// If the closest inline is void, delete it.
const inline = document.getClosestInline(startKey)
if (inline && inline.isVoid) {
transform.removeNodeByKey(inline.key, { normalize })
return
}
// If the range is at the start of the document, abort.
if (range.isAtEndOf(document)) {
return
}
// If the range is at the start of the text node, we need to figure out what
// is behind it to know how to delete...
const text = document.getDescendant(startKey)
if (range.isAtEndOf(text)) {
const next = document.getNextText(text.key)
const nextBlock = document.getClosestBlock(next.key)
const nextInline = document.getClosestInline(next.key)
// If the previous block is void, remove it.
if (nextBlock && nextBlock.isVoid) {
transform.removeNodeByKey(nextBlock.key, { normalize })
return
}
// If the previous inline is void, remove it.
if (nextInline && nextInline.isVoid) {
transform.removeNodeByKey(nextInline.key, { normalize })
return
}
// If the next text's block is inside the current block, then we need
// to remove a character when deleteing. Otherwise, we just want to join
// the two blocks together.
// If we're deleting by one character and the previous text node is not
// inside the current block, we need to join the two blocks together.
if (n == 1 && nextBlock != block) {
range = range.merge({
focusKey: next.key,
focusOffset: 0
})
transform.deleteAtRange(range, { normalize })
return
}
}
// If the remaining characters to the end of the node is greater than or equal
// to the number of characters to delete, just remove the characters forwards
// inside the current node.
if (n <= (text.length - focusOffset)) {
range = range.merge({
focusKey: next.key,
focusOffset: nextBlock == block ? 1 : 0
focusOffset: focusOffset + n
})
transform.deleteAtRange(range, { normalize })
return
}
// Otherwise, we need to see how many nodes forwards to go.
let node = text
let offset = focusOffset
let traversed = text.length - focusOffset
while (n > traversed) {
node = document.getNextText(node.key)
const next = traversed + node.length
if (n <= next) {
offset = n - traversed
break
} else {
traversed = next
}
}
// If the focus node is inside a void, go up until right before it.
if (document.hasVoidParent(node.key)) {
const parent = document.getClosest(node.key, p => p.isVoid)
node = document.getPreviousText(parent.key)
offset = node.length
}
range = range.merge({
focusOffset: focusOffset + n
focusKey: node.key,
focusOffset: offset,
})
transform.deleteAtRange(range, { normalize })

View File

@@ -59,8 +59,9 @@ function isWord(char, remaining) {
// If it's a chameleon character, recurse to see if the next one is or not.
if (CHAMELEON.test(char)) {
const next = remaining.charAt(0)
let next = remaining.charAt(0)
const length = getCharLength(next)
next = remaining.slice(0, length)
const rest = remaining.slice(length)
if (isWord(next, rest)) return true
}
@@ -136,13 +137,14 @@ function getWordOffset(text) {
while (char = text.charAt(i)) {
const l = getCharLength(char)
char = text.slice(i, i + l)
const rest = text.slice(i + l)
if (isWord(char, rest)) {
started = true
length++
length += l
} else if (!started) {
length++
length += l
} else {
break
}
@@ -164,7 +166,8 @@ function getWordOffset(text) {
function getWordOffsetBackward(text, offset) {
text = text.slice(0, offset)
text = reverse(text)
return getWordOffset(text)
const o = getWordOffset(text)
return o
}
/**
@@ -177,7 +180,8 @@ function getWordOffsetBackward(text, offset) {
function getWordOffsetForward(text, offset) {
text = text.slice(offset)
return getWordOffset(text)
const o = getWordOffset(text)
return o
}
/**

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const last = texts.last()
const range = selection.merge({
anchorKey: last.key,
anchorOffset: last.length,
focusKey: last.key,
focusOffset: last.length
})
return state
.transform()
.deleteCharBackwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: one
- kind: inline
type: link
nodes:
- kind: text
text: two
- kind: text
text: 📛

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: one
- kind: inline
type: link
nodes:
- kind: text
text: two
- kind: text
text: ""

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const last = texts.last()
const range = selection.merge({
anchorKey: last.key,
anchorOffset: last.length,
focusKey: last.key,
focusOffset: last.length
})
return state
.transform()
.deleteCharBackwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: one
- kind: inline
type: link
nodes:
- kind: text
text: tw📛
- kind: text
text: ""

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: one
- kind: inline
type: link
nodes:
- kind: text
text: tw
- kind: text
text: ""

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const second = texts.get(1)
const range = selection.merge({
anchorKey: second.key,
anchorOffset: second.length - 1,
focusKey: second.key,
focusOffset: second.length - 1
})
return state
.transform()
.deleteCharBackwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: one
- kind: inline
type: link
nodes:
- kind: text
text: tw📛o
- kind: text
text: ""

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: one
- kind: inline
type: link
nodes:
- kind: text
text: two
- kind: text
text: ""

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const last = texts.last()
const range = selection.merge({
anchorKey: last.key,
anchorOffset: last.length,
focusKey: last.key,
focusOffset: last.length
})
return state
.transform()
.deleteCharBackwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "one two three"

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "one two thre"

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const last = texts.last()
const range = selection.merge({
anchorKey: last.key,
anchorOffset: last.length - 2,
focusKey: last.key,
focusOffset: last.length - 2
})
return state
.transform()
.deleteCharBackwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "one two three"

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "one two thee"

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const first = texts.first()
const range = selection.merge({
anchorKey: first.key,
anchorOffset: 0,
focusKey: first.key,
focusOffset: 0,
})
return state
.transform()
.deleteCharForwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: 📛
- kind: inline
type: link
nodes:
- kind: text
text: two
- kind: text
text: three

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: ""
- kind: inline
type: link
nodes:
- kind: text
text: two
- kind: text
text: three

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const second = texts.get(1)
const range = selection.merge({
anchorKey: second.key,
anchorOffset: 2,
focusKey: second.key,
focusOffset: 2
})
return state
.transform()
.deleteCharForwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: one
- kind: inline
type: link
nodes:
- kind: text
text: tw📛o
- kind: text
text: ""

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: one
- kind: inline
type: link
nodes:
- kind: text
text: two
- kind: text
text: ""

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const first = texts.first()
const range = selection.merge({
anchorKey: first.key,
anchorOffset: 0,
focusKey: first.key,
focusOffset: 0,
})
return state
.transform()
.deleteCharForwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: ""
- kind: inline
type: link
nodes:
- kind: text
text: 📛two
- kind: text
text: three

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: ""
- kind: inline
type: link
nodes:
- kind: text
text: two
- kind: text
text: three

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const first = texts.first()
const range = selection.merge({
anchorKey: first.key,
anchorOffset: 1,
focusKey: first.key,
focusOffset: 1,
})
return state
.transform()
.deleteCharForwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "one two three"

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "oe two three"

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const first = texts.first()
const range = selection.merge({
anchorKey: first.key,
anchorOffset: 0,
focusKey: first.key,
focusOffset: 0,
})
return state
.transform()
.deleteCharForwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "one two three"

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "ne two three"

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const last = texts.last()
const range = selection.merge({
anchorKey: last.key,
anchorOffset: last.length,
focusKey: last.key,
focusOffset: last.length
})
return state
.transform()
.deleteWordBackwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: one
- kind: inline
type: link
nodes:
- kind: text
text: two
- kind: text
text: 📛

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: ""

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const last = texts.last()
const range = selection.merge({
anchorKey: last.key,
anchorOffset: last.length,
focusKey: last.key,
focusOffset: last.length
})
return state
.transform()
.deleteWordBackwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: one
- kind: inline
type: link
nodes:
- kind: text
text: tw📛o
- kind: text
text: ""

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: ""

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const last = texts.last()
const range = selection.merge({
anchorKey: last.key,
anchorOffset: last.length,
focusKey: last.key,
focusOffset: last.length
})
return state
.transform()
.deleteWordBackwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "one two three"

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "one two "

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const last = texts.last()
const range = selection.merge({
anchorKey: last.key,
anchorOffset: last.length - 2,
focusKey: last.key,
focusOffset: last.length - 2
})
return state
.transform()
.deleteWordBackwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "one two three"

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "one two ee"

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const first = texts.first()
const range = selection.merge({
anchorKey: first.key,
anchorOffset: 0,
focusKey: first.key,
focusOffset: 0
})
return state
.transform()
.deleteWordForwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: one
- kind: inline
type: link
nodes:
- kind: text
text: two
- kind: text
text: 📛

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: ""

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const first = texts.first()
const range = selection.merge({
anchorKey: first.key,
anchorOffset: 0,
focusKey: first.key,
focusOffset: 0
})
return state
.transform()
.deleteWordForwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,14 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: one
- kind: inline
type: link
nodes:
- kind: text
text: tw📛o
- kind: text
text: ""

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: ""

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const first = texts.first()
const range = selection.merge({
anchorKey: first.key,
anchorOffset: 1,
focusKey: first.key,
focusOffset: 1,
})
return state
.transform()
.deleteWordForwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "one two three"

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "o two three"

View File

@@ -0,0 +1,17 @@
export default function (state) {
const { document, selection } = state
const texts = document.getTexts()
const first = texts.first()
const range = selection.merge({
anchorKey: first.key,
anchorOffset: 0,
focusKey: first.key,
focusOffset: 0
})
return state
.transform()
.deleteWordForwardAtRange(range)
.apply()
}

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: "one two three"

View File

@@ -0,0 +1,7 @@
nodes:
- kind: block
type: paragraph
nodes:
- kind: text
text: " two three"