Skip to content

Commit

Permalink
Merge pull request #16452 from ckeditor/ck/6193
Browse files Browse the repository at this point in the history
Fix (find-and-replace): Allow to pass `searchTerm` when using `findCallback` in `find` command.
  • Loading branch information
f1ames authored Jul 2, 2024
2 parents 1aa7f16 + 6b0d8c2 commit 1cc6695
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 10 deletions.
21 changes: 20 additions & 1 deletion packages/ckeditor5-find-and-replace/src/findandreplacestate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,23 @@ export default class FindAndReplaceState extends /* #__PURE__ */ ObservableMixin
/**
* The callback function used to find matches in the document.
*/
export type FindCallback = ( ( { item, text }: { item: Item; text: string } ) => Array<ResultType> );
export type FindCallback = ( { item, text }: { item: Item; text: string } ) => FindCallbackResultObject | FindCallbackResult;

/**
* Represents the result of a find callback.
*
* The `searchText` attribute in the result object is used to determine if the search text has changed.
* If returned `searchText` is different than the last search text, the search results will be invalidated
* while searching for next item and the search will start from the beginning of the document.
*/
export type FindCallbackResultObject = {
results: Array<ResultType>;
searchText: string;
};

/**
* Represents the result of a find callback.
*
* @deprecated Use `FindCallbackResultObject` instead.
*/
export type FindCallbackResult = Array<ResultType>;
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default class FindAndReplaceUtils extends Plugin {
public updateFindResultFromRange(
range: Range,
model: Model,
findCallback: ( { item, text }: { item: Item; text: string } ) => Array<ResultType>,
findCallback: ( { item, text }: { item: Item; text: string } ) => Array<ResultType> | { results: Array<ResultType> },
startResults: Collection<ResultType> | null
): Collection<ResultType> {
const results = startResults || new Collection();
Expand All @@ -67,7 +67,7 @@ export default class FindAndReplaceUtils extends Plugin {
[ ...range ].forEach( ( { type, item } ) => {
if ( type === 'elementStart' ) {
if ( model.schema.checkChild( item, '$text' ) ) {
const foundItems = findCallback( {
let foundItems = findCallback( {
item,
text: this.rangeToText( model.createRangeIn( item as Element ) )
} );
Expand All @@ -76,6 +76,10 @@ export default class FindAndReplaceUtils extends Plugin {
return;
}

if ( 'results' in foundItems ) {
foundItems = foundItems.results;
}

foundItems.forEach( foundItem => {
const resultId = `findResult:${ uid() }`;
const marker = writer.addMarker( resultId, {
Expand Down
26 changes: 19 additions & 7 deletions packages/ckeditor5-find-and-replace/src/findcommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,31 @@ export default class FindCommand extends Command {
const findAndReplaceUtils: FindAndReplaceUtils = editor.plugins.get( 'FindAndReplaceUtils' );

let findCallback: FindCallback | undefined;
let callbackSearchText: string = '';

// Allow to execute `find()` on a plugin with a keyword only.
if ( typeof callbackOrText === 'string' ) {
findCallback = findAndReplaceUtils.findByTextCallback( callbackOrText, { matchCase, wholeWords } );

this._state.searchText = callbackOrText;
findCallback = ( ...args ) => ( {
results: findAndReplaceUtils.findByTextCallback( callbackOrText, { matchCase, wholeWords } )( ...args ),
searchText: callbackOrText
} );
} else {
findCallback = callbackOrText;
}

// Wrap the callback to get the search text that will be assigned to the state.
const oldCallback = findCallback;

findCallback = ( ...args ) => {
const result = oldCallback( ...args );

if ( result && 'searchText' in result ) {
callbackSearchText = result.searchText;
}

return result;
};

// Initial search is done on all nodes in all roots inside the content.
const results = model.document.getRootNames()
.reduce( ( ( currentResults: Collection<ResultType> | null, rootName ) => findAndReplaceUtils.updateFindResultFromRange(
Expand All @@ -82,10 +97,7 @@ export default class FindCommand extends Command {
this._state.clear( model );
this._state.results.addMany( results );
this._state.highlightedResult = results.get( 0 );

if ( typeof callbackOrText === 'string' ) {
this._state.searchText = callbackOrText;
}
this._state.searchText = callbackSearchText;

if ( findCallback ) {
this._state.lastSearchCallback = findCallback;
Expand Down
38 changes: 38 additions & 0 deletions packages/ckeditor5-find-and-replace/tests/findcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,15 @@ describe( 'FindCommand', () => {
);
} );

it( 'sets proper searchText state value', () => {
editor.setData( '<p>foo 🐛 bar</p>' );

const { results } = command.execute( '🐛' );

expect( results.length ).to.equal( 1 );
expect( command._state.searchText ).to.equal( '🐛' );
} );

describe( 'options.matchCase', () => {
it( 'set to true doesn\'t match differently cased string', () => {
editor.setData( '<p>foo bAr</p>' );
Expand Down Expand Up @@ -368,6 +377,35 @@ describe( 'FindCommand', () => {
} );
} );

describe( 'with callback passed', () => {
it( 'sets returned searchText attribute to the object result', () => {
const findAndReplaceUtils = editor.plugins.get( 'FindAndReplaceUtils' );

setData( model, '<paragraph>[]Foo bar baz. Bam bar bom.</paragraph>' );

const searchText = 'bar';
const { results } = command.execute( ( ...args ) => ( {
results: findAndReplaceUtils.findByTextCallback( searchText, {} )( ...args ),
searchText
} ) );

expect( results.length ).to.equal( 2 );
expect( command._state.searchText ).to.equal( searchText );
} );

it( 'sets empty searchText if array is returned', () => {
const findAndReplaceUtils = editor.plugins.get( 'FindAndReplaceUtils' );

setData( model, '<paragraph>[]Foo bar baz. Bam bar bom.</paragraph>' );

const searchText = 'bar';
const { results } = command.execute( findAndReplaceUtils.findByTextCallback( searchText, {} ) );

expect( results.length ).to.equal( 2 );
expect( command._state.searchText ).to.equal( '' );
} );
} );

it( 'adds marker synchronously', () => {
editor.setData( '<p>foo bar baz</p>' );

Expand Down

0 comments on commit 1cc6695

Please sign in to comment.