Skip to content

Commit

Permalink
Merge pull request #15867 from ckeditor/ck/4651-mention-and-following…
Browse files Browse the repository at this point in the history
…-spaces

Fix (mention): Inserting a mention should not append an extra white space if there was one already present in the content. A white space should not follow a mention inserted inside a pair of the empty matching brackets. Closes #4651.
  • Loading branch information
Dumluregn committed Feb 20, 2024
2 parents 9a14ebf + e54a9c8 commit 048a6ce
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 3 deletions.
32 changes: 30 additions & 2 deletions packages/ckeditor5-mention/src/mentioncommand.ts
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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 ) );
}
} );
}
}
45 changes: 44 additions & 1 deletion packages/ckeditor5-mention/tests/mentioncommand.js
Expand Up @@ -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';

Expand Down Expand Up @@ -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, '<paragraph>foo [] bar</paragraph>' );

command.execute( {
marker: '@',
mention: '@John'
} );

assertMention( doc.getRoot().getChild( 0 ).getChild( 1 ), '@John' );

expect( getData( model ) ).to.match(
/<paragraph>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( '<paragraph>\\' + openingBracket +
'<\\$text mention="{"uid":"[^"]+","_text":"@John","id":"@John"}">@John\\[\\]</\\$text>\\' +
closingBracket + '</paragraph>'
)
);
} );
} );
} );

function assertMention( textNode, id ) {
Expand Down

0 comments on commit 048a6ce

Please sign in to comment.