diff --git a/packages/ckeditor5-mention/src/mentioncommand.ts b/packages/ckeditor5-mention/src/mentioncommand.ts
index dbe8cedac42..08609394a75 100644
--- a/packages/ckeditor5-mention/src/mentioncommand.ts
+++ b/packages/ckeditor5-mention/src/mentioncommand.ts
@@ -13,6 +13,12 @@ import { CKEditorError, toMap } from 'ckeditor5/src/utils.js';
import { _addMentionAttributes } from './mentionediting.js';
+const BRACKET_PAIRS = {
+ '(': ')',
+ '[': ']',
+ '{': '}'
+} as const;
+
/**
* The mention command.
*
@@ -168,8 +174,30 @@ export default class MentionCommand extends Command {
attributesWithMention.set( 'mention', mention );
// Replace a range with the text with a mention.
- model.insertContent( writer.createText( mentionText, attributesWithMention ), range );
- model.insertContent( writer.createText( ' ', currentAttributes ), range!.start.getShiftedBy( mentionText.length ) );
+ const insertionRange = model.insertContent( writer.createText( mentionText, attributesWithMention ), range );
+ const nodeBefore = insertionRange.start.nodeBefore;
+ const nodeAfter = insertionRange.end.nodeAfter;
+
+ const isFollowedByWhiteSpace = nodeAfter && nodeAfter.is( '$text' ) && nodeAfter.data.startsWith( ' ' );
+ let isInsertedInBrackets = false;
+
+ if ( nodeBefore && nodeAfter && nodeBefore.is( '$text' ) && nodeAfter.is( '$text' ) ) {
+ const precedingCharacter = nodeBefore.data.slice( -1 );
+ const isPrecededByOpeningBracket = precedingCharacter in BRACKET_PAIRS;
+ const isFollowedByBracketClosure = isPrecededByOpeningBracket && nodeAfter.data.startsWith(
+ BRACKET_PAIRS[ precedingCharacter as keyof typeof BRACKET_PAIRS ]
+ );
+
+ isInsertedInBrackets = isPrecededByOpeningBracket && isFollowedByBracketClosure;
+ }
+
+ // Don't add a white space if either of the following is true:
+ // * there's already one after the mention;
+ // * the mention was inserted in the empty matching brackets.
+ // https://github.com/ckeditor/ckeditor5/issues/4651
+ if ( !isInsertedInBrackets && !isFollowedByWhiteSpace ) {
+ model.insertContent( writer.createText( ' ', currentAttributes ), range!.start.getShiftedBy( mentionText.length ) );
+ }
} );
}
}
diff --git a/packages/ckeditor5-mention/tests/mentioncommand.js b/packages/ckeditor5-mention/tests/mentioncommand.js
index 9a8bec022b4..87e972c1be9 100644
--- a/packages/ckeditor5-mention/tests/mentioncommand.js
+++ b/packages/ckeditor5-mention/tests/mentioncommand.js
@@ -4,7 +4,7 @@
*/
import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor.js';
-import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model.js';
+import { setData, getData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model.js';
import MentionCommand from '../src/mentioncommand.js';
@@ -177,6 +177,49 @@ describe( 'MentionCommand', () => {
expectToThrowCKEditorError( () => command.execute( options ), /mentioncommand-incorrect-id/, editor );
}
} );
+
+ it( 'should not insert a white space if the mention was already followed by one', () => {
+ setData( model, 'foo [] bar' );
+
+ command.execute( {
+ marker: '@',
+ mention: '@John'
+ } );
+
+ assertMention( doc.getRoot().getChild( 0 ).getChild( 1 ), '@John' );
+
+ expect( getData( model ) ).to.match(
+ /foo <\$text mention="{"uid":"[^"]+","_text":"@John","id":"@John"}">@John\[\]<\/\$text> bar<\/paragraph>/
+ );
+ } );
+
+ [ [ '(', ')' ], [ '[', ']' ], [ '{', '}' ] ].forEach( ( [ openingBracket, closingBracket ] ) => {
+ it( `should not insert a white space if the mention injected in "${ openingBracket }${ closingBracket }" brackets`, () => {
+ model.change( writer => {
+ const paragraph = writer.createElement( 'paragraph' );
+ const text = writer.createText( `${ openingBracket }${ closingBracket }` );
+
+ writer.append( text, paragraph );
+ writer.append( paragraph, model.document.getRoot() );
+
+ writer.setSelection( paragraph, 1 );
+ } );
+
+ command.execute( {
+ marker: '@',
+ mention: '@John'
+ } );
+
+ assertMention( doc.getRoot().getChild( 0 ).getChild( 1 ), '@John' );
+
+ expect( getData( model ) ).to.match(
+ new RegExp( '\\' + openingBracket +
+ '<\\$text mention="{"uid":"[^"]+","_text":"@John","id":"@John"}">@John\\[\\]\\$text>\\' +
+ closingBracket + ''
+ )
+ );
+ } );
+ } );
} );
function assertMention( textNode, id ) {