diff --git a/.changeset/sharp-donkeys-sparkle.md b/.changeset/sharp-donkeys-sparkle.md
new file mode 100644
index 000000000..e1da53aa9
--- /dev/null
+++ b/.changeset/sharp-donkeys-sparkle.md
@@ -0,0 +1,5 @@
+---
+'slate': patch
+---
+
+Fix setNodes when called with 'split' and a collapsed range
diff --git a/packages/slate/src/interfaces/range.ts b/packages/slate/src/interfaces/range.ts
index 0f68e1789..16352acbb 100644
--- a/packages/slate/src/interfaces/range.ts
+++ b/packages/slate/src/interfaces/range.ts
@@ -222,12 +222,16 @@ export const Range: RangeInterface = {
let affinityFocus: 'forward' | 'backward' | null
if (affinity === 'inward') {
+ // If the range is collapsed, make sure to use the same affinity to
+ // avoid the two points passing each other and expanding in the opposite
+ // direction
+ const isCollapsed = Range.isCollapsed(r)
if (Range.isForward(r)) {
affinityAnchor = 'forward'
- affinityFocus = 'backward'
+ affinityFocus = isCollapsed ? affinityAnchor : 'backward'
} else {
affinityAnchor = 'backward'
- affinityFocus = 'forward'
+ affinityFocus = isCollapsed ? affinityAnchor : 'forward'
}
} else if (affinity === 'outward') {
if (Range.isForward(r)) {
diff --git a/packages/slate/src/transforms/node.ts b/packages/slate/src/transforms/node.ts
index 00567dfd7..d4036a61e 100644
--- a/packages/slate/src/transforms/node.ts
+++ b/packages/slate/src/transforms/node.ts
@@ -594,6 +594,14 @@ export const NodeTransforms: NodeTransforms = {
}
if (split && Range.isRange(at)) {
+ if (
+ Range.isCollapsed(at) &&
+ Editor.leaf(editor, at.anchor)[0].text.length > 0
+ ) {
+ // If the range is collapsed in a non-empty node and 'split' is true, there's nothing to
+ // set that won't get normalized away
+ return
+ }
const rangeRef = Editor.rangeRef(editor, at, { affinity: 'inward' })
const [start, end] = Range.edges(at)
const splitMode = mode === 'lowest' ? 'lowest' : 'highest'
diff --git a/packages/slate/test/interfaces/Range/transform/inward-collapsed.tsx b/packages/slate/test/interfaces/Range/transform/inward-collapsed.tsx
new file mode 100644
index 000000000..003e7507b
--- /dev/null
+++ b/packages/slate/test/interfaces/Range/transform/inward-collapsed.tsx
@@ -0,0 +1,34 @@
+import { Range } from 'slate'
+
+export const input = {
+ anchor: {
+ path: [0, 0],
+ offset: 1,
+ },
+ focus: {
+ path: [0, 0],
+ offset: 1,
+ },
+}
+export const test = value => {
+ return Range.transform(
+ value,
+ {
+ type: 'split_node',
+ path: [0, 0],
+ position: 1,
+ properties: {},
+ },
+ { affinity: 'inward' }
+ )
+}
+export const output = {
+ anchor: {
+ path: [0, 1],
+ offset: 0,
+ },
+ focus: {
+ path: [0, 1],
+ offset: 0,
+ },
+}
diff --git a/packages/slate/test/transforms/setNodes/split/noop-collapsed.tsx b/packages/slate/test/transforms/setNodes/split/noop-collapsed.tsx
new file mode 100644
index 000000000..9b36beb26
--- /dev/null
+++ b/packages/slate/test/transforms/setNodes/split/noop-collapsed.tsx
@@ -0,0 +1,27 @@
+/** @jsx jsx */
+import { Transforms, Text } from 'slate'
+import { jsx } from '../../..'
+
+export const run = editor => {
+ Transforms.setNodes(
+ editor,
+ { someKey: true },
+ { match: Text.isText, split: true }
+ )
+}
+export const input = (
+
+
+ w
+ ord
+
+
+)
+export const output = (
+
+
+ w
+ ord
+
+
+)