@ckeditor/ckeditor5-typing 35.2.1 → 35.3.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.
- package/package.json +31 -18
- package/src/delete.js +68 -123
- package/src/deletecommand.js +205 -242
- package/src/deleteobserver.js +205 -112
- package/src/index.js +0 -6
- package/src/input.js +125 -28
- package/src/inserttextcommand.js +96 -0
- package/src/inserttextobserver.js +104 -0
- package/src/texttransformation.js +174 -384
- package/src/textwatcher.js +131 -171
- package/src/twostepcaretmovement.js +300 -341
- package/src/typing.js +9 -43
- package/src/typingconfig.js +5 -0
- package/src/utils/changebuffer.js +142 -151
- package/src/utils/findattributerange.js +12 -24
- package/src/utils/getlasttextline.js +11 -29
- package/src/utils/inlinehighlight.js +38 -52
- package/src/inputcommand.js +0 -100
- package/src/utils/injecttypingmutationshandling.js +0 -331
- package/src/utils/injectunsafekeystrokeshandling.js +0 -189
- package/src/utils/utils.js +0 -104
package/src/textwatcher.js
CHANGED
|
@@ -2,15 +2,11 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
5
|
/**
|
|
7
6
|
* @module typing/textwatcher
|
|
8
7
|
*/
|
|
9
|
-
|
|
10
|
-
import mix from '@ckeditor/ckeditor5-utils/src/mix';
|
|
11
|
-
import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
|
|
8
|
+
import { Observable } from '@ckeditor/ckeditor5-utils/src/observablemixin';
|
|
12
9
|
import getLastTextLine from './utils/getlasttextline';
|
|
13
|
-
|
|
14
10
|
/**
|
|
15
11
|
* The text watcher feature.
|
|
16
12
|
*
|
|
@@ -21,173 +17,137 @@ import getLastTextLine from './utils/getlasttextline';
|
|
|
21
17
|
* @private
|
|
22
18
|
* @mixes module:utils/observablemixin~ObservableMixin
|
|
23
19
|
*/
|
|
24
|
-
export default class TextWatcher {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
this.hasMatch = !!testResult;
|
|
155
|
-
|
|
156
|
-
if ( testResult ) {
|
|
157
|
-
const eventData = Object.assign( data, { text, range } );
|
|
158
|
-
|
|
159
|
-
// If the test callback returns an object with additional data, assign the data as well.
|
|
160
|
-
if ( typeof testResult == 'object' ) {
|
|
161
|
-
Object.assign( eventData, testResult );
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
this.fire( `matched:${ suffix }`, eventData );
|
|
165
|
-
}
|
|
166
|
-
}
|
|
20
|
+
export default class TextWatcher extends Observable {
|
|
21
|
+
/**
|
|
22
|
+
* Creates a text watcher instance.
|
|
23
|
+
*
|
|
24
|
+
* @param {module:engine/model/model~Model} model
|
|
25
|
+
* @param {Function} testCallback See {@link module:typing/textwatcher~TextWatcher#testCallback}.
|
|
26
|
+
*/
|
|
27
|
+
constructor(model, testCallback) {
|
|
28
|
+
super();
|
|
29
|
+
/**
|
|
30
|
+
* The editor's model.
|
|
31
|
+
*
|
|
32
|
+
* @readonly
|
|
33
|
+
* @member {module:engine/model/model~Model}
|
|
34
|
+
*/
|
|
35
|
+
this.model = model;
|
|
36
|
+
/**
|
|
37
|
+
* The function used to match the text.
|
|
38
|
+
*
|
|
39
|
+
* The test callback can return 3 values:
|
|
40
|
+
*
|
|
41
|
+
* * `false` if there is no match,
|
|
42
|
+
* * `true` if there is a match,
|
|
43
|
+
* * an object if there is a match and we want to pass some additional information to the {@link #event:matched:data} event.
|
|
44
|
+
*
|
|
45
|
+
* @member {Function} #testCallback
|
|
46
|
+
* @returns {Object} testResult
|
|
47
|
+
*/
|
|
48
|
+
this.testCallback = testCallback;
|
|
49
|
+
/**
|
|
50
|
+
* Whether there is a match currently.
|
|
51
|
+
*
|
|
52
|
+
* @readonly
|
|
53
|
+
* @member {Boolean}
|
|
54
|
+
*/
|
|
55
|
+
this._hasMatch = false;
|
|
56
|
+
/**
|
|
57
|
+
* Flag indicating whether the `TextWatcher` instance is enabled or disabled.
|
|
58
|
+
* A disabled TextWatcher will not evaluate text.
|
|
59
|
+
*
|
|
60
|
+
* To disable TextWatcher:
|
|
61
|
+
*
|
|
62
|
+
* const watcher = new TextWatcher( editor.model, testCallback );
|
|
63
|
+
*
|
|
64
|
+
* // After this a testCallback will not be called.
|
|
65
|
+
* watcher.isEnabled = false;
|
|
66
|
+
*
|
|
67
|
+
* @observable
|
|
68
|
+
* @member {Boolean} #isEnabled
|
|
69
|
+
*/
|
|
70
|
+
this.set('isEnabled', true);
|
|
71
|
+
// Toggle text watching on isEnabled state change.
|
|
72
|
+
this.on('change:isEnabled', () => {
|
|
73
|
+
if (this.isEnabled) {
|
|
74
|
+
this._startListening();
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
this.stopListening(model.document.selection);
|
|
78
|
+
this.stopListening(model.document);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
this._startListening();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* TODO
|
|
85
|
+
*/
|
|
86
|
+
get hasMatch() {
|
|
87
|
+
return this._hasMatch;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Starts listening to the editor for typing and selection events.
|
|
91
|
+
*
|
|
92
|
+
* @private
|
|
93
|
+
*/
|
|
94
|
+
_startListening() {
|
|
95
|
+
const model = this.model;
|
|
96
|
+
const document = model.document;
|
|
97
|
+
this.listenTo(document.selection, 'change:range', (evt, { directChange }) => {
|
|
98
|
+
// Indirect changes (i.e. when the user types or external changes are applied) are handled in the document's change event.
|
|
99
|
+
if (!directChange) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
// Act only on collapsed selection.
|
|
103
|
+
if (!document.selection.isCollapsed) {
|
|
104
|
+
if (this.hasMatch) {
|
|
105
|
+
this.fire('unmatched');
|
|
106
|
+
this._hasMatch = false;
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
this._evaluateTextBeforeSelection('selection');
|
|
111
|
+
});
|
|
112
|
+
this.listenTo(document, 'change:data', (evt, batch) => {
|
|
113
|
+
if (batch.isUndo || !batch.isLocal) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
this._evaluateTextBeforeSelection('data', { batch });
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Checks the editor content for matched text.
|
|
121
|
+
*
|
|
122
|
+
* @fires matched:data
|
|
123
|
+
* @fires matched:selection
|
|
124
|
+
* @fires unmatched
|
|
125
|
+
*
|
|
126
|
+
* @private
|
|
127
|
+
* @param {'data'|'selection'} suffix A suffix used for generating the event name.
|
|
128
|
+
* @param {Object} data Data object for event.
|
|
129
|
+
*/
|
|
130
|
+
_evaluateTextBeforeSelection(suffix, data = {}) {
|
|
131
|
+
const model = this.model;
|
|
132
|
+
const document = model.document;
|
|
133
|
+
const selection = document.selection;
|
|
134
|
+
const rangeBeforeSelection = model.createRange(model.createPositionAt(selection.focus.parent, 0), selection.focus);
|
|
135
|
+
const { text, range } = getLastTextLine(rangeBeforeSelection, model);
|
|
136
|
+
const testResult = this.testCallback(text);
|
|
137
|
+
if (!testResult && this.hasMatch) {
|
|
138
|
+
this.fire('unmatched');
|
|
139
|
+
}
|
|
140
|
+
this._hasMatch = !!testResult;
|
|
141
|
+
if (testResult) {
|
|
142
|
+
const eventData = Object.assign(data, { text, range });
|
|
143
|
+
// If the test callback returns an object with additional data, assign the data as well.
|
|
144
|
+
if (typeof testResult == 'object') {
|
|
145
|
+
Object.assign(eventData, testResult);
|
|
146
|
+
}
|
|
147
|
+
this.fire(`matched:${suffix}`, eventData);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
167
150
|
}
|
|
168
|
-
|
|
169
|
-
mix( TextWatcher, ObservableMixin );
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Fired whenever the text watcher found a match for data changes.
|
|
173
|
-
*
|
|
174
|
-
* @event matched:data
|
|
175
|
-
* @param {Object} data Event data.
|
|
176
|
-
* @param {String} data.text The full text before selection to which the regexp was applied.
|
|
177
|
-
* @param {module:engine/model/range~Range} data.range The range representing the position of the `data.text`.
|
|
178
|
-
* @param {Object} [data.testResult] The additional data returned from the {@link module:typing/textwatcher~TextWatcher#testCallback}.
|
|
179
|
-
*/
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Fired whenever the text watcher found a match for selection changes.
|
|
183
|
-
*
|
|
184
|
-
* @event matched:selection
|
|
185
|
-
* @param {Object} data Event data.
|
|
186
|
-
* @param {String} data.text The full text before selection.
|
|
187
|
-
* @param {module:engine/model/range~Range} data.range The range representing the position of the `data.text`.
|
|
188
|
-
* @param {Object} [data.testResult] The additional data returned from the {@link module:typing/textwatcher~TextWatcher#testCallback}.
|
|
189
|
-
*/
|
|
190
|
-
|
|
191
151
|
/**
|
|
192
152
|
* Fired whenever the text does not match anymore. Fired only when the text watcher found a match.
|
|
193
153
|
*
|