Skip to content

Commit

Permalink
Update getSelectedBlocks and jumpTo
Browse files Browse the repository at this point in the history
  • Loading branch information
filipsobol committed Dec 11, 2024
1 parent 476e12d commit 97250c3
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 14 deletions.
20 changes: 8 additions & 12 deletions packages/ckeditor5-engine/src/model/selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -674,18 +674,14 @@ export default class Selection extends /* #__PURE__ */ EmitterMixin( TypeCheckab

if ( value.type == 'elementEnd' && isUnvisitedTopBlock( block as any, visited, range ) ) {
yield block as Element;
} else if ( block.is( 'model:element' ) && block.root.document!.model.schema.isBlock( block ) ) {
if ( value.type == 'elementEnd' ) {
treewalker.jumpTo( value.nextPosition );
} else {
const position = treewalker.position.clone();

position.offset = block.maxOffset;

if ( range.containsPosition( position ) ) {
treewalker.jumpTo( position );
}
}
}
// If element is block, we can skip its children and jump to the end of it.
else if (
value.type == 'elementStart' &&
block.is( 'model:element' ) &&
block.root.document!.model.schema.isBlock( block )
) {
treewalker.jumpTo( Position._createAt( block, 'end' ) );
}
}

Expand Down
16 changes: 14 additions & 2 deletions packages/ckeditor5-engine/src/model/treewalker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,23 @@ export default class TreeWalker implements Iterable<TreeWalkerValue> {
}

/**
* Moves {@link #position} to provided position.
* Moves treewalker {@link #position} to provided `position`. Treewalker will
* continue traversing from that position.
*
* @param position Position to jump to
* If the provided position is before the start boundary, the position will be
* set to the start boundary. If the provided position is after the end boundary,
* the position will be set to the end boundary.
* This is done to prevent the treewalker from traversing outside the boundaries.
*
* @param position Position to jump to.
*/
public jumpTo( position: Position ): void {
if ( this._boundaryStartParent && position.isBefore( this.boundaries!.start ) ) {
position = this.boundaries!.start;
} else if ( this._boundaryEndParent && position.isAfter( this.boundaries!.end ) ) {
position = this.boundaries!.end;
}

this._position = position;
this._visitedParent = position.parent;
}
Expand Down
21 changes: 21 additions & 0 deletions packages/ckeditor5-engine/src/view/treewalker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,27 @@ export default class TreeWalker implements IterableIterator<TreeWalkerValue> {
}
}

/**
* Moves treewalker {@link #position} to provided `position`. Treewalker will
* continue traversing from that position.
*
* If the provided position is before the start boundary, the position will be
* set to the start boundary. If the provided position is after the end boundary,
* the position will be set to the end boundary.
* This is done to prevent the treewalker from traversing outside the boundaries.
*
* @param position Position to jump to.
*/
public jumpTo( position: Position ): void {
if ( this._boundaryStartParent && position.isBefore( this.boundaries!.start ) ) {
position = this.boundaries!.start;
} else if ( this._boundaryEndParent && position.isAfter( this.boundaries!.end ) ) {
position = this.boundaries!.end;
}

this._position = position;
}

/**
* Gets the next tree walker's value.
*
Expand Down
46 changes: 46 additions & 0 deletions packages/ckeditor5-engine/tests/model/treewalker.js
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,52 @@ describe( 'TreeWalker', () => {
expect( walker.position.parent ).to.equal( paragraph );
expect( walker.position.offset ).to.equal( 3 );
} );

it( 'cannot move position before the #_boundaryStartParent', () => {
const range = new Range(
new Position( paragraph, [ 2 ] ),
new Position( paragraph, [ 4 ] )
);
const walker = new TreeWalker( {
boundaries: range
} );

const positionBeforeAllowedRange = new Position( paragraph, [ 0 ] );

walker.jumpTo( positionBeforeAllowedRange );

// `jumpTo()` autocorrected the position to the first allowed position.
expect( walker.position.parent ).to.equal( paragraph );
expect( walker.position.offset ).to.equal( 2 );

walker.next();

expect( walker.position.parent ).to.equal( paragraph );
expect( walker.position.offset ).to.equal( 3 );
} );

it( 'cannot move position after the #_boundaryStartParent', () => {
const range = new Range(
new Position( paragraph, [ 0 ] ),
new Position( paragraph, [ 2 ] )
);
const walker = new TreeWalker( {
boundaries: range
} );

const positionAfterAllowedRange = new Position( paragraph, [ 4 ] );

// `jumpTo()` autocorrected the position to the last allowed position.
walker.jumpTo( positionAfterAllowedRange );

expect( walker.position.parent ).to.equal( paragraph );
expect( walker.position.offset ).to.equal( 2 );

walker.next();

expect( walker.position.parent ).to.equal( paragraph );
expect( walker.position.offset ).to.equal( 2 );
} );
} );
} );

Expand Down
64 changes: 64 additions & 0 deletions packages/ckeditor5-engine/tests/view/treewalker.js
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,70 @@ describe( 'TreeWalker', () => {
} );
} );
} );

describe( 'jumpTo', () => {
it( 'should jump to the given position', () => {
const walker = new TreeWalker( {
startPosition: Position._createAt( paragraph, 0 )
} );

walker.jumpTo( new Position( paragraph, 2 ) );

expect( walker.position.parent ).to.equal( paragraph );
expect( walker.position.offset ).to.equal( 2 );

walker.next();

expect( walker.position.parent ).to.equal( img2 );
expect( walker.position.offset ).to.equal( 0 );
} );

it( 'cannot move position before the #_boundaryStartParent', () => {
const range = new Range(
new Position( paragraph, 2 ),
new Position( paragraph, 4 )
);
const walker = new TreeWalker( {
boundaries: range
} );

const positionBeforeAllowedRange = new Position( paragraph, 0 );

walker.jumpTo( positionBeforeAllowedRange );

// `jumpTo()` autocorrected the position to the first allowed position.
expect( walker.position.parent ).to.equal( paragraph );
expect( walker.position.offset ).to.equal( 2 );

walker.next();

expect( walker.position.parent ).to.equal( img2 );
expect( walker.position.offset ).to.equal( 0 );
} );

it( 'cannot move position after the #_boundaryStartParent', () => {
const range = new Range(
new Position( paragraph, 0 ),
new Position( paragraph, 2 )
);
const walker = new TreeWalker( {
boundaries: range
} );

const positionAfterAllowedRange = new Position( paragraph, 4 );

// `jumpTo()` autocorrected the position to the last allowed position.
walker.jumpTo( positionAfterAllowedRange );

expect( walker.position.parent ).to.equal( paragraph );
expect( walker.position.offset ).to.equal( 2 );

walker.next();

expect( walker.position.parent ).to.equal( paragraph );
expect( walker.position.offset ).to.equal( 2 );
} );
} );
} );

function expectValue( value, expected, options = {} ) {
Expand Down

0 comments on commit 97250c3

Please sign in to comment.