Jelajahi Sumber

Introduced clearAttributes().

Piotr Jasiun 9 tahun lalu
induk
melakukan
e82d3bf6f7

+ 55 - 9
packages/ckeditor5-engine/src/treecontroller/model-selection-to-view-converters.js

@@ -12,7 +12,7 @@ import ViewRange from '../treeview/range.js';
  * Contains {@link engine.treeModel.Selection model selection} to {@link engine.treeView.Selection view selection} converters for
  * {@link engine.treeController.ModelConversionDispatcher}.
  *
- * @namespace engine.treeController.selectionToView
+ * @namespace engine.treeController.modelSelectionToView
  */
 
 /**
@@ -22,8 +22,8 @@ import ViewRange from '../treeview/range.js';
  *
  *		modelDispatcher.on( 'selection', convertRangeSelection() );
  *
- * @external engine.treeController.selectionToView
- * @function engine.treeController.selectionToView.convertRangeSelection
+ * @external engine.treeController.modelSelectionToView
+ * @function engine.treeController.modelSelectionToView.convertRangeSelection
  * @returns {Function} Selection converter.
  */
 export function convertRangeSelection() {
@@ -38,6 +38,8 @@ export function convertRangeSelection() {
 			return;
 		}
 
+		conversionApi.viewSelection.removeAllRanges();
+
 		for ( let range of selection.getRanges() ) {
 			const viewRange = conversionApi.mapper.toViewRange( range );
 			conversionApi.viewSelection.addRange( viewRange, selection.isBackward );
@@ -55,15 +57,18 @@ export function convertRangeSelection() {
  *
  * Example of view state before and after converting collapsed selection:
  *
- *		<p><strong>f^oo<strong>bar</p> -> <p><strong>f</strong>^<strong>oo</strong>bar</p>
+ *		<p><strong>f^oo<strong>bar</p>
+ *		-> <p><strong>f</strong>^<strong>oo</strong>bar</p>
  *
  * By breaking attribute elements like `<strong>` selection is in correct elements. See also complementary
- * {@link engine.treeController.selectionToView.convertSelectionAttribute attribute converter} for selection attributes,
+ * {@link engine.treeController.modelSelectionToView.convertSelectionAttribute attribute converter} for selection attributes,
  * which wraps collapsed selection into view elements. Those converters together ensure, that selection ends up in
  * appropriate elements.
  *
- * @external engine.treeController.selectionToView
- * @function engine.treeController.selectionToView.convertCollapsedSelection
+ * See also {@link engine.treeController.modelSelectionToView.clearAttributes} which do the clean-up by merging attributes.
+ *
+ * @external engine.treeController.modelSelectionToView
+ * @function engine.treeController.modelSelectionToView.convertCollapsedSelection
  * @returns {Function} Selection converter.
  */
 export function convertCollapsedSelection() {
@@ -82,6 +87,7 @@ export function convertCollapsedSelection() {
 		const viewPosition = conversionApi.mapper.toViewPosition( modelPosition );
 		const brokenPosition = conversionApi.writer.breakAttributes( viewPosition );
 
+		conversionApi.viewSelection.removeAllRanges();
 		conversionApi.viewSelection.addRange( new ViewRange( brokenPosition, brokenPosition ), selection.isBackward );
 	};
 }
@@ -132,8 +138,8 @@ export function convertCollapsedSelection() {
  * but then it got wrapped-back by `convertSelectionAttribute()` converter. In second example, notice how `<strong>` element
  * is broken to prevent putting selection in it, since selection has no `bold` attribute.
  *
- * @external engine.treeController.selectionToView
- * @function engine.treeController.selectionToView.convertCollapsedSelection
+ * @external engine.treeController.modelSelectionToView
+ * @function engine.treeController.modelSelectionToView.convertCollapsedSelection
  * @param {engine.treeView.AttributeElement|Function} elementCreator View element, or function returning a view element, which will
  * be used for wrapping.
  * @returns {Function} Selection converter.
@@ -162,3 +168,43 @@ export function convertSelectionAttribute( elementCreator ) {
 		conversionApi.viewSelection.addRange( new ViewRange( viewPosition, viewPosition ), selection.isBackward );
 	};
 }
+
+/**
+ * Function factory, creates a converter that clears artifacts after the previous
+ * {@link engine.treeModel.Selection model selection} conversion. It removes all empty
+ * {@link engine.treeView.AttributeElement view attribute elements} and merge sibling attributes at all start and end
+ * positions of all ranges.
+ *
+ *		<p><strong>^</strong></p>
+ *		-> <p>^</p>
+ *
+ *		<p><strong>foo</strong>^<strong>bar</strong>bar</p>
+ *		-> <p><strong>foo^bar<strong>bar</p>
+ *
+ *		<p><strong>foo</strong><em>^</em><strong>bar</strong>bar</p>
+ *		-> <p><strong>foo^bar<strong>bar</p>
+ *
+ * This listener should be assigned before any converter for the new selection:
+ *
+ *		modelDispatcher.on( 'selection', clearAttributes() );
+ *
+ * See {@link engine.treeController.modelSelectionToView.convertCollapsedSelection} which do the opposite by breaking
+ * attributes in the selection position.
+ *
+ * @external engine.treeController.modelSelectionToView
+ * @function engine.treeController.modelSelectionToView.clearAttributes
+ * @returns {Function} Selection converter.
+ */
+export function clearAttributes() {
+	return ( evt, data, consumable, conversionApi ) => {
+		for ( let range of conversionApi.viewSelection.getRanges() ) {
+			const parentNode = range.start.parent;
+
+			if ( parentNode instanceof ViewElement && parentNode.getChildCount() === 0 ) {
+				parentNode.parent.removeChildren( parentNode.getIndex() );
+			}
+		}
+
+		conversionApi.viewSelection.removeAllRanges();
+	};
+}

+ 8 - 20
packages/ckeditor5-engine/tests/treecontroller/model-converter-builder.js

@@ -21,7 +21,6 @@ import ViewAttributeElement from '/ckeditor5/engine/treeview/attributeelement.js
 import ViewText from '/ckeditor5/engine/treeview/text.js';
 import ViewWriter from '/ckeditor5/engine/treeview/writer.js';
 import ViewSelection from '/ckeditor5/engine/treeview/selection.js';
-import ViewRange from '/ckeditor5/engine/treeview/range.js';
 
 import Mapper from '/ckeditor5/engine/treecontroller/mapper.js';
 import ModelConversionDispatcher from '/ckeditor5/engine/treecontroller/modelconversiondispatcher.js';
@@ -32,6 +31,11 @@ import {
 	remove
 } from '/ckeditor5/engine/treecontroller/model-to-view-converters.js';
 
+import {
+	convertCollapsedSelection,
+	clearAttributes
+} from '/ckeditor5/engine/treecontroller/model-selection-to-view-converters.js';
+
 function viewAttributesToString( item ) {
 	let result = '';
 
@@ -195,25 +199,9 @@ describe( 'Model converter builder', () => {
 		} );
 
 		it( 'selection conversion', () => {
-			// This test requires collapsed range selection converter (breaking attributes), clearing view selection
-			// and clearing "artifacts" (empty nodes) before new conversion happens.
-			dispatcher.on( 'selection', ( evt, data, consumable, conversionApi ) => {
-				// Clear artifacts.
-				for ( let range of conversionApi.viewSelection.getRanges() ) {
-					const parentNode = range.start.parent;
-
-					if ( parentNode instanceof ViewElement && parentNode.getChildCount() === 0 ) {
-						parentNode.parent.removeChildren( parentNode.getIndex() );
-					}
-				}
-
-				// Break attributes.
-				const viewPosition = conversionApi.mapper.toViewPosition( data.selection.getFirstPosition() );
-				const brokenPosition = conversionApi.writer.breakAttributes( viewPosition );
-
-				// Set the new range as the only range of selection (clear old ranges).
-				conversionApi.viewSelection.setRanges( [ new ViewRange( brokenPosition, brokenPosition ) ], data.selection.isBackward );
-			} );
+			// This test requires collapsed range selection converter (breaking attributes)  and clearing "artifacts".
+			dispatcher.on( 'selection', clearAttributes() );
+			dispatcher.on( 'selection', convertCollapsedSelection() );
 
 			// Model converter builder should add selection converter.
 			BuildModelConverterFor( dispatcher ).fromAttribute( 'italic' ).toElement( ( value ) => new ViewAttributeElement( value ) );