diff --git a/.changeset/stupid-ants-buy.md b/.changeset/stupid-ants-buy.md
new file mode 100644
index 000000000..6cced8280
--- /dev/null
+++ b/.changeset/stupid-ants-buy.md
@@ -0,0 +1,9 @@
+---
+'slate': minor
+---
+
+- When removing a text node containing the cursor, always perfer placing the cursor in a sibling text node if one exists.
+ - Previously, the selection would enter a sibling inline in some circumstances, even when a sibling text node was available.
+ - The most noticeable effect of this change occurs when pressing backspace at the start of line N when the last non-empty node in line N-1 is an inline.
+ - Before, the cursor would be placed inside the inline.
+ - Now, the cursor is placed outside the inline.
diff --git a/packages/slate/src/interfaces/transforms/general.ts b/packages/slate/src/interfaces/transforms/general.ts
index 825d3d79c..d2e44c54d 100644
--- a/packages/slate/src/interfaces/transforms/general.ts
+++ b/packages/slate/src/interfaces/transforms/general.ts
@@ -246,8 +246,10 @@ export const GeneralTransforms: GeneralTransforms = {
let preferNext = false
if (prev && next) {
- if (Path.equals(next[1], path)) {
- preferNext = !Path.hasPrevious(next[1])
+ if (Path.isSibling(prev[1], path)) {
+ preferNext = false
+ } else if (Path.equals(next[1], path)) {
+ preferNext = true
} else {
preferNext =
Path.common(prev[1], path).length <
diff --git a/packages/slate/test/operations/remove_node/cursor-aunt-text-after.tsx b/packages/slate/test/operations/remove_node/cursor-aunt-text-after.tsx
new file mode 100644
index 000000000..12a73e2bb
--- /dev/null
+++ b/packages/slate/test/operations/remove_node/cursor-aunt-text-after.tsx
@@ -0,0 +1,37 @@
+/** @jsx jsx */
+import { jsx } from '../../'
+
+export const input = (
+
+
+
+
+ a
+
+
+
+
+ b
+
+
+)
+export const operations = [
+ {
+ type: 'remove_node',
+ path: [0, 1, 1],
+ node: { text: '', id: '1' },
+ },
+]
+export const output = (
+
+
+
+
+
+ a
+
+
+ b
+
+
+)
diff --git a/packages/slate/test/operations/remove_node/cursor-aunt-text-before.tsx b/packages/slate/test/operations/remove_node/cursor-aunt-text-before.tsx
new file mode 100644
index 000000000..59312c818
--- /dev/null
+++ b/packages/slate/test/operations/remove_node/cursor-aunt-text-before.tsx
@@ -0,0 +1,37 @@
+/** @jsx jsx */
+import { jsx } from '../../'
+
+export const input = (
+
+
+ a
+
+
+
+
+ b
+
+
+
+
+)
+export const operations = [
+ {
+ type: 'remove_node',
+ path: [0, 1, 0],
+ node: { text: '', id: '0' },
+ },
+]
+export const output = (
+
+
+ a
+
+
+ b
+
+
+
+
+
+)
diff --git a/packages/slate/test/operations/remove_node/cursor-sibling-inline-after.tsx b/packages/slate/test/operations/remove_node/cursor-sibling-inline-after.tsx
new file mode 100644
index 000000000..375ab920c
--- /dev/null
+++ b/packages/slate/test/operations/remove_node/cursor-sibling-inline-after.tsx
@@ -0,0 +1,35 @@
+/** @jsx jsx */
+import { jsx } from '../../'
+
+export const input = (
+
+ a
+
+
+
+
+ b
+
+
+
+)
+export const operations = [
+ {
+ type: 'remove_node',
+ path: [1, 0],
+ node: { text: '' },
+ },
+]
+export const output = (
+
+ a
+
+ {/* Recreated by normalizer */}
+
+
+ b
+
+
+
+
+)
diff --git a/packages/slate/test/operations/remove_node/cursor-sibling-inline-before-text-after.tsx b/packages/slate/test/operations/remove_node/cursor-sibling-inline-before-text-after.tsx
new file mode 100644
index 000000000..e4d004b9e
--- /dev/null
+++ b/packages/slate/test/operations/remove_node/cursor-sibling-inline-before-text-after.tsx
@@ -0,0 +1,33 @@
+/** @jsx jsx */
+import { jsx } from '../../'
+
+export const input = (
+
+
+
+ a
+
+
+
+ b
+
+
+)
+export const operations = [
+ {
+ type: 'remove_node',
+ path: [0, 2],
+ node: { text: '', id: '0' },
+ },
+]
+export const output = (
+
+
+
+ a
+
+ b
+
+
+
+)
diff --git a/packages/slate/test/operations/remove_node/cursor-sibling-inline-before.tsx b/packages/slate/test/operations/remove_node/cursor-sibling-inline-before.tsx
new file mode 100644
index 000000000..c885341fb
--- /dev/null
+++ b/packages/slate/test/operations/remove_node/cursor-sibling-inline-before.tsx
@@ -0,0 +1,35 @@
+/** @jsx jsx */
+import { jsx } from '../../'
+
+export const input = (
+
+
+
+ a
+
+
+
+
+ b
+
+)
+export const operations = [
+ {
+ type: 'remove_node',
+ path: [0, 2],
+ node: { text: '' },
+ },
+]
+export const output = (
+
+
+
+
+ a
+
+ {/* Recreated by normalizer */}
+
+
+ b
+
+)
diff --git a/packages/slate/test/operations/remove_node/cursor-sibling-text-after.tsx b/packages/slate/test/operations/remove_node/cursor-sibling-text-after.tsx
new file mode 100644
index 000000000..87456b1f9
--- /dev/null
+++ b/packages/slate/test/operations/remove_node/cursor-sibling-text-after.tsx
@@ -0,0 +1,31 @@
+/** @jsx jsx */
+import { jsx } from 'slate-hyperscript'
+
+export const input = (
+
+ a
+
+
+
+
+ b
+
+
+)
+export const operations = [
+ {
+ type: 'remove_node',
+ path: [1, 0],
+ node: { text: '', id: '0' },
+ },
+]
+export const output = (
+
+ a
+
+
+ b
+
+
+
+)
diff --git a/packages/slate/test/operations/remove_node/cursor-sibling-text-before-inline-after.tsx b/packages/slate/test/operations/remove_node/cursor-sibling-text-before-inline-after.tsx
new file mode 100644
index 000000000..60d2d9631
--- /dev/null
+++ b/packages/slate/test/operations/remove_node/cursor-sibling-text-before-inline-after.tsx
@@ -0,0 +1,33 @@
+/** @jsx jsx */
+import { jsx } from '../../'
+
+export const input = (
+
+
+ a
+
+
+
+ b
+
+
+
+)
+export const operations = [
+ {
+ type: 'remove_node',
+ path: [0, 1],
+ node: { text: '', id: '1' },
+ },
+]
+export const output = (
+
+
+
+ a
+
+ b
+
+
+
+)
diff --git a/packages/slate/test/operations/remove_node/cursor-sibling-text-before.tsx b/packages/slate/test/operations/remove_node/cursor-sibling-text-before.tsx
new file mode 100644
index 000000000..69e5111a6
--- /dev/null
+++ b/packages/slate/test/operations/remove_node/cursor-sibling-text-before.tsx
@@ -0,0 +1,31 @@
+/** @jsx jsx */
+import { jsx } from 'slate-hyperscript'
+
+export const input = (
+
+
+ a
+
+
+
+
+ b
+
+)
+export const operations = [
+ {
+ type: 'remove_node',
+ path: [0, 1],
+ node: { text: '', id: '1' },
+ },
+]
+export const output = (
+
+
+
+ a
+
+
+ b
+
+)
diff --git a/packages/slate/test/operations/remove_node/cursor-sibling-text-both-sides.tsx b/packages/slate/test/operations/remove_node/cursor-sibling-text-both-sides.tsx
new file mode 100644
index 000000000..930e3ce89
--- /dev/null
+++ b/packages/slate/test/operations/remove_node/cursor-sibling-text-both-sides.tsx
@@ -0,0 +1,31 @@
+/** @jsx jsx */
+import { jsx } from 'slate-hyperscript'
+
+export const input = (
+
+
+ a
+
+
+
+ b
+
+
+)
+export const operations = [
+ {
+ type: 'remove_node',
+ path: [0, 1],
+ node: { text: '', id: '1' },
+ },
+]
+export const output = (
+
+
+
+ a
+
+ b
+
+
+)
diff --git a/packages/slate/test/transforms/delete/point/inline-void-reverse.tsx b/packages/slate/test/transforms/delete/point/inline-void-reverse.tsx
index d9cd964fa..6b6b26a04 100644
--- a/packages/slate/test/transforms/delete/point/inline-void-reverse.tsx
+++ b/packages/slate/test/transforms/delete/point/inline-void-reverse.tsx
@@ -25,10 +25,9 @@ export const output = (
-
-
-
+
+
word