8
0
فهرست منبع

Merge pull request #71 from ckeditor/t/38

Fix: Mentions should work when different UTF character classes are used in the feed configuration. Closes #38.
Aleksander Nowodzinski 6 سال پیش
والد
کامیت
518a98a0b1

+ 7 - 6
packages/ckeditor5-mention/src/featuredetection.js

@@ -15,21 +15,22 @@
  */
  */
 export default {
 export default {
 	/**
 	/**
-	 * Indicates whether the current browser supports ES2018 Unicode punctuation groups `\p{P}`.
+	 * Indicates whether the current browser supports ES2018 Unicode groups like `\p{P}` or `\p{L}`.
 	 *
 	 *
 	 * @type {Boolean}
 	 * @type {Boolean}
 	 */
 	 */
-	isPunctuationGroupSupported: ( function() {
-		let punctuationSupported = false;
-		// Feature detection for Unicode punctuation groups. It's added in ES2018. Currently Firefox and Edge does not support it.
+	isUnicodeGroupSupported: ( function() {
+		let isSupported = false;
+
+		// Feature detection for Unicode groups. Added in ES2018. Currently Firefox and Edge do not support it.
 		// See https://github.com/ckeditor/ckeditor5-mention/issues/44#issuecomment-487002174.
 		// See https://github.com/ckeditor/ckeditor5-mention/issues/44#issuecomment-487002174.
 
 
 		try {
 		try {
-			punctuationSupported = '.'.search( new RegExp( '[\\p{P}]', 'u' ) ) === 0;
+			isSupported = 'ć'.search( new RegExp( '[\\p{L}]', 'u' ) ) === 0;
 		} catch ( error ) {
 		} catch ( error ) {
 			// Firefox throws a SyntaxError when the group is unsupported.
 			// Firefox throws a SyntaxError when the group is unsupported.
 		}
 		}
 
 
-		return punctuationSupported;
+		return isSupported;
 	}() )
 	}() )
 };
 };

+ 13 - 12
packages/ckeditor5-mention/src/mentionui.js

@@ -532,19 +532,20 @@ function getBalloonPanelPositions( preferredPosition ) {
 // @returns {RegExp}
 // @returns {RegExp}
 export function createRegExp( marker, minimumCharacters ) {
 export function createRegExp( marker, minimumCharacters ) {
 	const numberOfCharacters = minimumCharacters == 0 ? '*' : `{${ minimumCharacters },}`;
 	const numberOfCharacters = minimumCharacters == 0 ? '*' : `{${ minimumCharacters },}`;
-	const patternBase = featureDetection.isPunctuationGroupSupported ? '\\p{Ps}\\p{Pi}"\'' : '\\(\\[{"\'';
 
 
-	return new RegExp( buildPattern( patternBase, marker, numberOfCharacters ), 'u' );
-}
+	const openAfterCharacters = featureDetection.isUnicodeGroupSupported ? '\\p{Ps}\\p{Pi}"\'' : '\\(\\[{"\'';
+	const mentionCharacters = featureDetection.isUnicodeGroupSupported ? '\\p{L}\\p{N}' : 'a-zA-ZÀ-ž0-9';
 
 
-// Helper to build a RegExp pattern string for the marker.
-//
-// @param {String} whitelistedCharacters
-// @param {String} marker
-// @param {Number} minimumCharacters
-// @returns {String}
-function buildPattern( whitelistedCharacters, marker, numberOfCharacters ) {
-	return `(^|[ ${ whitelistedCharacters }])([${ marker }])([_a-zA-Z0-9À-ž]${ numberOfCharacters }?)$`;
+	// The pattern consists of 3 groups:
+	// - 0 (non-capturing): Opening sequence - start of the line, space or an opening punctuation character like "(" or "\"",
+	// - 1: The marker character,
+	// - 2: Mention input (taking the minimal length into consideration to trigger the UI),
+	//
+	// The pattern matches up to the caret (end of string switch - $).
+	//               (0:      opening sequence       )(1:  marker   )(2:                typed mention                 )$
+	const pattern = `(?:^|[ ${ openAfterCharacters }])([${ marker }])([_${ mentionCharacters }]${ numberOfCharacters })$`;
+
+	return new RegExp( pattern, 'u' );
 }
 }
 
 
 // Creates a test callback for the marker to be used in the text watcher instance.
 // Creates a test callback for the marker to be used in the text watcher instance.
@@ -567,7 +568,7 @@ function getFeedText( marker, text ) {
 
 
 	const match = text.match( regExp );
 	const match = text.match( regExp );
 
 
-	return match[ 3 ];
+	return match[ 2 ];
 }
 }
 
 
 // The default feed callback.
 // The default feed callback.

+ 41 - 8
packages/ckeditor5-mention/tests/mentionui.js

@@ -452,10 +452,10 @@ describe( 'MentionUI', () => {
 			let regExpStub;
 			let regExpStub;
 
 
 			// Cache the original value to restore it after the tests.
 			// Cache the original value to restore it after the tests.
-			const originalPunctuationSupport = featureDetection.isPunctuationGroupSupported;
+			const originalGroupSupport = featureDetection.isUnicodeGroupSupported;
 
 
 			before( () => {
 			before( () => {
-				featureDetection.isPunctuationGroupSupported = false;
+				featureDetection.isUnicodeGroupSupported = false;
 			} );
 			} );
 
 
 			beforeEach( () => {
 			beforeEach( () => {
@@ -468,21 +468,21 @@ describe( 'MentionUI', () => {
 			} );
 			} );
 
 
 			after( () => {
 			after( () => {
-				featureDetection.isPunctuationGroupSupported = originalPunctuationSupport;
+				featureDetection.isUnicodeGroupSupported = originalGroupSupport;
 			} );
 			} );
 
 
 			it( 'returns a simplified RegExp for browsers not supporting Unicode punctuation groups', () => {
 			it( 'returns a simplified RegExp for browsers not supporting Unicode punctuation groups', () => {
-				featureDetection.isPunctuationGroupSupported = false;
+				featureDetection.isUnicodeGroupSupported = false;
 				createRegExp( '@', 2 );
 				createRegExp( '@', 2 );
 				sinon.assert.calledOnce( regExpStub );
 				sinon.assert.calledOnce( regExpStub );
-				sinon.assert.calledWithExactly( regExpStub, '(^|[ \\(\\[{"\'])([@])([_a-zA-Z0-9À-ž]{2,}?)$', 'u' );
+				sinon.assert.calledWithExactly( regExpStub, '(?:^|[ \\(\\[{"\'])([@])([_a-zA-ZÀ-ž0-9]{2,})$', 'u' );
 			} );
 			} );
 
 
 			it( 'returns a ES2018 RegExp for browsers supporting Unicode punctuation groups', () => {
 			it( 'returns a ES2018 RegExp for browsers supporting Unicode punctuation groups', () => {
-				featureDetection.isPunctuationGroupSupported = true;
+				featureDetection.isUnicodeGroupSupported = true;
 				createRegExp( '@', 2 );
 				createRegExp( '@', 2 );
 				sinon.assert.calledOnce( regExpStub );
 				sinon.assert.calledOnce( regExpStub );
-				sinon.assert.calledWithExactly( regExpStub, '(^|[ \\p{Ps}\\p{Pi}"\'])([@])([_a-zA-Z0-9À-ž]{2,}?)$', 'u' );
+				sinon.assert.calledWithExactly( regExpStub, '(?:^|[ \\p{Ps}\\p{Pi}"\'])([@])([_\\p{L}\\p{N}]{2,})$', 'u' );
 			} );
 			} );
 		} );
 		} );
 
 
@@ -567,7 +567,7 @@ describe( 'MentionUI', () => {
 				// Belongs to Pi (Punctuation, Initial quote) group:
 				// Belongs to Pi (Punctuation, Initial quote) group:
 				'«', '‹', '⸌', ' ⸂', '⸠'
 				'«', '‹', '⸌', ' ⸂', '⸠'
 			] ) {
 			] ) {
-				testOpeningPunctuationCharacter( character, !featureDetection.isPunctuationGroupSupported );
+				testOpeningPunctuationCharacter( character, !featureDetection.isUnicodeGroupSupported );
 			}
 			}
 
 
 			it( 'should not show panel for marker in the middle of other word', () => {
 			it( 'should not show panel for marker in the middle of other word', () => {
@@ -823,6 +823,39 @@ describe( 'MentionUI', () => {
 			} );
 			} );
 		} );
 		} );
 
 
+		describe( 'unicode', () => {
+			beforeEach( () => {
+				return createClassicTestEditor( {
+					feeds: [
+						{
+							// Always return 5 items
+							feed: [ '@תַפּוּחַ', '@אַגָס', '@apple', '@pear' ],
+							marker: '@'
+						}
+					]
+				} );
+			} );
+
+			it( 'should open panel for unicode character ב', function() {
+				if ( !featureDetection.isUnicodeGroupSupported ) {
+					this.skip();
+				}
+
+				setData( model, '<paragraph>foo []</paragraph>' );
+
+				model.change( writer => {
+					writer.insertText( '@ס', doc.selection.getFirstPosition() );
+				} );
+
+				return waitForDebounce()
+					.then( () => {
+						expect( panelView.isVisible, 'panel is visible' ).to.be.true;
+						expect( editor.model.markers.has( 'mention' ), 'marker is inserted' ).to.be.true;
+						expect( mentionsView.items ).to.have.length( 1 );
+					} );
+			} );
+		} );
+
 		describe( 'asynchronous list with custom trigger', () => {
 		describe( 'asynchronous list with custom trigger', () => {
 			beforeEach( () => {
 			beforeEach( () => {
 				const issuesNumbers = [ '#100', '#101', '#102', '#103' ];
 				const issuesNumbers = [ '#100', '#101', '#102', '#103' ];