diff --git a/packages/editor/src/components/post-revisions-preview/block-diff.js b/packages/editor/src/components/post-revisions-preview/block-diff.js
index 95ed7197ca0d53..ce3be7a5e89a87 100644
--- a/packages/editor/src/components/post-revisions-preview/block-diff.js
+++ b/packages/editor/src/components/post-revisions-preview/block-diff.js
@@ -520,6 +520,67 @@ function describeFormatChange(
};
}
+/**
+ * Merge adjacent removed/added diff parts into fewer del/ins pairs.
+ *
+ * `diffWordsWithSpace` can emit alternating removed/added tokens for a run of
+ * changed words, which produces one del/ins pair per word, making the output
+ * more verbose for screen readers. This merges such runs into a single removed part and a
+ * single added part.
+ *
+ * @param {Array<{ added?: boolean, removed?: boolean, value: string }>} parts Output from `diffWordsWithSpace`.
+ * @return {Array<{ added?: boolean, removed?: boolean, value: string }>} Merged parts.
+ */
+function mergeTextDiffParts( parts ) {
+ const result = [];
+ let index = 0;
+
+ while ( index < parts.length ) {
+ const part = parts[ index ];
+
+ if ( ! part.removed && ! part.added ) {
+ result.push( part );
+ index++;
+ continue;
+ }
+
+ let removed = '';
+ let added = '';
+
+ while ( index < parts.length ) {
+ const current = parts[ index ];
+
+ if ( current.removed ) {
+ removed += current.value;
+ index++;
+ } else if ( current.added ) {
+ added += current.value;
+ index++;
+ } else if (
+ current.value.trim() === '' &&
+ index + 1 < parts.length &&
+ ( parts[ index + 1 ].removed || parts[ index + 1 ].added )
+ ) {
+ // Whitespace between changed tokens — include in both sides.
+ removed += current.value;
+ added += current.value;
+ index++;
+ } else {
+ break;
+ }
+ }
+
+ if ( removed ) {
+ result.push( { removed: true, value: removed } );
+ }
+ if ( added ) {
+ result.push( { added: true, value: added } );
+ }
+ }
+
+ return result;
+}
+
/**
* Apply inline diff formatting comparing two RichTextData values.
* - Text changes: apply revision/diff-removed and revision/diff-added formats
@@ -534,7 +595,9 @@ function applyRichTextDiff( currentRichText, previousRichText ) {
const previousText = previousRichText.toPlainText();
// Diff the plain text (words for cleaner output).
- const textDiff = diffWordsWithSpace( previousText, currentText );
+ const textDiff = mergeTextDiffParts(
+ diffWordsWithSpace( previousText, currentText )
+ );
let result = create( { text: '' } );
let currentIdx = 0;
diff --git a/packages/editor/src/components/post-revisions-preview/test/block-diff.jsdom.test.js b/packages/editor/src/components/post-revisions-preview/test/block-diff.jsdom.test.js
index dd797b5e632856..e4e6a7f04774ae 100644
--- a/packages/editor/src/components/post-revisions-preview/test/block-diff.jsdom.test.js
+++ b/packages/editor/src/components/post-revisions-preview/test/block-diff.jsdom.test.js
@@ -1139,6 +1139,33 @@ describe( 'diffRevisionContent', () => {
] );
} );
+ it( 'merges adjacent changed words into single del/ins pairs', () => {
+ const previous = serialize( [
+ createBlock( 'core/paragraph', {
+ content: 'one two three',
+ } ),
+ ] );
+ const current = serialize( [
+ createBlock( 'core/paragraph', {
+ content: 'alpha beta gamma',
+ } ),
+ ] );
+ const blocks = diffRevisionContent( current, previous );
+
+ expect( normalizeBlockTree( blocks ) ).toMatchObject( [
+ {
+ name: 'core/paragraph',
+ attributes: {
+ content:
+ 'one two threealpha beta gamma',
+ __revisionDiffStatus: {
+ status: 'modified',
+ },
+ },
+ },
+ ] );
+ } );
+
it( 'detects changed link text as modification', () => {
const previous = serialize( [
createBlock( 'core/paragraph', {
@@ -1155,12 +1182,13 @@ describe( 'diffRevisionContent', () => {
const blocks = diffRevisionContent( current, previous );
// Word-level diff: "our site" changed to "the website" within link.
+ // Adjacent changed words are merged into a single del/ins pair.
expect( normalizeBlockTree( blocks ) ).toMatchObject( [
{
name: 'core/paragraph',
attributes: {
content:
- 'Visit ourthe sitewebsite today',
+ 'Visit our sitethe website today',
__revisionDiffStatus: {
status: 'modified',
},
@@ -1323,7 +1351,7 @@ describe( 'diffRevisionContent', () => {
name: 'core/paragraph',
attributes: {
content:
- 'HelloGoodbye worldeveryone',
+ 'Hello worldGoodbye everyone',
__revisionDiffStatus: {
status: 'modified',
},