@@ -21,6 +21,7 @@ const EditorState = require('EditorState');
21
21
const UserAgent = require ( 'UserAgent' ) ;
22
22
23
23
const getEntityKeyForSelection = require ( 'getEntityKeyForSelection' ) ;
24
+ const gkx = require ( 'gkx' ) ;
24
25
const isEventHandled = require ( 'isEventHandled' ) ;
25
26
const isSelectionAtLeafStart = require ( 'isSelectionAtLeafStart' ) ;
26
27
const nullthrows = require ( 'nullthrows' ) ;
@@ -187,16 +188,73 @@ function editOnBeforeInput(
187
188
}
188
189
}
189
190
if ( ! mustPreventNative ) {
190
- // Check the old and new "fingerprints" of the current block to determine
191
- // whether this insertion requires any addition or removal of text nodes,
192
- // in which case we would prevent the native character insertion.
193
- const originalFingerprint = BlockTree . getFingerprint (
194
- editorState . getBlockTree ( anchorKey ) ,
195
- ) ;
196
- const newFingerprint = BlockTree . getFingerprint (
197
- newEditorState . getBlockTree ( anchorKey ) ,
198
- ) ;
199
- mustPreventNative = originalFingerprint !== newFingerprint ;
191
+ if ( gkx ( 'draft_improved_decorator_fingerprint' ) ) {
192
+ // Let's say we have a decorator that highlights hashtags. In many cases
193
+ // we need to prevent native behavior and rerender ourselves --
194
+ // particularly, any case *except* where the inserted characters end up
195
+ // anywhere except exactly where you put them.
196
+ //
197
+ // Using [] to denote a decorated leaf, some examples:
198
+ //
199
+ // 1. 'hi #' and append 'f'
200
+ // desired rendering: 'hi [#f]'
201
+ // native rendering would be: 'hi #f' (incorrect)
202
+ //
203
+ // 2. 'x [#foo]' and insert '#' before 'f'
204
+ // desired rendering: 'x #[#foo]'
205
+ // native rendering would be: 'x [##foo]' (incorrect)
206
+ //
207
+ // 3. '[#foobar]' and insert ' ' between 'foo' and 'bar'
208
+ // desired rendering: '[#foo] bar'
209
+ // native rendering would be: '[#foo bar]' (incorrect)
210
+ //
211
+ // 4. '[#foo]' and delete '#' [won't use this beforeinput codepath though]
212
+ // desired rendering: 'foo'
213
+ // native rendering would be: '[foo]' (incorrect)
214
+ //
215
+ // 5. '[#foo]' and append 'b'
216
+ // desired rendering: '[#foob]'
217
+ // native rendering would be: '[#foob]' (native insertion is OK here)
218
+ //
219
+ // It is safe to allow native insertion if and only if the full list of
220
+ // decorator ranges matches what we expect native insertion to give. We
221
+ // don't need to compare the content because the only possible mutation
222
+ // to consider here is inserting plain text and decorators can't affect
223
+ // text content.
224
+ const oldBlockTree = editorState . getBlockTree ( anchorKey ) ;
225
+ const newBlockTree = newEditorState . getBlockTree ( anchorKey ) ;
226
+ mustPreventNative =
227
+ oldBlockTree . size !== newBlockTree . size ||
228
+ oldBlockTree . zip ( newBlockTree ) . some ( ( [ oldLeafSet , newLeafSet ] ) => {
229
+ // selectionStart is guaranteed to be selectionEnd here
230
+ const oldStart = oldLeafSet . get ( 'start' ) ;
231
+ const adjustedStart =
232
+ oldStart + ( oldStart >= selectionStart ? chars . length : 0 ) ;
233
+ const oldEnd = oldLeafSet . get ( 'end' ) ;
234
+ const adjustedEnd =
235
+ oldEnd + ( oldEnd >= selectionStart ? chars . length : 0 ) ;
236
+ return (
237
+ // Different decorators
238
+ oldLeafSet . get ( 'decoratorKey' ) !== newLeafSet . get ( 'decoratorKey' ) ||
239
+ // Different number of inline styles
240
+ oldLeafSet . get ( 'leaves' ) . size !== newLeafSet . get ( 'leaves' ) . size ||
241
+ // Different effective decorator position
242
+ adjustedStart !== newLeafSet . get ( 'start' ) ||
243
+ adjustedEnd !== newLeafSet . get ( 'end' )
244
+ ) ;
245
+ } ) ;
246
+ } else {
247
+ // Check the old and new "fingerprints" of the current block to determine
248
+ // whether this insertion requires any addition or removal of text nodes,
249
+ // in which case we would prevent the native character insertion.
250
+ const originalFingerprint = BlockTree . getFingerprint (
251
+ editorState . getBlockTree ( anchorKey ) ,
252
+ ) ;
253
+ const newFingerprint = BlockTree . getFingerprint (
254
+ newEditorState . getBlockTree ( anchorKey ) ,
255
+ ) ;
256
+ mustPreventNative = originalFingerprint !== newFingerprint ;
257
+ }
200
258
}
201
259
if ( ! mustPreventNative ) {
202
260
mustPreventNative = mustPreventDefaultForCharacter ( chars ) ;
0 commit comments