Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ck/10146 find in multiroot #10200

Merged
merged 7 commits into from
Jul 27, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/_snippets/examples/multi-root-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
import Heading from '@ckeditor/ckeditor5-heading/src/heading';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
import FindAndReplace from '@ckeditor/ckeditor5-find-and-replace/src/findandreplace';
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
import List from '@ckeditor/ckeditor5-list/src/list';
import Link from '@ckeditor/ckeditor5-link/src/link';
Expand Down Expand Up @@ -391,11 +392,11 @@ MultirootEditor
}, {
plugins: [
Essentials, Paragraph, Heading, Bold, Italic, List, Link, BlockQuote, Image, ImageCaption,
ImageStyle, ImageToolbar, ImageUpload, Table, TableToolbar, MediaEmbed, EasyImage, CloudServices
ImageStyle, ImageToolbar, ImageUpload, Table, TableToolbar, MediaEmbed, EasyImage, CloudServices, FindAndReplace
],
toolbar: [
'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'uploadImage', 'blockQuote',
'insertTable', 'mediaEmbed', 'undo', 'redo' ],
'insertTable', 'mediaEmbed', 'findAndReplace', 'undo', 'redo' ],
image: {
toolbar: [
'imageStyle:inline', 'imageStyle:block', 'imageStyle:side', '|', 'toggleImageCaption', 'imageTextAlternative'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export default class FindAndReplaceUI extends Plugin {
this.formView = formView;

editor.keystrokes.set( 'Ctrl+F', ( data, cancelEvent ) => {
dropdown.buttonView.actionView.fire( 'execute' );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been fixed couple of days ago #10189 I'll merge the latest master branch.

dropdown.buttonView.fire( 'execute' );

cancelEvent();
} );
Expand Down
24 changes: 14 additions & 10 deletions packages/ckeditor5-find-and-replace/src/findcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,18 @@ export default class FindCommand extends Command {
findCallback = callbackOrText;
}

// Initial search is done on all nodes inside the content.
const range = model.createRangeIn( model.document.getRoot() );

const ret = {
results: updateFindResultFromRange( range, model, findCallback ),
findCallback
};
// Initial search is done on all nodes in all roots inside the content.
const results = model.document.getRootNames()
.reduce( ( ( currentResults, rootName ) => updateFindResultFromRange(
model.createRangeIn( model.document.getRoot( rootName ) ),
model,
findCallback,
currentResults
) ), null );

this.state.clear( model );
this.state.results.addMany( Array.from( ret.results ) );
this.state.highlightedResult = ret.results.get( 0 );
this.state.results.addMany( Array.from( results ) );
this.state.highlightedResult = results.get( 0 );

if ( typeof callbackOrText === 'string' ) {
this.state.searchText = callbackOrText;
Expand All @@ -80,6 +81,9 @@ export default class FindCommand extends Command {
this.state.matchCase = !!matchCase;
this.state.matchWholeWords = !!wholeWords;

return ret;
return {
results,
findCallback
};
}
}
11 changes: 8 additions & 3 deletions packages/ckeditor5-find-and-replace/src/replaceallcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,18 @@ export default class ReplaceAllCommand extends ReplaceCommand {
execute( newText, textToReplace ) {
const { editor } = this;
const { model } = editor;
const range = model.createRangeIn( model.document.getRoot() );

const results = textToReplace instanceof Collection ?
textToReplace : updateFindResultFromRange( range, model, findByTextCallback( textToReplace, this._state ) );
textToReplace : model.document.getRootNames()
.reduce( ( ( currentResults, rootName ) => updateFindResultFromRange(
model.createRangeIn( model.document.getRoot( rootName ) ),
model,
findByTextCallback( textToReplace, this._state ),
currentResults
) ), null );

if ( results.length ) {
this.editor.model.change( () => {
model.change( () => {
[ ...results ].forEach( searchResult => {
// Just reuse logic from the replace command to replace a single match.
super.execute( newText, searchResult );
Expand Down
51 changes: 51 additions & 0 deletions packages/ckeditor5-find-and-replace/tests/findcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,57 @@ describe( 'FindCommand', () => {
expect( results.length ).to.equal( 1 );
} );
} );

describe( 'in multi-root editor', () => {
let multiRootEditor, multiRootModel;

class MultiRootEditor extends ModelTestEditor {
constructor( config ) {
super( config );

this.model.document.createRoot( '$root', 'second' );
}
}

beforeEach( async () => {
multiRootEditor = await MultiRootEditor.create( { plugins: [ FindAndReplaceEditing, Paragraph ] } );
multiRootModel = multiRootEditor.model;

setData( multiRootModel, '<paragraph>Foo bar baz</paragraph>' );
setData( multiRootModel, '<paragraph>Foo bar baz</paragraph>', { rootName: 'second' } );
} );

afterEach( async () => {
await multiRootEditor.destroy();
} );

it( 'should place markers correctly in the model in every root', () => {
const { results } = multiRootEditor.execute( 'find', 'z' );
const [ markerMain, markerSecond ] = getSimplifiedMarkersFromResults( results );

expect( stringify( multiRootModel.document.getRoot( 'main' ), null, [ markerMain ] ) ).to.equal(
'<paragraph>Foo bar ba<X:start></X:start>z<X:end></X:end></paragraph>'
);

expect( stringify( multiRootModel.document.getRoot( 'second' ), null, [ markerSecond ] ) ).to.equal(
'<paragraph>Foo bar ba<X:start></X:start>z<X:end></X:end></paragraph>'
);
} );

it( 'should properly search for occurrences in every root', () => {
const { results } = multiRootEditor.execute( 'find', 'z' );

expect( results ).to.be.lengthOf( 2 );
} );

it( 'should properly search for all occurrences if the first occurrence is not in the main root', () => {
setData( multiRootModel, '<paragraph>Foo bar bar</paragraph>' );

const { results } = multiRootEditor.execute( 'find', 'z' );

expect( results ).to.be.lengthOf( 1 );
} );
} );
} );

it( 'adds marker synchronously', () => {
Expand Down
86 changes: 70 additions & 16 deletions packages/ckeditor5-find-and-replace/tests/findnextcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ describe( 'FindNextCommand', () => {

expect( command.isEnabled ).to.be.true;
} );

it( 'should be enabled if the next occurrence is not in the main root', async () => {
const multiRootEditor = await initMultiRootEditor();

multiRootEditor.execute( 'find', 'bar' );

expect( multiRootEditor.commands.get( 'findNext' ).isEnabled ).to.be.true;

await multiRootEditor.destroy();
} );
} );

describe( '_state', () => {
Expand All @@ -81,19 +91,14 @@ describe( 'FindNextCommand', () => {

command.execute();

const markers = Array.from( editor.model.markers )
.filter( marker => marker.name.startsWith( 'findResultHighlighted:' ) )
.map( marker => {
marker.name = 'findResultHighlighted:foo';
return marker;
} );
const markers = getSimplifiedHighlightedMarkers( model.markers );

expect( stringify( model.document.getRoot(), null, markers ) ).to.equal(
'<paragraph>' +
'Foo bar baz. Bam ' +
'<findResultHighlighted:foo:start></findResultHighlighted:foo:start>' +
'<highlightedResult:start></highlightedResult:start>' +
'bar' +
'<findResultHighlighted:foo:end></findResultHighlighted:foo:end>' +
'<highlightedResult:end></highlightedResult:end>' +
' bom. bar bar' +
'</paragraph>'
);
Expand All @@ -107,22 +112,71 @@ describe( 'FindNextCommand', () => {
command.execute();
command.execute();

const markers = Array.from( editor.model.markers )
.filter( marker => marker.name.startsWith( 'findResultHighlighted:' ) )
.map( marker => {
marker.name = 'findResultHighlighted:foo';
return marker;
} );
const markers = getSimplifiedHighlightedMarkers( model.markers );

expect( stringify( model.document.getRoot(), null, markers ) ).to.equal(
'<paragraph>' +
'Foo bar baz. Bam bar bom. ' +
'<findResultHighlighted:foo:start></findResultHighlighted:foo:start>' +
'<highlightedResult:start></highlightedResult:start>' +
'bar' +
'<findResultHighlighted:foo:end></findResultHighlighted:foo:end>' +
'<highlightedResult:end></highlightedResult:end>' +
' bar' +
'</paragraph>'
);
} );

it( 'should move to the next root', async () => {
const multiRootEditor = await initMultiRootEditor();
model = multiRootEditor.model;

multiRootEditor.execute( 'find', 'bar' );
multiRootEditor.execute( 'findNext' );

const markers = getSimplifiedHighlightedMarkers( model.markers );

expect( stringify( model.document.getRoot( 'second' ), null, markers ) ).to.equal(
'<paragraph>' +
'Foo ' +
'<highlightedResult:start></highlightedResult:start>' +
'bar' +
'<highlightedResult:end></highlightedResult:end>' +
' baz' +
'</paragraph>'
);

await multiRootEditor.destroy();
} );

/**
* Returns the highlighted markers from the markers map. All markers have their name simplified to "highlightedResult"
* as otherwise they're random and unique.
*/
function getSimplifiedHighlightedMarkers( markers ) {
return Array.from( markers )
.filter( marker => marker.name.startsWith( 'findResultHighlighted:' ) )
.map( marker => {
// Replace markers id to a predefined value, as originally these are unique random ids.
marker.name = 'highlightedResult';

return marker;
} );
}
} );

class MultiRootEditor extends ModelTestEditor {
constructor( config ) {
super( config );

this.model.document.createRoot( '$root', 'second' );
}
}

async function initMultiRootEditor() {
const multiRootEditor = await MultiRootEditor.create( { plugins: [ FindAndReplaceEditing, Paragraph ] } );

setData( multiRootEditor.model, '<paragraph>Foo bar baz</paragraph>' );
setData( multiRootEditor.model, '<paragraph>Foo bar baz</paragraph>', { rootName: 'second' } );

return multiRootEditor;
}
} );
81 changes: 65 additions & 16 deletions packages/ckeditor5-find-and-replace/tests/findpreviouscommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ describe( 'FindPreviousCommand', () => {

expect( command.isEnabled ).to.be.true;
} );

it( 'should be enabled if the next previous is not in the main root', async () => {
const multiRootEditor = await initMultiRootEditor();

multiRootEditor.execute( 'find', 'bar' );

expect( multiRootEditor.commands.get( 'findPrevious' ).isEnabled ).to.be.true;

multiRootEditor.destroy();
} );
} );

describe( 'state', () => {
Expand All @@ -80,19 +90,14 @@ describe( 'FindPreviousCommand', () => {

command.execute();

const markers = Array.from( editor.model.markers )
.filter( marker => marker.name.startsWith( 'findResultHighlighted:' ) )
.map( marker => {
marker.name = 'findResultHighlighted:foo';
return marker;
} );
const markers = getSimplifiedHighlightedMarkers( model.markers );

expect( stringify( model.document.getRoot(), null, markers ) ).to.equal(
'<paragraph>' +
'Foo bar baz. Bam ' +
'<findResultHighlighted:foo:start></findResultHighlighted:foo:start>' +
'<highlightedResult:start></highlightedResult:start>' +
'bar' +
'<findResultHighlighted:foo:end></findResultHighlighted:foo:end>' +
'<highlightedResult:end></highlightedResult:end>' +
' bom.' +
'</paragraph>'
);
Expand All @@ -106,22 +111,66 @@ describe( 'FindPreviousCommand', () => {
command.execute();
command.execute();

const markers = Array.from( editor.model.markers )
.filter( marker => marker.name.startsWith( 'findResultHighlighted:' ) )
.map( marker => {
marker.name = 'findResultHighlighted:foo';
return marker;
} );
const markers = getSimplifiedHighlightedMarkers( model.markers );

expect( stringify( model.document.getRoot(), null, markers ) ).to.equal(
'<paragraph>' +
'Foo ' +
'<findResultHighlighted:foo:start></findResultHighlighted:foo:start>' +
'<highlightedResult:start></highlightedResult:start>' +
'bar' +
'<findResultHighlighted:foo:end></findResultHighlighted:foo:end>' +
'<highlightedResult:end></highlightedResult:end>' +
' baz. Bam bar bom.' +
'</paragraph>'
);
} );

it( 'should move to the previous root', async () => {
const multiRootEditor = await initMultiRootEditor();

multiRootEditor.execute( 'find', 'bar' );
multiRootEditor.execute( 'findPrevious' );

const markers = getSimplifiedHighlightedMarkers( multiRootEditor.model.markers );

expect( stringify( multiRootEditor.model.document.getRoot( 'second' ), null, markers ) ).to.equal(
'<paragraph>' +
'Foo ' +
'<highlightedResult:start></highlightedResult:start>' +
'bar' +
'<highlightedResult:end></highlightedResult:end>' +
' baz' +
'</paragraph>'
);

multiRootEditor.destroy();
} );

function getSimplifiedHighlightedMarkers( markers ) {
return Array.from( markers )
.filter( marker => marker.name.startsWith( 'findResultHighlighted:' ) )
.map( marker => {
// Replace markers id to a predefined value, as originally these are unique random ids.
marker.name = 'highlightedResult';

return marker;
} );
}
} );

class MultiRootEditor extends ModelTestEditor {
constructor( config ) {
super( config );

this.model.document.createRoot( '$root', 'second' );
}
}

async function initMultiRootEditor() {
const multiRootEditor = await MultiRootEditor.create( { plugins: [ FindAndReplaceEditing, Paragraph ] } );

setData( multiRootEditor.model, '<paragraph>Foo bar baz</paragraph>' );
setData( multiRootEditor.model, '<paragraph>Foo bar baz</paragraph>', { rootName: 'second' } );

return multiRootEditor;
}
} );
Loading