Browse Source

Feature: Introduced DocumentSelection#markers Collection.

Oskar Wróbel 7 years ago
parent
commit
03c41a7e5b

+ 45 - 0
packages/ckeditor5-engine/src/model/documentselection.js

@@ -15,6 +15,7 @@ import LiveRange from './liverange';
 import Text from './text';
 import TextProxy from './textproxy';
 import toMap from '@ckeditor/ckeditor5-utils/src/tomap';
+import Collection from '@ckeditor/ckeditor5-utils/src/collection';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import log from '@ckeditor/ckeditor5-utils/src/log';
 import uid from '@ckeditor/ckeditor5-utils/src/uid';
@@ -150,6 +151,17 @@ export default class DocumentSelection {
 	}
 
 	/**
+	 * A collection of selection markers.
+	 * Marker is a selection marker when selection range is inside the marker range.
+	 *
+	 * @readonly
+	 * @type {module:utils/collection~Collection.<module:engine/model/markercollection~Marker>}
+	 */
+	get markers() {
+		return this._selection.markers;
+	}
+
+	/**
 	 * Used for the compatibility with the {@link module:engine/model/selection~Selection#isEqual} method.
 	 *
 	 * @protected
@@ -526,6 +538,12 @@ class LiveSelection extends Selection {
 	constructor( doc ) {
 		super();
 
+		// List of selection markers.
+		// Marker is a selection marker when selection range is inside the marker range.
+		//
+		// @type {module:utils/collection~Collection}
+		this.markers = new Collection( { idProperty: 'name' } );
+
 		// Document which owns this selection.
 		//
 		// @protected
@@ -586,6 +604,9 @@ class LiveSelection extends Selection {
 		} );
 
 		this.listenTo( this._document, 'change', ( evt, batch ) => {
+			// Update selection's markers.
+			this._updateMarkers();
+
 			// Update selection's attributes.
 			this._updateAttributes( false );
 
@@ -788,6 +809,30 @@ class LiveSelection extends Selection {
 		return liveRange;
 	}
 
+	_updateMarkers() {
+		const markers = [];
+
+		for ( const marker of this._model.markers ) {
+			for ( const selectionRange of this.getRanges() ) {
+				if ( marker.getRange().containsRange( selectionRange ) ) {
+					markers.push( marker );
+				}
+			}
+		}
+
+		for ( const marker of markers ) {
+			if ( this.markers.getIndex( marker ) < 0 ) {
+				this.markers.add( marker );
+			}
+		}
+
+		for ( const marker of Array.from( this.markers ) ) {
+			if ( !markers.includes( marker ) ) {
+				this.markers.remove( marker );
+			}
+		}
+	}
+
 	// Updates this selection attributes according to its ranges and the {@link module:engine/model/document~Document model document}.
 	//
 	// @protected

+ 152 - 0
packages/ckeditor5-engine/tests/model/documentselection.js

@@ -16,6 +16,7 @@ import MoveOperation from '../../src/model/operation/moveoperation';
 import AttributeOperation from '../../src/model/operation/attributeoperation';
 import SplitOperation from '../../src/model/operation/splitoperation';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
+import Collection from '@ckeditor/ckeditor5-utils/src/collection';
 import count from '@ckeditor/ckeditor5-utils/src/count';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 import { setData, getData } from '../../src/dev-utils/model';
@@ -197,6 +198,157 @@ describe( 'DocumentSelection', () => {
 		} );
 	} );
 
+	describe( 'markers', () => {
+		it( 'should implement #markers collection', () => {
+			expect( selection.markers ).to.instanceof( Collection );
+			expect( selection.markers ).to.length( 0 );
+		} );
+
+		it( 'should add markers to the collection when selection is inside the marker range', () => {
+			model.change( writer => {
+				writer.setSelection( writer.createRange(
+					writer.createPositionFromPath( root, [ 2, 2 ] ),
+					writer.createPositionFromPath( root, [ 2, 4 ] )
+				) );
+
+				writer.addMarker( 'marker-1', {
+					range: writer.createRange(
+						writer.createPositionFromPath( root, [ 0, 0 ] ),
+						writer.createPositionFromPath( root, [ 2, 2 ] )
+					),
+					usingOperation: false
+				} );
+
+				writer.addMarker( 'marker-2', {
+					range: writer.createRange(
+						writer.createPositionFromPath( root, [ 2, 2 ] ),
+						writer.createPositionFromPath( root, [ 2, 4 ] )
+					),
+					usingOperation: false
+				} );
+
+				writer.addMarker( 'marker-3', {
+					range: writer.createRange(
+						writer.createPositionFromPath( root, [ 2, 1 ] ),
+						writer.createPositionFromPath( root, [ 2, 5 ] )
+					),
+					usingOperation: false
+				} );
+
+				writer.addMarker( 'marker-4', {
+					range: writer.createRange(
+						writer.createPositionFromPath( root, [ 2, 4 ] ),
+						writer.createPositionFromPath( root, [ 3, 0 ] )
+					),
+					usingOperation: false
+				} );
+			} );
+
+			expect( selection.markers.map( marker => marker.name ) ).to.have.members( [ 'marker-3' ] );
+		} );
+
+		it( 'should update markers after selection change', () => {
+			model.change( writer => {
+				writer.setSelection( writer.createRange(
+					writer.createPositionFromPath( root, [ 2, 1 ] ),
+					writer.createPositionFromPath( root, [ 2, 2 ] )
+				) );
+
+				writer.addMarker( 'marker-1', {
+					range: writer.createRange(
+						writer.createPositionFromPath( root, [ 2, 0 ] ),
+						writer.createPositionFromPath( root, [ 2, 6 ] )
+					),
+					usingOperation: false
+				} );
+
+				writer.addMarker( 'marker-2', {
+					range: writer.createRange(
+						writer.createPositionFromPath( root, [ 2, 0 ] ),
+						writer.createPositionFromPath( root, [ 2, 3 ] )
+					),
+					usingOperation: false
+				} );
+
+				writer.addMarker( 'marker-3', {
+					range: writer.createRange(
+						writer.createPositionFromPath( root, [ 2, 3 ] ),
+						writer.createPositionFromPath( root, [ 2, 6 ] )
+					),
+					usingOperation: false
+				} );
+			} );
+
+			expect( selection.markers.map( marker => marker.name ) ).to.have.members( [ 'marker-1', 'marker-2' ] );
+
+			model.change( writer => {
+				writer.setSelection( writer.createRange(
+					writer.createPositionFromPath( root, [ 2, 4 ] ),
+					writer.createPositionFromPath( root, [ 2, 5 ] )
+				) );
+			} );
+
+			expect( selection.markers.map( marker => marker.name ) ).to.have.members( [ 'marker-1', 'marker-3' ] );
+		} );
+
+		it( 'should update markers after markers change', () => {
+			model.change( writer => {
+				writer.setSelection( writer.createRange(
+					writer.createPositionFromPath( root, [ 2, 1 ] ),
+					writer.createPositionFromPath( root, [ 2, 2 ] )
+				) );
+
+				writer.addMarker( 'marker-1', {
+					range: writer.createRange(
+						writer.createPositionFromPath( root, [ 2, 0 ] ),
+						writer.createPositionFromPath( root, [ 2, 6 ] )
+					),
+					usingOperation: false
+				} );
+
+				writer.addMarker( 'marker-2', {
+					range: writer.createRange(
+						writer.createPositionFromPath( root, [ 2, 0 ] ),
+						writer.createPositionFromPath( root, [ 2, 3 ] )
+					),
+					usingOperation: false
+				} );
+
+				writer.addMarker( 'marker-3', {
+					range: writer.createRange(
+						writer.createPositionFromPath( root, [ 2, 3 ] ),
+						writer.createPositionFromPath( root, [ 2, 6 ] )
+					),
+					usingOperation: false
+				} );
+			} );
+
+			expect( selection.markers.map( marker => marker.name ), 1 ).to.have.members( [ 'marker-1', 'marker-2' ] );
+
+			model.change( writer => {
+				writer.removeMarker( 'marker-1' );
+
+				writer.updateMarker( 'marker-2', {
+					range: writer.createRange(
+						writer.createPositionFromPath( root, [ 2, 3 ] ),
+						writer.createPositionFromPath( root, [ 2, 6 ] )
+					),
+					usingOperation: false
+				} );
+
+				writer.updateMarker( 'marker-3', {
+					range: writer.createRange(
+						writer.createPositionFromPath( root, [ 2, 0 ] ),
+						writer.createPositionFromPath( root, [ 2, 3 ] )
+					),
+					usingOperation: false
+				} );
+			} );
+
+			expect( selection.markers.map( marker => marker.name ), 2 ).to.have.members( [ 'marker-3' ] );
+		} );
+	} );
+
 	describe( 'destroy()', () => {
 		it( 'should unbind all events', () => {
 			selection._setTo( [ range, liveRange ] );