@apollohg/react-native-prose-editor 0.5.15 → 0.5.17

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.
@@ -4,151 +4,223 @@ import expo.modules.kotlin.modules.Module
4
4
  import expo.modules.kotlin.modules.ModuleDefinition
5
5
  import uniffi.editor_core.*
6
6
 
7
+ internal fun nativeULong(value: Int): ULong? =
8
+ if (value >= 0) value.toULong() else null
9
+
10
+ internal fun nativeUInt(value: Int): UInt? =
11
+ if (value >= 0) value.toUInt() else null
12
+
13
+ internal fun nativeArgumentError(field: String): String =
14
+ "{\"error\":\"invalid $field\"}"
15
+
7
16
  class NativeEditorModule : Module() {
8
17
  override fun definition() = ModuleDefinition {
9
18
  Name("NativeEditor")
10
19
 
11
20
  Function("editorCreate") { configJson: String ->
12
- editorCreate(configJson).toLong()
21
+ editorCreate(configJson).toLong().also { id ->
22
+ NativeEditorViewRegistry.markEditorCreated(id)
23
+ }
13
24
  }
14
25
  Function("editorDestroy") { id: Int ->
15
- editorDestroy(id.toULong())
26
+ val editorId = nativeULong(id) ?: return@Function
27
+ NativeEditorViewRegistry.invalidateDestroyedEditor(id.toLong())
28
+ editorDestroy(editorId)
29
+ }
30
+ Function("editorPrepareForCommand") { id: Int ->
31
+ nativeULong(id) ?: return@Function nativeArgumentError("editor id")
32
+ NativeEditorViewRegistry.prepareForCommandJSON(id.toLong())
16
33
  }
17
34
  Function("collaborationSessionCreate") { configJson: String ->
18
35
  collaborationSessionCreate(configJson).toLong()
19
36
  }
20
37
  Function("collaborationSessionDestroy") { id: Int ->
21
- collaborationSessionDestroy(id.toULong())
38
+ val sessionId = nativeULong(id) ?: return@Function
39
+ collaborationSessionDestroy(sessionId)
22
40
  }
23
41
  Function("collaborationSessionGetDocumentJson") { id: Int ->
24
- collaborationSessionGetDocumentJson(id.toULong())
42
+ val sessionId = nativeULong(id) ?: return@Function "{}"
43
+ collaborationSessionGetDocumentJson(sessionId)
25
44
  }
26
45
  Function("collaborationSessionGetEncodedState") { id: Int ->
27
- collaborationSessionGetEncodedState(id.toULong())
46
+ val sessionId = nativeULong(id) ?: return@Function "[]"
47
+ collaborationSessionGetEncodedState(sessionId)
28
48
  }
29
49
  Function("collaborationSessionGetPeersJson") { id: Int ->
30
- collaborationSessionGetPeersJson(id.toULong())
50
+ val sessionId = nativeULong(id) ?: return@Function "[]"
51
+ collaborationSessionGetPeersJson(sessionId)
31
52
  }
32
53
  Function("collaborationSessionStart") { id: Int ->
33
- collaborationSessionStart(id.toULong())
54
+ val sessionId = nativeULong(id) ?: return@Function nativeArgumentError("session id")
55
+ collaborationSessionStart(sessionId)
34
56
  }
35
57
  Function("collaborationSessionApplyLocalDocumentJson") { id: Int, json: String ->
36
- collaborationSessionApplyLocalDocumentJson(id.toULong(), json)
58
+ val sessionId = nativeULong(id) ?: return@Function nativeArgumentError("session id")
59
+ collaborationSessionApplyLocalDocumentJson(sessionId, json)
37
60
  }
38
61
  Function("collaborationSessionApplyEncodedState") { id: Int, encodedStateJson: String ->
39
- collaborationSessionApplyEncodedState(id.toULong(), encodedStateJson)
62
+ val sessionId = nativeULong(id) ?: return@Function nativeArgumentError("session id")
63
+ collaborationSessionApplyEncodedState(sessionId, encodedStateJson)
40
64
  }
41
65
  Function("collaborationSessionReplaceEncodedState") { id: Int, encodedStateJson: String ->
42
- collaborationSessionReplaceEncodedState(id.toULong(), encodedStateJson)
66
+ val sessionId = nativeULong(id) ?: return@Function nativeArgumentError("session id")
67
+ collaborationSessionReplaceEncodedState(sessionId, encodedStateJson)
43
68
  }
44
69
  Function("collaborationSessionHandleMessage") { id: Int, messageJson: String ->
45
- collaborationSessionHandleMessage(id.toULong(), messageJson)
70
+ val sessionId = nativeULong(id) ?: return@Function nativeArgumentError("session id")
71
+ collaborationSessionHandleMessage(sessionId, messageJson)
46
72
  }
47
73
  Function("collaborationSessionSetLocalAwareness") { id: Int, awarenessJson: String ->
48
- collaborationSessionSetLocalAwareness(id.toULong(), awarenessJson)
74
+ val sessionId = nativeULong(id) ?: return@Function nativeArgumentError("session id")
75
+ collaborationSessionSetLocalAwareness(sessionId, awarenessJson)
49
76
  }
50
77
  Function("collaborationSessionClearLocalAwareness") { id: Int ->
51
- collaborationSessionClearLocalAwareness(id.toULong())
78
+ val sessionId = nativeULong(id) ?: return@Function nativeArgumentError("session id")
79
+ collaborationSessionClearLocalAwareness(sessionId)
52
80
  }
53
81
 
54
82
  Function("editorSetHtml") { id: Int, html: String ->
55
- editorSetHtml(id.toULong(), html)
83
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
84
+ editorSetHtml(editorId, html)
56
85
  }
57
86
  Function("editorGetHtml") { id: Int ->
58
- editorGetHtml(id.toULong())
87
+ val editorId = nativeULong(id) ?: return@Function ""
88
+ editorGetHtml(editorId)
59
89
  }
60
90
  Function("editorSetJson") { id: Int, json: String ->
61
- editorSetJson(id.toULong(), json)
91
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
92
+ editorSetJson(editorId, json)
62
93
  }
63
94
  Function("editorGetJson") { id: Int ->
64
- editorGetJson(id.toULong())
95
+ val editorId = nativeULong(id) ?: return@Function "{}"
96
+ editorGetJson(editorId)
65
97
  }
66
98
  Function("editorGetContentSnapshot") { id: Int ->
67
- editorGetContentSnapshot(id.toULong())
99
+ val editorId = nativeULong(id) ?: return@Function "{\"html\":\"\",\"json\":{}}"
100
+ editorGetContentSnapshot(editorId)
68
101
  }
69
102
 
70
103
  Function("editorInsertText") { id: Int, pos: Int, text: String ->
71
- editorInsertText(id.toULong(), pos.toUInt(), text)
104
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
105
+ val position = nativeUInt(pos) ?: return@Function nativeArgumentError("position")
106
+ editorInsertText(editorId, position, text)
72
107
  }
73
108
  Function("editorReplaceSelectionText") { id: Int, text: String ->
74
- editorReplaceSelectionText(id.toULong(), text)
109
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
110
+ editorReplaceSelectionText(editorId, text)
75
111
  }
76
112
  Function("editorDeleteRange") { id: Int, from: Int, to: Int ->
77
- editorDeleteRange(id.toULong(), from.toUInt(), to.toUInt())
113
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
114
+ val fromPosition = nativeUInt(from) ?: return@Function nativeArgumentError("position")
115
+ val toPosition = nativeUInt(to) ?: return@Function nativeArgumentError("position")
116
+ editorDeleteRange(editorId, fromPosition, toPosition)
78
117
  }
79
118
  Function("editorSplitBlock") { id: Int, pos: Int ->
80
- editorSplitBlock(id.toULong(), pos.toUInt())
119
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
120
+ val position = nativeUInt(pos) ?: return@Function nativeArgumentError("position")
121
+ editorSplitBlock(editorId, position)
81
122
  }
82
123
  Function("editorInsertContentHtml") { id: Int, html: String ->
83
- editorInsertContentHtml(id.toULong(), html)
124
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
125
+ editorInsertContentHtml(editorId, html)
84
126
  }
85
127
  Function("editorReplaceHtml") { id: Int, html: String ->
86
- editorReplaceHtml(id.toULong(), html)
128
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
129
+ editorReplaceHtml(editorId, html)
87
130
  }
88
131
  Function("editorReplaceJson") { id: Int, json: String ->
89
- editorReplaceJson(id.toULong(), json)
132
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
133
+ editorReplaceJson(editorId, json)
90
134
  }
91
135
  Function("editorInsertContentJson") { id: Int, json: String ->
92
- editorInsertContentJson(id.toULong(), json)
136
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
137
+ editorInsertContentJson(editorId, json)
93
138
  }
94
139
  Function(
95
140
  "editorInsertContentJsonAtSelectionScalar"
96
141
  ) { id: Int, scalarAnchor: Int, scalarHead: Int, json: String ->
142
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
143
+ val anchor = nativeUInt(scalarAnchor) ?: return@Function nativeArgumentError("position")
144
+ val head = nativeUInt(scalarHead) ?: return@Function nativeArgumentError("position")
97
145
  editorInsertContentJsonAtSelectionScalar(
98
- id.toULong(),
99
- scalarAnchor.toUInt(),
100
- scalarHead.toUInt(),
146
+ editorId,
147
+ anchor,
148
+ head,
101
149
  json
102
150
  )
103
151
  }
104
152
  Function("editorWrapInList") { id: Int, listType: String ->
105
- editorWrapInList(id.toULong(), listType)
153
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
154
+ editorWrapInList(editorId, listType)
106
155
  }
107
156
  Function("editorUnwrapFromList") { id: Int ->
108
- editorUnwrapFromList(id.toULong())
157
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
158
+ editorUnwrapFromList(editorId)
109
159
  }
110
160
  Function("editorIndentListItem") { id: Int ->
111
- editorIndentListItem(id.toULong())
161
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
162
+ editorIndentListItem(editorId)
112
163
  }
113
164
  Function("editorOutdentListItem") { id: Int ->
114
- editorOutdentListItem(id.toULong())
165
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
166
+ editorOutdentListItem(editorId)
115
167
  }
116
168
  Function("editorInsertNode") { id: Int, nodeType: String ->
117
- editorInsertNode(id.toULong(), nodeType)
169
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
170
+ editorInsertNode(editorId, nodeType)
118
171
  }
119
172
 
120
173
  Function("editorInsertTextScalar") { id: Int, scalarPos: Int, text: String ->
121
- editorInsertTextScalar(id.toULong(), scalarPos.toUInt(), text)
174
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
175
+ val position = nativeUInt(scalarPos) ?: return@Function nativeArgumentError("position")
176
+ editorInsertTextScalar(editorId, position, text)
122
177
  }
123
178
  Function("editorDeleteScalarRange") { id: Int, scalarFrom: Int, scalarTo: Int ->
124
- editorDeleteScalarRange(id.toULong(), scalarFrom.toUInt(), scalarTo.toUInt())
179
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
180
+ val fromPosition = nativeUInt(scalarFrom) ?: return@Function nativeArgumentError("position")
181
+ val toPosition = nativeUInt(scalarTo) ?: return@Function nativeArgumentError("position")
182
+ editorDeleteScalarRange(editorId, fromPosition, toPosition)
125
183
  }
126
184
  Function("editorReplaceTextScalar") { id: Int, scalarFrom: Int, scalarTo: Int, text: String ->
127
- editorReplaceTextScalar(id.toULong(), scalarFrom.toUInt(), scalarTo.toUInt(), text)
185
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
186
+ val fromPosition = nativeUInt(scalarFrom) ?: return@Function nativeArgumentError("position")
187
+ val toPosition = nativeUInt(scalarTo) ?: return@Function nativeArgumentError("position")
188
+ editorReplaceTextScalar(editorId, fromPosition, toPosition, text)
128
189
  }
129
190
  Function("editorSplitBlockScalar") { id: Int, scalarPos: Int ->
130
- editorSplitBlockScalar(id.toULong(), scalarPos.toUInt())
191
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
192
+ val position = nativeUInt(scalarPos) ?: return@Function nativeArgumentError("position")
193
+ editorSplitBlockScalar(editorId, position)
131
194
  }
132
195
  Function("editorDeleteAndSplitScalar") { id: Int, scalarFrom: Int, scalarTo: Int ->
133
- editorDeleteAndSplitScalar(id.toULong(), scalarFrom.toUInt(), scalarTo.toUInt())
196
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
197
+ val fromPosition = nativeUInt(scalarFrom) ?: return@Function nativeArgumentError("position")
198
+ val toPosition = nativeUInt(scalarTo) ?: return@Function nativeArgumentError("position")
199
+ editorDeleteAndSplitScalar(editorId, fromPosition, toPosition)
134
200
  }
135
201
  Function(
136
202
  "editorToggleMarkAtSelectionScalar"
137
203
  ) { id: Int, scalarAnchor: Int, scalarHead: Int, markName: String ->
204
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
205
+ val anchor = nativeUInt(scalarAnchor) ?: return@Function nativeArgumentError("position")
206
+ val head = nativeUInt(scalarHead) ?: return@Function nativeArgumentError("position")
138
207
  editorToggleMarkAtSelectionScalar(
139
- id.toULong(),
140
- scalarAnchor.toUInt(),
141
- scalarHead.toUInt(),
208
+ editorId,
209
+ anchor,
210
+ head,
142
211
  markName
143
212
  )
144
213
  }
145
214
  Function(
146
215
  "editorSetMarkAtSelectionScalar"
147
216
  ) { id: Int, scalarAnchor: Int, scalarHead: Int, markName: String, attrsJson: String ->
217
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
218
+ val anchor = nativeUInt(scalarAnchor) ?: return@Function nativeArgumentError("position")
219
+ val head = nativeUInt(scalarHead) ?: return@Function nativeArgumentError("position")
148
220
  editorSetMarkAtSelectionScalar(
149
- id.toULong(),
150
- scalarAnchor.toUInt(),
151
- scalarHead.toUInt(),
221
+ editorId,
222
+ anchor,
223
+ head,
152
224
  markName,
153
225
  attrsJson
154
226
  )
@@ -156,136 +228,182 @@ class NativeEditorModule : Module() {
156
228
  Function(
157
229
  "editorUnsetMarkAtSelectionScalar"
158
230
  ) { id: Int, scalarAnchor: Int, scalarHead: Int, markName: String ->
231
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
232
+ val anchor = nativeUInt(scalarAnchor) ?: return@Function nativeArgumentError("position")
233
+ val head = nativeUInt(scalarHead) ?: return@Function nativeArgumentError("position")
159
234
  editorUnsetMarkAtSelectionScalar(
160
- id.toULong(),
161
- scalarAnchor.toUInt(),
162
- scalarHead.toUInt(),
235
+ editorId,
236
+ anchor,
237
+ head,
163
238
  markName
164
239
  )
165
240
  }
166
241
  Function(
167
242
  "editorToggleBlockquoteAtSelectionScalar"
168
243
  ) { id: Int, scalarAnchor: Int, scalarHead: Int ->
244
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
245
+ val anchor = nativeUInt(scalarAnchor) ?: return@Function nativeArgumentError("position")
246
+ val head = nativeUInt(scalarHead) ?: return@Function nativeArgumentError("position")
169
247
  editorToggleBlockquoteAtSelectionScalar(
170
- id.toULong(),
171
- scalarAnchor.toUInt(),
172
- scalarHead.toUInt()
248
+ editorId,
249
+ anchor,
250
+ head
173
251
  )
174
252
  }
175
253
  Function(
176
254
  "editorToggleHeadingAtSelectionScalar"
177
255
  ) { id: Int, scalarAnchor: Int, scalarHead: Int, level: Int ->
256
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
257
+ val anchor = nativeUInt(scalarAnchor) ?: return@Function nativeArgumentError("position")
258
+ val head = nativeUInt(scalarHead) ?: return@Function nativeArgumentError("position")
178
259
  if (level !in 1..6) {
179
260
  return@Function "{\"error\":\"invalid heading level\"}"
180
261
  }
181
262
  editorToggleHeadingAtSelectionScalar(
182
- id.toULong(),
183
- scalarAnchor.toUInt(),
184
- scalarHead.toUInt(),
263
+ editorId,
264
+ anchor,
265
+ head,
185
266
  level.toUByte()
186
267
  )
187
268
  }
188
269
  Function(
189
270
  "editorWrapInListAtSelectionScalar"
190
271
  ) { id: Int, scalarAnchor: Int, scalarHead: Int, listType: String ->
272
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
273
+ val anchor = nativeUInt(scalarAnchor) ?: return@Function nativeArgumentError("position")
274
+ val head = nativeUInt(scalarHead) ?: return@Function nativeArgumentError("position")
191
275
  editorWrapInListAtSelectionScalar(
192
- id.toULong(),
193
- scalarAnchor.toUInt(),
194
- scalarHead.toUInt(),
276
+ editorId,
277
+ anchor,
278
+ head,
195
279
  listType
196
280
  )
197
281
  }
198
282
  Function(
199
283
  "editorUnwrapFromListAtSelectionScalar"
200
284
  ) { id: Int, scalarAnchor: Int, scalarHead: Int ->
285
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
286
+ val anchor = nativeUInt(scalarAnchor) ?: return@Function nativeArgumentError("position")
287
+ val head = nativeUInt(scalarHead) ?: return@Function nativeArgumentError("position")
201
288
  editorUnwrapFromListAtSelectionScalar(
202
- id.toULong(),
203
- scalarAnchor.toUInt(),
204
- scalarHead.toUInt()
289
+ editorId,
290
+ anchor,
291
+ head
205
292
  )
206
293
  }
207
294
  Function(
208
295
  "editorIndentListItemAtSelectionScalar"
209
296
  ) { id: Int, scalarAnchor: Int, scalarHead: Int ->
297
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
298
+ val anchor = nativeUInt(scalarAnchor) ?: return@Function nativeArgumentError("position")
299
+ val head = nativeUInt(scalarHead) ?: return@Function nativeArgumentError("position")
210
300
  editorIndentListItemAtSelectionScalar(
211
- id.toULong(),
212
- scalarAnchor.toUInt(),
213
- scalarHead.toUInt()
301
+ editorId,
302
+ anchor,
303
+ head
214
304
  )
215
305
  }
216
306
  Function(
217
307
  "editorOutdentListItemAtSelectionScalar"
218
308
  ) { id: Int, scalarAnchor: Int, scalarHead: Int ->
309
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
310
+ val anchor = nativeUInt(scalarAnchor) ?: return@Function nativeArgumentError("position")
311
+ val head = nativeUInt(scalarHead) ?: return@Function nativeArgumentError("position")
219
312
  editorOutdentListItemAtSelectionScalar(
220
- id.toULong(),
221
- scalarAnchor.toUInt(),
222
- scalarHead.toUInt()
313
+ editorId,
314
+ anchor,
315
+ head
223
316
  )
224
317
  }
225
318
  Function(
226
319
  "editorInsertNodeAtSelectionScalar"
227
320
  ) { id: Int, scalarAnchor: Int, scalarHead: Int, nodeType: String ->
321
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
322
+ val anchor = nativeUInt(scalarAnchor) ?: return@Function nativeArgumentError("position")
323
+ val head = nativeUInt(scalarHead) ?: return@Function nativeArgumentError("position")
228
324
  editorInsertNodeAtSelectionScalar(
229
- id.toULong(),
230
- scalarAnchor.toUInt(),
231
- scalarHead.toUInt(),
325
+ editorId,
326
+ anchor,
327
+ head,
232
328
  nodeType
233
329
  )
234
330
  }
235
331
 
236
332
  Function("editorToggleMark") { id: Int, markName: String ->
237
- editorToggleMark(id.toULong(), markName)
333
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
334
+ editorToggleMark(editorId, markName)
238
335
  }
239
336
  Function("editorSetMark") { id: Int, markName: String, attrsJson: String ->
240
- editorSetMark(id.toULong(), markName, attrsJson)
337
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
338
+ editorSetMark(editorId, markName, attrsJson)
241
339
  }
242
340
  Function("editorUnsetMark") { id: Int, markName: String ->
243
- editorUnsetMark(id.toULong(), markName)
341
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
342
+ editorUnsetMark(editorId, markName)
244
343
  }
245
344
  Function("editorToggleBlockquote") { id: Int ->
246
- editorToggleBlockquote(id.toULong())
345
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
346
+ editorToggleBlockquote(editorId)
247
347
  }
248
348
  Function("editorToggleHeading") { id: Int, level: Int ->
349
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
249
350
  if (level !in 1..6) {
250
351
  return@Function "{\"error\":\"invalid heading level\"}"
251
352
  }
252
- editorToggleHeading(id.toULong(), level.toUByte())
353
+ editorToggleHeading(editorId, level.toUByte())
253
354
  }
254
355
 
255
356
  Function("editorSetSelection") { id: Int, anchor: Int, head: Int ->
256
- editorSetSelection(id.toULong(), anchor.toUInt(), head.toUInt())
357
+ val editorId = nativeULong(id) ?: return@Function
358
+ val anchorPosition = nativeUInt(anchor) ?: return@Function
359
+ val headPosition = nativeUInt(head) ?: return@Function
360
+ editorSetSelection(editorId, anchorPosition, headPosition)
257
361
  }
258
362
  Function("editorSetSelectionScalar") { id: Int, scalarAnchor: Int, scalarHead: Int ->
259
- editorSetSelectionScalar(id.toULong(), scalarAnchor.toUInt(), scalarHead.toUInt())
363
+ val editorId = nativeULong(id) ?: return@Function
364
+ val anchor = nativeUInt(scalarAnchor) ?: return@Function
365
+ val head = nativeUInt(scalarHead) ?: return@Function
366
+ editorSetSelectionScalar(editorId, anchor, head)
260
367
  }
261
368
  Function("editorGetSelection") { id: Int ->
262
- editorGetSelection(id.toULong())
369
+ val editorId = nativeULong(id) ?: return@Function "{\"type\":\"text\",\"anchor\":0,\"head\":0}"
370
+ editorGetSelection(editorId)
263
371
  }
264
372
  Function("editorGetSelectionState") { id: Int ->
265
- editorGetSelectionState(id.toULong())
373
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
374
+ editorGetSelectionState(editorId)
266
375
  }
267
376
  Function("editorDocToScalar") { id: Int, docPos: Int ->
268
- editorDocToScalar(id.toULong(), docPos.toUInt()).toInt()
377
+ val editorId = nativeULong(id) ?: return@Function 0
378
+ val position = nativeUInt(docPos) ?: return@Function 0
379
+ editorDocToScalar(editorId, position).toInt()
269
380
  }
270
381
  Function("editorScalarToDoc") { id: Int, scalar: Int ->
271
- editorScalarToDoc(id.toULong(), scalar.toUInt()).toInt()
382
+ val editorId = nativeULong(id) ?: return@Function 0
383
+ val position = nativeUInt(scalar) ?: return@Function 0
384
+ editorScalarToDoc(editorId, position).toInt()
272
385
  }
273
386
 
274
387
  Function("editorGetCurrentState") { id: Int ->
275
- editorGetCurrentState(id.toULong())
388
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
389
+ editorGetCurrentState(editorId)
276
390
  }
277
391
 
278
392
  Function("editorUndo") { id: Int ->
279
- editorUndo(id.toULong())
393
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
394
+ editorUndo(editorId)
280
395
  }
281
396
  Function("editorRedo") { id: Int ->
282
- editorRedo(id.toULong())
397
+ val editorId = nativeULong(id) ?: return@Function nativeArgumentError("editor id")
398
+ editorRedo(editorId)
283
399
  }
284
400
  Function("editorCanUndo") { id: Int ->
285
- editorCanUndo(id.toULong())
401
+ val editorId = nativeULong(id) ?: return@Function false
402
+ editorCanUndo(editorId)
286
403
  }
287
404
  Function("editorCanRedo") { id: Int ->
288
- editorCanRedo(id.toULong())
405
+ val editorId = nativeULong(id) ?: return@Function false
406
+ editorCanRedo(editorId)
289
407
  }
290
408
  Function("renderDocumentJson") { configJson: String, json: String ->
291
409
  val editorId = editorCreate(configJson)
@@ -320,15 +438,16 @@ class NativeEditorModule : Module() {
320
438
  "onSelectionChange",
321
439
  "onFocusChange",
322
440
  "onContentHeightChange",
441
+ "onEditorReady",
323
442
  "onToolbarAction",
324
443
  "onAddonEvent"
325
444
  )
326
445
 
327
446
  Prop("editorId") { view: NativeEditorExpoView, id: Int ->
328
- view.setEditorId(id.toLong())
447
+ view.setEditorId(nativeULong(id)?.toLong() ?: 0L)
329
448
  }
330
449
  Prop("editable") { view: NativeEditorExpoView, editable: Boolean ->
331
- view.richTextView.editorEditText.isEditable = editable
450
+ view.setEditable(editable)
332
451
  }
333
452
  Prop("placeholder") { view: NativeEditorExpoView, placeholder: String ->
334
453
  view.richTextView.editorEditText.placeholderText = placeholder
@@ -375,6 +494,9 @@ class NativeEditorModule : Module() {
375
494
  Prop("editorUpdateJson") { view: NativeEditorExpoView, editorUpdateJson: String? ->
376
495
  view.setPendingEditorUpdateJson(editorUpdateJson)
377
496
  }
497
+ Prop("editorUpdateEditorId") { view: NativeEditorExpoView, editorUpdateEditorId: Int? ->
498
+ view.setPendingEditorUpdateEditorId(editorUpdateEditorId?.let { nativeULong(it)?.toLong() })
499
+ }
378
500
  Prop("editorUpdateRevision") { view: NativeEditorExpoView, editorUpdateRevision: Int ->
379
501
  view.setPendingEditorUpdateRevision(editorUpdateRevision)
380
502
  }
@@ -61,6 +61,33 @@ object PositionBridge {
61
61
  return if (nextBoundary == BreakIterator.DONE) text.length else nextBoundary
62
62
  }
63
63
 
64
+ /**
65
+ * Snaps a UTF-16 offset out of the middle of a surrogate pair without
66
+ * applying full grapheme-cluster expansion.
67
+ */
68
+ fun snapToScalarBoundary(
69
+ utf16Offset: Int,
70
+ text: String,
71
+ biasForward: Boolean
72
+ ): Int {
73
+ val clampedOffset = utf16Offset.coerceIn(0, text.length)
74
+ if (clampedOffset <= 0 || clampedOffset >= text.length) return clampedOffset
75
+
76
+ val previous = text[clampedOffset - 1]
77
+ val current = text[clampedOffset]
78
+ if (Character.isHighSurrogate(previous) && Character.isLowSurrogate(current)) {
79
+ return if (biasForward) clampedOffset + 1 else clampedOffset - 1
80
+ }
81
+ return clampedOffset
82
+ }
83
+
84
+ fun snapRangeToScalarBoundaries(start: Int, end: Int, text: String): Pair<Int, Int> {
85
+ val lower = minOf(start, end).coerceIn(0, text.length)
86
+ val upper = maxOf(start, end).coerceIn(0, text.length)
87
+ return snapToScalarBoundary(lower, text, biasForward = false) to
88
+ snapToScalarBoundary(upper, text, biasForward = true)
89
+ }
90
+
64
91
  private fun conversionTableFor(text: String): ConversionTable {
65
92
  val lastText = cachedText
66
93
  val lastTable = cachedTable
@@ -56,16 +56,15 @@ class RichTextEditorView @JvmOverloads constructor(
56
56
  internal var appliedCornerRadiusPx: Float = 0f
57
57
  internal var appliedBackgroundColorForTesting: Int = Color.WHITE
58
58
 
59
+ private var currentEditorId: Long = 0
60
+ private var deferEditorUnbindOnDetach = false
61
+ internal var onBeforeDetachedFromWindow: (() -> Unit)? = null
62
+
59
63
  /** Binds or unbinds the Rust editor instance. */
60
- var editorId: Long = 0
64
+ var editorId: Long
65
+ get() = currentEditorId
61
66
  set(value) {
62
- field = value
63
- if (value != 0L) {
64
- editorEditText.bindEditor(value)
65
- } else {
66
- editorEditText.unbindEditor()
67
- }
68
- refreshOverlays()
67
+ setEditorId(value, bindEditor = true)
69
68
  }
70
69
 
71
70
  init {
@@ -178,6 +177,8 @@ class RichTextEditorView @JvmOverloads constructor(
178
177
  requestLayout()
179
178
  }
180
179
 
180
+ internal fun viewportBottomInsetPxForTesting(): Int = viewportBottomInsetPx
181
+
181
182
  fun setRemoteSelections(selections: List<RemoteSelectionDecoration>) {
182
183
  remoteSelectionOverlayView.setRemoteSelections(selections)
183
184
  }
@@ -217,9 +218,57 @@ class RichTextEditorView @JvmOverloads constructor(
217
218
  editorEditText.applyUpdateJSON(editorGetCurrentState(editorId.toULong()), notifyListener = false)
218
219
  }
219
220
 
221
+ internal fun rebindEditorIfNeeded(notifyListener: Boolean = true) {
222
+ if (editorId != 0L && editorEditText.editorId != editorId) {
223
+ setEditorId(editorId, bindEditor = true, notifyListener = notifyListener)
224
+ }
225
+ }
226
+
227
+ internal fun setEditorIdWhileDetached(value: Long) {
228
+ setEditorId(value, bindEditor = false)
229
+ }
230
+
231
+ internal fun deferEditorUnbindOnNextDetach() {
232
+ deferEditorUnbindOnDetach = true
233
+ }
234
+
235
+ internal fun clearDeferredEditorUnbind() {
236
+ deferEditorUnbindOnDetach = false
237
+ }
238
+
239
+ internal fun unbindEditorForDetachedViewIfNeeded() {
240
+ if (isAttachedToWindow) return
241
+ deferEditorUnbindOnDetach = false
242
+ if (editorId != 0L) {
243
+ editorEditText.unbindEditor()
244
+ }
245
+ }
246
+
247
+ private fun setEditorId(value: Long, bindEditor: Boolean, notifyListener: Boolean = true) {
248
+ val targetBoundEditorId = if (bindEditor) value else 0L
249
+ if (currentEditorId == value && editorEditText.editorId == targetBoundEditorId) return
250
+ if (currentEditorId != value || editorEditText.editorId != targetBoundEditorId) {
251
+ editorEditText.discardTransientNativeInputForEditorRebind()
252
+ }
253
+ currentEditorId = value
254
+ if (bindEditor && value != 0L) {
255
+ editorEditText.bindEditor(value, notifyListener = notifyListener)
256
+ } else {
257
+ editorEditText.unbindEditor()
258
+ }
259
+ refreshOverlays()
260
+ }
261
+
262
+ override fun onAttachedToWindow() {
263
+ super.onAttachedToWindow()
264
+ clearDeferredEditorUnbind()
265
+ rebindEditorIfNeeded()
266
+ }
267
+
220
268
  override fun onDetachedFromWindow() {
269
+ onBeforeDetachedFromWindow?.invoke()
221
270
  super.onDetachedFromWindow()
222
- if (editorId != 0L) {
271
+ if (editorId != 0L && !deferEditorUnbindOnDetach) {
223
272
  editorEditText.unbindEditor()
224
273
  }
225
274
  }