@ckeditor/ckeditor5-find-and-replace 40.0.0 → 40.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,143 +1,143 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- import { Plugin } from 'ckeditor5/src/core';
6
- import { Collection, uid } from 'ckeditor5/src/utils';
7
- import { escapeRegExp } from 'lodash-es';
8
- /**
9
- * A set of helpers related to find and replace.
10
- */
11
- export default class FindAndReplaceUtils extends Plugin {
12
- /**
13
- * @inheritDoc
14
- */
15
- static get pluginName() {
16
- return 'FindAndReplaceUtils';
17
- }
18
- /**
19
- * Executes findCallback and updates search results list.
20
- *
21
- * @param range The model range to scan for matches.
22
- * @param model The model.
23
- * @param findCallback The callback that should return `true` if provided text matches the search term.
24
- * @param startResults An optional collection of find matches that the function should
25
- * start with. This would be a collection returned by a previous `updateFindResultFromRange()` call.
26
- * @returns A collection of objects describing find match.
27
- *
28
- * An example structure:
29
- *
30
- * ```js
31
- * {
32
- * id: resultId,
33
- * label: foundItem.label,
34
- * marker
35
- * }
36
- * ```
37
- */
38
- updateFindResultFromRange(range, model, findCallback, startResults) {
39
- const results = startResults || new Collection();
40
- model.change(writer => {
41
- [...range].forEach(({ type, item }) => {
42
- if (type === 'elementStart') {
43
- if (model.schema.checkChild(item, '$text')) {
44
- const foundItems = findCallback({
45
- item,
46
- text: this.rangeToText(model.createRangeIn(item))
47
- });
48
- if (!foundItems) {
49
- return;
50
- }
51
- foundItems.forEach(foundItem => {
52
- const resultId = `findResult:${uid()}`;
53
- const marker = writer.addMarker(resultId, {
54
- usingOperation: false,
55
- affectsData: false,
56
- range: writer.createRange(writer.createPositionAt(item, foundItem.start), writer.createPositionAt(item, foundItem.end))
57
- });
58
- const index = findInsertIndex(results, marker);
59
- results.add({
60
- id: resultId,
61
- label: foundItem.label,
62
- marker
63
- }, index);
64
- });
65
- }
66
- }
67
- });
68
- });
69
- return results;
70
- }
71
- /**
72
- * Returns text representation of a range. The returned text length should be the same as range length.
73
- * In order to achieve this, this function will replace inline elements (text-line) as new line character ("\n").
74
- *
75
- * @param range The model range.
76
- * @returns The text content of the provided range.
77
- */
78
- rangeToText(range) {
79
- return Array.from(range.getItems()).reduce((rangeText, node) => {
80
- // Trim text to a last occurrence of an inline element and update range start.
81
- if (!(node.is('$text') || node.is('$textProxy'))) {
82
- // Editor has only one inline element defined in schema: `<softBreak>` which is treated as new line character in blocks.
83
- // Special handling might be needed for other inline elements (inline widgets).
84
- return `${rangeText}\n`;
85
- }
86
- return rangeText + node.data;
87
- }, '');
88
- }
89
- /**
90
- * Creates a text matching callback for a specified search term and matching options.
91
- *
92
- * @param searchTerm The search term.
93
- * @param options Matching options.
94
- * - options.matchCase=false If set to `true` letter casing will be ignored.
95
- * - options.wholeWords=false If set to `true` only whole words that match `callbackOrText` will be matched.
96
- */
97
- findByTextCallback(searchTerm, options) {
98
- let flags = 'gu';
99
- if (!options.matchCase) {
100
- flags += 'i';
101
- }
102
- let regExpQuery = `(${escapeRegExp(searchTerm)})`;
103
- if (options.wholeWords) {
104
- const nonLetterGroup = '[^a-zA-Z\u00C0-\u024F\u1E00-\u1EFF]';
105
- if (!new RegExp('^' + nonLetterGroup).test(searchTerm)) {
106
- regExpQuery = `(^|${nonLetterGroup}|_)${regExpQuery}`;
107
- }
108
- if (!new RegExp(nonLetterGroup + '$').test(searchTerm)) {
109
- regExpQuery = `${regExpQuery}(?=_|${nonLetterGroup}|$)`;
110
- }
111
- }
112
- const regExp = new RegExp(regExpQuery, flags);
113
- function findCallback({ text }) {
114
- const matches = [...text.matchAll(regExp)];
115
- return matches.map(regexpMatchToFindResult);
116
- }
117
- return findCallback;
118
- }
119
- }
120
- // Finds the appropriate index in the resultsList Collection.
121
- function findInsertIndex(resultsList, markerToInsert) {
122
- const result = resultsList.find(({ marker }) => {
123
- return markerToInsert.getStart().isBefore(marker.getStart());
124
- });
125
- return result ? resultsList.getIndex(result) : resultsList.length;
126
- }
127
- /**
128
- * Maps RegExp match result to find result.
129
- */
130
- function regexpMatchToFindResult(matchResult) {
131
- const lastGroupIndex = matchResult.length - 1;
132
- let startOffset = matchResult.index;
133
- // Searches with match all flag have an extra matching group with empty string or white space matched before the word.
134
- // If the search term starts with the space already, there is no extra group even with match all flag on.
135
- if (matchResult.length === 3) {
136
- startOffset += matchResult[1].length;
137
- }
138
- return {
139
- label: matchResult[lastGroupIndex],
140
- start: startOffset,
141
- end: startOffset + matchResult[lastGroupIndex].length
142
- };
143
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ import { Plugin } from 'ckeditor5/src/core';
6
+ import { Collection, uid } from 'ckeditor5/src/utils';
7
+ import { escapeRegExp } from 'lodash-es';
8
+ /**
9
+ * A set of helpers related to find and replace.
10
+ */
11
+ export default class FindAndReplaceUtils extends Plugin {
12
+ /**
13
+ * @inheritDoc
14
+ */
15
+ static get pluginName() {
16
+ return 'FindAndReplaceUtils';
17
+ }
18
+ /**
19
+ * Executes findCallback and updates search results list.
20
+ *
21
+ * @param range The model range to scan for matches.
22
+ * @param model The model.
23
+ * @param findCallback The callback that should return `true` if provided text matches the search term.
24
+ * @param startResults An optional collection of find matches that the function should
25
+ * start with. This would be a collection returned by a previous `updateFindResultFromRange()` call.
26
+ * @returns A collection of objects describing find match.
27
+ *
28
+ * An example structure:
29
+ *
30
+ * ```js
31
+ * {
32
+ * id: resultId,
33
+ * label: foundItem.label,
34
+ * marker
35
+ * }
36
+ * ```
37
+ */
38
+ updateFindResultFromRange(range, model, findCallback, startResults) {
39
+ const results = startResults || new Collection();
40
+ model.change(writer => {
41
+ [...range].forEach(({ type, item }) => {
42
+ if (type === 'elementStart') {
43
+ if (model.schema.checkChild(item, '$text')) {
44
+ const foundItems = findCallback({
45
+ item,
46
+ text: this.rangeToText(model.createRangeIn(item))
47
+ });
48
+ if (!foundItems) {
49
+ return;
50
+ }
51
+ foundItems.forEach(foundItem => {
52
+ const resultId = `findResult:${uid()}`;
53
+ const marker = writer.addMarker(resultId, {
54
+ usingOperation: false,
55
+ affectsData: false,
56
+ range: writer.createRange(writer.createPositionAt(item, foundItem.start), writer.createPositionAt(item, foundItem.end))
57
+ });
58
+ const index = findInsertIndex(results, marker);
59
+ results.add({
60
+ id: resultId,
61
+ label: foundItem.label,
62
+ marker
63
+ }, index);
64
+ });
65
+ }
66
+ }
67
+ });
68
+ });
69
+ return results;
70
+ }
71
+ /**
72
+ * Returns text representation of a range. The returned text length should be the same as range length.
73
+ * In order to achieve this, this function will replace inline elements (text-line) as new line character ("\n").
74
+ *
75
+ * @param range The model range.
76
+ * @returns The text content of the provided range.
77
+ */
78
+ rangeToText(range) {
79
+ return Array.from(range.getItems()).reduce((rangeText, node) => {
80
+ // Trim text to a last occurrence of an inline element and update range start.
81
+ if (!(node.is('$text') || node.is('$textProxy'))) {
82
+ // Editor has only one inline element defined in schema: `<softBreak>` which is treated as new line character in blocks.
83
+ // Special handling might be needed for other inline elements (inline widgets).
84
+ return `${rangeText}\n`;
85
+ }
86
+ return rangeText + node.data;
87
+ }, '');
88
+ }
89
+ /**
90
+ * Creates a text matching callback for a specified search term and matching options.
91
+ *
92
+ * @param searchTerm The search term.
93
+ * @param options Matching options.
94
+ * - options.matchCase=false If set to `true` letter casing will be ignored.
95
+ * - options.wholeWords=false If set to `true` only whole words that match `callbackOrText` will be matched.
96
+ */
97
+ findByTextCallback(searchTerm, options) {
98
+ let flags = 'gu';
99
+ if (!options.matchCase) {
100
+ flags += 'i';
101
+ }
102
+ let regExpQuery = `(${escapeRegExp(searchTerm)})`;
103
+ if (options.wholeWords) {
104
+ const nonLetterGroup = '[^a-zA-Z\u00C0-\u024F\u1E00-\u1EFF]';
105
+ if (!new RegExp('^' + nonLetterGroup).test(searchTerm)) {
106
+ regExpQuery = `(^|${nonLetterGroup}|_)${regExpQuery}`;
107
+ }
108
+ if (!new RegExp(nonLetterGroup + '$').test(searchTerm)) {
109
+ regExpQuery = `${regExpQuery}(?=_|${nonLetterGroup}|$)`;
110
+ }
111
+ }
112
+ const regExp = new RegExp(regExpQuery, flags);
113
+ function findCallback({ text }) {
114
+ const matches = [...text.matchAll(regExp)];
115
+ return matches.map(regexpMatchToFindResult);
116
+ }
117
+ return findCallback;
118
+ }
119
+ }
120
+ // Finds the appropriate index in the resultsList Collection.
121
+ function findInsertIndex(resultsList, markerToInsert) {
122
+ const result = resultsList.find(({ marker }) => {
123
+ return markerToInsert.getStart().isBefore(marker.getStart());
124
+ });
125
+ return result ? resultsList.getIndex(result) : resultsList.length;
126
+ }
127
+ /**
128
+ * Maps RegExp match result to find result.
129
+ */
130
+ function regexpMatchToFindResult(matchResult) {
131
+ const lastGroupIndex = matchResult.length - 1;
132
+ let startOffset = matchResult.index;
133
+ // Searches with match all flag have an extra matching group with empty string or white space matched before the word.
134
+ // If the search term starts with the space already, there is no extra group even with match all flag on.
135
+ if (matchResult.length === 3) {
136
+ startOffset += matchResult[1].length;
137
+ }
138
+ return {
139
+ label: matchResult[lastGroupIndex],
140
+ start: startOffset,
141
+ end: startOffset + matchResult[lastGroupIndex].length
142
+ };
143
+ }
@@ -1,51 +1,51 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @module find-and-replace/findcommand
7
- */
8
- import type { Item } from 'ckeditor5/src/engine';
9
- import { Command, type Editor } from 'ckeditor5/src/core';
10
- import type { Collection } from 'ckeditor5/src/utils';
11
- import type FindAndReplaceState from './findandreplacestate';
12
- import type { ResultType } from './findandreplace';
13
- /**
14
- * The find command. It is used by the {@link module:find-and-replace/findandreplace~FindAndReplace find and replace feature}.
15
- */
16
- export default class FindCommand extends Command {
17
- /**
18
- * The find and replace state object used for command operations.
19
- */
20
- private _state;
21
- /**
22
- * Creates a new `FindCommand` instance.
23
- *
24
- * @param editor The editor on which this command will be used.
25
- * @param state An object to hold plugin state.
26
- */
27
- constructor(editor: Editor, state: FindAndReplaceState);
28
- /**
29
- * Executes the command.
30
- *
31
- * @param callbackOrText
32
- * @param options Options object.
33
- * @param options.matchCase If set to `true`, the letter case will be matched.
34
- * @param options.wholeWords If set to `true`, only whole words that match `callbackOrText` will be matched.
35
- *
36
- * @fires execute
37
- */
38
- execute(callbackOrText: string | (({ item, text }: {
39
- item: Item;
40
- text: string;
41
- }) => Array<ResultType>), { matchCase, wholeWords }?: {
42
- matchCase?: boolean;
43
- wholeWords?: boolean;
44
- }): {
45
- results: Collection<ResultType>;
46
- findCallback: (({ item, text }: {
47
- item: Item;
48
- text: string;
49
- }) => Array<ResultType>);
50
- };
51
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module find-and-replace/findcommand
7
+ */
8
+ import type { Item } from 'ckeditor5/src/engine';
9
+ import { Command, type Editor } from 'ckeditor5/src/core';
10
+ import type { Collection } from 'ckeditor5/src/utils';
11
+ import type FindAndReplaceState from './findandreplacestate';
12
+ import type { ResultType } from './findandreplace';
13
+ /**
14
+ * The find command. It is used by the {@link module:find-and-replace/findandreplace~FindAndReplace find and replace feature}.
15
+ */
16
+ export default class FindCommand extends Command {
17
+ /**
18
+ * The find and replace state object used for command operations.
19
+ */
20
+ private _state;
21
+ /**
22
+ * Creates a new `FindCommand` instance.
23
+ *
24
+ * @param editor The editor on which this command will be used.
25
+ * @param state An object to hold plugin state.
26
+ */
27
+ constructor(editor: Editor, state: FindAndReplaceState);
28
+ /**
29
+ * Executes the command.
30
+ *
31
+ * @param callbackOrText
32
+ * @param options Options object.
33
+ * @param options.matchCase If set to `true`, the letter case will be matched.
34
+ * @param options.wholeWords If set to `true`, only whole words that match `callbackOrText` will be matched.
35
+ *
36
+ * @fires execute
37
+ */
38
+ execute(callbackOrText: string | (({ item, text }: {
39
+ item: Item;
40
+ text: string;
41
+ }) => Array<ResultType>), { matchCase, wholeWords }?: {
42
+ matchCase?: boolean;
43
+ wholeWords?: boolean;
44
+ }): {
45
+ results: Collection<ResultType>;
46
+ findCallback: (({ item, text }: {
47
+ item: Item;
48
+ text: string;
49
+ }) => Array<ResultType>);
50
+ };
51
+ }
@@ -1,63 +1,63 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- import { Command } from 'ckeditor5/src/core';
6
- /**
7
- * The find command. It is used by the {@link module:find-and-replace/findandreplace~FindAndReplace find and replace feature}.
8
- */
9
- export default class FindCommand extends Command {
10
- /**
11
- * Creates a new `FindCommand` instance.
12
- *
13
- * @param editor The editor on which this command will be used.
14
- * @param state An object to hold plugin state.
15
- */
16
- constructor(editor, state) {
17
- super(editor);
18
- // The find command is always enabled.
19
- this.isEnabled = true;
20
- // It does not affect data so should be enabled in read-only mode.
21
- this.affectsData = false;
22
- this._state = state;
23
- }
24
- /**
25
- * Executes the command.
26
- *
27
- * @param callbackOrText
28
- * @param options Options object.
29
- * @param options.matchCase If set to `true`, the letter case will be matched.
30
- * @param options.wholeWords If set to `true`, only whole words that match `callbackOrText` will be matched.
31
- *
32
- * @fires execute
33
- */
34
- execute(callbackOrText, { matchCase, wholeWords } = {}) {
35
- const { editor } = this;
36
- const { model } = editor;
37
- const findAndReplaceUtils = editor.plugins.get('FindAndReplaceUtils');
38
- let findCallback;
39
- // Allow to execute `find()` on a plugin with a keyword only.
40
- if (typeof callbackOrText === 'string') {
41
- findCallback = findAndReplaceUtils.findByTextCallback(callbackOrText, { matchCase, wholeWords });
42
- this._state.searchText = callbackOrText;
43
- }
44
- else {
45
- findCallback = callbackOrText;
46
- }
47
- // Initial search is done on all nodes in all roots inside the content.
48
- const results = model.document.getRootNames()
49
- .reduce(((currentResults, rootName) => findAndReplaceUtils.updateFindResultFromRange(model.createRangeIn(model.document.getRoot(rootName)), model, findCallback, currentResults)), null);
50
- this._state.clear(model);
51
- this._state.results.addMany(results);
52
- this._state.highlightedResult = results.get(0);
53
- if (typeof callbackOrText === 'string') {
54
- this._state.searchText = callbackOrText;
55
- }
56
- this._state.matchCase = !!matchCase;
57
- this._state.matchWholeWords = !!wholeWords;
58
- return {
59
- results,
60
- findCallback
61
- };
62
- }
63
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ import { Command } from 'ckeditor5/src/core';
6
+ /**
7
+ * The find command. It is used by the {@link module:find-and-replace/findandreplace~FindAndReplace find and replace feature}.
8
+ */
9
+ export default class FindCommand extends Command {
10
+ /**
11
+ * Creates a new `FindCommand` instance.
12
+ *
13
+ * @param editor The editor on which this command will be used.
14
+ * @param state An object to hold plugin state.
15
+ */
16
+ constructor(editor, state) {
17
+ super(editor);
18
+ // The find command is always enabled.
19
+ this.isEnabled = true;
20
+ // It does not affect data so should be enabled in read-only mode.
21
+ this.affectsData = false;
22
+ this._state = state;
23
+ }
24
+ /**
25
+ * Executes the command.
26
+ *
27
+ * @param callbackOrText
28
+ * @param options Options object.
29
+ * @param options.matchCase If set to `true`, the letter case will be matched.
30
+ * @param options.wholeWords If set to `true`, only whole words that match `callbackOrText` will be matched.
31
+ *
32
+ * @fires execute
33
+ */
34
+ execute(callbackOrText, { matchCase, wholeWords } = {}) {
35
+ const { editor } = this;
36
+ const { model } = editor;
37
+ const findAndReplaceUtils = editor.plugins.get('FindAndReplaceUtils');
38
+ let findCallback;
39
+ // Allow to execute `find()` on a plugin with a keyword only.
40
+ if (typeof callbackOrText === 'string') {
41
+ findCallback = findAndReplaceUtils.findByTextCallback(callbackOrText, { matchCase, wholeWords });
42
+ this._state.searchText = callbackOrText;
43
+ }
44
+ else {
45
+ findCallback = callbackOrText;
46
+ }
47
+ // Initial search is done on all nodes in all roots inside the content.
48
+ const results = model.document.getRootNames()
49
+ .reduce(((currentResults, rootName) => findAndReplaceUtils.updateFindResultFromRange(model.createRangeIn(model.document.getRoot(rootName)), model, findCallback, currentResults)), null);
50
+ this._state.clear(model);
51
+ this._state.results.addMany(results);
52
+ this._state.highlightedResult = results.get(0);
53
+ if (typeof callbackOrText === 'string') {
54
+ this._state.searchText = callbackOrText;
55
+ }
56
+ this._state.matchCase = !!matchCase;
57
+ this._state.matchWholeWords = !!wholeWords;
58
+ return {
59
+ results,
60
+ findCallback
61
+ };
62
+ }
63
+ }
@@ -1,35 +1,35 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @module find-and-replace/findnextcommand
7
- */
8
- import { Command, type Editor } from 'ckeditor5/src/core';
9
- import type FindAndReplaceState from './findandreplacestate';
10
- /**
11
- * The find next command. Moves the highlight to the next search result.
12
- *
13
- * It is used by the {@link module:find-and-replace/findandreplace~FindAndReplace find and replace feature}.
14
- */
15
- export default class FindNextCommand extends Command {
16
- /**
17
- * The find and replace state object used for command operations.
18
- */
19
- protected _state: FindAndReplaceState;
20
- /**
21
- * Creates a new `FindNextCommand` instance.
22
- *
23
- * @param editor The editor on which this command will be used.
24
- * @param state An object to hold plugin state.
25
- */
26
- constructor(editor: Editor, state: FindAndReplaceState);
27
- /**
28
- * @inheritDoc
29
- */
30
- refresh(): void;
31
- /**
32
- * @inheritDoc
33
- */
34
- execute(): void;
35
- }
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module find-and-replace/findnextcommand
7
+ */
8
+ import { Command, type Editor } from 'ckeditor5/src/core';
9
+ import type FindAndReplaceState from './findandreplacestate';
10
+ /**
11
+ * The find next command. Moves the highlight to the next search result.
12
+ *
13
+ * It is used by the {@link module:find-and-replace/findandreplace~FindAndReplace find and replace feature}.
14
+ */
15
+ export default class FindNextCommand extends Command {
16
+ /**
17
+ * The find and replace state object used for command operations.
18
+ */
19
+ protected _state: FindAndReplaceState;
20
+ /**
21
+ * Creates a new `FindNextCommand` instance.
22
+ *
23
+ * @param editor The editor on which this command will be used.
24
+ * @param state An object to hold plugin state.
25
+ */
26
+ constructor(editor: Editor, state: FindAndReplaceState);
27
+ /**
28
+ * @inheritDoc
29
+ */
30
+ refresh(): void;
31
+ /**
32
+ * @inheritDoc
33
+ */
34
+ execute(): void;
35
+ }