@openeditor/react-native-prose-editor 0.0.7 → 0.0.8
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/android/build.gradle +35 -0
- package/android/src/main/java/com/apollohg/editor/CaretGeometry.kt +2 -2
- package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +138 -597
- package/android/src/main/java/com/apollohg/editor/EditorInputConnection.kt +13 -3
- package/android/src/main/java/com/apollohg/editor/InputSnapshotSupport.kt +6 -0
- package/android/src/main/java/com/apollohg/editor/NativeBlockEditorSurface.kt +1086 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +1 -1
- package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +26 -50
- package/android/src/main/java/com/apollohg/editor/NativeImageLoader.kt +89 -0
- package/android/src/main/java/com/apollohg/editor/RemoteSelectionOverlayView.kt +31 -66
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +98 -33
- package/dist/EditorToolbar.d.ts +1 -1
- package/dist/EditorToolbar.js +19 -0
- package/dist/NativeEditorBridge.d.ts +62 -35
- package/dist/NativeEditorBridge.js +122 -79
- package/dist/NativeProseViewer.d.ts +13 -34
- package/dist/NativeProseViewer.js +40 -465
- package/dist/NativeRichTextEditor.d.ts +10 -1
- package/dist/NativeRichTextEditor.js +72 -34
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/schemas.d.ts +11 -0
- package/dist/schemas.js +149 -0
- package/dist/useNativeEditor.d.ts +3 -3
- package/dist/useNativeEditor.js +3 -3
- package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
- package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
- package/ios/Generated_editor_core.swift +97 -2
- package/ios/NativeBlockEditorSurface.swift +1393 -0
- package/ios/NativeEditorExpoView.swift +3 -3
- package/ios/NativeEditorModule.swift +30 -45
- package/ios/NativeInputSupport.swift +396 -0
- package/ios/PositionBridge.swift +4 -4
- package/ios/RichTextEditorView.swift +333 -786
- package/ios/editor_coreFFI/editor_coreFFI.h +147 -70
- package/package.json +1 -1
- package/rust/android/arm64-v8a/libeditor_core.so +0 -0
- package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
- package/rust/android/x86_64/libeditor_core.so +0 -0
- package/rust/bindings/kotlin/uniffi/editor_core/editor_core.kt +2087 -1399
- package/android/src/androidTest/AndroidManifest.xml +0 -8
- package/android/src/androidTest/java/com/apollohg/editor/MeasureHeightInstrumentedTest.kt +0 -53
- package/android/src/androidTest/java/com/apollohg/editor/NativeDeviceCollaborationInitialSyncTest.kt +0 -241
- package/android/src/androidTest/java/com/apollohg/editor/NativeDeviceImeRegressionTest.kt +0 -338
- package/android/src/androidTest/java/com/apollohg/editor/NativeDeviceOutsideTapTest.kt +0 -789
- package/android/src/androidTest/java/com/apollohg/editor/NativeDevicePerformanceTest.kt +0 -350
- package/android/src/androidTest/java/com/apollohg/editor/NativeEditorOutsideTapActivity.kt +0 -5
- package/android/src/main/java/com/apollohg/editor/NativeProseViewerExpoView.kt +0 -315
- package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +0 -2172
- package/android/src/sharedTest/java/com/apollohg/editor/NativePerformanceSupport.kt +0 -482
- package/android/src/test/java/com/apollohg/editor/CaretGeometryTest.kt +0 -137
- package/android/src/test/java/com/apollohg/editor/EditorEditTextHardwareKeyTest.kt +0 -321
- package/android/src/test/java/com/apollohg/editor/EditorInputConnectionTest.kt +0 -4202
- package/android/src/test/java/com/apollohg/editor/NativeEditorExpoViewTest.kt +0 -2581
- package/android/src/test/java/com/apollohg/editor/NativeEditorModuleTest.kt +0 -27
- package/android/src/test/java/com/apollohg/editor/NativePerformanceTest.kt +0 -197
- package/android/src/test/java/com/apollohg/editor/NativeProseViewerExpoViewTest.kt +0 -84
- package/android/src/test/java/com/apollohg/editor/NativeToolbarTest.kt +0 -544
- package/android/src/test/java/com/apollohg/editor/PositionBridgeTest.kt +0 -461
- package/android/src/test/java/com/apollohg/editor/RenderBridgeTest.kt +0 -2054
- package/android/src/test/java/com/apollohg/editor/RichTextEditorViewTest.kt +0 -1367
- package/ios/EditorLayoutManager.swift +0 -774
- package/ios/NativeProseViewerExpoView.swift +0 -276
- package/ios/RenderBridge.swift +0 -1708
- package/ios/Tests/NativePerformanceTests.swift +0 -687
- package/ios/Tests/PositionBridgeTests.swift +0 -706
- package/ios/Tests/RenderBridgeTests.swift +0 -2236
- package/ios/Tests/RichTextEditorViewTests.swift +0 -6548
|
@@ -1,4202 +0,0 @@
|
|
|
1
|
-
package com.openeditor.editor
|
|
2
|
-
|
|
3
|
-
import android.content.ClipData
|
|
4
|
-
import android.content.ClipboardManager
|
|
5
|
-
import android.content.Context
|
|
6
|
-
import android.content.Intent
|
|
7
|
-
import android.graphics.Color
|
|
8
|
-
import android.net.Uri
|
|
9
|
-
import android.os.Bundle
|
|
10
|
-
import android.os.Looper
|
|
11
|
-
import android.provider.Settings
|
|
12
|
-
import android.text.Selection
|
|
13
|
-
import android.text.InputType
|
|
14
|
-
import android.text.style.AbsoluteSizeSpan
|
|
15
|
-
import android.view.KeyEvent
|
|
16
|
-
import android.view.accessibility.AccessibilityNodeInfo
|
|
17
|
-
import android.view.inputmethod.BaseInputConnection
|
|
18
|
-
import android.view.inputmethod.CompletionInfo
|
|
19
|
-
import android.view.inputmethod.CorrectionInfo
|
|
20
|
-
import android.view.inputmethod.EditorInfo
|
|
21
|
-
import org.json.JSONArray
|
|
22
|
-
import org.json.JSONObject
|
|
23
|
-
import org.junit.Assert.assertEquals
|
|
24
|
-
import org.junit.Assert.assertFalse
|
|
25
|
-
import org.junit.Assert.assertNotNull
|
|
26
|
-
import org.junit.Assert.assertNull
|
|
27
|
-
import org.junit.Assert.assertTrue
|
|
28
|
-
import org.junit.Test
|
|
29
|
-
import org.junit.runner.RunWith
|
|
30
|
-
import org.robolectric.Shadows.shadowOf
|
|
31
|
-
import org.robolectric.RobolectricTestRunner
|
|
32
|
-
import org.robolectric.RuntimeEnvironment
|
|
33
|
-
import org.robolectric.annotation.Config
|
|
34
|
-
import java.time.Duration
|
|
35
|
-
|
|
36
|
-
@RunWith(RobolectricTestRunner::class)
|
|
37
|
-
@Config(sdk = [34])
|
|
38
|
-
class EditorInputConnectionTest {
|
|
39
|
-
@Test
|
|
40
|
-
fun `editor input traits use rich text defaults`() {
|
|
41
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
42
|
-
|
|
43
|
-
assertEquals(InputType.TYPE_CLASS_TEXT, editText.inputType and InputType.TYPE_MASK_CLASS)
|
|
44
|
-
assertTrue(editText.inputType hasInputFlag InputType.TYPE_TEXT_FLAG_MULTI_LINE)
|
|
45
|
-
assertTrue(editText.inputType hasInputFlag InputType.TYPE_TEXT_FLAG_AUTO_CORRECT)
|
|
46
|
-
assertTrue(editText.inputType hasInputFlag InputType.TYPE_TEXT_FLAG_CAP_SENTENCES)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
@Test
|
|
50
|
-
fun `editor input traits apply React keyboard props`() {
|
|
51
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
52
|
-
|
|
53
|
-
editText.setKeyboardType("email-address")
|
|
54
|
-
editText.setAutoCapitalize("none")
|
|
55
|
-
editText.setAutoCorrect(false)
|
|
56
|
-
|
|
57
|
-
assertEquals(InputType.TYPE_CLASS_TEXT, editText.inputType and InputType.TYPE_MASK_CLASS)
|
|
58
|
-
assertTrue(editText.inputType hasInputFlag InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS)
|
|
59
|
-
assertTrue(editText.inputType hasInputFlag InputType.TYPE_TEXT_FLAG_MULTI_LINE)
|
|
60
|
-
assertTrue(editText.inputType hasInputFlag InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS)
|
|
61
|
-
assertFalse(editText.inputType hasInputFlag InputType.TYPE_TEXT_FLAG_AUTO_CORRECT)
|
|
62
|
-
assertFalse(editText.inputType hasInputFlag InputType.TYPE_TEXT_FLAG_CAP_SENTENCES)
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
@Test
|
|
66
|
-
fun `cursor caps mode treats rendered empty block start as sentence start`() {
|
|
67
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
68
|
-
editText.applyUpdateJSON(renderBlocksUpdateJson("Hello", "\u200B"), notifyListener = false)
|
|
69
|
-
editText.setSelection(editText.text?.length ?: 0)
|
|
70
|
-
|
|
71
|
-
assertEquals("Hello\n\u200B", editText.text.toString())
|
|
72
|
-
assertTrue(
|
|
73
|
-
editText.cursorCapsModeForEditor(
|
|
74
|
-
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES,
|
|
75
|
-
baseCapsMode = 0
|
|
76
|
-
) hasInputFlag InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
val editorInfo = EditorInfo()
|
|
80
|
-
val inputConnection = editText.onCreateInputConnection(editorInfo)
|
|
81
|
-
assertNotNull(inputConnection)
|
|
82
|
-
assertTrue(editorInfo.initialCapsMode hasInputFlag InputType.TYPE_TEXT_FLAG_CAP_SENTENCES)
|
|
83
|
-
assertTrue(
|
|
84
|
-
inputConnection!!.getCursorCapsMode(InputType.TYPE_TEXT_FLAG_CAP_SENTENCES)
|
|
85
|
-
hasInputFlag InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
|
|
86
|
-
)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
@Test
|
|
90
|
-
fun `text before cursor hides synthetic empty block placeholder from IME context`() {
|
|
91
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
92
|
-
editText.applyUpdateJSON(renderBlocksUpdateJson("Hello", "\u200B"), notifyListener = false)
|
|
93
|
-
editText.setSelection(editText.text?.length ?: 0)
|
|
94
|
-
|
|
95
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
96
|
-
assertNotNull(inputConnection)
|
|
97
|
-
|
|
98
|
-
assertEquals("\n", inputConnection!!.getTextBeforeCursor(1, 0).toString())
|
|
99
|
-
assertEquals("Hello\n", inputConnection.getTextBeforeCursor(20, 0).toString())
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
@Test
|
|
103
|
-
fun `initial surrounding text removes synthetic placeholder for IME sentence caps`() {
|
|
104
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
105
|
-
editText.applyUpdateJSON(renderBlocksUpdateJson("Hello", "\u200B"), notifyListener = false)
|
|
106
|
-
editText.setSelection(editText.text?.length ?: 0)
|
|
107
|
-
|
|
108
|
-
val editorInfo = EditorInfo()
|
|
109
|
-
val inputConnection = editText.onCreateInputConnection(editorInfo)
|
|
110
|
-
assertNotNull(inputConnection)
|
|
111
|
-
|
|
112
|
-
assertEquals("Hello\n", editorInfo.getInitialTextBeforeCursor(20, 0).toString())
|
|
113
|
-
assertEquals(editText.selectionStart - 1, editorInfo.initialSelStart)
|
|
114
|
-
assertEquals(editText.selectionEnd - 1, editorInfo.initialSelEnd)
|
|
115
|
-
assertFalse(editorInfo.getInitialTextBeforeCursor(20, 0).toString().contains("\u200B"))
|
|
116
|
-
assertTrue(
|
|
117
|
-
editorInfo.initialCapsMode hasInputFlag InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
|
|
118
|
-
)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
@Test
|
|
122
|
-
fun `Samsung composing text at rendered line start is sentence capitalized`() {
|
|
123
|
-
val context = RuntimeEnvironment.getApplication()
|
|
124
|
-
val editText = EditorEditText(context)
|
|
125
|
-
editText.applyUpdateJSON(renderBlocksUpdateJson("Hello", "\u200B"), notifyListener = false)
|
|
126
|
-
editText.setSelection(editText.text?.length ?: 0)
|
|
127
|
-
editText.editorId = 1
|
|
128
|
-
|
|
129
|
-
withDefaultInputMethod(context, "com.samsung.android.honeyboard/.service.HoneyBoardService") {
|
|
130
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
131
|
-
assertNotNull(inputConnection)
|
|
132
|
-
|
|
133
|
-
assertTrue(inputConnection!!.setComposingText("test", 1))
|
|
134
|
-
|
|
135
|
-
assertEquals("Test", editText.composingTextForEditor())
|
|
136
|
-
assertTrue(
|
|
137
|
-
editText.imeTraceSnapshotForTesting().any {
|
|
138
|
-
it.contains("samsungSentenceCapsFallback")
|
|
139
|
-
}
|
|
140
|
-
)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
@Test
|
|
145
|
-
fun `cursor caps mode does not force sentence caps mid line`() {
|
|
146
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
147
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello "), notifyListener = false)
|
|
148
|
-
editText.setSelection(editText.text?.length ?: 0)
|
|
149
|
-
|
|
150
|
-
assertFalse(
|
|
151
|
-
editText.cursorCapsModeForEditor(
|
|
152
|
-
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES,
|
|
153
|
-
baseCapsMode = 0
|
|
154
|
-
) hasInputFlag InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
|
|
155
|
-
)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
@Test
|
|
159
|
-
fun `editor numeric keyboard type maps to numeric input class`() {
|
|
160
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
161
|
-
|
|
162
|
-
editText.setKeyboardType("numeric")
|
|
163
|
-
|
|
164
|
-
assertEquals(InputType.TYPE_CLASS_NUMBER, editText.inputType and InputType.TYPE_MASK_CLASS)
|
|
165
|
-
assertTrue(editText.inputType hasInputFlag InputType.TYPE_NUMBER_FLAG_DECIMAL)
|
|
166
|
-
assertTrue(editText.inputType hasInputFlag InputType.TYPE_NUMBER_FLAG_SIGNED)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
@Test
|
|
170
|
-
fun `input trait changes stale old connection and fresh connection accepts input`() {
|
|
171
|
-
val traitChanges: List<(EditorEditText) -> Unit> = listOf(
|
|
172
|
-
{ it.setAutoCorrect(false) },
|
|
173
|
-
{ it.setKeyboardType("email-address") }
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
for (changeTrait in traitChanges) {
|
|
177
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
178
|
-
editText.applyUpdateJSON(renderUpdateJson("abc"), notifyListener = false)
|
|
179
|
-
editText.setSelection(3)
|
|
180
|
-
editText.editorId = 1
|
|
181
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
182
|
-
|
|
183
|
-
var insertedText: String? = null
|
|
184
|
-
var insertedScalar: Int? = null
|
|
185
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
186
|
-
insertedText = text
|
|
187
|
-
insertedScalar = scalar
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
val oldConnection = editText.onCreateInputConnection(EditorInfo())
|
|
191
|
-
assertNotNull(oldConnection)
|
|
192
|
-
|
|
193
|
-
changeTrait(editText)
|
|
194
|
-
|
|
195
|
-
assertTrue(oldConnection!!.commitText("old", 1))
|
|
196
|
-
assertNull(insertedText)
|
|
197
|
-
|
|
198
|
-
val freshConnection = editText.onCreateInputConnection(EditorInfo())
|
|
199
|
-
assertNotNull(freshConnection)
|
|
200
|
-
assertTrue(freshConnection!!.commitText("fresh", 1))
|
|
201
|
-
|
|
202
|
-
assertEquals("fresh", insertedText)
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
@Test
|
|
207
|
-
fun `external clear keeps same editor input connection accepting input`() {
|
|
208
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
209
|
-
editText.applyUpdateJSON(renderUpdateJson("sent"), notifyListener = false)
|
|
210
|
-
assertTrue(editText.requestFocus())
|
|
211
|
-
editText.setSelection(4)
|
|
212
|
-
editText.editorId = 1
|
|
213
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
214
|
-
|
|
215
|
-
var insertedText: String? = null
|
|
216
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
217
|
-
insertedText = text
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
221
|
-
assertNotNull(inputConnection)
|
|
222
|
-
|
|
223
|
-
editText.applyUpdateJSON(
|
|
224
|
-
renderUpdateJson("\u200B"),
|
|
225
|
-
notifyListener = false,
|
|
226
|
-
refreshInputConnectionForExternalUpdate = true
|
|
227
|
-
)
|
|
228
|
-
editText.setSelection(editText.text?.length ?: 0)
|
|
229
|
-
|
|
230
|
-
assertTrue(
|
|
231
|
-
editText.imeTraceSnapshotForTesting().any {
|
|
232
|
-
it.contains("restartInput:source=externalUpdate")
|
|
233
|
-
}
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
assertTrue(inputConnection!!.commitText("fresh", 1))
|
|
237
|
-
|
|
238
|
-
assertEquals("fresh", insertedText)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
@Test
|
|
242
|
-
fun `external clear keeps same editor input connection accepting composition`() {
|
|
243
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
244
|
-
editText.applyUpdateJSON(renderUpdateJson("sent"), notifyListener = false)
|
|
245
|
-
assertTrue(editText.requestFocus())
|
|
246
|
-
editText.setSelection(4)
|
|
247
|
-
editText.editorId = 1
|
|
248
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
249
|
-
|
|
250
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
251
|
-
assertNotNull(inputConnection)
|
|
252
|
-
|
|
253
|
-
editText.applyUpdateJSON(
|
|
254
|
-
renderUpdateJson("\u200B"),
|
|
255
|
-
notifyListener = false,
|
|
256
|
-
refreshInputConnectionForExternalUpdate = true
|
|
257
|
-
)
|
|
258
|
-
editText.setSelection(editText.text?.length ?: 0)
|
|
259
|
-
|
|
260
|
-
assertTrue(inputConnection!!.setComposingText("f", 1))
|
|
261
|
-
assertTrue(editText.text?.toString()?.contains("f") == true)
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
@Test
|
|
265
|
-
fun `external clear invalidates rendered editor content`() {
|
|
266
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
267
|
-
editText.applyUpdateJSON(renderUpdateJson("sent"), notifyListener = false)
|
|
268
|
-
shadowOf(editText).clearWasInvalidated()
|
|
269
|
-
|
|
270
|
-
editText.applyUpdateJSON(
|
|
271
|
-
renderUpdateJson(""),
|
|
272
|
-
notifyListener = false,
|
|
273
|
-
refreshInputConnectionForExternalUpdate = true
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
assertEquals("", editText.text?.toString())
|
|
277
|
-
assertTrue(shadowOf(editText).wasInvalidated())
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
@Test
|
|
281
|
-
fun `external clear after deferred Rust update clears stale visible text`() {
|
|
282
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
283
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
284
|
-
assertTrue(editText.requestFocus())
|
|
285
|
-
editText.setSelection(0)
|
|
286
|
-
editText.editorId = 1
|
|
287
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
288
|
-
|
|
289
|
-
var insertedText: String? = null
|
|
290
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
291
|
-
insertedText = text
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
295
|
-
assertNotNull(inputConnection)
|
|
296
|
-
|
|
297
|
-
editText.runWithDeferredRustUpdateApplication {
|
|
298
|
-
editText.runWithTransientInputMutationGuard {
|
|
299
|
-
editText.text!!.insert(0, "second")
|
|
300
|
-
editText.setSelection(6)
|
|
301
|
-
true
|
|
302
|
-
}
|
|
303
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson("second"))
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
assertTrue(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
307
|
-
assertEquals("second", editText.text?.toString())
|
|
308
|
-
|
|
309
|
-
editText.applyUpdateJSON(
|
|
310
|
-
renderUpdateJson(""),
|
|
311
|
-
notifyListener = false,
|
|
312
|
-
refreshInputConnectionForExternalUpdate = true
|
|
313
|
-
)
|
|
314
|
-
editText.setSelection(editText.text?.length ?: 0)
|
|
315
|
-
|
|
316
|
-
assertFalse(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
317
|
-
assertEquals("", editText.text?.toString())
|
|
318
|
-
|
|
319
|
-
assertTrue(inputConnection!!.commitText("next", 1))
|
|
320
|
-
assertEquals("next", insertedText)
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
@Test
|
|
324
|
-
fun `external clear after preflight native mutation keeps same editor input connection accepting input`() {
|
|
325
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
326
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
327
|
-
assertTrue(editText.requestFocus())
|
|
328
|
-
editText.setSelection(0)
|
|
329
|
-
editText.editorId = 1
|
|
330
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
331
|
-
|
|
332
|
-
var renderedText = ""
|
|
333
|
-
var insertedText: String? = null
|
|
334
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
335
|
-
insertedText = text
|
|
336
|
-
renderedText = renderedText.substring(0, scalar.coerceIn(0, renderedText.length)) +
|
|
337
|
-
text +
|
|
338
|
-
renderedText.substring(scalar.coerceIn(0, renderedText.length))
|
|
339
|
-
editText.applyUpdateJSON(renderUpdateJson(renderedText), notifyListener = false)
|
|
340
|
-
editText.setSelection(renderedText.length)
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
344
|
-
assertNotNull(inputConnection)
|
|
345
|
-
|
|
346
|
-
editText.runWithTransientInputMutationGuard {
|
|
347
|
-
editText.text!!.insert(0, "second")
|
|
348
|
-
editText.setSelection(6)
|
|
349
|
-
true
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
assertTrue(editText.prepareForExternalEditorUpdate())
|
|
353
|
-
assertEquals("second", insertedText)
|
|
354
|
-
assertEquals("second", editText.text?.toString())
|
|
355
|
-
|
|
356
|
-
renderedText = ""
|
|
357
|
-
insertedText = null
|
|
358
|
-
editText.applyUpdateJSON(
|
|
359
|
-
renderUpdateJson("\u200B"),
|
|
360
|
-
notifyListener = false,
|
|
361
|
-
refreshInputConnectionForExternalUpdate = true
|
|
362
|
-
)
|
|
363
|
-
editText.setSelection(editText.text?.length ?: 0)
|
|
364
|
-
|
|
365
|
-
assertEquals("\u200B", editText.text?.toString())
|
|
366
|
-
assertTrue(inputConnection!!.commitText("next", 1))
|
|
367
|
-
assertEquals("next", insertedText)
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
@Test
|
|
371
|
-
fun `destroyed editor input session consumes IME changes without Rust mutation`() {
|
|
372
|
-
val editorId = 880001L
|
|
373
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
374
|
-
editText.applyUpdateJSON(renderUpdateJson("abc"), notifyListener = false)
|
|
375
|
-
editText.setSelection(3)
|
|
376
|
-
editText.editorId = editorId
|
|
377
|
-
|
|
378
|
-
var insertedText: String? = null
|
|
379
|
-
var replacedText: Triple<Int, Int, String>? = null
|
|
380
|
-
var deletedRange: Pair<Int, Int>? = null
|
|
381
|
-
var deletedBackward: Pair<Int, Int>? = null
|
|
382
|
-
var syncedSelection: Pair<Int, Int>? = null
|
|
383
|
-
editText.onInsertTextInRustForTesting = { text, _ -> insertedText = text }
|
|
384
|
-
editText.onReplaceTextInRustForTesting = { from, to, text ->
|
|
385
|
-
replacedText = Triple(from, to, text)
|
|
386
|
-
}
|
|
387
|
-
editText.onDeleteRangeInRustForTesting = { from, to -> deletedRange = from to to }
|
|
388
|
-
editText.onDeleteBackwardAtSelectionScalarInRustForTesting = { anchor, head ->
|
|
389
|
-
deletedBackward = anchor to head
|
|
390
|
-
}
|
|
391
|
-
editText.onSetSelectionScalarInRustForTesting = { anchor, head ->
|
|
392
|
-
syncedSelection = anchor to head
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
NativeEditorViewRegistry.markEditorCreated(editorId)
|
|
396
|
-
try {
|
|
397
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
398
|
-
assertNotNull(inputConnection)
|
|
399
|
-
|
|
400
|
-
NativeEditorViewRegistry.invalidateDestroyedEditor(editorId)
|
|
401
|
-
|
|
402
|
-
assertTrue(inputConnection!!.commitText("x", 1))
|
|
403
|
-
assertTrue(inputConnection.commitCompletion(CompletionInfo(0, 0, "done")))
|
|
404
|
-
assertTrue(inputConnection.commitCorrection(CorrectionInfo(0, "abc", "xyz")))
|
|
405
|
-
assertTrue(inputConnection.setComposingText("z", 1))
|
|
406
|
-
assertTrue(inputConnection.deleteSurroundingText(1, 0))
|
|
407
|
-
editText.setSelection(0)
|
|
408
|
-
|
|
409
|
-
assertNull(insertedText)
|
|
410
|
-
assertNull(replacedText)
|
|
411
|
-
assertNull(deletedRange)
|
|
412
|
-
assertNull(deletedBackward)
|
|
413
|
-
assertNull(syncedSelection)
|
|
414
|
-
} finally {
|
|
415
|
-
NativeEditorViewRegistry.markEditorCreated(editorId)
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
@Test
|
|
420
|
-
fun `input trait change during active composition restores authorized text before fresh input`() {
|
|
421
|
-
val traitChanges: List<(EditorEditText) -> Unit> = listOf(
|
|
422
|
-
{ it.setAutoCorrect(false) },
|
|
423
|
-
{ it.setKeyboardType("email-address") }
|
|
424
|
-
)
|
|
425
|
-
|
|
426
|
-
for (changeTrait in traitChanges) {
|
|
427
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
428
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
429
|
-
editText.setSelection(6)
|
|
430
|
-
editText.editorId = 1
|
|
431
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
432
|
-
|
|
433
|
-
var insertedText: String? = null
|
|
434
|
-
var insertedScalar: Int? = null
|
|
435
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
436
|
-
insertedText = text
|
|
437
|
-
insertedScalar = scalar
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
val oldConnection = editText.onCreateInputConnection(EditorInfo())
|
|
441
|
-
assertNotNull(oldConnection)
|
|
442
|
-
assertTrue(oldConnection!!.setComposingText("brave ", 1))
|
|
443
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
444
|
-
|
|
445
|
-
changeTrait(editText)
|
|
446
|
-
|
|
447
|
-
assertEquals("Hello world", editText.text?.toString())
|
|
448
|
-
assertEquals(6, editText.selectionStart)
|
|
449
|
-
assertEquals(6, editText.selectionEnd)
|
|
450
|
-
|
|
451
|
-
assertTrue(oldConnection.commitText("brave ", 1))
|
|
452
|
-
assertNull(insertedText)
|
|
453
|
-
assertNull(insertedScalar)
|
|
454
|
-
|
|
455
|
-
val freshConnection = editText.onCreateInputConnection(EditorInfo())
|
|
456
|
-
assertNotNull(freshConnection)
|
|
457
|
-
assertTrue(freshConnection!!.commitText("fresh", 1))
|
|
458
|
-
|
|
459
|
-
assertEquals("fresh", insertedText)
|
|
460
|
-
assertEquals(6, insertedScalar)
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
@Test
|
|
465
|
-
fun `old input connection remains usable after framework recreation from render`() {
|
|
466
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
467
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
468
|
-
editText.setSelection(0)
|
|
469
|
-
editText.editorId = 1
|
|
470
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
471
|
-
|
|
472
|
-
val inserted = mutableListOf<Pair<String, Int>>()
|
|
473
|
-
val rendered = StringBuilder()
|
|
474
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
475
|
-
inserted.add(text to scalar)
|
|
476
|
-
rendered.insert(scalar.coerceIn(0, rendered.length), text)
|
|
477
|
-
editText.applyUpdateJSON(renderUpdateJson(rendered.toString()), notifyListener = false)
|
|
478
|
-
editText.setSelection(rendered.length)
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
val originalConnection = editText.onCreateInputConnection(EditorInfo())
|
|
482
|
-
assertNotNull(originalConnection)
|
|
483
|
-
assertTrue(originalConnection!!.commitText("a", 1))
|
|
484
|
-
|
|
485
|
-
val recreatedConnection = editText.onCreateInputConnection(EditorInfo())
|
|
486
|
-
assertNotNull(recreatedConnection)
|
|
487
|
-
|
|
488
|
-
assertTrue(originalConnection.commitText("b", 1))
|
|
489
|
-
|
|
490
|
-
assertEquals(listOf("a" to 0, "b" to 1), inserted)
|
|
491
|
-
assertEquals("ab", editText.text?.toString())
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
@Test
|
|
495
|
-
fun `input trait change suppresses stale direct native mutation adoption`() {
|
|
496
|
-
val traitChanges: List<(EditorEditText) -> Unit> = listOf(
|
|
497
|
-
{ it.setAutoCorrect(false) },
|
|
498
|
-
{ it.setKeyboardType("email-address") }
|
|
499
|
-
)
|
|
500
|
-
|
|
501
|
-
for (changeTrait in traitChanges) {
|
|
502
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
503
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
504
|
-
assertTrue(editText.requestFocus())
|
|
505
|
-
editText.setSelection(6)
|
|
506
|
-
editText.editorId = 1
|
|
507
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
508
|
-
|
|
509
|
-
var insertedText: String? = null
|
|
510
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
511
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
512
|
-
insertedText = text
|
|
513
|
-
}
|
|
514
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
515
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
val oldConnection = editText.onCreateInputConnection(EditorInfo())
|
|
519
|
-
assertNotNull(oldConnection)
|
|
520
|
-
assertTrue(oldConnection!!.setComposingText("brave ", 1))
|
|
521
|
-
|
|
522
|
-
changeTrait(editText)
|
|
523
|
-
assertEquals("Hello world", editText.text?.toString())
|
|
524
|
-
|
|
525
|
-
editText.runWithTransientInputMutationGuard {
|
|
526
|
-
editText.text!!.insert(6, "stale ")
|
|
527
|
-
true
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
assertFalse(editText.prepareForExternalEditorUpdate())
|
|
531
|
-
assertNull(insertedText)
|
|
532
|
-
assertNull(replacement)
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
@Test
|
|
537
|
-
fun `native mutation adoption suppression clears after authorized render update`() {
|
|
538
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
539
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
540
|
-
assertTrue(editText.requestFocus())
|
|
541
|
-
editText.setSelection(6)
|
|
542
|
-
editText.editorId = 1
|
|
543
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
544
|
-
|
|
545
|
-
var insertedText: String? = null
|
|
546
|
-
var insertedScalar: Int? = null
|
|
547
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
548
|
-
insertedText = text
|
|
549
|
-
insertedScalar = scalar
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
val oldConnection = editText.onCreateInputConnection(EditorInfo())
|
|
553
|
-
assertNotNull(oldConnection)
|
|
554
|
-
assertTrue(oldConnection!!.setComposingText("brave ", 1))
|
|
555
|
-
|
|
556
|
-
editText.setAutoCorrect(false)
|
|
557
|
-
editText.runWithTransientInputMutationGuard {
|
|
558
|
-
editText.text!!.insert(6, "stale ")
|
|
559
|
-
true
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
assertFalse(editText.prepareForExternalEditorUpdate())
|
|
563
|
-
assertNull(insertedText)
|
|
564
|
-
assertNull(insertedScalar)
|
|
565
|
-
|
|
566
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
567
|
-
editText.text!!.insert(6, "fresh ")
|
|
568
|
-
|
|
569
|
-
assertEquals("fresh ", insertedText)
|
|
570
|
-
assertEquals(6, insertedScalar)
|
|
571
|
-
assertEquals(0, editText.reconciliationCount)
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
@Test
|
|
575
|
-
fun `native mutation adoption suppression clears after skipped authorized render update`() {
|
|
576
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication()).apply {
|
|
577
|
-
captureApplyUpdateTraceForTesting = true
|
|
578
|
-
}
|
|
579
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
580
|
-
assertTrue(editText.requestFocus())
|
|
581
|
-
editText.setSelection(6)
|
|
582
|
-
editText.editorId = 1
|
|
583
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
584
|
-
|
|
585
|
-
var insertedText: String? = null
|
|
586
|
-
var insertedScalar: Int? = null
|
|
587
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
588
|
-
insertedText = text
|
|
589
|
-
insertedScalar = scalar
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
val oldConnection = editText.onCreateInputConnection(EditorInfo())
|
|
593
|
-
assertNotNull(oldConnection)
|
|
594
|
-
assertTrue(oldConnection!!.setComposingText("brave ", 1))
|
|
595
|
-
|
|
596
|
-
editText.setAutoCorrect(false)
|
|
597
|
-
assertEquals("Hello world", editText.text?.toString())
|
|
598
|
-
|
|
599
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
600
|
-
assertTrue(editText.lastApplyUpdateTrace()?.skippedRender == true)
|
|
601
|
-
|
|
602
|
-
editText.text!!.insert(6, "fresh ")
|
|
603
|
-
|
|
604
|
-
assertEquals("fresh ", insertedText)
|
|
605
|
-
assertEquals(6, insertedScalar)
|
|
606
|
-
assertEquals(0, editText.reconciliationCount)
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
@Test
|
|
610
|
-
fun `code point delete length matches ascii backspace`() {
|
|
611
|
-
val text = "Hello"
|
|
612
|
-
val cursor = 5
|
|
613
|
-
|
|
614
|
-
val beforeUtf16Length = EditorInputConnection.codePointsToUtf16Length(
|
|
615
|
-
text = text,
|
|
616
|
-
fromUtf16Offset = cursor,
|
|
617
|
-
codePointCount = 1,
|
|
618
|
-
forward = false
|
|
619
|
-
)
|
|
620
|
-
|
|
621
|
-
assertEquals(1, beforeUtf16Length)
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
@Test
|
|
625
|
-
fun `code point delete length counts surrogate pair as two utf16 code units`() {
|
|
626
|
-
val text = "A😀B"
|
|
627
|
-
val cursor = 3
|
|
628
|
-
|
|
629
|
-
val beforeUtf16Length = EditorInputConnection.codePointsToUtf16Length(
|
|
630
|
-
text = text,
|
|
631
|
-
fromUtf16Offset = cursor,
|
|
632
|
-
codePointCount = 1,
|
|
633
|
-
forward = false
|
|
634
|
-
)
|
|
635
|
-
|
|
636
|
-
assertEquals(2, beforeUtf16Length)
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
@Test
|
|
640
|
-
fun `code point forward delete length counts surrogate pair as two utf16 code units`() {
|
|
641
|
-
val text = "A😀B"
|
|
642
|
-
val cursor = 1
|
|
643
|
-
|
|
644
|
-
val afterUtf16Length = EditorInputConnection.codePointsToUtf16Length(
|
|
645
|
-
text = text,
|
|
646
|
-
fromUtf16Offset = cursor,
|
|
647
|
-
codePointCount = 1,
|
|
648
|
-
forward = true
|
|
649
|
-
)
|
|
650
|
-
|
|
651
|
-
assertEquals(2, afterUtf16Length)
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
@Test
|
|
655
|
-
fun `read only composing text and region are consumed without mutating text`() {
|
|
656
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
657
|
-
editText.applyUpdateJSON(renderUpdateJson("abc"), notifyListener = false)
|
|
658
|
-
editText.setSelection(1)
|
|
659
|
-
editText.editorId = 1
|
|
660
|
-
editText.isEditable = false
|
|
661
|
-
|
|
662
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
663
|
-
assertNotNull(inputConnection)
|
|
664
|
-
|
|
665
|
-
assertTrue(inputConnection!!.setComposingText("X", 1))
|
|
666
|
-
assertTrue(inputConnection.setComposingRegion(0, 2))
|
|
667
|
-
assertEquals("abc", editText.text?.toString())
|
|
668
|
-
assertNull(editText.composingTextForEditor())
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
@Test
|
|
672
|
-
fun `read only input connection mutations are consumed without mutating text`() {
|
|
673
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
674
|
-
editText.applyUpdateJSON(renderUpdateJson("abc"), notifyListener = false)
|
|
675
|
-
editText.setSelection(3)
|
|
676
|
-
editText.editorId = 1
|
|
677
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
678
|
-
assertNotNull(inputConnection)
|
|
679
|
-
|
|
680
|
-
editText.isEditable = false
|
|
681
|
-
|
|
682
|
-
assertTrue(inputConnection!!.commitText("X", 1))
|
|
683
|
-
assertTrue(inputConnection.deleteSurroundingText(1, 0))
|
|
684
|
-
assertTrue(inputConnection.deleteSurroundingTextInCodePoints(1, 0))
|
|
685
|
-
assertTrue(inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)))
|
|
686
|
-
assertEquals("abc", editText.text?.toString())
|
|
687
|
-
assertEquals(3, editText.selectionStart)
|
|
688
|
-
assertEquals(3, editText.selectionEnd)
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
@Test
|
|
692
|
-
fun `composing text does not trigger reconciliation while edit text is transient`() {
|
|
693
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
694
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
695
|
-
editText.setSelection(0)
|
|
696
|
-
editText.editorId = 1
|
|
697
|
-
|
|
698
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
699
|
-
assertNotNull(inputConnection)
|
|
700
|
-
|
|
701
|
-
val handled = inputConnection!!.setComposingText("abc", 1)
|
|
702
|
-
|
|
703
|
-
assertTrue(handled)
|
|
704
|
-
assertEquals("abc", editText.text?.toString())
|
|
705
|
-
assertEquals(0, editText.reconciliationCount)
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
@Test
|
|
709
|
-
fun `commit text uses original authorized offset while composing text is visible`() {
|
|
710
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
711
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
712
|
-
editText.setSelection(6)
|
|
713
|
-
editText.editorId = 1
|
|
714
|
-
|
|
715
|
-
var insertedText: String? = null
|
|
716
|
-
var insertedScalar: Int? = null
|
|
717
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
718
|
-
insertedText = text
|
|
719
|
-
insertedScalar = scalar
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
723
|
-
assertNotNull(inputConnection)
|
|
724
|
-
inputConnection!!.setComposingText("brave ", 1)
|
|
725
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
726
|
-
|
|
727
|
-
val handled = inputConnection.commitText("brave ", 1)
|
|
728
|
-
|
|
729
|
-
assertTrue(handled)
|
|
730
|
-
assertEquals("brave ", insertedText)
|
|
731
|
-
assertEquals(6, insertedScalar)
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
@Test
|
|
735
|
-
fun `composing region after visible composing text preserves original authorized range`() {
|
|
736
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
737
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
738
|
-
editText.setSelection(6)
|
|
739
|
-
editText.editorId = 1
|
|
740
|
-
|
|
741
|
-
var insertedText: String? = null
|
|
742
|
-
var insertedScalar: Int? = null
|
|
743
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
744
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
745
|
-
insertedText = text
|
|
746
|
-
insertedScalar = scalar
|
|
747
|
-
}
|
|
748
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
749
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
753
|
-
assertNotNull(inputConnection)
|
|
754
|
-
assertTrue(inputConnection!!.setComposingText("brave ", 1))
|
|
755
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
756
|
-
|
|
757
|
-
assertTrue(inputConnection.setComposingRegion(0, 5))
|
|
758
|
-
assertTrue(inputConnection.commitText("brave ", 1))
|
|
759
|
-
|
|
760
|
-
assertEquals("brave ", insertedText)
|
|
761
|
-
assertEquals(6, insertedScalar)
|
|
762
|
-
assertEquals(null, replacement)
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
@Test
|
|
766
|
-
fun `repeated composing region updates authorized replacement before visible composing text`() {
|
|
767
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
768
|
-
editText.applyUpdateJSON(renderUpdateJson("abcde"), notifyListener = false)
|
|
769
|
-
editText.setSelection(0)
|
|
770
|
-
editText.editorId = 1
|
|
771
|
-
|
|
772
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
773
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
774
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
778
|
-
assertNotNull(inputConnection)
|
|
779
|
-
|
|
780
|
-
assertTrue(inputConnection!!.setComposingRegion(0, 1))
|
|
781
|
-
assertTrue(inputConnection.setComposingRegion(0, 5))
|
|
782
|
-
assertTrue(inputConnection.commitText("ABCDE", 1))
|
|
783
|
-
|
|
784
|
-
assertEquals(Triple(0, 5, "ABCDE"), replacement)
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
@Test
|
|
788
|
-
fun `commit text replaces original authorized selection while composing text is visible`() {
|
|
789
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
790
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
791
|
-
editText.setSelection(6, 11)
|
|
792
|
-
editText.editorId = 1
|
|
793
|
-
|
|
794
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
795
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
796
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
800
|
-
assertNotNull(inputConnection)
|
|
801
|
-
inputConnection!!.setComposingText("there", 1)
|
|
802
|
-
assertEquals("Hello there", editText.text?.toString())
|
|
803
|
-
|
|
804
|
-
val handled = inputConnection.commitText("there", 1)
|
|
805
|
-
|
|
806
|
-
assertTrue(handled)
|
|
807
|
-
assertEquals(Triple(6, 11, "there"), replacement)
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
@Test
|
|
811
|
-
fun `delete during composition edits transient text without mutating rust`() {
|
|
812
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
813
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
814
|
-
editText.setSelection(0)
|
|
815
|
-
editText.editorId = 1
|
|
816
|
-
|
|
817
|
-
var deleteCalled = false
|
|
818
|
-
editText.onDeleteRangeInRustForTesting = { _, _ ->
|
|
819
|
-
deleteCalled = true
|
|
820
|
-
}
|
|
821
|
-
editText.onInsertTextInRustForTesting = { _, _ -> }
|
|
822
|
-
var insertedText: String? = null
|
|
823
|
-
var insertedScalar: Int? = null
|
|
824
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
825
|
-
insertedText = text
|
|
826
|
-
insertedScalar = scalar
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
830
|
-
assertNotNull(inputConnection)
|
|
831
|
-
inputConnection!!.setComposingText("abc", 1)
|
|
832
|
-
|
|
833
|
-
val deleteHandled = inputConnection.deleteSurroundingText(1, 0)
|
|
834
|
-
val commitHandled = inputConnection.commitText("ab", 1)
|
|
835
|
-
|
|
836
|
-
assertTrue(deleteHandled)
|
|
837
|
-
assertTrue(commitHandled)
|
|
838
|
-
assertFalse(deleteCalled)
|
|
839
|
-
assertEquals("ab", insertedText)
|
|
840
|
-
assertEquals(0, insertedScalar)
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
@Test
|
|
844
|
-
fun `key event backspace during composition edits transient text without mutating rust`() {
|
|
845
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
846
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello "), notifyListener = false)
|
|
847
|
-
editText.setSelection(6)
|
|
848
|
-
editText.editorId = 1
|
|
849
|
-
|
|
850
|
-
var deleteCalled = false
|
|
851
|
-
editText.onDeleteRangeInRustForTesting = { _, _ ->
|
|
852
|
-
deleteCalled = true
|
|
853
|
-
}
|
|
854
|
-
editText.onInsertTextInRustForTesting = { _, _ -> }
|
|
855
|
-
|
|
856
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
857
|
-
assertNotNull(inputConnection)
|
|
858
|
-
assertTrue(inputConnection!!.setComposingText("abc", 1))
|
|
859
|
-
|
|
860
|
-
assertTrue(inputConnection.sendKeyEvent(android.view.KeyEvent(
|
|
861
|
-
android.view.KeyEvent.ACTION_DOWN,
|
|
862
|
-
android.view.KeyEvent.KEYCODE_DEL
|
|
863
|
-
)))
|
|
864
|
-
assertTrue(inputConnection.commitText("ab", 1))
|
|
865
|
-
|
|
866
|
-
assertFalse(deleteCalled)
|
|
867
|
-
assertEquals(0, editText.reconciliationCount)
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
@Test
|
|
871
|
-
fun `duplicate composition key event across view and input connection edits transient text once`() {
|
|
872
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
873
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello "), notifyListener = false)
|
|
874
|
-
editText.setSelection(6)
|
|
875
|
-
editText.editorId = 1
|
|
876
|
-
|
|
877
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
878
|
-
assertNotNull(inputConnection)
|
|
879
|
-
assertTrue(inputConnection!!.setComposingText("abc", 1))
|
|
880
|
-
|
|
881
|
-
val event = KeyEvent(100L, 100L, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0)
|
|
882
|
-
assertTrue(editText.dispatchKeyEvent(event))
|
|
883
|
-
assertTrue(inputConnection.sendKeyEvent(event))
|
|
884
|
-
|
|
885
|
-
assertEquals("Hello ab", editText.text?.toString())
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
@Test
|
|
889
|
-
fun `duplicate forward delete composition key event stays on transient composition path`() {
|
|
890
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
891
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
892
|
-
editText.setSelection(0)
|
|
893
|
-
editText.editorId = 1
|
|
894
|
-
|
|
895
|
-
var deleteCalled = false
|
|
896
|
-
editText.onDeleteRangeInRustForTesting = { _, _ ->
|
|
897
|
-
deleteCalled = true
|
|
898
|
-
}
|
|
899
|
-
var insertedText: String? = null
|
|
900
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
901
|
-
insertedText = text
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
905
|
-
assertNotNull(inputConnection)
|
|
906
|
-
assertTrue(inputConnection!!.setComposingText("abc", 1))
|
|
907
|
-
editText.setSelection(0)
|
|
908
|
-
|
|
909
|
-
val event = KeyEvent(100L, 100L, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_FORWARD_DEL, 0)
|
|
910
|
-
assertTrue(editText.dispatchKeyEvent(event))
|
|
911
|
-
assertTrue(inputConnection.sendKeyEvent(event))
|
|
912
|
-
|
|
913
|
-
assertFalse(deleteCalled)
|
|
914
|
-
assertEquals("bc", editText.text?.toString())
|
|
915
|
-
assertTrue(inputConnection.finishComposingText())
|
|
916
|
-
assertEquals("bc", insertedText)
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
@Test
|
|
920
|
-
fun `forward delete composition edit refreshes composing text before finish commit`() {
|
|
921
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
922
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
923
|
-
editText.setSelection(0)
|
|
924
|
-
editText.editorId = 1
|
|
925
|
-
|
|
926
|
-
var deleteCalled = false
|
|
927
|
-
editText.onDeleteRangeInRustForTesting = { _, _ ->
|
|
928
|
-
deleteCalled = true
|
|
929
|
-
}
|
|
930
|
-
var insertedText: String? = null
|
|
931
|
-
var insertedScalar: Int? = null
|
|
932
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
933
|
-
insertedText = text
|
|
934
|
-
insertedScalar = scalar
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
938
|
-
assertNotNull(inputConnection)
|
|
939
|
-
assertTrue(inputConnection!!.setComposingText("abc", 1))
|
|
940
|
-
editText.setSelection(0)
|
|
941
|
-
|
|
942
|
-
assertTrue(inputConnection.deleteSurroundingText(0, 1))
|
|
943
|
-
assertTrue(inputConnection.finishComposingText())
|
|
944
|
-
|
|
945
|
-
assertFalse(deleteCalled)
|
|
946
|
-
assertEquals("bc", insertedText)
|
|
947
|
-
assertEquals(0, insertedScalar)
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
@Test
|
|
951
|
-
fun `hardware backspace composition fallback does not split emoji surrogate pair`() {
|
|
952
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
953
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
954
|
-
editText.setSelection(0)
|
|
955
|
-
editText.editorId = 1
|
|
956
|
-
|
|
957
|
-
var insertedText: String? = null
|
|
958
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
959
|
-
insertedText = text
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
963
|
-
assertNotNull(inputConnection)
|
|
964
|
-
assertTrue(inputConnection!!.setComposingText("😀", 1))
|
|
965
|
-
editText.setSelection("😀".length)
|
|
966
|
-
|
|
967
|
-
val event = KeyEvent(100L, 100L, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0)
|
|
968
|
-
assertTrue(editText.dispatchKeyEvent(event))
|
|
969
|
-
|
|
970
|
-
assertEquals("", editText.text?.toString())
|
|
971
|
-
assertTrue(inputConnection.finishComposingText())
|
|
972
|
-
assertNull(insertedText)
|
|
973
|
-
assertEquals(0, editText.reconciliationCount)
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
@Test
|
|
977
|
-
fun `hardware forward delete composition fallback does not split emoji surrogate pair`() {
|
|
978
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
979
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
980
|
-
editText.setSelection(0)
|
|
981
|
-
editText.editorId = 1
|
|
982
|
-
|
|
983
|
-
var insertedText: String? = null
|
|
984
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
985
|
-
insertedText = text
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
989
|
-
assertNotNull(inputConnection)
|
|
990
|
-
assertTrue(inputConnection!!.setComposingText("😀", 1))
|
|
991
|
-
editText.setSelection(0)
|
|
992
|
-
|
|
993
|
-
val event = KeyEvent(100L, 100L, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_FORWARD_DEL, 0)
|
|
994
|
-
assertTrue(editText.dispatchKeyEvent(event))
|
|
995
|
-
|
|
996
|
-
assertEquals("", editText.text?.toString())
|
|
997
|
-
assertTrue(inputConnection.finishComposingText())
|
|
998
|
-
assertNull(insertedText)
|
|
999
|
-
assertEquals(0, editText.reconciliationCount)
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
@Test
|
|
1003
|
-
fun `hardware backspace inside composing emoji deletes whole surrogate pair`() {
|
|
1004
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1005
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
1006
|
-
editText.setSelection(0)
|
|
1007
|
-
editText.editorId = 1
|
|
1008
|
-
|
|
1009
|
-
var insertedText: String? = null
|
|
1010
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1011
|
-
insertedText = text
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1015
|
-
assertNotNull(inputConnection)
|
|
1016
|
-
assertTrue(inputConnection!!.setComposingText("😀", 1))
|
|
1017
|
-
editText.setSelection(1)
|
|
1018
|
-
|
|
1019
|
-
val event = KeyEvent(100L, 100L, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0)
|
|
1020
|
-
assertTrue(editText.dispatchKeyEvent(event))
|
|
1021
|
-
|
|
1022
|
-
assertEquals("", editText.text?.toString())
|
|
1023
|
-
assertTrue(inputConnection.finishComposingText())
|
|
1024
|
-
assertNull(insertedText)
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
@Test
|
|
1028
|
-
fun `hardware forward delete inside composing emoji deletes whole surrogate pair`() {
|
|
1029
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1030
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
1031
|
-
editText.setSelection(0)
|
|
1032
|
-
editText.editorId = 1
|
|
1033
|
-
|
|
1034
|
-
var insertedText: String? = null
|
|
1035
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1036
|
-
insertedText = text
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1040
|
-
assertNotNull(inputConnection)
|
|
1041
|
-
assertTrue(inputConnection!!.setComposingText("😀", 1))
|
|
1042
|
-
editText.setSelection(1)
|
|
1043
|
-
|
|
1044
|
-
val event = KeyEvent(100L, 100L, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_FORWARD_DEL, 0)
|
|
1045
|
-
assertTrue(editText.dispatchKeyEvent(event))
|
|
1046
|
-
|
|
1047
|
-
assertEquals("", editText.text?.toString())
|
|
1048
|
-
assertTrue(inputConnection.finishComposingText())
|
|
1049
|
-
assertNull(insertedText)
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
@Test
|
|
1053
|
-
fun `printable hardware key inside composing emoji replaces whole surrogate pair`() {
|
|
1054
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1055
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
1056
|
-
editText.setSelection(0)
|
|
1057
|
-
editText.editorId = 1
|
|
1058
|
-
|
|
1059
|
-
var insertedText: String? = null
|
|
1060
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1061
|
-
insertedText = text
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1065
|
-
assertNotNull(inputConnection)
|
|
1066
|
-
assertTrue(inputConnection!!.setComposingText("😀", 1))
|
|
1067
|
-
editText.setSelection(1)
|
|
1068
|
-
|
|
1069
|
-
val event = KeyEvent(100L, 100L, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A, 0)
|
|
1070
|
-
assertTrue(editText.dispatchKeyEvent(event))
|
|
1071
|
-
|
|
1072
|
-
assertEquals("a", editText.text?.toString())
|
|
1073
|
-
assertTrue(inputConnection.finishComposingText())
|
|
1074
|
-
assertEquals("a", insertedText)
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
@Test
|
|
1078
|
-
fun `hardware backspace composition fallback deletes one code point from combining text`() {
|
|
1079
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1080
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
1081
|
-
editText.setSelection(0)
|
|
1082
|
-
editText.editorId = 1
|
|
1083
|
-
|
|
1084
|
-
var insertedText: String? = null
|
|
1085
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1086
|
-
insertedText = text
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1090
|
-
assertNotNull(inputConnection)
|
|
1091
|
-
assertTrue(inputConnection!!.setComposingText("e\u0301", 1))
|
|
1092
|
-
editText.setSelection("e\u0301".length)
|
|
1093
|
-
|
|
1094
|
-
val event = KeyEvent(100L, 100L, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0)
|
|
1095
|
-
assertTrue(editText.dispatchKeyEvent(event))
|
|
1096
|
-
|
|
1097
|
-
assertEquals("e", editText.text?.toString())
|
|
1098
|
-
assertTrue(inputConnection.finishComposingText())
|
|
1099
|
-
assertEquals("e", insertedText)
|
|
1100
|
-
assertEquals(0, editText.reconciliationCount)
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
@Test
|
|
1104
|
-
fun `commit completion routes autocomplete text through rust`() {
|
|
1105
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1106
|
-
editText.applyUpdateJSON(renderUpdateJson("hel"), notifyListener = false)
|
|
1107
|
-
editText.setSelection(0, 3)
|
|
1108
|
-
editText.editorId = 1
|
|
1109
|
-
|
|
1110
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1111
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1112
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1116
|
-
assertNotNull(inputConnection)
|
|
1117
|
-
|
|
1118
|
-
assertTrue(inputConnection!!.commitCompletion(CompletionInfo(1L, 0, "hello")))
|
|
1119
|
-
|
|
1120
|
-
assertEquals(Triple(0, 3, "hello"), replacement)
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
@Test
|
|
1124
|
-
fun `commit correction routes corrected text through rust`() {
|
|
1125
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1126
|
-
editText.applyUpdateJSON(renderUpdateJson("teh"), notifyListener = false)
|
|
1127
|
-
editText.setSelection(0, 3)
|
|
1128
|
-
editText.editorId = 1
|
|
1129
|
-
|
|
1130
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1131
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1132
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1136
|
-
assertNotNull(inputConnection)
|
|
1137
|
-
|
|
1138
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(0, "teh", "the")))
|
|
1139
|
-
|
|
1140
|
-
assertEquals(Triple(0, 3, "the"), replacement)
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
@Test
|
|
1144
|
-
fun `commit correction replaces correction offset when caret is collapsed after word`() {
|
|
1145
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1146
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
1147
|
-
editText.setSelection(4)
|
|
1148
|
-
editText.editorId = 1
|
|
1149
|
-
|
|
1150
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1151
|
-
var insertedText: String? = null
|
|
1152
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1153
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1154
|
-
}
|
|
1155
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1156
|
-
insertedText = text
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1160
|
-
assertNotNull(inputConnection)
|
|
1161
|
-
|
|
1162
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(0, "teh", "the")))
|
|
1163
|
-
|
|
1164
|
-
assertEquals(Triple(0, 3, "the"), replacement)
|
|
1165
|
-
assertNull(insertedText)
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
@Test
|
|
1169
|
-
fun `stale commit correction range is consumed without inserting at caret`() {
|
|
1170
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1171
|
-
editText.applyUpdateJSON(renderUpdateJson("tah "), notifyListener = false)
|
|
1172
|
-
editText.setSelection(4)
|
|
1173
|
-
editText.editorId = 1
|
|
1174
|
-
|
|
1175
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1176
|
-
var insertedText: String? = null
|
|
1177
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1178
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1179
|
-
}
|
|
1180
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1181
|
-
insertedText = text
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1185
|
-
assertNotNull(inputConnection)
|
|
1186
|
-
|
|
1187
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(0, "teh", "the")))
|
|
1188
|
-
|
|
1189
|
-
assertNull(replacement)
|
|
1190
|
-
assertNull(insertedText)
|
|
1191
|
-
assertEquals("tah ", editText.text?.toString())
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
@Test
|
|
1195
|
-
fun `commit correction with missing old text replaces word at offset`() {
|
|
1196
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1197
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
1198
|
-
editText.setSelection(4)
|
|
1199
|
-
editText.editorId = 1
|
|
1200
|
-
|
|
1201
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1202
|
-
var insertedText: String? = null
|
|
1203
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1204
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1205
|
-
}
|
|
1206
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1207
|
-
insertedText = text
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1211
|
-
assertNotNull(inputConnection)
|
|
1212
|
-
|
|
1213
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(0, null, "the")))
|
|
1214
|
-
|
|
1215
|
-
assertEquals(Triple(0, 3, "the"), replacement)
|
|
1216
|
-
assertNull(insertedText)
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
@Test
|
|
1220
|
-
fun `commit correction with missing old text and invalid offset is consumed without inserting`() {
|
|
1221
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1222
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
1223
|
-
editText.setSelection(4)
|
|
1224
|
-
editText.editorId = 1
|
|
1225
|
-
|
|
1226
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1227
|
-
var insertedText: String? = null
|
|
1228
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1229
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1230
|
-
}
|
|
1231
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1232
|
-
insertedText = text
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1236
|
-
assertNotNull(inputConnection)
|
|
1237
|
-
|
|
1238
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(-1, null, "the")))
|
|
1239
|
-
|
|
1240
|
-
assertNull(replacement)
|
|
1241
|
-
assertNull(insertedText)
|
|
1242
|
-
assertEquals("teh ", editText.text?.toString())
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
@Test
|
|
1246
|
-
fun `commit correction with missing old text replaces word at sentence offset`() {
|
|
1247
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1248
|
-
editText.applyUpdateJSON(renderUpdateJson("say teh now"), notifyListener = false)
|
|
1249
|
-
editText.setSelection(7)
|
|
1250
|
-
editText.editorId = 1
|
|
1251
|
-
|
|
1252
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1253
|
-
var insertedText: String? = null
|
|
1254
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1255
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1256
|
-
}
|
|
1257
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1258
|
-
insertedText = text
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1262
|
-
assertNotNull(inputConnection)
|
|
1263
|
-
|
|
1264
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(4, null, "the")))
|
|
1265
|
-
|
|
1266
|
-
assertEquals(Triple(4, 7, "the"), replacement)
|
|
1267
|
-
assertNull(insertedText)
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
@Test
|
|
1271
|
-
fun `commit correction with missing old text replaces word containing offset`() {
|
|
1272
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1273
|
-
editText.applyUpdateJSON(renderUpdateJson("say teh now"), notifyListener = false)
|
|
1274
|
-
editText.setSelection(7)
|
|
1275
|
-
editText.editorId = 1
|
|
1276
|
-
|
|
1277
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1278
|
-
var insertedText: String? = null
|
|
1279
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1280
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1281
|
-
}
|
|
1282
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1283
|
-
insertedText = text
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1287
|
-
assertNotNull(inputConnection)
|
|
1288
|
-
|
|
1289
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(5, null, "the")))
|
|
1290
|
-
|
|
1291
|
-
assertEquals(Triple(4, 7, "the"), replacement)
|
|
1292
|
-
assertNull(insertedText)
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
@Test
|
|
1296
|
-
fun `commit correction with missing old text preserves trailing punctuation`() {
|
|
1297
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1298
|
-
editText.applyUpdateJSON(renderUpdateJson("teh."), notifyListener = false)
|
|
1299
|
-
editText.setSelection(4)
|
|
1300
|
-
editText.editorId = 1
|
|
1301
|
-
|
|
1302
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1303
|
-
var insertedText: String? = null
|
|
1304
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1305
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1306
|
-
}
|
|
1307
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1308
|
-
insertedText = text
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1312
|
-
assertNotNull(inputConnection)
|
|
1313
|
-
|
|
1314
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(0, null, "the")))
|
|
1315
|
-
|
|
1316
|
-
assertEquals(Triple(0, 3, "the"), replacement)
|
|
1317
|
-
assertNull(insertedText)
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
@Test
|
|
1321
|
-
fun `commit correction with missing old text preserves punctuation inside sentence`() {
|
|
1322
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1323
|
-
editText.applyUpdateJSON(renderUpdateJson("say teh, now"), notifyListener = false)
|
|
1324
|
-
editText.setSelection(8)
|
|
1325
|
-
editText.editorId = 1
|
|
1326
|
-
|
|
1327
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1328
|
-
var insertedText: String? = null
|
|
1329
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1330
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1331
|
-
}
|
|
1332
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1333
|
-
insertedText = text
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1337
|
-
assertNotNull(inputConnection)
|
|
1338
|
-
|
|
1339
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(5, null, "the")))
|
|
1340
|
-
|
|
1341
|
-
assertEquals(Triple(4, 7, "the"), replacement)
|
|
1342
|
-
assertNull(insertedText)
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
@Test
|
|
1346
|
-
fun `commit correction with missing old text on punctuation is consumed without inserting`() {
|
|
1347
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1348
|
-
editText.applyUpdateJSON(renderUpdateJson("teh."), notifyListener = false)
|
|
1349
|
-
editText.setSelection(4)
|
|
1350
|
-
editText.editorId = 1
|
|
1351
|
-
|
|
1352
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1353
|
-
var insertedText: String? = null
|
|
1354
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1355
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1356
|
-
}
|
|
1357
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1358
|
-
insertedText = text
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1362
|
-
assertNotNull(inputConnection)
|
|
1363
|
-
|
|
1364
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(3, null, "the")))
|
|
1365
|
-
|
|
1366
|
-
assertNull(replacement)
|
|
1367
|
-
assertNull(insertedText)
|
|
1368
|
-
assertEquals("teh.", editText.text?.toString())
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
|
-
@Test
|
|
1372
|
-
fun `commit correction with missing old text keeps internal hyphen in token`() {
|
|
1373
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1374
|
-
editText.applyUpdateJSON(renderUpdateJson("dont-stop "), notifyListener = false)
|
|
1375
|
-
editText.setSelection(10)
|
|
1376
|
-
editText.editorId = 1
|
|
1377
|
-
|
|
1378
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1379
|
-
var insertedText: String? = null
|
|
1380
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1381
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1382
|
-
}
|
|
1383
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1384
|
-
insertedText = text
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1388
|
-
assertNotNull(inputConnection)
|
|
1389
|
-
|
|
1390
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(4, null, "don't-stop")))
|
|
1391
|
-
|
|
1392
|
-
assertEquals(Triple(0, 9, "don't-stop"), replacement)
|
|
1393
|
-
assertNull(insertedText)
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
@Test
|
|
1397
|
-
fun `commit correction with missing old text keeps internal apostrophe in token`() {
|
|
1398
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1399
|
-
editText.applyUpdateJSON(renderUpdateJson("cant's "), notifyListener = false)
|
|
1400
|
-
editText.setSelection(7)
|
|
1401
|
-
editText.editorId = 1
|
|
1402
|
-
|
|
1403
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1404
|
-
var insertedText: String? = null
|
|
1405
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1406
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1407
|
-
}
|
|
1408
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1409
|
-
insertedText = text
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1413
|
-
assertNotNull(inputConnection)
|
|
1414
|
-
|
|
1415
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(4, null, "can't")))
|
|
1416
|
-
|
|
1417
|
-
assertEquals(Triple(0, 6, "can't"), replacement)
|
|
1418
|
-
assertNull(insertedText)
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
@Test
|
|
1422
|
-
fun `commit correction with missing old text on whitespace is consumed without inserting`() {
|
|
1423
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1424
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
1425
|
-
editText.setSelection(4)
|
|
1426
|
-
editText.editorId = 1
|
|
1427
|
-
|
|
1428
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1429
|
-
var insertedText: String? = null
|
|
1430
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1431
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1432
|
-
}
|
|
1433
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1434
|
-
insertedText = text
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1438
|
-
assertNotNull(inputConnection)
|
|
1439
|
-
|
|
1440
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(3, null, "the")))
|
|
1441
|
-
|
|
1442
|
-
assertNull(replacement)
|
|
1443
|
-
assertNull(insertedText)
|
|
1444
|
-
assertEquals("teh ", editText.text?.toString())
|
|
1445
|
-
}
|
|
1446
|
-
|
|
1447
|
-
@Test
|
|
1448
|
-
fun `commit correction with missing old text does not split surrogate pair word`() {
|
|
1449
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1450
|
-
editText.applyUpdateJSON(renderUpdateJson("te😀h "), notifyListener = false)
|
|
1451
|
-
editText.setSelection(5)
|
|
1452
|
-
editText.editorId = 1
|
|
1453
|
-
|
|
1454
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1455
|
-
var insertedText: String? = null
|
|
1456
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1457
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1458
|
-
}
|
|
1459
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1460
|
-
insertedText = text
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1464
|
-
assertNotNull(inputConnection)
|
|
1465
|
-
|
|
1466
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(3, null, "term")))
|
|
1467
|
-
|
|
1468
|
-
assertEquals(Triple(0, 4, "term"), replacement)
|
|
1469
|
-
assertNull(insertedText)
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
@Test
|
|
1473
|
-
fun `commit correction with old text and invalid offset is consumed without inserting`() {
|
|
1474
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1475
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
1476
|
-
editText.setSelection(4)
|
|
1477
|
-
editText.editorId = 1
|
|
1478
|
-
|
|
1479
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1480
|
-
var insertedText: String? = null
|
|
1481
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1482
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1483
|
-
}
|
|
1484
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1485
|
-
insertedText = text
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1489
|
-
assertNotNull(inputConnection)
|
|
1490
|
-
|
|
1491
|
-
assertTrue(inputConnection!!.commitCorrection(CorrectionInfo(-1, "teh", "the")))
|
|
1492
|
-
|
|
1493
|
-
assertNull(replacement)
|
|
1494
|
-
assertNull(insertedText)
|
|
1495
|
-
assertEquals("teh ", editText.text?.toString())
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
@Test
|
|
1499
|
-
fun `commit correction during visible composition commits corrected composing text`() {
|
|
1500
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1501
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
1502
|
-
editText.setSelection(0)
|
|
1503
|
-
editText.editorId = 1
|
|
1504
|
-
|
|
1505
|
-
var insertedText: String? = null
|
|
1506
|
-
var insertedScalar: Int? = null
|
|
1507
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
1508
|
-
insertedText = text
|
|
1509
|
-
insertedScalar = scalar
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1513
|
-
assertNotNull(inputConnection)
|
|
1514
|
-
assertTrue(inputConnection!!.setComposingText("teh", 1))
|
|
1515
|
-
|
|
1516
|
-
assertTrue(inputConnection.commitCorrection(CorrectionInfo(0, "teh", "the")))
|
|
1517
|
-
|
|
1518
|
-
assertNull(insertedText)
|
|
1519
|
-
assertNull(insertedScalar)
|
|
1520
|
-
|
|
1521
|
-
assertTrue(inputConnection.commitText("the", 1))
|
|
1522
|
-
|
|
1523
|
-
assertEquals("the", insertedText)
|
|
1524
|
-
assertEquals(0, insertedScalar)
|
|
1525
|
-
assertEquals(0, editText.reconciliationCount)
|
|
1526
|
-
}
|
|
1527
|
-
|
|
1528
|
-
@Test
|
|
1529
|
-
fun `matching commit text after composition correction applies once so space can follow`() {
|
|
1530
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1531
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
1532
|
-
editText.setSelection(0)
|
|
1533
|
-
editText.editorId = 1
|
|
1534
|
-
|
|
1535
|
-
val rendered = StringBuilder()
|
|
1536
|
-
val inserts = mutableListOf<Pair<String, Int>>()
|
|
1537
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
1538
|
-
inserts.add(text to scalar)
|
|
1539
|
-
rendered.insert(scalar.coerceIn(0, rendered.length), text)
|
|
1540
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1544
|
-
assertNotNull(inputConnection)
|
|
1545
|
-
assertTrue(inputConnection!!.setComposingText("wouldnt", 1))
|
|
1546
|
-
|
|
1547
|
-
assertTrue(inputConnection.commitCorrection(CorrectionInfo(0, "wouldnt", "wouldn't")))
|
|
1548
|
-
|
|
1549
|
-
assertTrue(inserts.isEmpty())
|
|
1550
|
-
assertEquals("wouldnt", editText.text?.toString())
|
|
1551
|
-
assertFalse(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
1552
|
-
|
|
1553
|
-
assertTrue(inputConnection.commitText("wouldn't", 1))
|
|
1554
|
-
|
|
1555
|
-
assertEquals(listOf("wouldn't" to 0), inserts)
|
|
1556
|
-
assertEquals("wouldn't", editText.text?.toString())
|
|
1557
|
-
assertTrue(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
1558
|
-
|
|
1559
|
-
assertTrue(inputConnection.commitText(" ", 1))
|
|
1560
|
-
|
|
1561
|
-
assertEquals(listOf("wouldn't" to 0, " " to 8), inserts)
|
|
1562
|
-
assertEquals("wouldn't ", editText.text?.toString())
|
|
1563
|
-
assertFalse(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
1564
|
-
|
|
1565
|
-
shadowOf(Looper.getMainLooper()).idle()
|
|
1566
|
-
|
|
1567
|
-
assertEquals("wouldn't ", editText.text?.toString())
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
@Test
|
|
1571
|
-
fun `single letter composition correction followed by commit text keeps uppercase replacement`() {
|
|
1572
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1573
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
1574
|
-
editText.setSelection(0)
|
|
1575
|
-
editText.editorId = 1
|
|
1576
|
-
|
|
1577
|
-
val rendered = StringBuilder()
|
|
1578
|
-
val inserts = mutableListOf<Pair<String, Int>>()
|
|
1579
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
1580
|
-
inserts.add(text to scalar)
|
|
1581
|
-
rendered.insert(scalar.coerceIn(0, rendered.length), text)
|
|
1582
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1586
|
-
assertNotNull(inputConnection)
|
|
1587
|
-
assertTrue(inputConnection!!.setComposingText("i", 1))
|
|
1588
|
-
|
|
1589
|
-
assertTrue(inputConnection.commitCorrection(CorrectionInfo(0, "i", "I")))
|
|
1590
|
-
assertTrue(inputConnection.commitText("I", 1))
|
|
1591
|
-
|
|
1592
|
-
assertEquals(listOf("I" to 0), inserts)
|
|
1593
|
-
assertEquals("I", editText.text?.toString())
|
|
1594
|
-
|
|
1595
|
-
shadowOf(Looper.getMainLooper()).idle()
|
|
1596
|
-
|
|
1597
|
-
assertEquals("I", editText.text?.toString())
|
|
1598
|
-
}
|
|
1599
|
-
|
|
1600
|
-
@Test
|
|
1601
|
-
fun `single letter composition correction applies when ime sends no follow up commit text`() {
|
|
1602
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1603
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
1604
|
-
editText.setSelection(0)
|
|
1605
|
-
editText.editorId = 1
|
|
1606
|
-
|
|
1607
|
-
val rendered = StringBuilder()
|
|
1608
|
-
val inserts = mutableListOf<Pair<String, Int>>()
|
|
1609
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
1610
|
-
inserts.add(text to scalar)
|
|
1611
|
-
rendered.insert(scalar.coerceIn(0, rendered.length), text)
|
|
1612
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1616
|
-
assertNotNull(inputConnection)
|
|
1617
|
-
assertTrue(inputConnection!!.setComposingText("i", 1))
|
|
1618
|
-
|
|
1619
|
-
assertTrue(inputConnection.commitCorrection(CorrectionInfo(0, "i", "I")))
|
|
1620
|
-
|
|
1621
|
-
assertTrue(inserts.isEmpty())
|
|
1622
|
-
assertEquals("i", editText.text?.toString())
|
|
1623
|
-
|
|
1624
|
-
shadowOf(Looper.getMainLooper()).idle()
|
|
1625
|
-
|
|
1626
|
-
assertEquals(listOf("I" to 0), inserts)
|
|
1627
|
-
assertEquals("I", editText.text?.toString())
|
|
1628
|
-
}
|
|
1629
|
-
|
|
1630
|
-
@Test
|
|
1631
|
-
fun `printable hardware key during composition stays transient until finish commit`() {
|
|
1632
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1633
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
1634
|
-
editText.setSelection(0)
|
|
1635
|
-
editText.editorId = 1
|
|
1636
|
-
|
|
1637
|
-
var insertedText: String? = null
|
|
1638
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1639
|
-
insertedText = text
|
|
1640
|
-
}
|
|
1641
|
-
|
|
1642
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1643
|
-
assertNotNull(inputConnection)
|
|
1644
|
-
assertTrue(inputConnection!!.setComposingText("b", 1))
|
|
1645
|
-
|
|
1646
|
-
val event = KeyEvent(100L, 100L, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A, 0)
|
|
1647
|
-
assertTrue(editText.dispatchKeyEvent(event))
|
|
1648
|
-
|
|
1649
|
-
assertEquals(0, editText.reconciliationCount)
|
|
1650
|
-
assertEquals("ba", editText.text?.toString())
|
|
1651
|
-
assertTrue(inputConnection.finishComposingText())
|
|
1652
|
-
assertEquals("ba", insertedText)
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
|
-
@Test
|
|
1656
|
-
fun `printable input connection key during composition stays transient until finish commit`() {
|
|
1657
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1658
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
1659
|
-
editText.setSelection(0)
|
|
1660
|
-
editText.editorId = 1
|
|
1661
|
-
|
|
1662
|
-
var insertedText: String? = null
|
|
1663
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1664
|
-
insertedText = text
|
|
1665
|
-
}
|
|
1666
|
-
|
|
1667
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1668
|
-
assertNotNull(inputConnection)
|
|
1669
|
-
assertTrue(inputConnection!!.setComposingText("b", 1))
|
|
1670
|
-
|
|
1671
|
-
val event = KeyEvent(100L, 100L, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A, 0)
|
|
1672
|
-
assertTrue(inputConnection.sendKeyEvent(event))
|
|
1673
|
-
|
|
1674
|
-
assertEquals(0, editText.reconciliationCount)
|
|
1675
|
-
assertEquals("ba", editText.text?.toString())
|
|
1676
|
-
assertTrue(inputConnection.finishComposingText())
|
|
1677
|
-
assertEquals("ba", insertedText)
|
|
1678
|
-
}
|
|
1679
|
-
|
|
1680
|
-
@Test
|
|
1681
|
-
fun `read only completion and correction are consumed without mutating text`() {
|
|
1682
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1683
|
-
editText.applyUpdateJSON(renderUpdateJson("abc"), notifyListener = false)
|
|
1684
|
-
editText.setSelection(0, 3)
|
|
1685
|
-
editText.editorId = 1
|
|
1686
|
-
editText.isEditable = false
|
|
1687
|
-
|
|
1688
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1689
|
-
assertNotNull(inputConnection)
|
|
1690
|
-
|
|
1691
|
-
assertTrue(inputConnection!!.commitCompletion(CompletionInfo(1L, 0, "replacement")))
|
|
1692
|
-
assertTrue(inputConnection.commitCorrection(CorrectionInfo(0, "abc", "replacement")))
|
|
1693
|
-
assertEquals("abc", editText.text?.toString())
|
|
1694
|
-
}
|
|
1695
|
-
|
|
1696
|
-
@Test
|
|
1697
|
-
fun `key event enter during composition does not split rust before commit`() {
|
|
1698
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1699
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello "), notifyListener = false)
|
|
1700
|
-
editText.setSelection(6)
|
|
1701
|
-
editText.editorId = 1
|
|
1702
|
-
|
|
1703
|
-
var deleteAndSplitCalled = false
|
|
1704
|
-
editText.onDeleteAndSplitScalarInRustForTesting = { _, _ ->
|
|
1705
|
-
deleteAndSplitCalled = true
|
|
1706
|
-
}
|
|
1707
|
-
|
|
1708
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1709
|
-
assertNotNull(inputConnection)
|
|
1710
|
-
assertTrue(inputConnection!!.setComposingText("abc", 1))
|
|
1711
|
-
|
|
1712
|
-
assertTrue(inputConnection.sendKeyEvent(android.view.KeyEvent(
|
|
1713
|
-
android.view.KeyEvent.ACTION_DOWN,
|
|
1714
|
-
android.view.KeyEvent.KEYCODE_ENTER
|
|
1715
|
-
)))
|
|
1716
|
-
|
|
1717
|
-
assertFalse(deleteAndSplitCalled)
|
|
1718
|
-
assertEquals(0, editText.reconciliationCount)
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
|
-
@Test
|
|
1722
|
-
fun `commit text after composing region replaces original authorized range`() {
|
|
1723
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1724
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
1725
|
-
editText.setSelection(3)
|
|
1726
|
-
editText.editorId = 1
|
|
1727
|
-
|
|
1728
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1729
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1730
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1731
|
-
}
|
|
1732
|
-
|
|
1733
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1734
|
-
assertNotNull(inputConnection)
|
|
1735
|
-
|
|
1736
|
-
assertTrue(inputConnection!!.setComposingRegion(0, 3))
|
|
1737
|
-
assertTrue(inputConnection.commitText("the", 1))
|
|
1738
|
-
|
|
1739
|
-
assertEquals(Triple(0, 3, "the"), replacement)
|
|
1740
|
-
assertEquals(0, editText.reconciliationCount)
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
@Test
|
|
1744
|
-
fun `multiline composition commits as structured content`() {
|
|
1745
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1746
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
1747
|
-
editText.setSelection(6, 11)
|
|
1748
|
-
editText.editorId = 1
|
|
1749
|
-
|
|
1750
|
-
var insertedContent: Triple<Int, Int, String>? = null
|
|
1751
|
-
editText.onInsertContentJsonAtSelectionScalarForTesting = { scalarFrom, scalarTo, json ->
|
|
1752
|
-
insertedContent = Triple(scalarFrom, scalarTo, json)
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1756
|
-
assertNotNull(inputConnection)
|
|
1757
|
-
|
|
1758
|
-
assertTrue(inputConnection!!.setComposingRegion(6, 11))
|
|
1759
|
-
assertTrue(inputConnection.commitText("one\ntwo", 1))
|
|
1760
|
-
|
|
1761
|
-
val (scalarFrom, scalarTo, json) = insertedContent!!
|
|
1762
|
-
assertEquals(6, scalarFrom)
|
|
1763
|
-
assertEquals(11, scalarTo)
|
|
1764
|
-
val content = JSONObject(json).getJSONArray("content")
|
|
1765
|
-
assertEquals("one", content.getJSONObject(0).getJSONArray("content").getJSONObject(0).getString("text"))
|
|
1766
|
-
assertEquals("two", content.getJSONObject(1).getJSONArray("content").getJSONObject(0).getString("text"))
|
|
1767
|
-
}
|
|
1768
|
-
|
|
1769
|
-
@Test
|
|
1770
|
-
fun `commit newline after composing region delete splits original authorized range`() {
|
|
1771
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1772
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
1773
|
-
editText.setSelection(6, 11)
|
|
1774
|
-
editText.editorId = 1
|
|
1775
|
-
|
|
1776
|
-
var deletedAndSplitRange: Pair<Int, Int>? = null
|
|
1777
|
-
editText.onDeleteAndSplitScalarInRustForTesting = { scalarFrom, scalarTo ->
|
|
1778
|
-
deletedAndSplitRange = scalarFrom to scalarTo
|
|
1779
|
-
}
|
|
1780
|
-
|
|
1781
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1782
|
-
assertNotNull(inputConnection)
|
|
1783
|
-
|
|
1784
|
-
assertTrue(inputConnection!!.setComposingRegion(6, 11))
|
|
1785
|
-
assertTrue(inputConnection.commitText("\n", 1))
|
|
1786
|
-
|
|
1787
|
-
assertEquals(6 to 11, deletedAndSplitRange)
|
|
1788
|
-
assertEquals(0, editText.reconciliationCount)
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
|
-
@Test
|
|
1792
|
-
fun `empty commit text after composing region deletes authorized text`() {
|
|
1793
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1794
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
1795
|
-
editText.setSelection(3)
|
|
1796
|
-
editText.editorId = 1
|
|
1797
|
-
|
|
1798
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1799
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1800
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1801
|
-
}
|
|
1802
|
-
var deletedRange: Pair<Int, Int>? = null
|
|
1803
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
1804
|
-
deletedRange = scalarFrom to scalarTo
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1808
|
-
assertNotNull(inputConnection)
|
|
1809
|
-
|
|
1810
|
-
assertTrue(inputConnection!!.setComposingRegion(0, 3))
|
|
1811
|
-
assertTrue(inputConnection.commitText("", 1))
|
|
1812
|
-
|
|
1813
|
-
assertEquals(null, replacement)
|
|
1814
|
-
assertEquals(0 to 3, deletedRange)
|
|
1815
|
-
assertEquals(0, editText.reconciliationCount)
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
@Test
|
|
1819
|
-
fun `finish composing text after unchanged composing region skips no-op replacement`() {
|
|
1820
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1821
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
1822
|
-
editText.setSelection(3)
|
|
1823
|
-
editText.editorId = 1
|
|
1824
|
-
|
|
1825
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1826
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1827
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1828
|
-
}
|
|
1829
|
-
var syncedSelection: Pair<Int, Int>? = null
|
|
1830
|
-
editText.onSetSelectionScalarInRustForTesting = { anchor, head ->
|
|
1831
|
-
syncedSelection = anchor to head
|
|
1832
|
-
}
|
|
1833
|
-
|
|
1834
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1835
|
-
assertNotNull(inputConnection)
|
|
1836
|
-
|
|
1837
|
-
assertTrue(inputConnection!!.setComposingRegion(0, 3))
|
|
1838
|
-
assertTrue(inputConnection.finishComposingText())
|
|
1839
|
-
|
|
1840
|
-
assertEquals(null, replacement)
|
|
1841
|
-
assertNotNull(syncedSelection)
|
|
1842
|
-
assertEquals(0, editText.reconciliationCount)
|
|
1843
|
-
}
|
|
1844
|
-
|
|
1845
|
-
@Test
|
|
1846
|
-
fun `unchanged newline composition is treated as no-op before split handling`() {
|
|
1847
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1848
|
-
editText.applyUpdateJSON(renderUpdateJson("\n"), notifyListener = false)
|
|
1849
|
-
editText.setSelection(0, 1)
|
|
1850
|
-
editText.editorId = 1
|
|
1851
|
-
|
|
1852
|
-
var deleteAndSplitCalled = false
|
|
1853
|
-
editText.onDeleteAndSplitScalarInRustForTesting = { _, _ ->
|
|
1854
|
-
deleteAndSplitCalled = true
|
|
1855
|
-
}
|
|
1856
|
-
var selectedScalar: Pair<Int, Int>? = null
|
|
1857
|
-
editText.onSetSelectionScalarInRustForTesting = { anchor, head ->
|
|
1858
|
-
selectedScalar = anchor to head
|
|
1859
|
-
}
|
|
1860
|
-
|
|
1861
|
-
editText.handleCompositionCommit("\n", 0, 1)
|
|
1862
|
-
|
|
1863
|
-
assertFalse(deleteAndSplitCalled)
|
|
1864
|
-
assertEquals(1 to 1, selectedScalar)
|
|
1865
|
-
assertEquals("\n", editText.text?.toString())
|
|
1866
|
-
}
|
|
1867
|
-
|
|
1868
|
-
@Test
|
|
1869
|
-
fun `finish composing text after unchanged composing region moves default cursor to range end`() {
|
|
1870
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1871
|
-
editText.applyUpdateJSON(renderUpdateJson("abc"), notifyListener = false)
|
|
1872
|
-
editText.setSelection(0)
|
|
1873
|
-
editText.editorId = 1
|
|
1874
|
-
|
|
1875
|
-
var selectedScalar: Pair<Int, Int>? = null
|
|
1876
|
-
editText.onSetSelectionScalarInRustForTesting = { anchor, head ->
|
|
1877
|
-
selectedScalar = anchor to head
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1881
|
-
assertNotNull(inputConnection)
|
|
1882
|
-
|
|
1883
|
-
assertTrue(inputConnection!!.setComposingRegion(0, 3))
|
|
1884
|
-
assertTrue(inputConnection.commitText("abc", 1))
|
|
1885
|
-
|
|
1886
|
-
assertEquals(3 to 3, selectedScalar)
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
@Test
|
|
1890
|
-
fun `finish composing text with empty composition restores and handles cancellation`() {
|
|
1891
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1892
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
1893
|
-
editText.setSelection(0)
|
|
1894
|
-
editText.editorId = 1
|
|
1895
|
-
|
|
1896
|
-
var insertedText: String? = null
|
|
1897
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1898
|
-
insertedText = text
|
|
1899
|
-
}
|
|
1900
|
-
|
|
1901
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1902
|
-
assertNotNull(inputConnection)
|
|
1903
|
-
|
|
1904
|
-
assertTrue(inputConnection!!.setComposingText("", 1))
|
|
1905
|
-
assertTrue(inputConnection.finishComposingText())
|
|
1906
|
-
|
|
1907
|
-
assertEquals("", editText.text?.toString())
|
|
1908
|
-
assertEquals(null, insertedText)
|
|
1909
|
-
assertEquals(0, editText.reconciliationCount)
|
|
1910
|
-
}
|
|
1911
|
-
|
|
1912
|
-
@Test
|
|
1913
|
-
fun `finish composing text with empty selected composition deletes replacement range`() {
|
|
1914
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1915
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
1916
|
-
editText.setSelection(6, 11)
|
|
1917
|
-
editText.editorId = 1
|
|
1918
|
-
|
|
1919
|
-
var deletedRange: Pair<Int, Int>? = null
|
|
1920
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
1921
|
-
deletedRange = scalarFrom to scalarTo
|
|
1922
|
-
}
|
|
1923
|
-
|
|
1924
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1925
|
-
assertNotNull(inputConnection)
|
|
1926
|
-
assertTrue(inputConnection!!.setComposingText("", 1))
|
|
1927
|
-
|
|
1928
|
-
assertTrue(inputConnection.finishComposingText())
|
|
1929
|
-
|
|
1930
|
-
assertEquals(6 to 11, deletedRange)
|
|
1931
|
-
assertEquals(0, editText.reconciliationCount)
|
|
1932
|
-
}
|
|
1933
|
-
|
|
1934
|
-
@Test
|
|
1935
|
-
fun `composition replacement range invalidates after authorized render changes`() {
|
|
1936
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1937
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
1938
|
-
editText.setSelection(6)
|
|
1939
|
-
editText.editorId = 1
|
|
1940
|
-
|
|
1941
|
-
var insertedText: String? = null
|
|
1942
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1943
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1944
|
-
insertedText = text
|
|
1945
|
-
}
|
|
1946
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1947
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1951
|
-
assertNotNull(inputConnection)
|
|
1952
|
-
assertTrue(inputConnection!!.setComposingText("brave ", 1))
|
|
1953
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
1954
|
-
|
|
1955
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello updated world"), notifyListener = false)
|
|
1956
|
-
|
|
1957
|
-
assertTrue(inputConnection.finishComposingText())
|
|
1958
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
1959
|
-
assertNull(insertedText)
|
|
1960
|
-
assertNull(replacement)
|
|
1961
|
-
}
|
|
1962
|
-
|
|
1963
|
-
@Test
|
|
1964
|
-
fun `commit text after authorized render change is consumed without inserting stale composition`() {
|
|
1965
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1966
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
1967
|
-
editText.setSelection(6)
|
|
1968
|
-
editText.editorId = 1
|
|
1969
|
-
|
|
1970
|
-
var insertedText: String? = null
|
|
1971
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
1972
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
1973
|
-
insertedText = text
|
|
1974
|
-
}
|
|
1975
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
1976
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
1977
|
-
}
|
|
1978
|
-
|
|
1979
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
1980
|
-
assertNotNull(inputConnection)
|
|
1981
|
-
assertTrue(inputConnection!!.setComposingText("brave ", 1))
|
|
1982
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
1983
|
-
|
|
1984
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello updated world"), notifyListener = false)
|
|
1985
|
-
|
|
1986
|
-
assertTrue(inputConnection.commitText("brave ", 1))
|
|
1987
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
1988
|
-
assertNull(insertedText)
|
|
1989
|
-
assertNull(replacement)
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
|
-
@Test
|
|
1993
|
-
fun `commit correction after authorized render change is consumed without replacing matching text`() {
|
|
1994
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
1995
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
1996
|
-
editText.setSelection(6)
|
|
1997
|
-
editText.editorId = 1
|
|
1998
|
-
|
|
1999
|
-
var insertedText: String? = null
|
|
2000
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
2001
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2002
|
-
insertedText = text
|
|
2003
|
-
}
|
|
2004
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
2005
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
2006
|
-
}
|
|
2007
|
-
|
|
2008
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2009
|
-
assertNotNull(inputConnection)
|
|
2010
|
-
assertTrue(inputConnection!!.setComposingText("brave ", 1))
|
|
2011
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2012
|
-
|
|
2013
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello brave world"), notifyListener = false)
|
|
2014
|
-
|
|
2015
|
-
assertTrue(inputConnection.commitCorrection(CorrectionInfo(6, "brave ", "braver ")))
|
|
2016
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2017
|
-
assertNull(insertedText)
|
|
2018
|
-
assertNull(replacement)
|
|
2019
|
-
}
|
|
2020
|
-
|
|
2021
|
-
@Test
|
|
2022
|
-
fun `composing text after authorized render change does not reauthorize stale commit`() {
|
|
2023
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2024
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2025
|
-
editText.setSelection(6)
|
|
2026
|
-
editText.editorId = 1
|
|
2027
|
-
|
|
2028
|
-
var insertedText: String? = null
|
|
2029
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
2030
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2031
|
-
insertedText = text
|
|
2032
|
-
}
|
|
2033
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
2034
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
2035
|
-
}
|
|
2036
|
-
|
|
2037
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2038
|
-
assertNotNull(inputConnection)
|
|
2039
|
-
assertTrue(inputConnection!!.setComposingText("brave ", 1))
|
|
2040
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2041
|
-
|
|
2042
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello updated world"), notifyListener = false)
|
|
2043
|
-
|
|
2044
|
-
assertTrue(inputConnection.setComposingText("braver ", 1))
|
|
2045
|
-
assertTrue(inputConnection.commitText("braver ", 1))
|
|
2046
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
2047
|
-
assertNull(insertedText)
|
|
2048
|
-
assertNull(replacement)
|
|
2049
|
-
}
|
|
2050
|
-
|
|
2051
|
-
@Test
|
|
2052
|
-
fun `composing region after authorized render change does not reauthorize stale commit`() {
|
|
2053
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2054
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2055
|
-
editText.setSelection(6)
|
|
2056
|
-
editText.editorId = 1
|
|
2057
|
-
|
|
2058
|
-
var insertedText: String? = null
|
|
2059
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
2060
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2061
|
-
insertedText = text
|
|
2062
|
-
}
|
|
2063
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
2064
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
2065
|
-
}
|
|
2066
|
-
|
|
2067
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2068
|
-
assertNotNull(inputConnection)
|
|
2069
|
-
assertTrue(inputConnection!!.setComposingText("brave ", 1))
|
|
2070
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2071
|
-
|
|
2072
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello updated world"), notifyListener = false)
|
|
2073
|
-
|
|
2074
|
-
assertTrue(inputConnection.setComposingRegion(6, 13))
|
|
2075
|
-
assertTrue(inputConnection.commitText("braver ", 1))
|
|
2076
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
2077
|
-
assertNull(insertedText)
|
|
2078
|
-
assertNull(replacement)
|
|
2079
|
-
}
|
|
2080
|
-
|
|
2081
|
-
@Test
|
|
2082
|
-
fun `delete surrounding text after authorized render change is consumed without deleting authorized text`() {
|
|
2083
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2084
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2085
|
-
editText.setSelection(6)
|
|
2086
|
-
editText.editorId = 1
|
|
2087
|
-
|
|
2088
|
-
var deleteRange: Pair<Int, Int>? = null
|
|
2089
|
-
var deleteBackward: Pair<Int, Int>? = null
|
|
2090
|
-
var insertedText: String? = null
|
|
2091
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
2092
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
2093
|
-
deleteRange = scalarFrom to scalarTo
|
|
2094
|
-
}
|
|
2095
|
-
editText.onDeleteBackwardAtSelectionScalarInRustForTesting = { anchor, head ->
|
|
2096
|
-
deleteBackward = anchor to head
|
|
2097
|
-
}
|
|
2098
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2099
|
-
insertedText = text
|
|
2100
|
-
}
|
|
2101
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
2102
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
2103
|
-
}
|
|
2104
|
-
|
|
2105
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2106
|
-
assertNotNull(inputConnection)
|
|
2107
|
-
assertTrue(inputConnection!!.setComposingText("brave ", 1))
|
|
2108
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2109
|
-
|
|
2110
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello updated world"), notifyListener = false)
|
|
2111
|
-
|
|
2112
|
-
assertTrue(inputConnection.deleteSurroundingText(1, 0))
|
|
2113
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
2114
|
-
assertNull(deleteRange)
|
|
2115
|
-
assertNull(deleteBackward)
|
|
2116
|
-
assertNull(insertedText)
|
|
2117
|
-
assertNull(replacement)
|
|
2118
|
-
}
|
|
2119
|
-
|
|
2120
|
-
@Test
|
|
2121
|
-
fun `delete surrounding text in code points after authorized render change is consumed without mutation`() {
|
|
2122
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2123
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2124
|
-
editText.setSelection(6)
|
|
2125
|
-
editText.editorId = 1
|
|
2126
|
-
|
|
2127
|
-
var deleteRange: Pair<Int, Int>? = null
|
|
2128
|
-
var deleteBackward: Pair<Int, Int>? = null
|
|
2129
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
2130
|
-
deleteRange = scalarFrom to scalarTo
|
|
2131
|
-
}
|
|
2132
|
-
editText.onDeleteBackwardAtSelectionScalarInRustForTesting = { anchor, head ->
|
|
2133
|
-
deleteBackward = anchor to head
|
|
2134
|
-
}
|
|
2135
|
-
|
|
2136
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2137
|
-
assertNotNull(inputConnection)
|
|
2138
|
-
assertTrue(inputConnection!!.setComposingText("brave ", 1))
|
|
2139
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2140
|
-
|
|
2141
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello updated world"), notifyListener = false)
|
|
2142
|
-
|
|
2143
|
-
assertTrue(inputConnection.deleteSurroundingTextInCodePoints(1, 0))
|
|
2144
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
2145
|
-
assertNull(deleteRange)
|
|
2146
|
-
assertNull(deleteBackward)
|
|
2147
|
-
}
|
|
2148
|
-
|
|
2149
|
-
@Test
|
|
2150
|
-
fun `no-op delete after authorized render change does not allow stale commit`() {
|
|
2151
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2152
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2153
|
-
editText.setSelection(6)
|
|
2154
|
-
editText.editorId = 1
|
|
2155
|
-
|
|
2156
|
-
var insertedText: String? = null
|
|
2157
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
2158
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2159
|
-
insertedText = text
|
|
2160
|
-
}
|
|
2161
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
2162
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
2163
|
-
}
|
|
2164
|
-
|
|
2165
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2166
|
-
assertNotNull(inputConnection)
|
|
2167
|
-
assertTrue(inputConnection!!.setComposingText("brave ", 1))
|
|
2168
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2169
|
-
|
|
2170
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello updated world"), notifyListener = false)
|
|
2171
|
-
|
|
2172
|
-
assertTrue(inputConnection.deleteSurroundingText(0, 0))
|
|
2173
|
-
assertTrue(inputConnection.commitText("braver ", 1))
|
|
2174
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
2175
|
-
assertNull(insertedText)
|
|
2176
|
-
assertNull(replacement)
|
|
2177
|
-
}
|
|
2178
|
-
|
|
2179
|
-
@Test
|
|
2180
|
-
fun `no-op code point delete after authorized render change does not allow stale commit`() {
|
|
2181
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2182
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2183
|
-
editText.setSelection(6)
|
|
2184
|
-
editText.editorId = 1
|
|
2185
|
-
|
|
2186
|
-
var insertedText: String? = null
|
|
2187
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
2188
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2189
|
-
insertedText = text
|
|
2190
|
-
}
|
|
2191
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
2192
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2196
|
-
assertNotNull(inputConnection)
|
|
2197
|
-
assertTrue(inputConnection!!.setComposingText("brave ", 1))
|
|
2198
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2199
|
-
|
|
2200
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello updated world"), notifyListener = false)
|
|
2201
|
-
|
|
2202
|
-
assertTrue(inputConnection.deleteSurroundingTextInCodePoints(0, 0))
|
|
2203
|
-
assertTrue(inputConnection.commitText("braver ", 1))
|
|
2204
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
2205
|
-
assertNull(insertedText)
|
|
2206
|
-
assertNull(replacement)
|
|
2207
|
-
}
|
|
2208
|
-
|
|
2209
|
-
@Test
|
|
2210
|
-
fun `delete key event after authorized render change is consumed without rust mutation`() {
|
|
2211
|
-
for (keyCode in listOf(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_FORWARD_DEL)) {
|
|
2212
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2213
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2214
|
-
editText.setSelection(6)
|
|
2215
|
-
editText.editorId = 1
|
|
2216
|
-
|
|
2217
|
-
var deleteRange: Pair<Int, Int>? = null
|
|
2218
|
-
var deleteBackward: Pair<Int, Int>? = null
|
|
2219
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
2220
|
-
deleteRange = scalarFrom to scalarTo
|
|
2221
|
-
}
|
|
2222
|
-
editText.onDeleteBackwardAtSelectionScalarInRustForTesting = { anchor, head ->
|
|
2223
|
-
deleteBackward = anchor to head
|
|
2224
|
-
}
|
|
2225
|
-
|
|
2226
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2227
|
-
assertNotNull(inputConnection)
|
|
2228
|
-
assertTrue(inputConnection!!.setComposingText("brave ", 1))
|
|
2229
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2230
|
-
|
|
2231
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello updated world"), notifyListener = false)
|
|
2232
|
-
|
|
2233
|
-
assertTrue(inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, keyCode)))
|
|
2234
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
2235
|
-
assertNull(deleteRange)
|
|
2236
|
-
assertNull(deleteBackward)
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2239
|
-
|
|
2240
|
-
@Test
|
|
2241
|
-
fun `printable key event after authorized render change is consumed without inserting text`() {
|
|
2242
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2243
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2244
|
-
editText.setSelection(6)
|
|
2245
|
-
editText.editorId = 1
|
|
2246
|
-
|
|
2247
|
-
var insertedText: String? = null
|
|
2248
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
2249
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2250
|
-
insertedText = text
|
|
2251
|
-
}
|
|
2252
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
2253
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
2254
|
-
}
|
|
2255
|
-
|
|
2256
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2257
|
-
assertNotNull(inputConnection)
|
|
2258
|
-
assertTrue(inputConnection!!.setComposingText("brave ", 1))
|
|
2259
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2260
|
-
|
|
2261
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello updated world"), notifyListener = false)
|
|
2262
|
-
|
|
2263
|
-
val event = KeyEvent(100L, 100L, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A, 0)
|
|
2264
|
-
assertTrue(inputConnection.sendKeyEvent(event))
|
|
2265
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
2266
|
-
assertNull(insertedText)
|
|
2267
|
-
assertNull(replacement)
|
|
2268
|
-
}
|
|
2269
|
-
|
|
2270
|
-
@Test
|
|
2271
|
-
fun `fresh input connection after stale key up accepts new commit`() {
|
|
2272
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2273
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2274
|
-
editText.setSelection(6)
|
|
2275
|
-
editText.editorId = 1
|
|
2276
|
-
|
|
2277
|
-
val staleConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2278
|
-
assertNotNull(staleConnection)
|
|
2279
|
-
assertTrue(staleConnection!!.setComposingText("brave ", 1))
|
|
2280
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2281
|
-
|
|
2282
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello updated world"), notifyListener = false)
|
|
2283
|
-
|
|
2284
|
-
assertTrue(staleConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)))
|
|
2285
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
2286
|
-
|
|
2287
|
-
var insertedText: String? = null
|
|
2288
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2289
|
-
insertedText = text
|
|
2290
|
-
}
|
|
2291
|
-
|
|
2292
|
-
val freshConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2293
|
-
assertNotNull(freshConnection)
|
|
2294
|
-
assertTrue(freshConnection!!.commitText(" fresh", 1))
|
|
2295
|
-
|
|
2296
|
-
assertEquals(" fresh", insertedText)
|
|
2297
|
-
}
|
|
2298
|
-
|
|
2299
|
-
@Test
|
|
2300
|
-
fun `key up after authorized render change does not clear invalidation before stale commit`() {
|
|
2301
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2302
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2303
|
-
editText.setSelection(6)
|
|
2304
|
-
editText.editorId = 1
|
|
2305
|
-
|
|
2306
|
-
var insertedText: String? = null
|
|
2307
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
2308
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2309
|
-
insertedText = text
|
|
2310
|
-
}
|
|
2311
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
2312
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
2313
|
-
}
|
|
2314
|
-
|
|
2315
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2316
|
-
assertNotNull(inputConnection)
|
|
2317
|
-
assertTrue(inputConnection!!.setComposingText("brave ", 1))
|
|
2318
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2319
|
-
|
|
2320
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello updated world"), notifyListener = false)
|
|
2321
|
-
|
|
2322
|
-
assertTrue(inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)))
|
|
2323
|
-
assertTrue(inputConnection.commitText("brave ", 1))
|
|
2324
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
2325
|
-
assertNull(insertedText)
|
|
2326
|
-
assertNull(replacement)
|
|
2327
|
-
}
|
|
2328
|
-
|
|
2329
|
-
@Test
|
|
2330
|
-
fun `fresh input connection after stale selection accepts new commit`() {
|
|
2331
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2332
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2333
|
-
editText.setSelection(6)
|
|
2334
|
-
editText.editorId = 1
|
|
2335
|
-
|
|
2336
|
-
val staleConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2337
|
-
assertNotNull(staleConnection)
|
|
2338
|
-
assertTrue(staleConnection!!.setComposingText("brave ", 1))
|
|
2339
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2340
|
-
|
|
2341
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello updated world"), notifyListener = false)
|
|
2342
|
-
|
|
2343
|
-
var syncedSelection: Pair<Int, Int>? = null
|
|
2344
|
-
editText.onSetSelectionScalarInRustForTesting = { anchor, head ->
|
|
2345
|
-
syncedSelection = anchor to head
|
|
2346
|
-
}
|
|
2347
|
-
|
|
2348
|
-
assertTrue(staleConnection.setSelection(6, 13))
|
|
2349
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
2350
|
-
assertNull(syncedSelection)
|
|
2351
|
-
|
|
2352
|
-
var insertedText: String? = null
|
|
2353
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2354
|
-
insertedText = text
|
|
2355
|
-
}
|
|
2356
|
-
|
|
2357
|
-
val freshConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2358
|
-
assertNotNull(freshConnection)
|
|
2359
|
-
assertTrue(freshConnection!!.commitText(" fresh", 1))
|
|
2360
|
-
|
|
2361
|
-
assertEquals(" fresh", insertedText)
|
|
2362
|
-
}
|
|
2363
|
-
|
|
2364
|
-
@Test
|
|
2365
|
-
fun `set selection after authorized render change does not reauthorize stale commit`() {
|
|
2366
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2367
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2368
|
-
editText.setSelection(6)
|
|
2369
|
-
editText.editorId = 1
|
|
2370
|
-
|
|
2371
|
-
var insertedText: String? = null
|
|
2372
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
2373
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2374
|
-
insertedText = text
|
|
2375
|
-
}
|
|
2376
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
2377
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
2378
|
-
}
|
|
2379
|
-
|
|
2380
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2381
|
-
assertNotNull(inputConnection)
|
|
2382
|
-
assertTrue(inputConnection!!.setComposingText("brave ", 1))
|
|
2383
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2384
|
-
|
|
2385
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello updated world"), notifyListener = false)
|
|
2386
|
-
|
|
2387
|
-
var syncedSelection: Pair<Int, Int>? = null
|
|
2388
|
-
editText.onSetSelectionScalarInRustForTesting = { anchor, head ->
|
|
2389
|
-
syncedSelection = anchor to head
|
|
2390
|
-
}
|
|
2391
|
-
|
|
2392
|
-
assertTrue(inputConnection.setSelection(6, 13))
|
|
2393
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
2394
|
-
assertNull(syncedSelection)
|
|
2395
|
-
|
|
2396
|
-
assertTrue(inputConnection.commitText("braver ", 1))
|
|
2397
|
-
assertEquals("Hello updated world", editText.text?.toString())
|
|
2398
|
-
assertNull(insertedText)
|
|
2399
|
-
assertNull(replacement)
|
|
2400
|
-
}
|
|
2401
|
-
|
|
2402
|
-
@Test
|
|
2403
|
-
fun `set selection without invalidation delegates and syncs selection`() {
|
|
2404
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2405
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2406
|
-
editText.setSelection(0)
|
|
2407
|
-
editText.editorId = 1
|
|
2408
|
-
|
|
2409
|
-
var syncedSelection: Pair<Int, Int>? = null
|
|
2410
|
-
editText.onSetSelectionScalarInRustForTesting = { anchor, head ->
|
|
2411
|
-
syncedSelection = anchor to head
|
|
2412
|
-
}
|
|
2413
|
-
|
|
2414
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2415
|
-
assertNotNull(inputConnection)
|
|
2416
|
-
|
|
2417
|
-
assertTrue(inputConnection!!.setSelection(6, 11))
|
|
2418
|
-
assertEquals(6 to 11, syncedSelection)
|
|
2419
|
-
assertEquals(6, editText.selectionStart)
|
|
2420
|
-
assertEquals(11, editText.selectionEnd)
|
|
2421
|
-
}
|
|
2422
|
-
|
|
2423
|
-
@Test
|
|
2424
|
-
fun `stale input connection is consumed after editor rebind`() {
|
|
2425
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2426
|
-
editText.applyUpdateJSON(renderUpdateJson("first"), notifyListener = false)
|
|
2427
|
-
editText.setSelection(5)
|
|
2428
|
-
editText.editorId = 1
|
|
2429
|
-
|
|
2430
|
-
var insertedText: String? = null
|
|
2431
|
-
var deleteRange: Pair<Int, Int>? = null
|
|
2432
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2433
|
-
insertedText = text
|
|
2434
|
-
}
|
|
2435
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
2436
|
-
deleteRange = scalarFrom to scalarTo
|
|
2437
|
-
}
|
|
2438
|
-
|
|
2439
|
-
val staleConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2440
|
-
assertNotNull(staleConnection)
|
|
2441
|
-
|
|
2442
|
-
editText.discardTransientNativeInputForEditorRebind()
|
|
2443
|
-
editText.editorId = 0
|
|
2444
|
-
editText.applyUpdateJSON(renderUpdateJson("second"), notifyListener = false)
|
|
2445
|
-
editText.setSelection(6)
|
|
2446
|
-
editText.editorId = 2
|
|
2447
|
-
|
|
2448
|
-
assertTrue(staleConnection!!.commitText("X", 1))
|
|
2449
|
-
assertTrue(staleConnection.deleteSurroundingText(1, 0))
|
|
2450
|
-
|
|
2451
|
-
assertEquals("second", editText.text?.toString())
|
|
2452
|
-
assertNull(insertedText)
|
|
2453
|
-
assertNull(deleteRange)
|
|
2454
|
-
}
|
|
2455
|
-
|
|
2456
|
-
@Test
|
|
2457
|
-
fun `focused read only toggle restarts input and keeps stale connection blocked`() {
|
|
2458
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2459
|
-
editText.applyUpdateJSON(renderUpdateJson("abc"), notifyListener = false)
|
|
2460
|
-
editText.setSelection(3)
|
|
2461
|
-
editText.editorId = 1
|
|
2462
|
-
assertTrue(editText.requestFocus())
|
|
2463
|
-
|
|
2464
|
-
var insertedText: String? = null
|
|
2465
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2466
|
-
insertedText = text
|
|
2467
|
-
}
|
|
2468
|
-
|
|
2469
|
-
val staleConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2470
|
-
assertNotNull(staleConnection)
|
|
2471
|
-
|
|
2472
|
-
editText.isEditable = false
|
|
2473
|
-
editText.isEditable = true
|
|
2474
|
-
|
|
2475
|
-
assertTrue(
|
|
2476
|
-
editText.imeTraceSnapshotForTesting().any {
|
|
2477
|
-
it.contains("restartInput:source=editable")
|
|
2478
|
-
}
|
|
2479
|
-
)
|
|
2480
|
-
|
|
2481
|
-
assertTrue(staleConnection!!.commitText("X", 1))
|
|
2482
|
-
|
|
2483
|
-
assertEquals("abc", editText.text?.toString())
|
|
2484
|
-
assertNull(insertedText)
|
|
2485
|
-
|
|
2486
|
-
val freshConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2487
|
-
assertNotNull(freshConnection)
|
|
2488
|
-
|
|
2489
|
-
assertTrue(freshConnection!!.commitText("Y", 1))
|
|
2490
|
-
assertEquals("Y", insertedText)
|
|
2491
|
-
}
|
|
2492
|
-
|
|
2493
|
-
@Test
|
|
2494
|
-
fun `command preflight flushes empty selected composition as deletion`() {
|
|
2495
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2496
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2497
|
-
editText.setSelection(6, 11)
|
|
2498
|
-
editText.editorId = 1
|
|
2499
|
-
|
|
2500
|
-
var deletedRange: Pair<Int, Int>? = null
|
|
2501
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
2502
|
-
deletedRange = scalarFrom to scalarTo
|
|
2503
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello "), notifyListener = false)
|
|
2504
|
-
}
|
|
2505
|
-
|
|
2506
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2507
|
-
assertNotNull(inputConnection)
|
|
2508
|
-
assertTrue(inputConnection!!.setComposingText("", 1))
|
|
2509
|
-
|
|
2510
|
-
assertTrue(editText.prepareForExternalEditorUpdate())
|
|
2511
|
-
|
|
2512
|
-
assertEquals(6 to 11, deletedRange)
|
|
2513
|
-
assertEquals(0, editText.reconciliationCount)
|
|
2514
|
-
}
|
|
2515
|
-
|
|
2516
|
-
@Test
|
|
2517
|
-
fun `commit text after input connection recreation uses persisted composition range`() {
|
|
2518
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2519
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2520
|
-
editText.setSelection(6)
|
|
2521
|
-
editText.editorId = 1
|
|
2522
|
-
|
|
2523
|
-
var insertedText: String? = null
|
|
2524
|
-
var insertedScalar: Int? = null
|
|
2525
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
2526
|
-
insertedText = text
|
|
2527
|
-
insertedScalar = scalar
|
|
2528
|
-
}
|
|
2529
|
-
|
|
2530
|
-
val firstConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2531
|
-
assertNotNull(firstConnection)
|
|
2532
|
-
assertTrue(firstConnection!!.setComposingText("brave ", 1))
|
|
2533
|
-
assertEquals("Hello brave world", editText.text?.toString())
|
|
2534
|
-
|
|
2535
|
-
val recreatedConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2536
|
-
assertNotNull(recreatedConnection)
|
|
2537
|
-
assertTrue(recreatedConnection!!.commitText("brave ", 1))
|
|
2538
|
-
|
|
2539
|
-
assertEquals("brave ", insertedText)
|
|
2540
|
-
assertEquals(6, insertedScalar)
|
|
2541
|
-
}
|
|
2542
|
-
|
|
2543
|
-
@Test
|
|
2544
|
-
fun `composing text uses rendered paragraph font size before Samsung space commit`() {
|
|
2545
|
-
val context = RuntimeEnvironment.getApplication()
|
|
2546
|
-
val density = context.resources.displayMetrics.density
|
|
2547
|
-
val editText = EditorEditText(context)
|
|
2548
|
-
editText.setBaseStyle(24f * density, Color.BLACK, Color.WHITE)
|
|
2549
|
-
editText.applyTheme(
|
|
2550
|
-
EditorTheme.fromJson(
|
|
2551
|
-
"""
|
|
2552
|
-
{
|
|
2553
|
-
"text": { "fontSize": 12, "color": "#112233" }
|
|
2554
|
-
}
|
|
2555
|
-
""".trimIndent()
|
|
2556
|
-
)
|
|
2557
|
-
)
|
|
2558
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
2559
|
-
editText.setSelection(0)
|
|
2560
|
-
editText.editorId = 1
|
|
2561
|
-
|
|
2562
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2563
|
-
assertNotNull(inputConnection)
|
|
2564
|
-
assertTrue(inputConnection!!.setComposingText("word", 1))
|
|
2565
|
-
|
|
2566
|
-
val sizeSpans = editText.text!!.getSpans(0, 4, AbsoluteSizeSpan::class.java)
|
|
2567
|
-
assertTrue(sizeSpans.any { it.size == (12f * density).toInt() })
|
|
2568
|
-
}
|
|
2569
|
-
|
|
2570
|
-
@Test
|
|
2571
|
-
fun `finish composing defers render so pending Samsung space commit uses same connection`() {
|
|
2572
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2573
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
2574
|
-
editText.setSelection(0)
|
|
2575
|
-
editText.editorId = 1
|
|
2576
|
-
|
|
2577
|
-
val updates = mutableListOf<String>()
|
|
2578
|
-
editText.editorListener = object : EditorEditText.EditorListener {
|
|
2579
|
-
override fun onSelectionChanged(anchor: Int, head: Int) = Unit
|
|
2580
|
-
override fun onEditorUpdate(updateJSON: String) {
|
|
2581
|
-
updates.add(updateJSON)
|
|
2582
|
-
}
|
|
2583
|
-
}
|
|
2584
|
-
|
|
2585
|
-
val inserted = mutableListOf<Pair<String, Int>>()
|
|
2586
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
2587
|
-
inserted.add(text to scalar)
|
|
2588
|
-
val renderedText = when (text) {
|
|
2589
|
-
"word" -> "word"
|
|
2590
|
-
" " -> "word "
|
|
2591
|
-
else -> text
|
|
2592
|
-
}
|
|
2593
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(renderedText))
|
|
2594
|
-
}
|
|
2595
|
-
|
|
2596
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2597
|
-
assertNotNull(inputConnection)
|
|
2598
|
-
assertTrue(inputConnection!!.setComposingText("word", 1))
|
|
2599
|
-
|
|
2600
|
-
assertTrue(inputConnection.finishComposingText())
|
|
2601
|
-
|
|
2602
|
-
assertEquals(listOf("word" to 0), inserted)
|
|
2603
|
-
assertTrue(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
2604
|
-
assertTrue(updates.isEmpty())
|
|
2605
|
-
|
|
2606
|
-
assertTrue(inputConnection.commitText(" ", 1))
|
|
2607
|
-
|
|
2608
|
-
assertEquals(listOf("word" to 0, " " to 4), inserted)
|
|
2609
|
-
assertEquals("word ", editText.text?.toString())
|
|
2610
|
-
assertEquals(1, updates.size)
|
|
2611
|
-
|
|
2612
|
-
shadowOf(Looper.getMainLooper()).idle()
|
|
2613
|
-
|
|
2614
|
-
assertFalse(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
2615
|
-
assertEquals("word ", editText.text?.toString())
|
|
2616
|
-
assertEquals(1, updates.size)
|
|
2617
|
-
}
|
|
2618
|
-
|
|
2619
|
-
@Test
|
|
2620
|
-
fun `finish composing deferred render applies on next loop without pending commit`() {
|
|
2621
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2622
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
2623
|
-
editText.setSelection(0)
|
|
2624
|
-
editText.editorId = 1
|
|
2625
|
-
|
|
2626
|
-
val updates = mutableListOf<String>()
|
|
2627
|
-
editText.editorListener = object : EditorEditText.EditorListener {
|
|
2628
|
-
override fun onSelectionChanged(anchor: Int, head: Int) = Unit
|
|
2629
|
-
override fun onEditorUpdate(updateJSON: String) {
|
|
2630
|
-
updates.add(updateJSON)
|
|
2631
|
-
}
|
|
2632
|
-
}
|
|
2633
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2634
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(text))
|
|
2635
|
-
}
|
|
2636
|
-
|
|
2637
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2638
|
-
assertNotNull(inputConnection)
|
|
2639
|
-
assertTrue(inputConnection!!.setComposingText("word", 1))
|
|
2640
|
-
|
|
2641
|
-
assertTrue(inputConnection.finishComposingText())
|
|
2642
|
-
|
|
2643
|
-
assertTrue(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
2644
|
-
assertTrue(updates.isEmpty())
|
|
2645
|
-
|
|
2646
|
-
shadowOf(Looper.getMainLooper()).idle()
|
|
2647
|
-
|
|
2648
|
-
assertFalse(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
2649
|
-
assertEquals("word", editText.text?.toString())
|
|
2650
|
-
assertEquals(1, updates.size)
|
|
2651
|
-
}
|
|
2652
|
-
|
|
2653
|
-
@Test
|
|
2654
|
-
fun `composition commit defers render so Samsung autocorrect space commit survives`() {
|
|
2655
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2656
|
-
editText.applyUpdateJSON(renderUpdateJson("teh"), notifyListener = false)
|
|
2657
|
-
editText.setSelection(0, 3)
|
|
2658
|
-
editText.editorId = 1
|
|
2659
|
-
|
|
2660
|
-
val updates = mutableListOf<String>()
|
|
2661
|
-
editText.editorListener = object : EditorEditText.EditorListener {
|
|
2662
|
-
override fun onSelectionChanged(anchor: Int, head: Int) = Unit
|
|
2663
|
-
override fun onEditorUpdate(updateJSON: String) {
|
|
2664
|
-
updates.add(updateJSON)
|
|
2665
|
-
}
|
|
2666
|
-
}
|
|
2667
|
-
|
|
2668
|
-
val rendered = StringBuilder("teh")
|
|
2669
|
-
val replacements = mutableListOf<Triple<Int, Int, String>>()
|
|
2670
|
-
val inserts = mutableListOf<Pair<String, Int>>()
|
|
2671
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
2672
|
-
replacements.add(Triple(scalarFrom, scalarTo, text))
|
|
2673
|
-
rendered.replace(scalarFrom, scalarTo, text)
|
|
2674
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
2675
|
-
}
|
|
2676
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
2677
|
-
inserts.add(text to scalar)
|
|
2678
|
-
rendered.insert(scalar.coerceIn(0, rendered.length), text)
|
|
2679
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
2680
|
-
}
|
|
2681
|
-
|
|
2682
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2683
|
-
assertNotNull(inputConnection)
|
|
2684
|
-
assertTrue(inputConnection!!.setComposingRegion(0, 3))
|
|
2685
|
-
|
|
2686
|
-
assertTrue(inputConnection.commitText("the", 1))
|
|
2687
|
-
|
|
2688
|
-
assertEquals(listOf(Triple(0, 3, "the")), replacements)
|
|
2689
|
-
assertTrue(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
2690
|
-
assertTrue(updates.isEmpty())
|
|
2691
|
-
|
|
2692
|
-
assertTrue(inputConnection.commitText(" ", 1))
|
|
2693
|
-
|
|
2694
|
-
assertEquals(listOf(" " to 3), inserts)
|
|
2695
|
-
assertEquals("the ", editText.text?.toString())
|
|
2696
|
-
assertFalse(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
2697
|
-
|
|
2698
|
-
shadowOf(Looper.getMainLooper()).idle()
|
|
2699
|
-
|
|
2700
|
-
assertEquals("the ", editText.text?.toString())
|
|
2701
|
-
}
|
|
2702
|
-
|
|
2703
|
-
@Test
|
|
2704
|
-
fun `composition commit uses composing span when tracked range is collapsed`() {
|
|
2705
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2706
|
-
editText.applyUpdateJSON(renderUpdateJson("wouldnt"), notifyListener = false)
|
|
2707
|
-
editText.setSelection(7)
|
|
2708
|
-
editText.editorId = 1
|
|
2709
|
-
|
|
2710
|
-
val rendered = StringBuilder("wouldnt")
|
|
2711
|
-
val replacements = mutableListOf<Triple<Int, Int, String>>()
|
|
2712
|
-
val inserts = mutableListOf<Pair<String, Int>>()
|
|
2713
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
2714
|
-
replacements.add(Triple(scalarFrom, scalarTo, text))
|
|
2715
|
-
rendered.replace(scalarFrom, scalarTo, text)
|
|
2716
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
2717
|
-
}
|
|
2718
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
2719
|
-
inserts.add(text to scalar)
|
|
2720
|
-
rendered.insert(scalar.coerceIn(0, rendered.length), text)
|
|
2721
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
2722
|
-
}
|
|
2723
|
-
|
|
2724
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2725
|
-
assertNotNull(inputConnection)
|
|
2726
|
-
assertTrue(inputConnection!!.setComposingRegion(0, 7))
|
|
2727
|
-
editText.setCompositionReplacementRange(7, 7)
|
|
2728
|
-
|
|
2729
|
-
assertTrue(inputConnection.commitText("wouldn't", 1))
|
|
2730
|
-
|
|
2731
|
-
assertEquals(listOf(Triple(0, 7, "wouldn't")), replacements)
|
|
2732
|
-
assertTrue(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
2733
|
-
|
|
2734
|
-
assertTrue(inputConnection.commitText(" ", 1))
|
|
2735
|
-
|
|
2736
|
-
assertEquals(listOf(" " to 8), inserts)
|
|
2737
|
-
assertEquals("wouldn't ", editText.text?.toString())
|
|
2738
|
-
|
|
2739
|
-
shadowOf(Looper.getMainLooper()).idle()
|
|
2740
|
-
|
|
2741
|
-
assertEquals("wouldn't ", editText.text?.toString())
|
|
2742
|
-
}
|
|
2743
|
-
|
|
2744
|
-
@Test
|
|
2745
|
-
fun `composition commit adopts already visible correction instead of inserting duplicate word`() {
|
|
2746
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2747
|
-
editText.applyUpdateJSON(renderUpdateJson("wouldnt"), notifyListener = false)
|
|
2748
|
-
editText.setSelection(7)
|
|
2749
|
-
editText.editorId = 1
|
|
2750
|
-
|
|
2751
|
-
val rendered = StringBuilder("wouldnt")
|
|
2752
|
-
val replacements = mutableListOf<Triple<Int, Int, String>>()
|
|
2753
|
-
val inserts = mutableListOf<Pair<String, Int>>()
|
|
2754
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
2755
|
-
replacements.add(Triple(scalarFrom, scalarTo, text))
|
|
2756
|
-
rendered.replace(scalarFrom, scalarTo, text)
|
|
2757
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
2758
|
-
}
|
|
2759
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
2760
|
-
inserts.add(text to scalar)
|
|
2761
|
-
rendered.insert(scalar.coerceIn(0, rendered.length), text)
|
|
2762
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
2763
|
-
}
|
|
2764
|
-
|
|
2765
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2766
|
-
assertNotNull(inputConnection)
|
|
2767
|
-
editText.setCompositionReplacementRange(7, 7)
|
|
2768
|
-
editText.runWithTransientInputMutationGuard {
|
|
2769
|
-
editText.text!!.replace(0, 7, "wouldn't")
|
|
2770
|
-
Selection.setSelection(editText.text!!, 8, 8)
|
|
2771
|
-
true
|
|
2772
|
-
}
|
|
2773
|
-
|
|
2774
|
-
assertTrue(inputConnection!!.commitText("wouldn't", 1))
|
|
2775
|
-
|
|
2776
|
-
assertTrue(replacements.isEmpty())
|
|
2777
|
-
assertEquals(listOf("'" to 6), inserts)
|
|
2778
|
-
assertEquals("wouldn't", editText.text?.toString())
|
|
2779
|
-
assertTrue(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
2780
|
-
|
|
2781
|
-
assertTrue(inputConnection.commitText(" ", 1))
|
|
2782
|
-
|
|
2783
|
-
assertEquals(listOf("'" to 6, " " to 8), inserts)
|
|
2784
|
-
assertEquals("wouldn't ", editText.text?.toString())
|
|
2785
|
-
|
|
2786
|
-
shadowOf(Looper.getMainLooper()).idle()
|
|
2787
|
-
|
|
2788
|
-
assertEquals("wouldn't ", editText.text?.toString())
|
|
2789
|
-
}
|
|
2790
|
-
|
|
2791
|
-
@Test
|
|
2792
|
-
fun `already visible multi typo correction uses visible replacement range`() {
|
|
2793
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2794
|
-
editText.applyUpdateJSON(renderUpdateJson("woudlnt"), notifyListener = false)
|
|
2795
|
-
editText.setSelection(7)
|
|
2796
|
-
editText.editorId = 1
|
|
2797
|
-
|
|
2798
|
-
val rendered = StringBuilder("woudlnt")
|
|
2799
|
-
val replacements = mutableListOf<Triple<Int, Int, String>>()
|
|
2800
|
-
val inserts = mutableListOf<Pair<String, Int>>()
|
|
2801
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
2802
|
-
replacements.add(Triple(scalarFrom, scalarTo, text))
|
|
2803
|
-
rendered.replace(scalarFrom, scalarTo, text)
|
|
2804
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
2805
|
-
}
|
|
2806
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
2807
|
-
inserts.add(text to scalar)
|
|
2808
|
-
rendered.insert(scalar.coerceIn(0, rendered.length), text)
|
|
2809
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
2810
|
-
}
|
|
2811
|
-
|
|
2812
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2813
|
-
assertNotNull(inputConnection)
|
|
2814
|
-
editText.setCompositionReplacementRange(7, 7)
|
|
2815
|
-
editText.runWithTransientInputMutationGuard {
|
|
2816
|
-
editText.text!!.replace(0, 7, "wouldn't")
|
|
2817
|
-
Selection.setSelection(editText.text!!, 8, 8)
|
|
2818
|
-
true
|
|
2819
|
-
}
|
|
2820
|
-
|
|
2821
|
-
assertTrue(inputConnection!!.commitText("wouldn't", 1))
|
|
2822
|
-
|
|
2823
|
-
assertEquals(listOf(Triple(3, 6, "ldn'")), replacements)
|
|
2824
|
-
assertTrue(inserts.isEmpty())
|
|
2825
|
-
assertEquals("wouldn't", editText.text?.toString())
|
|
2826
|
-
}
|
|
2827
|
-
|
|
2828
|
-
@Test
|
|
2829
|
-
fun `empty commit text over composing range deletes original range`() {
|
|
2830
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2831
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2832
|
-
editText.setSelection(6, 11)
|
|
2833
|
-
editText.editorId = 1
|
|
2834
|
-
|
|
2835
|
-
var deletedRange: Pair<Int, Int>? = null
|
|
2836
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
2837
|
-
deletedRange = scalarFrom to scalarTo
|
|
2838
|
-
}
|
|
2839
|
-
|
|
2840
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2841
|
-
assertNotNull(inputConnection)
|
|
2842
|
-
|
|
2843
|
-
assertTrue(inputConnection!!.setComposingRegion(6, 11))
|
|
2844
|
-
assertTrue(inputConnection.commitText("", 1))
|
|
2845
|
-
|
|
2846
|
-
assertEquals(6 to 11, deletedRange)
|
|
2847
|
-
}
|
|
2848
|
-
|
|
2849
|
-
@Test
|
|
2850
|
-
fun `commit text honors requested cursor position`() {
|
|
2851
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2852
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello "), notifyListener = false)
|
|
2853
|
-
editText.setSelection(6)
|
|
2854
|
-
editText.editorId = 1
|
|
2855
|
-
|
|
2856
|
-
var insertedText: String? = null
|
|
2857
|
-
var selectedScalar: Pair<Int, Int>? = null
|
|
2858
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2859
|
-
insertedText = text
|
|
2860
|
-
}
|
|
2861
|
-
editText.onSetSelectionScalarInRustForTesting = { anchor, head ->
|
|
2862
|
-
selectedScalar = anchor to head
|
|
2863
|
-
}
|
|
2864
|
-
|
|
2865
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2866
|
-
assertNotNull(inputConnection)
|
|
2867
|
-
|
|
2868
|
-
assertTrue(inputConnection!!.commitText("()", 0))
|
|
2869
|
-
|
|
2870
|
-
assertEquals("()", insertedText)
|
|
2871
|
-
assertEquals(6 to 6, selectedScalar)
|
|
2872
|
-
}
|
|
2873
|
-
|
|
2874
|
-
@Test
|
|
2875
|
-
fun `no-op composition commit honors requested cursor position`() {
|
|
2876
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2877
|
-
editText.applyUpdateJSON(renderUpdateJson("abc"), notifyListener = false)
|
|
2878
|
-
editText.setSelection(3)
|
|
2879
|
-
editText.editorId = 1
|
|
2880
|
-
|
|
2881
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
2882
|
-
var selectedScalar: Pair<Int, Int>? = null
|
|
2883
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
2884
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
2885
|
-
}
|
|
2886
|
-
editText.onSetSelectionScalarInRustForTesting = { anchor, head ->
|
|
2887
|
-
selectedScalar = anchor to head
|
|
2888
|
-
}
|
|
2889
|
-
|
|
2890
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2891
|
-
assertNotNull(inputConnection)
|
|
2892
|
-
|
|
2893
|
-
assertTrue(inputConnection!!.setComposingRegion(0, 3))
|
|
2894
|
-
assertTrue(inputConnection.commitText("abc", 0))
|
|
2895
|
-
|
|
2896
|
-
assertNull(replacement)
|
|
2897
|
-
assertEquals(0 to 0, selectedScalar)
|
|
2898
|
-
}
|
|
2899
|
-
|
|
2900
|
-
@Test
|
|
2901
|
-
fun `command preflight flushes visible composing text before toolbar commands`() {
|
|
2902
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2903
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
2904
|
-
editText.setSelection(0)
|
|
2905
|
-
editText.editorId = 1
|
|
2906
|
-
|
|
2907
|
-
var insertedText: String? = null
|
|
2908
|
-
var insertedScalar: Int? = null
|
|
2909
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
2910
|
-
insertedText = text
|
|
2911
|
-
insertedScalar = scalar
|
|
2912
|
-
editText.applyUpdateJSON(renderUpdateJson(text), notifyListener = false)
|
|
2913
|
-
}
|
|
2914
|
-
|
|
2915
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2916
|
-
assertNotNull(inputConnection)
|
|
2917
|
-
inputConnection!!.setComposingText("abc", 1)
|
|
2918
|
-
|
|
2919
|
-
val ready = editText.prepareForExternalEditorUpdate()
|
|
2920
|
-
|
|
2921
|
-
assertTrue(ready)
|
|
2922
|
-
assertEquals("abc", insertedText)
|
|
2923
|
-
assertEquals(0, insertedScalar)
|
|
2924
|
-
assertEquals(0, editText.reconciliationCount)
|
|
2925
|
-
}
|
|
2926
|
-
|
|
2927
|
-
@Test
|
|
2928
|
-
fun `command preflight blocks and restores cancelled empty composing text`() {
|
|
2929
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2930
|
-
editText.applyUpdateJSON(renderUpdateJson(""), notifyListener = false)
|
|
2931
|
-
editText.setSelection(0)
|
|
2932
|
-
editText.editorId = 1
|
|
2933
|
-
|
|
2934
|
-
var insertedText: String? = null
|
|
2935
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
2936
|
-
insertedText = text
|
|
2937
|
-
}
|
|
2938
|
-
|
|
2939
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
2940
|
-
assertNotNull(inputConnection)
|
|
2941
|
-
assertTrue(inputConnection!!.setComposingText("", 1))
|
|
2942
|
-
assertEquals("", editText.text?.toString())
|
|
2943
|
-
|
|
2944
|
-
val ready = editText.prepareForExternalEditorUpdate()
|
|
2945
|
-
|
|
2946
|
-
assertFalse(ready)
|
|
2947
|
-
assertEquals("", editText.text?.toString())
|
|
2948
|
-
assertEquals(null, insertedText)
|
|
2949
|
-
assertEquals(0, editText.reconciliationCount)
|
|
2950
|
-
}
|
|
2951
|
-
|
|
2952
|
-
@Test
|
|
2953
|
-
fun `focused native insertion mutation commits to rust instead of reconciliation`() {
|
|
2954
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2955
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2956
|
-
assertTrue(editText.requestFocus())
|
|
2957
|
-
editText.editorId = 1
|
|
2958
|
-
|
|
2959
|
-
var insertedText: String? = null
|
|
2960
|
-
var insertedScalar: Int? = null
|
|
2961
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
2962
|
-
insertedText = text
|
|
2963
|
-
insertedScalar = scalar
|
|
2964
|
-
}
|
|
2965
|
-
|
|
2966
|
-
editText.text!!.insert(6, "brave ")
|
|
2967
|
-
|
|
2968
|
-
assertEquals("brave ", insertedText)
|
|
2969
|
-
assertEquals(6, insertedScalar)
|
|
2970
|
-
assertEquals(0, editText.reconciliationCount)
|
|
2971
|
-
}
|
|
2972
|
-
|
|
2973
|
-
@Test
|
|
2974
|
-
fun `focused native multiline insertion uses structured content insertion`() {
|
|
2975
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2976
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
2977
|
-
assertTrue(editText.requestFocus())
|
|
2978
|
-
editText.editorId = 1
|
|
2979
|
-
|
|
2980
|
-
var insertedContent: Triple<Int, Int, String>? = null
|
|
2981
|
-
editText.onInsertContentJsonAtSelectionScalarForTesting = { scalarFrom, scalarTo, json ->
|
|
2982
|
-
insertedContent = Triple(scalarFrom, scalarTo, json)
|
|
2983
|
-
}
|
|
2984
|
-
|
|
2985
|
-
editText.text!!.insert(6, "one\ntwo")
|
|
2986
|
-
|
|
2987
|
-
val (scalarFrom, scalarTo, json) = insertedContent!!
|
|
2988
|
-
assertEquals(6, scalarFrom)
|
|
2989
|
-
assertEquals(6, scalarTo)
|
|
2990
|
-
val content = JSONObject(json).getJSONArray("content")
|
|
2991
|
-
assertEquals("one", content.getJSONObject(0).getJSONArray("content").getJSONObject(0).getString("text"))
|
|
2992
|
-
assertEquals("two", content.getJSONObject(1).getJSONArray("content").getJSONObject(0).getString("text"))
|
|
2993
|
-
assertEquals(0, editText.reconciliationCount)
|
|
2994
|
-
}
|
|
2995
|
-
|
|
2996
|
-
@Test
|
|
2997
|
-
fun `focused native replacement mutation commits to rust instead of reconciliation`() {
|
|
2998
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
2999
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3000
|
-
assertTrue(editText.requestFocus())
|
|
3001
|
-
editText.editorId = 1
|
|
3002
|
-
|
|
3003
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3004
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3005
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3006
|
-
}
|
|
3007
|
-
|
|
3008
|
-
editText.text!!.replace(6, 11, "there")
|
|
3009
|
-
|
|
3010
|
-
assertEquals(Triple(6, 11, "there"), replacement)
|
|
3011
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3012
|
-
}
|
|
3013
|
-
|
|
3014
|
-
@Test
|
|
3015
|
-
fun `focused native deletion mutation commits to rust instead of reconciliation`() {
|
|
3016
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3017
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3018
|
-
assertTrue(editText.requestFocus())
|
|
3019
|
-
editText.editorId = 1
|
|
3020
|
-
|
|
3021
|
-
var deletedRange: Pair<Int, Int>? = null
|
|
3022
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
3023
|
-
deletedRange = scalarFrom to scalarTo
|
|
3024
|
-
}
|
|
3025
|
-
|
|
3026
|
-
editText.text!!.delete(5, 6)
|
|
3027
|
-
|
|
3028
|
-
assertEquals(5 to 6, deletedRange)
|
|
3029
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3030
|
-
}
|
|
3031
|
-
|
|
3032
|
-
@Test
|
|
3033
|
-
fun `native emoji replacement snaps diff to scalar boundaries`() {
|
|
3034
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3035
|
-
editText.applyUpdateJSON(renderUpdateJson("😀 ok"), notifyListener = false)
|
|
3036
|
-
assertTrue(editText.requestFocus())
|
|
3037
|
-
editText.editorId = 1
|
|
3038
|
-
|
|
3039
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3040
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3041
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3042
|
-
}
|
|
3043
|
-
|
|
3044
|
-
editText.text!!.replace(0, 2, "😁")
|
|
3045
|
-
|
|
3046
|
-
assertEquals(Triple(0, 1, "😁"), replacement)
|
|
3047
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3048
|
-
}
|
|
3049
|
-
|
|
3050
|
-
@Test
|
|
3051
|
-
fun `native autocorrect immediately after blur commits during blur grace window`() {
|
|
3052
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3053
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
3054
|
-
assertTrue(editText.requestFocus())
|
|
3055
|
-
editText.editorId = 1
|
|
3056
|
-
|
|
3057
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3058
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3059
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3060
|
-
}
|
|
3061
|
-
|
|
3062
|
-
editText.clearFocus()
|
|
3063
|
-
editText.text!!.replace(0, 3, "the")
|
|
3064
|
-
|
|
3065
|
-
assertEquals(Triple(1, 3, "he"), replacement)
|
|
3066
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3067
|
-
}
|
|
3068
|
-
|
|
3069
|
-
@Test
|
|
3070
|
-
fun `native autocorrect after blur commits even when ime leaves composing span`() {
|
|
3071
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3072
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
3073
|
-
assertTrue(editText.requestFocus())
|
|
3074
|
-
editText.editorId = 1
|
|
3075
|
-
|
|
3076
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3077
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3078
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3079
|
-
}
|
|
3080
|
-
|
|
3081
|
-
editText.clearFocus()
|
|
3082
|
-
editText.runWithTransientInputMutationGuard {
|
|
3083
|
-
editText.text!!.replace(0, 3, "the")
|
|
3084
|
-
BaseInputConnection.setComposingSpans(editText.text!!)
|
|
3085
|
-
true
|
|
3086
|
-
}
|
|
3087
|
-
|
|
3088
|
-
assertTrue(editText.prepareForExternalEditorUpdate())
|
|
3089
|
-
assertEquals(Triple(1, 3, "he"), replacement)
|
|
3090
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3091
|
-
}
|
|
3092
|
-
|
|
3093
|
-
@Test
|
|
3094
|
-
fun `native composing diff after blur is not adopted as final mutation`() {
|
|
3095
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3096
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3097
|
-
assertTrue(editText.requestFocus())
|
|
3098
|
-
editText.setSelection(6)
|
|
3099
|
-
editText.editorId = 1
|
|
3100
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
3101
|
-
|
|
3102
|
-
var insertedText: String? = null
|
|
3103
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3104
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
3105
|
-
insertedText = text
|
|
3106
|
-
}
|
|
3107
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3108
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3109
|
-
}
|
|
3110
|
-
|
|
3111
|
-
editText.setCompositionReplacementRange(6, 6)
|
|
3112
|
-
editText.setComposingTextForEditor("brave ")
|
|
3113
|
-
editText.runWithTransientInputMutationGuard {
|
|
3114
|
-
editText.text!!.insert(6, "braver ")
|
|
3115
|
-
BaseInputConnection.setComposingSpans(editText.text!!)
|
|
3116
|
-
true
|
|
3117
|
-
}
|
|
3118
|
-
editText.clearFocus()
|
|
3119
|
-
|
|
3120
|
-
assertFalse(editText.prepareForExternalEditorUpdate())
|
|
3121
|
-
assertNull(insertedText)
|
|
3122
|
-
assertNull(replacement)
|
|
3123
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3124
|
-
}
|
|
3125
|
-
|
|
3126
|
-
@Test
|
|
3127
|
-
fun `input trait change after blur suppresses stale direct native mutation adoption`() {
|
|
3128
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3129
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3130
|
-
assertTrue(editText.requestFocus())
|
|
3131
|
-
editText.setSelection(6)
|
|
3132
|
-
editText.editorId = 1
|
|
3133
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
3134
|
-
|
|
3135
|
-
var insertedText: String? = null
|
|
3136
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3137
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
3138
|
-
insertedText = text
|
|
3139
|
-
}
|
|
3140
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3141
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3142
|
-
}
|
|
3143
|
-
|
|
3144
|
-
val oldConnection = editText.onCreateInputConnection(EditorInfo())
|
|
3145
|
-
assertNotNull(oldConnection)
|
|
3146
|
-
assertTrue(oldConnection!!.setComposingText("brave ", 1))
|
|
3147
|
-
|
|
3148
|
-
editText.clearFocus()
|
|
3149
|
-
editText.setAutoCorrect(false)
|
|
3150
|
-
assertEquals("Hello world", editText.text?.toString())
|
|
3151
|
-
|
|
3152
|
-
editText.runWithTransientInputMutationGuard {
|
|
3153
|
-
editText.text!!.insert(6, "stale ")
|
|
3154
|
-
true
|
|
3155
|
-
}
|
|
3156
|
-
|
|
3157
|
-
assertFalse(editText.prepareForExternalEditorUpdate())
|
|
3158
|
-
assertNull(insertedText)
|
|
3159
|
-
assertNull(replacement)
|
|
3160
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3161
|
-
}
|
|
3162
|
-
|
|
3163
|
-
@Test
|
|
3164
|
-
fun `native mutation after blur is only adopted once`() {
|
|
3165
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3166
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
3167
|
-
assertTrue(editText.requestFocus())
|
|
3168
|
-
editText.editorId = 1
|
|
3169
|
-
|
|
3170
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3171
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3172
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3173
|
-
}
|
|
3174
|
-
|
|
3175
|
-
editText.clearFocus()
|
|
3176
|
-
editText.runWithTransientInputMutationGuard {
|
|
3177
|
-
editText.text!!.replace(0, 3, "the")
|
|
3178
|
-
true
|
|
3179
|
-
}
|
|
3180
|
-
|
|
3181
|
-
assertTrue(editText.prepareForExternalEditorUpdate())
|
|
3182
|
-
assertEquals(Triple(1, 3, "he"), replacement)
|
|
3183
|
-
|
|
3184
|
-
replacement = null
|
|
3185
|
-
editText.runWithTransientInputMutationGuard {
|
|
3186
|
-
editText.text!!.replace(0, 3, "tha")
|
|
3187
|
-
true
|
|
3188
|
-
}
|
|
3189
|
-
|
|
3190
|
-
assertFalse(editText.prepareForExternalEditorUpdate())
|
|
3191
|
-
assertNull(replacement)
|
|
3192
|
-
}
|
|
3193
|
-
|
|
3194
|
-
@Test
|
|
3195
|
-
fun `native mutation after blur grace window expires is not adopted`() {
|
|
3196
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3197
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
3198
|
-
assertTrue(editText.requestFocus())
|
|
3199
|
-
editText.editorId = 1
|
|
3200
|
-
|
|
3201
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3202
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3203
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3204
|
-
}
|
|
3205
|
-
|
|
3206
|
-
editText.clearFocus()
|
|
3207
|
-
shadowOf(Looper.getMainLooper()).idleFor(Duration.ofMillis(800))
|
|
3208
|
-
editText.runWithTransientInputMutationGuard {
|
|
3209
|
-
editText.text!!.replace(0, 3, "the")
|
|
3210
|
-
true
|
|
3211
|
-
}
|
|
3212
|
-
|
|
3213
|
-
assertFalse(editText.prepareForExternalEditorUpdate())
|
|
3214
|
-
assertNull(replacement)
|
|
3215
|
-
}
|
|
3216
|
-
|
|
3217
|
-
@Test
|
|
3218
|
-
fun `native mutation after blur is only adopted once after applied update render`() {
|
|
3219
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3220
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
3221
|
-
assertTrue(editText.requestFocus())
|
|
3222
|
-
editText.editorId = 1
|
|
3223
|
-
|
|
3224
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3225
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3226
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3227
|
-
editText.applyUpdateJSON(renderUpdateJson("the "), notifyListener = false)
|
|
3228
|
-
}
|
|
3229
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
3230
|
-
|
|
3231
|
-
editText.clearFocus()
|
|
3232
|
-
editText.runWithTransientInputMutationGuard {
|
|
3233
|
-
editText.text!!.replace(0, 3, "the")
|
|
3234
|
-
true
|
|
3235
|
-
}
|
|
3236
|
-
|
|
3237
|
-
assertTrue(editText.prepareForExternalEditorUpdate())
|
|
3238
|
-
assertEquals(Triple(1, 3, "he"), replacement)
|
|
3239
|
-
assertEquals("the ", editText.text?.toString())
|
|
3240
|
-
|
|
3241
|
-
replacement = null
|
|
3242
|
-
editText.runWithTransientInputMutationGuard {
|
|
3243
|
-
editText.text!!.replace(0, 3, "tha")
|
|
3244
|
-
true
|
|
3245
|
-
}
|
|
3246
|
-
|
|
3247
|
-
assertFalse(editText.prepareForExternalEditorUpdate())
|
|
3248
|
-
assertNull(replacement)
|
|
3249
|
-
}
|
|
3250
|
-
|
|
3251
|
-
@Test
|
|
3252
|
-
fun `native mutation after blur window clears after skipped authorized render`() {
|
|
3253
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication()).apply {
|
|
3254
|
-
captureApplyUpdateTraceForTesting = true
|
|
3255
|
-
}
|
|
3256
|
-
editText.applyUpdateJSON(renderUpdateJson("the "), notifyListener = false)
|
|
3257
|
-
assertTrue(editText.requestFocus())
|
|
3258
|
-
editText.editorId = 1
|
|
3259
|
-
|
|
3260
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3261
|
-
var insertedText: String? = null
|
|
3262
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3263
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3264
|
-
}
|
|
3265
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
3266
|
-
insertedText = text
|
|
3267
|
-
}
|
|
3268
|
-
|
|
3269
|
-
editText.clearFocus()
|
|
3270
|
-
editText.applyUpdateJSON(renderUpdateJson("the "), notifyListener = false)
|
|
3271
|
-
assertTrue(editText.lastApplyUpdateTrace()?.skippedRender == true)
|
|
3272
|
-
|
|
3273
|
-
editText.runWithTransientInputMutationGuard {
|
|
3274
|
-
editText.text!!.replace(0, 3, "tha")
|
|
3275
|
-
true
|
|
3276
|
-
}
|
|
3277
|
-
|
|
3278
|
-
assertFalse(editText.prepareForExternalEditorUpdate())
|
|
3279
|
-
assertNull(replacement)
|
|
3280
|
-
assertNull(insertedText)
|
|
3281
|
-
}
|
|
3282
|
-
|
|
3283
|
-
@Test
|
|
3284
|
-
fun `native autocorrect preserves final ime selection`() {
|
|
3285
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3286
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
3287
|
-
assertTrue(editText.requestFocus())
|
|
3288
|
-
editText.setSelection(4)
|
|
3289
|
-
editText.editorId = 1
|
|
3290
|
-
|
|
3291
|
-
editText.onReplaceTextInRustForTesting = { _, _, _ -> }
|
|
3292
|
-
|
|
3293
|
-
editText.text!!.replace(0, 3, "the")
|
|
3294
|
-
|
|
3295
|
-
assertEquals(4, editText.selectionStart)
|
|
3296
|
-
assertEquals(4, editText.selectionEnd)
|
|
3297
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3298
|
-
}
|
|
3299
|
-
|
|
3300
|
-
@Test
|
|
3301
|
-
fun `native autocorrect preserves backward selection direction`() {
|
|
3302
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3303
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3304
|
-
assertTrue(editText.requestFocus())
|
|
3305
|
-
editText.editorId = 1
|
|
3306
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
3307
|
-
Selection.setSelection(editText.text, 11, 6)
|
|
3308
|
-
|
|
3309
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3310
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3311
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3312
|
-
}
|
|
3313
|
-
|
|
3314
|
-
editText.text!!.replace(0, 5, "Hi")
|
|
3315
|
-
|
|
3316
|
-
assertEquals(Triple(1, 5, "i"), replacement)
|
|
3317
|
-
assertTrue(editText.selectionStart > editText.selectionEnd)
|
|
3318
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3319
|
-
}
|
|
3320
|
-
|
|
3321
|
-
@Test
|
|
3322
|
-
fun `focused native autocorrect with stray composing span commits when no composition is tracked`() {
|
|
3323
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3324
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
3325
|
-
assertTrue(editText.requestFocus())
|
|
3326
|
-
editText.editorId = 1
|
|
3327
|
-
|
|
3328
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3329
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3330
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3331
|
-
}
|
|
3332
|
-
|
|
3333
|
-
BaseInputConnection.setComposingSpans(editText.text!!)
|
|
3334
|
-
editText.text!!.replace(0, 3, "the")
|
|
3335
|
-
|
|
3336
|
-
assertEquals(Triple(1, 3, "he"), replacement)
|
|
3337
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3338
|
-
}
|
|
3339
|
-
|
|
3340
|
-
@Test
|
|
3341
|
-
fun `focused native autocorrect with tracked composition commits instead of reconciliation`() {
|
|
3342
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3343
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
3344
|
-
assertTrue(editText.requestFocus())
|
|
3345
|
-
editText.editorId = 1
|
|
3346
|
-
|
|
3347
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3348
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3349
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3350
|
-
}
|
|
3351
|
-
|
|
3352
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
3353
|
-
assertNotNull(inputConnection)
|
|
3354
|
-
assertTrue(inputConnection!!.setComposingRegion(0, 3))
|
|
3355
|
-
BaseInputConnection.setComposingSpans(editText.text!!)
|
|
3356
|
-
|
|
3357
|
-
editText.text!!.replace(0, 3, "the")
|
|
3358
|
-
|
|
3359
|
-
assertEquals(Triple(1, 3, "he"), replacement)
|
|
3360
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3361
|
-
}
|
|
3362
|
-
|
|
3363
|
-
@Test
|
|
3364
|
-
fun `focused native insertion at tracked composition boundary commits as final mutation`() {
|
|
3365
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3366
|
-
editText.applyUpdateJSON(renderUpdateJson("word "), notifyListener = false)
|
|
3367
|
-
assertTrue(editText.requestFocus())
|
|
3368
|
-
editText.editorId = 1
|
|
3369
|
-
|
|
3370
|
-
var insertedText: String? = null
|
|
3371
|
-
var insertedScalar: Int? = null
|
|
3372
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
3373
|
-
insertedText = text
|
|
3374
|
-
insertedScalar = scalar
|
|
3375
|
-
}
|
|
3376
|
-
|
|
3377
|
-
editText.setCompositionReplacementRange(0, 4)
|
|
3378
|
-
editText.text!!.insert(4, "!")
|
|
3379
|
-
|
|
3380
|
-
assertEquals("!", insertedText)
|
|
3381
|
-
assertEquals(4, insertedScalar)
|
|
3382
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3383
|
-
}
|
|
3384
|
-
|
|
3385
|
-
@Test
|
|
3386
|
-
fun `focused native insertion at collapsed tracked composition range commits at caret`() {
|
|
3387
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3388
|
-
editText.applyUpdateJSON(renderUpdateJson("abcd"), notifyListener = false)
|
|
3389
|
-
assertTrue(editText.requestFocus())
|
|
3390
|
-
editText.editorId = 1
|
|
3391
|
-
|
|
3392
|
-
var insertedText: String? = null
|
|
3393
|
-
var insertedScalar: Int? = null
|
|
3394
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
3395
|
-
insertedText = text
|
|
3396
|
-
insertedScalar = scalar
|
|
3397
|
-
}
|
|
3398
|
-
|
|
3399
|
-
editText.setCompositionReplacementRange(2, 2)
|
|
3400
|
-
editText.text!!.insert(2, "X")
|
|
3401
|
-
|
|
3402
|
-
assertEquals("X", insertedText)
|
|
3403
|
-
assertEquals(2, insertedScalar)
|
|
3404
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3405
|
-
}
|
|
3406
|
-
|
|
3407
|
-
@Test
|
|
3408
|
-
fun `focused native mutation outside tracked composition range is not adopted`() {
|
|
3409
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3410
|
-
editText.applyUpdateJSON(renderUpdateJson("teh word"), notifyListener = false)
|
|
3411
|
-
assertTrue(editText.requestFocus())
|
|
3412
|
-
editText.editorId = 1
|
|
3413
|
-
|
|
3414
|
-
var insertedText: String? = null
|
|
3415
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3416
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
3417
|
-
insertedText = text
|
|
3418
|
-
}
|
|
3419
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3420
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3421
|
-
}
|
|
3422
|
-
|
|
3423
|
-
editText.setCompositionReplacementRange(0, 3)
|
|
3424
|
-
editText.runWithTransientInputMutationGuard {
|
|
3425
|
-
editText.text!!.insert(4, "!")
|
|
3426
|
-
true
|
|
3427
|
-
}
|
|
3428
|
-
|
|
3429
|
-
assertFalse(editText.prepareForExternalEditorUpdate())
|
|
3430
|
-
assertNull(insertedText)
|
|
3431
|
-
assertNull(replacement)
|
|
3432
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3433
|
-
}
|
|
3434
|
-
|
|
3435
|
-
@Test
|
|
3436
|
-
fun `focused native composing diff with tracked composing text is not adopted as final mutation`() {
|
|
3437
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3438
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3439
|
-
assertTrue(editText.requestFocus())
|
|
3440
|
-
editText.setSelection(6)
|
|
3441
|
-
editText.editorId = 1
|
|
3442
|
-
|
|
3443
|
-
var insertedText: String? = null
|
|
3444
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3445
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
3446
|
-
insertedText = text
|
|
3447
|
-
}
|
|
3448
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3449
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3450
|
-
}
|
|
3451
|
-
|
|
3452
|
-
editText.setCompositionReplacementRange(6, 6)
|
|
3453
|
-
editText.setComposingTextForEditor("brave ")
|
|
3454
|
-
editText.runWithTransientInputMutationGuard {
|
|
3455
|
-
editText.text!!.insert(6, "braver ")
|
|
3456
|
-
true
|
|
3457
|
-
}
|
|
3458
|
-
|
|
3459
|
-
assertFalse(editText.prepareForExternalEditorUpdate())
|
|
3460
|
-
assertNull(insertedText)
|
|
3461
|
-
assertNull(replacement)
|
|
3462
|
-
assertEquals(0, editText.reconciliationCount)
|
|
3463
|
-
}
|
|
3464
|
-
|
|
3465
|
-
@Test
|
|
3466
|
-
fun `native autocorrect retires old input connection before late commit`() {
|
|
3467
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3468
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
3469
|
-
assertTrue(editText.requestFocus())
|
|
3470
|
-
editText.editorId = 1
|
|
3471
|
-
|
|
3472
|
-
val replacements = mutableListOf<Triple<Int, Int, String>>()
|
|
3473
|
-
var insertedText: String? = null
|
|
3474
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3475
|
-
replacements.add(Triple(scalarFrom, scalarTo, text))
|
|
3476
|
-
}
|
|
3477
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
3478
|
-
insertedText = text
|
|
3479
|
-
}
|
|
3480
|
-
|
|
3481
|
-
val oldConnection = editText.onCreateInputConnection(EditorInfo())
|
|
3482
|
-
assertNotNull(oldConnection)
|
|
3483
|
-
assertTrue(oldConnection!!.setComposingRegion(0, 3))
|
|
3484
|
-
|
|
3485
|
-
editText.text!!.replace(0, 3, "the")
|
|
3486
|
-
|
|
3487
|
-
assertEquals(listOf(Triple(1, 3, "he")), replacements)
|
|
3488
|
-
|
|
3489
|
-
assertTrue(oldConnection.commitText("the", 1))
|
|
3490
|
-
assertTrue(oldConnection.finishComposingText())
|
|
3491
|
-
|
|
3492
|
-
assertEquals(listOf(Triple(1, 3, "he")), replacements)
|
|
3493
|
-
assertNull(insertedText)
|
|
3494
|
-
}
|
|
3495
|
-
|
|
3496
|
-
@Test
|
|
3497
|
-
fun `fresh input connection after native autocorrect accepts new commit`() {
|
|
3498
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3499
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
3500
|
-
assertTrue(editText.requestFocus())
|
|
3501
|
-
editText.setSelection(4)
|
|
3502
|
-
editText.editorId = 1
|
|
3503
|
-
editText.onSetSelectionScalarInRustForTesting = { _, _ -> }
|
|
3504
|
-
|
|
3505
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3506
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3507
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3508
|
-
editText.applyUpdateJSON(renderUpdateJson("the "), notifyListener = false)
|
|
3509
|
-
}
|
|
3510
|
-
|
|
3511
|
-
val oldConnection = editText.onCreateInputConnection(EditorInfo())
|
|
3512
|
-
assertNotNull(oldConnection)
|
|
3513
|
-
|
|
3514
|
-
editText.text!!.replace(0, 3, "the")
|
|
3515
|
-
|
|
3516
|
-
assertEquals(Triple(1, 3, "he"), replacement)
|
|
3517
|
-
|
|
3518
|
-
var insertedText: String? = null
|
|
3519
|
-
editText.onInsertTextInRustForTesting = { text, _ ->
|
|
3520
|
-
insertedText = text
|
|
3521
|
-
}
|
|
3522
|
-
|
|
3523
|
-
val freshConnection = editText.onCreateInputConnection(EditorInfo())
|
|
3524
|
-
assertNotNull(freshConnection)
|
|
3525
|
-
assertTrue(freshConnection!!.commitText("!", 1))
|
|
3526
|
-
|
|
3527
|
-
assertEquals("!", insertedText)
|
|
3528
|
-
}
|
|
3529
|
-
|
|
3530
|
-
@Test
|
|
3531
|
-
fun `text commit replaces normalized backward selection range`() {
|
|
3532
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3533
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3534
|
-
Selection.setSelection(editText.text, 11, 6)
|
|
3535
|
-
editText.editorId = 1
|
|
3536
|
-
|
|
3537
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3538
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3539
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3540
|
-
}
|
|
3541
|
-
|
|
3542
|
-
editText.handleTextCommit("there")
|
|
3543
|
-
|
|
3544
|
-
assertEquals(Triple(6, 11, "there"), replacement)
|
|
3545
|
-
}
|
|
3546
|
-
|
|
3547
|
-
@Test
|
|
3548
|
-
fun `text replacement commit does not optimistically mutate visible text`() {
|
|
3549
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3550
|
-
editText.applyUpdateJSON(renderUpdateJson("teh "), notifyListener = false)
|
|
3551
|
-
editText.setSelection(0, 3)
|
|
3552
|
-
editText.editorId = 1
|
|
3553
|
-
|
|
3554
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3555
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3556
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3557
|
-
}
|
|
3558
|
-
|
|
3559
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
3560
|
-
assertNotNull(inputConnection)
|
|
3561
|
-
|
|
3562
|
-
assertTrue(inputConnection!!.commitText("the", 1))
|
|
3563
|
-
|
|
3564
|
-
assertEquals(Triple(0, 3, "the"), replacement)
|
|
3565
|
-
assertEquals("teh ", editText.text?.toString())
|
|
3566
|
-
assertFalse(
|
|
3567
|
-
editText.imeTraceSnapshotForTesting().any {
|
|
3568
|
-
it.contains("optimisticVisibleTextCommit")
|
|
3569
|
-
}
|
|
3570
|
-
)
|
|
3571
|
-
}
|
|
3572
|
-
|
|
3573
|
-
@Test
|
|
3574
|
-
fun `bulk surrounding delete defers render so autocorrect replacement commit survives`() {
|
|
3575
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3576
|
-
editText.applyUpdateJSON(renderUpdateJson("teh"), notifyListener = false)
|
|
3577
|
-
editText.setSelection(3)
|
|
3578
|
-
editText.editorId = 1
|
|
3579
|
-
|
|
3580
|
-
val rendered = StringBuilder("teh")
|
|
3581
|
-
val deletes = mutableListOf<Pair<Int, Int>>()
|
|
3582
|
-
val inserts = mutableListOf<Pair<String, Int>>()
|
|
3583
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
3584
|
-
deletes.add(scalarFrom to scalarTo)
|
|
3585
|
-
rendered.delete(scalarFrom, scalarTo)
|
|
3586
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
3587
|
-
}
|
|
3588
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
3589
|
-
inserts.add(text to scalar)
|
|
3590
|
-
rendered.insert(scalar.coerceIn(0, rendered.length), text)
|
|
3591
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
3592
|
-
}
|
|
3593
|
-
|
|
3594
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
3595
|
-
assertNotNull(inputConnection)
|
|
3596
|
-
|
|
3597
|
-
assertTrue(inputConnection!!.deleteSurroundingText(3, 0))
|
|
3598
|
-
|
|
3599
|
-
assertEquals(listOf(0 to 3), deletes)
|
|
3600
|
-
assertEquals("", editText.text?.toString())
|
|
3601
|
-
assertTrue(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
3602
|
-
|
|
3603
|
-
assertTrue(inputConnection.commitText("the", 1))
|
|
3604
|
-
|
|
3605
|
-
assertEquals(listOf("the" to 0), inserts)
|
|
3606
|
-
assertEquals("the", editText.text?.toString())
|
|
3607
|
-
assertFalse(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
3608
|
-
|
|
3609
|
-
shadowOf(Looper.getMainLooper()).idle()
|
|
3610
|
-
|
|
3611
|
-
assertEquals("the", editText.text?.toString())
|
|
3612
|
-
}
|
|
3613
|
-
|
|
3614
|
-
@Test
|
|
3615
|
-
fun `single character surrounding delete defers render so case replacement commit survives`() {
|
|
3616
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3617
|
-
editText.applyUpdateJSON(renderUpdateJson("i"), notifyListener = false)
|
|
3618
|
-
editText.setSelection(1)
|
|
3619
|
-
editText.editorId = 1
|
|
3620
|
-
|
|
3621
|
-
val updates = mutableListOf<String>()
|
|
3622
|
-
editText.editorListener = object : EditorEditText.EditorListener {
|
|
3623
|
-
override fun onSelectionChanged(anchor: Int, head: Int) = Unit
|
|
3624
|
-
override fun onEditorUpdate(updateJSON: String) {
|
|
3625
|
-
updates.add(updateJSON)
|
|
3626
|
-
}
|
|
3627
|
-
}
|
|
3628
|
-
|
|
3629
|
-
val rendered = StringBuilder("i")
|
|
3630
|
-
val deletes = mutableListOf<Pair<Int, Int>>()
|
|
3631
|
-
val inserts = mutableListOf<Pair<String, Int>>()
|
|
3632
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
3633
|
-
deletes.add(scalarFrom to scalarTo)
|
|
3634
|
-
rendered.delete(scalarFrom, scalarTo)
|
|
3635
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
3636
|
-
}
|
|
3637
|
-
editText.onInsertTextInRustForTesting = { text, scalar ->
|
|
3638
|
-
inserts.add(text to scalar)
|
|
3639
|
-
rendered.insert(scalar.coerceIn(0, rendered.length), text)
|
|
3640
|
-
editText.applyRustUpdateJSONForTesting(renderUpdateJson(rendered.toString()))
|
|
3641
|
-
}
|
|
3642
|
-
|
|
3643
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
3644
|
-
assertNotNull(inputConnection)
|
|
3645
|
-
|
|
3646
|
-
assertTrue(inputConnection!!.deleteSurroundingText(1, 0))
|
|
3647
|
-
|
|
3648
|
-
assertEquals(listOf(0 to 1), deletes)
|
|
3649
|
-
assertEquals("", editText.text?.toString())
|
|
3650
|
-
assertTrue(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
3651
|
-
assertTrue(updates.isEmpty())
|
|
3652
|
-
|
|
3653
|
-
assertTrue(inputConnection.commitText("I", 1))
|
|
3654
|
-
|
|
3655
|
-
assertEquals(listOf("I" to 0), inserts)
|
|
3656
|
-
assertEquals("I", editText.text?.toString())
|
|
3657
|
-
assertEquals(1, updates.size)
|
|
3658
|
-
|
|
3659
|
-
shadowOf(Looper.getMainLooper()).idle()
|
|
3660
|
-
|
|
3661
|
-
assertFalse(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
3662
|
-
assertEquals("I", editText.text?.toString())
|
|
3663
|
-
assertEquals(1, updates.size)
|
|
3664
|
-
}
|
|
3665
|
-
|
|
3666
|
-
@Test
|
|
3667
|
-
fun `bulk surrounding delete no-op does not queue rust delete`() {
|
|
3668
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3669
|
-
editText.applyUpdateJSON(renderUpdateJson("abc"), notifyListener = false)
|
|
3670
|
-
editText.setSelection(0)
|
|
3671
|
-
editText.editorId = 1
|
|
3672
|
-
|
|
3673
|
-
val deletes = mutableListOf<Pair<Int, Int>>()
|
|
3674
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
3675
|
-
deletes.add(scalarFrom to scalarTo)
|
|
3676
|
-
}
|
|
3677
|
-
|
|
3678
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
3679
|
-
assertNotNull(inputConnection)
|
|
3680
|
-
|
|
3681
|
-
assertTrue(inputConnection!!.deleteSurroundingText(3, 0))
|
|
3682
|
-
|
|
3683
|
-
assertTrue(deletes.isEmpty())
|
|
3684
|
-
assertEquals("abc", editText.text?.toString())
|
|
3685
|
-
assertFalse(editText.hasDeferredRustUpdateApplicationForTesting())
|
|
3686
|
-
}
|
|
3687
|
-
|
|
3688
|
-
@Test
|
|
3689
|
-
fun `text commit snaps split surrogate selection to scalar boundaries`() {
|
|
3690
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3691
|
-
editText.applyUpdateJSON(renderUpdateJson("A😀B"), notifyListener = false)
|
|
3692
|
-
editText.setSelection(2, 3)
|
|
3693
|
-
editText.editorId = 1
|
|
3694
|
-
|
|
3695
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3696
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3697
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3698
|
-
}
|
|
3699
|
-
|
|
3700
|
-
editText.handleTextCommit("X")
|
|
3701
|
-
|
|
3702
|
-
assertEquals(Triple(1, 2, "X"), replacement)
|
|
3703
|
-
}
|
|
3704
|
-
|
|
3705
|
-
@Test
|
|
3706
|
-
fun `selection sync snaps split surrogate selection to scalar boundaries`() {
|
|
3707
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3708
|
-
editText.applyUpdateJSON(renderUpdateJson("A😀B"), notifyListener = false)
|
|
3709
|
-
editText.editorId = 1
|
|
3710
|
-
|
|
3711
|
-
var syncedSelection: Pair<Int, Int>? = null
|
|
3712
|
-
editText.onSetSelectionScalarInRustForTesting = { anchor, head ->
|
|
3713
|
-
syncedSelection = anchor to head
|
|
3714
|
-
}
|
|
3715
|
-
|
|
3716
|
-
editText.setSelection(2, 3)
|
|
3717
|
-
|
|
3718
|
-
assertEquals(1 to 2, syncedSelection)
|
|
3719
|
-
}
|
|
3720
|
-
|
|
3721
|
-
@Test
|
|
3722
|
-
fun `selection sync preserves backward anchor and head direction`() {
|
|
3723
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3724
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3725
|
-
editText.editorId = 1
|
|
3726
|
-
|
|
3727
|
-
var syncedSelection: Pair<Int, Int>? = null
|
|
3728
|
-
editText.onSetSelectionScalarInRustForTesting = { anchor, head ->
|
|
3729
|
-
syncedSelection = anchor to head
|
|
3730
|
-
}
|
|
3731
|
-
|
|
3732
|
-
Selection.setSelection(editText.text, 11, 6)
|
|
3733
|
-
|
|
3734
|
-
assertEquals(11 to 6, syncedSelection)
|
|
3735
|
-
}
|
|
3736
|
-
|
|
3737
|
-
@Test
|
|
3738
|
-
fun `collapsed composition range snaps split surrogate caret to insertion point`() {
|
|
3739
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3740
|
-
editText.applyUpdateJSON(renderUpdateJson("A😀B"), notifyListener = false)
|
|
3741
|
-
|
|
3742
|
-
editText.setCompositionReplacementRange(2, 2)
|
|
3743
|
-
|
|
3744
|
-
assertEquals(3 to 3, editText.compositionReplacementRange())
|
|
3745
|
-
}
|
|
3746
|
-
|
|
3747
|
-
@Test
|
|
3748
|
-
fun `backspace deletes normalized backward selection range`() {
|
|
3749
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3750
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3751
|
-
Selection.setSelection(editText.text, 11, 6)
|
|
3752
|
-
editText.editorId = 1
|
|
3753
|
-
|
|
3754
|
-
var deletedRange: Pair<Int, Int>? = null
|
|
3755
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
3756
|
-
deletedRange = scalarFrom to scalarTo
|
|
3757
|
-
}
|
|
3758
|
-
|
|
3759
|
-
editText.handleBackspace()
|
|
3760
|
-
|
|
3761
|
-
assertEquals(6 to 11, deletedRange)
|
|
3762
|
-
}
|
|
3763
|
-
|
|
3764
|
-
@Test
|
|
3765
|
-
fun `backspace snaps split surrogate selection to scalar boundaries`() {
|
|
3766
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3767
|
-
editText.applyUpdateJSON(renderUpdateJson("A😀B"), notifyListener = false)
|
|
3768
|
-
editText.setSelection(2, 3)
|
|
3769
|
-
editText.editorId = 1
|
|
3770
|
-
|
|
3771
|
-
var deletedRange: Pair<Int, Int>? = null
|
|
3772
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
3773
|
-
deletedRange = scalarFrom to scalarTo
|
|
3774
|
-
}
|
|
3775
|
-
|
|
3776
|
-
editText.handleBackspace()
|
|
3777
|
-
|
|
3778
|
-
assertEquals(1 to 2, deletedRange)
|
|
3779
|
-
}
|
|
3780
|
-
|
|
3781
|
-
@Test
|
|
3782
|
-
fun `delete surrounding text deletes forward selected range`() {
|
|
3783
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3784
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3785
|
-
editText.setSelection(6, 11)
|
|
3786
|
-
editText.editorId = 1
|
|
3787
|
-
|
|
3788
|
-
var deletedRange: Pair<Int, Int>? = null
|
|
3789
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
3790
|
-
deletedRange = scalarFrom to scalarTo
|
|
3791
|
-
}
|
|
3792
|
-
|
|
3793
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
3794
|
-
assertNotNull(inputConnection)
|
|
3795
|
-
|
|
3796
|
-
assertTrue(inputConnection!!.deleteSurroundingText(1, 0))
|
|
3797
|
-
|
|
3798
|
-
assertEquals(6 to 11, deletedRange)
|
|
3799
|
-
}
|
|
3800
|
-
|
|
3801
|
-
@Test
|
|
3802
|
-
fun `delete surrounding text in code points deletes backward selected range`() {
|
|
3803
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3804
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3805
|
-
Selection.setSelection(editText.text, 11, 6)
|
|
3806
|
-
editText.editorId = 1
|
|
3807
|
-
|
|
3808
|
-
var deletedRange: Pair<Int, Int>? = null
|
|
3809
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
3810
|
-
deletedRange = scalarFrom to scalarTo
|
|
3811
|
-
}
|
|
3812
|
-
|
|
3813
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
3814
|
-
assertNotNull(inputConnection)
|
|
3815
|
-
|
|
3816
|
-
assertTrue(inputConnection!!.deleteSurroundingTextInCodePoints(1, 0))
|
|
3817
|
-
|
|
3818
|
-
assertEquals(6 to 11, deletedRange)
|
|
3819
|
-
}
|
|
3820
|
-
|
|
3821
|
-
@Test
|
|
3822
|
-
fun `delete surrounding text snaps split surrogate ranges to scalar boundaries`() {
|
|
3823
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3824
|
-
editText.applyUpdateJSON(renderUpdateJson("A😀B"), notifyListener = false)
|
|
3825
|
-
editText.setSelection(2)
|
|
3826
|
-
editText.editorId = 1
|
|
3827
|
-
|
|
3828
|
-
var deletedRange: Pair<Int, Int>? = null
|
|
3829
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
3830
|
-
deletedRange = scalarFrom to scalarTo
|
|
3831
|
-
}
|
|
3832
|
-
|
|
3833
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
3834
|
-
assertNotNull(inputConnection)
|
|
3835
|
-
|
|
3836
|
-
assertTrue(inputConnection!!.deleteSurroundingText(0, 1))
|
|
3837
|
-
|
|
3838
|
-
assertEquals(1 to 2, deletedRange)
|
|
3839
|
-
}
|
|
3840
|
-
|
|
3841
|
-
@Test
|
|
3842
|
-
fun `plain paste replaces selected range`() {
|
|
3843
|
-
val context = RuntimeEnvironment.getApplication()
|
|
3844
|
-
val editText = EditorEditText(context)
|
|
3845
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3846
|
-
editText.setSelection(6, 11)
|
|
3847
|
-
editText.editorId = 1
|
|
3848
|
-
|
|
3849
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3850
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3851
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3852
|
-
}
|
|
3853
|
-
|
|
3854
|
-
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
3855
|
-
clipboard.setPrimaryClip(ClipData.newPlainText("plain", "there"))
|
|
3856
|
-
|
|
3857
|
-
assertTrue(editText.onTextContextMenuItem(android.R.id.paste))
|
|
3858
|
-
|
|
3859
|
-
assertEquals(Triple(6, 11, "there"), replacement)
|
|
3860
|
-
}
|
|
3861
|
-
|
|
3862
|
-
@Test
|
|
3863
|
-
fun `paste as plain text ignores html and routes plain text through rust`() {
|
|
3864
|
-
val context = RuntimeEnvironment.getApplication()
|
|
3865
|
-
val editText = EditorEditText(context)
|
|
3866
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3867
|
-
editText.setSelection(6, 11)
|
|
3868
|
-
editText.editorId = 1
|
|
3869
|
-
|
|
3870
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3871
|
-
var insertedHtml: String? = null
|
|
3872
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3873
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3874
|
-
}
|
|
3875
|
-
editText.onInsertContentHtmlInRustForTesting = { html ->
|
|
3876
|
-
insertedHtml = html
|
|
3877
|
-
}
|
|
3878
|
-
|
|
3879
|
-
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
3880
|
-
clipboard.setPrimaryClip(
|
|
3881
|
-
ClipData.newHtmlText("html", "there", "<strong>there</strong>")
|
|
3882
|
-
)
|
|
3883
|
-
|
|
3884
|
-
assertTrue(editText.onTextContextMenuItem(android.R.id.pasteAsPlainText))
|
|
3885
|
-
|
|
3886
|
-
assertNull(insertedHtml)
|
|
3887
|
-
assertEquals(Triple(6, 11, "there"), replacement)
|
|
3888
|
-
}
|
|
3889
|
-
|
|
3890
|
-
@Test
|
|
3891
|
-
fun `plain paste coerces non text clipboard item through rust`() {
|
|
3892
|
-
val context = RuntimeEnvironment.getApplication()
|
|
3893
|
-
val editText = EditorEditText(context)
|
|
3894
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3895
|
-
editText.setSelection(6, 11)
|
|
3896
|
-
editText.editorId = 1
|
|
3897
|
-
|
|
3898
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3899
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3900
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3901
|
-
}
|
|
3902
|
-
|
|
3903
|
-
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://example.test/share"))
|
|
3904
|
-
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
3905
|
-
clipboard.setPrimaryClip(ClipData.newIntent("intent", intent))
|
|
3906
|
-
|
|
3907
|
-
assertTrue(editText.onTextContextMenuItem(android.R.id.paste))
|
|
3908
|
-
|
|
3909
|
-
assertEquals(
|
|
3910
|
-
Triple(6, 11, intent.toUri(Intent.URI_INTENT_SCHEME)),
|
|
3911
|
-
replacement
|
|
3912
|
-
)
|
|
3913
|
-
}
|
|
3914
|
-
|
|
3915
|
-
@Test
|
|
3916
|
-
fun `editable cut copies selection and deletes through rust`() {
|
|
3917
|
-
val context = RuntimeEnvironment.getApplication()
|
|
3918
|
-
val editText = EditorEditText(context)
|
|
3919
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3920
|
-
editText.setSelection(6, 11)
|
|
3921
|
-
editText.editorId = 1
|
|
3922
|
-
|
|
3923
|
-
var deletedRange: Pair<Int, Int>? = null
|
|
3924
|
-
editText.onDeleteRangeInRustForTesting = { scalarFrom, scalarTo ->
|
|
3925
|
-
deletedRange = scalarFrom to scalarTo
|
|
3926
|
-
}
|
|
3927
|
-
|
|
3928
|
-
assertTrue(editText.onTextContextMenuItem(android.R.id.cut))
|
|
3929
|
-
|
|
3930
|
-
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
3931
|
-
assertEquals("world", clipboard.primaryClip?.getItemAt(0)?.text?.toString())
|
|
3932
|
-
assertEquals(6 to 11, deletedRange)
|
|
3933
|
-
assertEquals("Hello world", editText.text?.toString())
|
|
3934
|
-
}
|
|
3935
|
-
|
|
3936
|
-
@Test
|
|
3937
|
-
fun `read only cut and paste as plain text are consumed without mutating text`() {
|
|
3938
|
-
val context = RuntimeEnvironment.getApplication()
|
|
3939
|
-
val editText = EditorEditText(context)
|
|
3940
|
-
editText.applyUpdateJSON(renderUpdateJson("abc"), notifyListener = false)
|
|
3941
|
-
editText.setSelection(0, 3)
|
|
3942
|
-
editText.isEditable = false
|
|
3943
|
-
|
|
3944
|
-
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
3945
|
-
clipboard.setPrimaryClip(ClipData.newPlainText("plain", "X"))
|
|
3946
|
-
|
|
3947
|
-
assertTrue(editText.onTextContextMenuItem(android.R.id.cut))
|
|
3948
|
-
assertTrue(editText.onTextContextMenuItem(android.R.id.paste))
|
|
3949
|
-
assertTrue(editText.onTextContextMenuItem(android.R.id.pasteAsPlainText))
|
|
3950
|
-
assertEquals("abc", editText.text?.toString())
|
|
3951
|
-
}
|
|
3952
|
-
|
|
3953
|
-
@Test
|
|
3954
|
-
fun `editable accessibility set text replaces full document through rust`() {
|
|
3955
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3956
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3957
|
-
editText.setSelection(6, 11)
|
|
3958
|
-
editText.editorId = 1
|
|
3959
|
-
|
|
3960
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3961
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3962
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3963
|
-
}
|
|
3964
|
-
val args = Bundle().apply {
|
|
3965
|
-
putCharSequence(
|
|
3966
|
-
AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
|
|
3967
|
-
"there"
|
|
3968
|
-
)
|
|
3969
|
-
}
|
|
3970
|
-
|
|
3971
|
-
assertTrue(
|
|
3972
|
-
editText.performAccessibilityAction(
|
|
3973
|
-
AccessibilityNodeInfo.ACTION_SET_TEXT,
|
|
3974
|
-
args
|
|
3975
|
-
)
|
|
3976
|
-
)
|
|
3977
|
-
|
|
3978
|
-
assertEquals(Triple(0, 11, "there"), replacement)
|
|
3979
|
-
assertEquals("Hello world", editText.text?.toString())
|
|
3980
|
-
}
|
|
3981
|
-
|
|
3982
|
-
@Test
|
|
3983
|
-
fun `editable accessibility set text replaces full document when selection is collapsed`() {
|
|
3984
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
3985
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
3986
|
-
editText.setSelection(6)
|
|
3987
|
-
editText.editorId = 1
|
|
3988
|
-
|
|
3989
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
3990
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
3991
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
3992
|
-
}
|
|
3993
|
-
val args = Bundle().apply {
|
|
3994
|
-
putCharSequence(
|
|
3995
|
-
AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
|
|
3996
|
-
"replacement"
|
|
3997
|
-
)
|
|
3998
|
-
}
|
|
3999
|
-
|
|
4000
|
-
assertTrue(
|
|
4001
|
-
editText.performAccessibilityAction(
|
|
4002
|
-
AccessibilityNodeInfo.ACTION_SET_TEXT,
|
|
4003
|
-
args
|
|
4004
|
-
)
|
|
4005
|
-
)
|
|
4006
|
-
|
|
4007
|
-
assertEquals(Triple(0, 11, "replacement"), replacement)
|
|
4008
|
-
}
|
|
4009
|
-
|
|
4010
|
-
@Test
|
|
4011
|
-
fun `read only accessibility text mutations are rejected without mutating text`() {
|
|
4012
|
-
val context = RuntimeEnvironment.getApplication()
|
|
4013
|
-
val editText = EditorEditText(context)
|
|
4014
|
-
editText.applyUpdateJSON(renderUpdateJson("abc"), notifyListener = false)
|
|
4015
|
-
editText.setSelection(0, 3)
|
|
4016
|
-
editText.isEditable = false
|
|
4017
|
-
|
|
4018
|
-
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
4019
|
-
clipboard.setPrimaryClip(ClipData.newPlainText("plain", "X"))
|
|
4020
|
-
val setTextArgs = Bundle().apply {
|
|
4021
|
-
putCharSequence(
|
|
4022
|
-
AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
|
|
4023
|
-
"X"
|
|
4024
|
-
)
|
|
4025
|
-
}
|
|
4026
|
-
|
|
4027
|
-
assertFalse(
|
|
4028
|
-
editText.performAccessibilityAction(
|
|
4029
|
-
AccessibilityNodeInfo.ACTION_SET_TEXT,
|
|
4030
|
-
setTextArgs
|
|
4031
|
-
)
|
|
4032
|
-
)
|
|
4033
|
-
assertFalse(editText.performAccessibilityAction(AccessibilityNodeInfo.ACTION_PASTE, null))
|
|
4034
|
-
assertFalse(editText.performAccessibilityAction(AccessibilityNodeInfo.ACTION_CUT, null))
|
|
4035
|
-
assertEquals("abc", editText.text?.toString())
|
|
4036
|
-
}
|
|
4037
|
-
|
|
4038
|
-
@Test
|
|
4039
|
-
fun `read only input connection consumes printable and forward delete keys`() {
|
|
4040
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
4041
|
-
editText.applyUpdateJSON(renderUpdateJson("abc"), notifyListener = false)
|
|
4042
|
-
editText.setSelection(1)
|
|
4043
|
-
editText.editorId = 1
|
|
4044
|
-
editText.isEditable = false
|
|
4045
|
-
|
|
4046
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
4047
|
-
assertNotNull(inputConnection)
|
|
4048
|
-
|
|
4049
|
-
assertTrue(inputConnection!!.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A)))
|
|
4050
|
-
assertTrue(inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SPACE)))
|
|
4051
|
-
assertTrue(inputConnection.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_FORWARD_DEL)))
|
|
4052
|
-
assertEquals("abc", editText.text?.toString())
|
|
4053
|
-
assertEquals(1, editText.selectionStart)
|
|
4054
|
-
assertEquals(1, editText.selectionEnd)
|
|
4055
|
-
}
|
|
4056
|
-
|
|
4057
|
-
@Test
|
|
4058
|
-
fun `read only multiple character key events are consumed without mutating text`() {
|
|
4059
|
-
val editText = EditorEditText(RuntimeEnvironment.getApplication())
|
|
4060
|
-
editText.applyUpdateJSON(renderUpdateJson("abc"), notifyListener = false)
|
|
4061
|
-
editText.setSelection(1)
|
|
4062
|
-
editText.editorId = 1
|
|
4063
|
-
editText.isEditable = false
|
|
4064
|
-
|
|
4065
|
-
val inputConnection = editText.onCreateInputConnection(EditorInfo())
|
|
4066
|
-
assertNotNull(inputConnection)
|
|
4067
|
-
val multipleCharactersEvent = KeyEvent(100L, "é", 0, 0)
|
|
4068
|
-
|
|
4069
|
-
assertTrue(editText.dispatchKeyEvent(multipleCharactersEvent))
|
|
4070
|
-
assertTrue(inputConnection!!.sendKeyEvent(multipleCharactersEvent))
|
|
4071
|
-
assertEquals("abc", editText.text?.toString())
|
|
4072
|
-
assertEquals(1, editText.selectionStart)
|
|
4073
|
-
assertEquals(1, editText.selectionEnd)
|
|
4074
|
-
}
|
|
4075
|
-
|
|
4076
|
-
@Test
|
|
4077
|
-
fun `plain paste snaps split surrogate selection to scalar boundaries`() {
|
|
4078
|
-
val context = RuntimeEnvironment.getApplication()
|
|
4079
|
-
val editText = EditorEditText(context)
|
|
4080
|
-
editText.applyUpdateJSON(renderUpdateJson("A😀B"), notifyListener = false)
|
|
4081
|
-
editText.setSelection(2, 3)
|
|
4082
|
-
editText.editorId = 1
|
|
4083
|
-
|
|
4084
|
-
var replacement: Triple<Int, Int, String>? = null
|
|
4085
|
-
editText.onReplaceTextInRustForTesting = { scalarFrom, scalarTo, text ->
|
|
4086
|
-
replacement = Triple(scalarFrom, scalarTo, text)
|
|
4087
|
-
}
|
|
4088
|
-
|
|
4089
|
-
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
4090
|
-
clipboard.setPrimaryClip(ClipData.newPlainText("plain", "X"))
|
|
4091
|
-
|
|
4092
|
-
assertTrue(editText.onTextContextMenuItem(android.R.id.paste))
|
|
4093
|
-
|
|
4094
|
-
assertEquals(Triple(1, 2, "X"), replacement)
|
|
4095
|
-
}
|
|
4096
|
-
|
|
4097
|
-
@Test
|
|
4098
|
-
fun `multiline plain paste inserts structured content`() {
|
|
4099
|
-
val context = RuntimeEnvironment.getApplication()
|
|
4100
|
-
val editText = EditorEditText(context)
|
|
4101
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
4102
|
-
editText.setSelection(6, 11)
|
|
4103
|
-
editText.editorId = 1
|
|
4104
|
-
|
|
4105
|
-
var insertedContent: Triple<Int, Int, String>? = null
|
|
4106
|
-
editText.onInsertContentJsonAtSelectionScalarForTesting = { scalarFrom, scalarTo, json ->
|
|
4107
|
-
insertedContent = Triple(scalarFrom, scalarTo, json)
|
|
4108
|
-
}
|
|
4109
|
-
|
|
4110
|
-
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
4111
|
-
clipboard.setPrimaryClip(ClipData.newPlainText("plain", "one\ntwo"))
|
|
4112
|
-
|
|
4113
|
-
assertTrue(editText.onTextContextMenuItem(android.R.id.paste))
|
|
4114
|
-
|
|
4115
|
-
val (scalarFrom, scalarTo, json) = insertedContent!!
|
|
4116
|
-
assertEquals(6, scalarFrom)
|
|
4117
|
-
assertEquals(11, scalarTo)
|
|
4118
|
-
val content = JSONObject(json).getJSONArray("content")
|
|
4119
|
-
assertEquals("one", content.getJSONObject(0).getJSONArray("content").getJSONObject(0).getString("text"))
|
|
4120
|
-
assertEquals("two", content.getJSONObject(1).getJSONArray("content").getJSONObject(0).getString("text"))
|
|
4121
|
-
}
|
|
4122
|
-
|
|
4123
|
-
@Test
|
|
4124
|
-
fun `html paste syncs current selection before inserting html`() {
|
|
4125
|
-
val context = RuntimeEnvironment.getApplication()
|
|
4126
|
-
val editText = EditorEditText(context)
|
|
4127
|
-
editText.applyUpdateJSON(renderUpdateJson("Hello world"), notifyListener = false)
|
|
4128
|
-
editText.setSelection(6, 11)
|
|
4129
|
-
editText.editorId = 1
|
|
4130
|
-
|
|
4131
|
-
var syncedSelection: Pair<Int, Int>? = null
|
|
4132
|
-
editText.onSetSelectionScalarInRustForTesting = { anchor, head ->
|
|
4133
|
-
syncedSelection = anchor to head
|
|
4134
|
-
}
|
|
4135
|
-
var insertedHtml: String? = null
|
|
4136
|
-
editText.onInsertContentHtmlInRustForTesting = { html ->
|
|
4137
|
-
insertedHtml = html
|
|
4138
|
-
}
|
|
4139
|
-
|
|
4140
|
-
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
4141
|
-
clipboard.setPrimaryClip(
|
|
4142
|
-
ClipData.newHtmlText("html", "there", "<strong>there</strong>")
|
|
4143
|
-
)
|
|
4144
|
-
|
|
4145
|
-
assertTrue(editText.onTextContextMenuItem(android.R.id.paste))
|
|
4146
|
-
|
|
4147
|
-
assertEquals(6 to 11, syncedSelection)
|
|
4148
|
-
assertEquals("<strong>there</strong>", insertedHtml)
|
|
4149
|
-
}
|
|
4150
|
-
|
|
4151
|
-
private fun renderUpdateJson(text: String): String =
|
|
4152
|
-
renderBlocksUpdateJson(text)
|
|
4153
|
-
|
|
4154
|
-
private fun renderBlocksUpdateJson(vararg texts: String): String =
|
|
4155
|
-
JSONObject()
|
|
4156
|
-
.put(
|
|
4157
|
-
"renderBlocks",
|
|
4158
|
-
JSONArray().apply {
|
|
4159
|
-
texts.forEach { put(paragraphRenderBlock(it)) }
|
|
4160
|
-
}
|
|
4161
|
-
)
|
|
4162
|
-
.toString()
|
|
4163
|
-
|
|
4164
|
-
private fun paragraphRenderBlock(text: String): JSONArray =
|
|
4165
|
-
JSONArray()
|
|
4166
|
-
.put(
|
|
4167
|
-
JSONObject()
|
|
4168
|
-
.put("type", "blockStart")
|
|
4169
|
-
.put("nodeType", "paragraph")
|
|
4170
|
-
.put("depth", 0)
|
|
4171
|
-
)
|
|
4172
|
-
.put(
|
|
4173
|
-
JSONObject()
|
|
4174
|
-
.put("type", "textRun")
|
|
4175
|
-
.put("text", text)
|
|
4176
|
-
.put("marks", JSONArray())
|
|
4177
|
-
)
|
|
4178
|
-
.put(JSONObject().put("type", "blockEnd"))
|
|
4179
|
-
|
|
4180
|
-
private fun withDefaultInputMethod(context: Context, inputMethodId: String, block: () -> Unit) {
|
|
4181
|
-
val previous = Settings.Secure.getString(
|
|
4182
|
-
context.contentResolver,
|
|
4183
|
-
Settings.Secure.DEFAULT_INPUT_METHOD
|
|
4184
|
-
)
|
|
4185
|
-
Settings.Secure.putString(
|
|
4186
|
-
context.contentResolver,
|
|
4187
|
-
Settings.Secure.DEFAULT_INPUT_METHOD,
|
|
4188
|
-
inputMethodId
|
|
4189
|
-
)
|
|
4190
|
-
try {
|
|
4191
|
-
block()
|
|
4192
|
-
} finally {
|
|
4193
|
-
Settings.Secure.putString(
|
|
4194
|
-
context.contentResolver,
|
|
4195
|
-
Settings.Secure.DEFAULT_INPUT_METHOD,
|
|
4196
|
-
previous
|
|
4197
|
-
)
|
|
4198
|
-
}
|
|
4199
|
-
}
|
|
4200
|
-
|
|
4201
|
-
private infix fun Int.hasInputFlag(flag: Int): Boolean = (this and flag) == flag
|
|
4202
|
-
}
|