@apollohg/react-native-rich-text-editor 2.0.0 → 2.0.2
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/README.md +8 -116
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/android/build.gradle +3 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextImages.kt +25 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2RenderValidation.kt +1 -0
- package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +101 -33
- package/android/src/main/java/com/apollohg/editor/RenderMediaSpans.kt +15 -3
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +14 -0
- package/android/src/main/java/com/apollohg/editor/viewer/AndroidProseLayoutEngine.kt +1 -1
- package/app.plugin.js +42 -39
- package/dist/AtomHost.js +18 -11
- package/dist/EditorAddon.js +4 -2
- package/dist/EditorAddonNormalization.js +17 -9
- package/dist/EditorMentionThemeNormalization.js +6 -3
- package/dist/EditorMentionThemeValidation.js +28 -11
- package/dist/EditorStyleSheetNormalization.js +85 -38
- package/dist/EditorStyleSheetTypes.d.ts +1 -1
- package/dist/EditorTheme.d.ts +4 -0
- package/dist/EditorTheme.js +5 -3
- package/dist/EditorToolbarComponent.d.ts +1 -1
- package/dist/EditorToolbarComponent.js +1 -1
- package/dist/EditorToolbarRegistry.js +5 -5
- package/dist/EditorToolbarTypes.d.ts +12 -12
- package/dist/EditorToolbarVisuals.js +1 -1
- package/dist/ExternalTextComposition.js +89 -43
- package/dist/ImageLoadingPolicy.d.ts +1 -1
- package/dist/NativeEditorBoundaryError.d.ts +7 -7
- package/dist/NativeEditorBoundaryError.js +9 -5
- package/dist/NativeEditorCollaborationTransport.d.ts +7 -7
- package/dist/NativeEditorCollaborationTransport.js +28 -15
- package/dist/NativeEditorCreateConfig.js +11 -6
- package/dist/NativeEditorCreateJson.js +51 -25
- package/dist/NativeEditorCreateValidation.js +11 -6
- package/dist/NativeEditorDocumentBridge.d.ts +1 -1
- package/dist/NativeEditorDocumentBridge.js +32 -18
- package/dist/NativeEditorDocumentHandle.d.ts +4 -4
- package/dist/NativeEditorDocumentHandle.js +1 -1
- package/dist/NativeEditorLocalAwareness.js +25 -13
- package/dist/NativeEditorNativeModule.d.ts +8 -1
- package/dist/NativeEditorNativeModule.js +7 -5
- package/dist/NativeEditorRenderNormalization.js +66 -25
- package/dist/NativeEditorResultNormalization.js +53 -29
- package/dist/NativeEditorTypes.d.ts +10 -10
- package/dist/NativeEditorV2Decimal.js +6 -3
- package/dist/NativeProseViewer.js +11 -3
- package/dist/ResourceLimits.d.ts +2 -2
- package/dist/ResourceLimits.js +7 -2
- package/dist/RichTextEditor.js +1 -1
- package/dist/RichTextEditorNativeTypes.d.ts +1 -1
- package/dist/RichTextEditorNativeView.d.ts +1 -3
- package/dist/RichTextEditorSerialization.js +24 -14
- package/dist/RichTextEditorTypes.d.ts +1 -1
- package/dist/YjsCollaboration.d.ts +7 -7
- package/dist/YjsCollaboration.js +42 -26
- package/dist/addons.d.ts +4 -4
- package/dist/addons.js +3 -3
- package/dist/atomInstances.js +6 -3
- package/dist/atomUpdates.js +7 -4
- package/dist/atoms.js +20 -12
- package/dist/attributeValidation.js +55 -24
- package/dist/contentExpression.js +80 -42
- package/dist/listMarkers.d.ts +1 -1
- package/dist/listMarkers.js +1 -1
- package/dist/schemaDefinition.d.ts +1 -1
- package/dist/schemaDefinition.js +23 -14
- package/dist/schemaDocument.js +1 -1
- package/dist/schemaDocumentConstruction.js +25 -14
- package/dist/schemaNormalization.js +51 -29
- package/dist/schemaPresets.d.ts +1 -1
- package/dist/schemaPresets.js +9 -4
- package/dist/schemaResolution.js +46 -25
- package/dist/specs/PreparedProseViewerNativeComponent.d.ts +4 -0
- package/dist/useEditorToolbarInteractions.d.ts +2 -2
- package/dist/useEditorToolbarInteractions.js +45 -23
- package/dist/useEditorToolbarItems.d.ts +1 -1
- package/dist/useEditorToolbarItems.js +4 -3
- package/dist/useEditorToolbarPresentation.d.ts +3 -3
- package/dist/useEditorToolbarPresentation.js +4 -4
- package/dist/useEditorToolbarState.d.ts +1 -1
- package/dist/useEditorToolbarState.js +1 -1
- package/dist/useFocusPreservingFrames.js +11 -8
- package/dist/useNativeEditor.d.ts +1 -1
- package/dist/useNativeEditor.js +21 -11
- package/dist/useRichTextEditorCommands.d.ts +2 -2
- package/dist/useRichTextEditorCommands.js +59 -29
- package/dist/useRichTextEditorEvents.d.ts +3 -3
- package/dist/useRichTextEditorEvents.js +75 -32
- package/dist/useRichTextEditorMentions.d.ts +4 -4
- package/dist/useRichTextEditorMentions.js +48 -29
- package/dist/useRichTextEditorPresentation.d.ts +4 -4
- package/dist/useRichTextEditorPresentation.js +25 -23
- package/dist/useRichTextEditorState.d.ts +3 -3
- package/dist/useRichTextEditorState.js +20 -15
- package/dist/useRichTextEditorUpdates.d.ts +1 -1
- package/dist/useRichTextEditorUpdates.js +99 -41
- package/dist/useViewerAtoms.js +63 -36
- 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/EditorInteractionOverlays.swift +114 -122
- package/ios/EditorLayoutManager.swift +13 -3
- package/ios/EditorStyleSheet.swift +12 -1
- package/ios/EditorTextView+Images.swift +32 -10
- package/ios/EditorTextView.swift +1 -1
- package/ios/EditorV2Adapter+RenderValidation.swift +2 -2
- package/ios/NativeToolbar+ButtonStyle.swift +17 -5
- package/ios/NativeToolbar+Diagnostics.swift +15 -0
- package/ios/RenderBridge+Atoms.swift +0 -1
- package/ios/RenderBridge+Attachments.swift +0 -29
- package/ios/RichTextEditorView.swift +4 -12
- package/ios/Viewer/CoreTextProseLayoutEngine+AttributedText.swift +3 -2
- package/package.json +11 -3
- package/react-native.config.js +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/libeditor_core.so +0 -0
- package/rust/android/x86_64/libeditor_core.so +0 -0
- package/src/AtomHost.tsx +58 -28
- package/src/EditorAddon.ts +18 -8
- package/src/EditorAddonNormalization.ts +40 -11
- package/src/EditorMentionThemeNormalization.ts +15 -4
- package/src/EditorMentionThemeValidation.ts +51 -14
- package/src/EditorStyleSheet.ts +1 -0
- package/src/EditorStyleSheetNormalization.ts +195 -64
- package/src/EditorStyleSheetTypes.ts +1 -1
- package/src/EditorTheme.ts +13 -5
- package/src/EditorToolbarComponent.tsx +2 -1
- package/src/EditorToolbarRegistry.tsx +23 -13
- package/src/EditorToolbarTypes.tsx +12 -12
- package/src/EditorToolbarVisuals.tsx +6 -3
- package/src/EditorUpdateRevision.ts +1 -0
- package/src/ExternalTextComposition.ts +259 -49
- package/src/ImageLoadingPolicy.ts +3 -1
- package/src/NativeEditorBoundaryError.ts +38 -11
- package/src/NativeEditorCollaborationTransport.ts +108 -27
- package/src/NativeEditorCreateConfig.ts +92 -19
- package/src/NativeEditorCreateJson.ts +163 -26
- package/src/NativeEditorCreateValidation.ts +33 -12
- package/src/NativeEditorDocumentBridge.ts +111 -19
- package/src/NativeEditorDocumentHandle.ts +9 -4
- package/src/NativeEditorLocalAwareness.ts +70 -15
- package/src/NativeEditorNativeModule.ts +13 -9
- package/src/NativeEditorRenderNormalization.ts +128 -29
- package/src/NativeEditorResultNormalization.ts +143 -32
- package/src/NativeEditorTypes.ts +12 -11
- package/src/NativeEditorV2Decimal.ts +12 -3
- package/src/NativeProseViewer.tsx +46 -16
- package/src/ResourceLimits.ts +15 -8
- package/src/RichTextEditor.tsx +3 -1
- package/src/RichTextEditorNativeTypes.tsx +1 -1
- package/src/RichTextEditorNativeView.tsx +2 -8
- package/src/RichTextEditorSerialization.tsx +70 -24
- package/src/RichTextEditorTypes.tsx +1 -1
- package/src/YjsCollaboration.ts +143 -47
- package/src/addons.ts +12 -8
- package/src/atomInstances.ts +25 -5
- package/src/atomUpdates.ts +23 -6
- package/src/atoms.ts +85 -18
- package/src/attributeValidation.ts +97 -33
- package/src/contentExpression.ts +218 -63
- package/src/listMarkers.ts +1 -1
- package/src/schemaDefinition.ts +82 -20
- package/src/schemaDocument.ts +15 -1
- package/src/schemaDocumentConstruction.ts +80 -16
- package/src/schemaNormalization.ts +155 -41
- package/src/schemaPresets.ts +37 -31
- package/src/schemaResolution.ts +126 -27
- package/src/specs/PreparedProseViewerNativeComponent.ts +4 -2
- package/src/useEditorToolbarInteractions.tsx +85 -36
- package/src/useEditorToolbarItems.tsx +50 -22
- package/src/useEditorToolbarPresentation.tsx +81 -58
- package/src/useEditorToolbarState.tsx +8 -6
- package/src/useFocusPreservingFrames.ts +43 -15
- package/src/useNativeEditor.ts +111 -51
- package/src/useRichTextEditorCommands.tsx +127 -54
- package/src/useRichTextEditorEvents.tsx +144 -42
- package/src/useRichTextEditorMentions.tsx +118 -37
- package/src/useRichTextEditorPresentation.tsx +128 -114
- package/src/useRichTextEditorState.tsx +76 -40
- package/src/useRichTextEditorUpdates.tsx +207 -56
- package/src/useViewerAtoms.tsx +162 -66
|
@@ -77,6 +77,7 @@ let nextExternalCompositionId = 1n;
|
|
|
77
77
|
function allocateExternalCompositionId(): string {
|
|
78
78
|
const id = nextExternalCompositionId.toString(10);
|
|
79
79
|
nextExternalCompositionId += 1n;
|
|
80
|
+
|
|
80
81
|
return id;
|
|
81
82
|
}
|
|
82
83
|
|
|
@@ -122,7 +123,8 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
122
123
|
|
|
123
124
|
function hasExactKeys(value: Record<string, unknown>, keys: readonly string[]): boolean {
|
|
124
125
|
const actual = Object.keys(value);
|
|
125
|
-
|
|
126
|
+
|
|
127
|
+
return actual.length === keys.length && actual.every(key => keys.includes(key));
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
const ERROR_KEYS = [
|
|
@@ -137,42 +139,63 @@ const ERROR_KEYS = [
|
|
|
137
139
|
] as const;
|
|
138
140
|
|
|
139
141
|
function parseError(value: unknown): NativeEditorError | null {
|
|
140
|
-
if (!isRecord(value) || !hasExactKeys(value, ERROR_KEYS))
|
|
142
|
+
if (!isRecord(value) || !hasExactKeys(value, ERROR_KEYS)) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
141
146
|
return normalizeNativeEditorV2Error({ error: value });
|
|
142
147
|
}
|
|
143
148
|
|
|
144
149
|
function parseNativeCompositionResult(resultJson: string): NativeCompositionResult {
|
|
145
150
|
let value: unknown;
|
|
151
|
+
|
|
146
152
|
try {
|
|
147
153
|
value = JSON.parse(resultJson);
|
|
148
154
|
} catch {
|
|
149
155
|
throw invalidResultError();
|
|
150
156
|
}
|
|
157
|
+
|
|
151
158
|
if (!isRecord(value) || value.version !== 1 || typeof value.type !== 'string') {
|
|
152
159
|
throw invalidResultError();
|
|
153
160
|
}
|
|
154
161
|
|
|
155
162
|
if (value.type === 'active') {
|
|
156
163
|
if (
|
|
157
|
-
!hasExactKeys(value, ['version', 'type', 'sessionId']) ||
|
|
164
|
+
!hasExactKeys(value, [ 'version', 'type', 'sessionId' ]) ||
|
|
158
165
|
typeof value.sessionId !== 'string'
|
|
159
166
|
) {
|
|
160
167
|
throw invalidResultError();
|
|
161
168
|
}
|
|
169
|
+
|
|
162
170
|
return { version: 1, type: 'active', sessionId: value.sessionId };
|
|
163
171
|
}
|
|
164
172
|
|
|
165
173
|
if (value.type === 'ended') {
|
|
166
174
|
const keys = Object.prototype.hasOwnProperty.call(value, 'error')
|
|
167
|
-
? ['version',
|
|
168
|
-
|
|
175
|
+
? [ 'version',
|
|
176
|
+
'type',
|
|
177
|
+
'sessionId',
|
|
178
|
+
'outcome',
|
|
179
|
+
'cause',
|
|
180
|
+
'text',
|
|
181
|
+
'error' ]
|
|
182
|
+
: [ 'version',
|
|
183
|
+
'type',
|
|
184
|
+
'sessionId',
|
|
185
|
+
'outcome',
|
|
186
|
+
'cause',
|
|
187
|
+
'text' ];
|
|
188
|
+
|
|
169
189
|
const validOutcome = value.outcome === 'committed' || value.outcome === 'cancelled';
|
|
190
|
+
|
|
170
191
|
const validCause =
|
|
171
192
|
value.cause === 'consumer' ||
|
|
172
193
|
value.cause === 'interaction' ||
|
|
173
194
|
value.cause === 'documentChange' ||
|
|
174
195
|
value.cause === 'lifecycle';
|
|
196
|
+
|
|
175
197
|
const error = keys.includes('error') ? parseError(value.error) : undefined;
|
|
198
|
+
|
|
176
199
|
if (
|
|
177
200
|
!hasExactKeys(value, keys) ||
|
|
178
201
|
typeof value.sessionId !== 'string' ||
|
|
@@ -183,6 +206,7 @@ function parseNativeCompositionResult(resultJson: string): NativeCompositionResu
|
|
|
183
206
|
) {
|
|
184
207
|
throw invalidResultError();
|
|
185
208
|
}
|
|
209
|
+
|
|
186
210
|
return {
|
|
187
211
|
version: 1,
|
|
188
212
|
type: 'ended',
|
|
@@ -196,13 +220,15 @@ function parseNativeCompositionResult(resultJson: string): NativeCompositionResu
|
|
|
196
220
|
|
|
197
221
|
if (value.type === 'error') {
|
|
198
222
|
const error = parseError(value.error);
|
|
223
|
+
|
|
199
224
|
if (
|
|
200
|
-
!hasExactKeys(value, ['version', 'type', 'sessionId', 'error']) ||
|
|
225
|
+
!hasExactKeys(value, [ 'version', 'type', 'sessionId', 'error' ]) ||
|
|
201
226
|
(value.sessionId !== null && typeof value.sessionId !== 'string') ||
|
|
202
227
|
error == null
|
|
203
228
|
) {
|
|
204
229
|
throw invalidResultError();
|
|
205
230
|
}
|
|
231
|
+
|
|
206
232
|
return { version: 1, type: 'error', sessionId: value.sessionId, error };
|
|
207
233
|
}
|
|
208
234
|
|
|
@@ -214,8 +240,10 @@ function requireActiveResult(result: NativeCompositionResult, sessionId: string)
|
|
|
214
240
|
if (result.sessionId !== null && result.sessionId !== sessionId) {
|
|
215
241
|
throw invalidResultError();
|
|
216
242
|
}
|
|
243
|
+
|
|
217
244
|
throw nativeEditorV2ErrorToException(result.error);
|
|
218
245
|
}
|
|
246
|
+
|
|
219
247
|
if (result.type !== 'active' || result.sessionId !== sessionId) {
|
|
220
248
|
throw invalidResultError();
|
|
221
249
|
}
|
|
@@ -229,141 +257,227 @@ function requireEndResult(
|
|
|
229
257
|
if (result.sessionId !== null && result.sessionId !== sessionId) {
|
|
230
258
|
throw invalidResultError();
|
|
231
259
|
}
|
|
260
|
+
|
|
232
261
|
throw nativeEditorV2ErrorToException(result.error);
|
|
233
262
|
}
|
|
263
|
+
|
|
234
264
|
if (result.type !== 'ended' || result.sessionId !== sessionId) {
|
|
235
265
|
throw invalidResultError();
|
|
236
266
|
}
|
|
267
|
+
|
|
237
268
|
return result;
|
|
238
269
|
}
|
|
239
270
|
|
|
240
271
|
function settleWaiters(waiters: Waiter[], error?: unknown): void {
|
|
241
272
|
for (const waiter of waiters) {
|
|
242
|
-
if (error === undefined)
|
|
243
|
-
|
|
273
|
+
if (error === undefined) {
|
|
274
|
+
waiter.resolve();
|
|
275
|
+
} else {
|
|
276
|
+
waiter.reject(error);
|
|
277
|
+
}
|
|
244
278
|
}
|
|
245
279
|
}
|
|
246
280
|
|
|
247
281
|
class ManagedExternalTextCompositionSession implements ExternalTextCompositionSession {
|
|
248
282
|
latestText = '';
|
|
283
|
+
|
|
249
284
|
private terminal = false;
|
|
285
|
+
|
|
250
286
|
private terminalResult: NativeCompositionEndResult | null = null;
|
|
287
|
+
|
|
251
288
|
private terminalError: NativeEditorErrorBase | null = null;
|
|
289
|
+
|
|
252
290
|
private resolveTerminal!: (result: NativeCompositionEndResult) => void;
|
|
253
|
-
|
|
291
|
+
|
|
292
|
+
private readonly terminalCompletion = new Promise<NativeCompositionEndResult>(resolve => {
|
|
254
293
|
this.resolveTerminal = resolve;
|
|
255
294
|
});
|
|
295
|
+
|
|
256
296
|
private closing: 'commit' | 'cancel' | null = null;
|
|
297
|
+
|
|
257
298
|
private updateInFlight: Promise<void> | null = null;
|
|
299
|
+
|
|
258
300
|
private inFlightWaiters: Waiter[] = [];
|
|
301
|
+
|
|
259
302
|
private pendingUpdate: PendingUpdate | null = null;
|
|
303
|
+
|
|
260
304
|
private cancelPromise: Promise<void> | null = null;
|
|
261
305
|
|
|
262
306
|
constructor(
|
|
263
307
|
readonly id: string,
|
|
264
308
|
private readonly host: ManagedExternalTextCompositionHost,
|
|
265
309
|
private readonly options: ExternalTextCompositionOptions
|
|
266
|
-
) {
|
|
310
|
+
) {
|
|
311
|
+
}
|
|
267
312
|
|
|
268
313
|
update(text: string): Promise<void> {
|
|
269
|
-
if (!this.canMutate())
|
|
314
|
+
if (!this.canMutate()) {
|
|
315
|
+
return Promise.reject(endedLifecycleError());
|
|
316
|
+
}
|
|
317
|
+
|
|
270
318
|
this.latestText = text;
|
|
319
|
+
|
|
271
320
|
return new Promise<void>((resolve, reject) => {
|
|
272
321
|
const waiter = { resolve, reject };
|
|
322
|
+
|
|
273
323
|
if (this.updateInFlight != null) {
|
|
274
324
|
if (this.pendingUpdate == null) {
|
|
275
|
-
this.pendingUpdate = { text, waiters: [waiter] };
|
|
325
|
+
this.pendingUpdate = { text, waiters: [ waiter ] };
|
|
276
326
|
} else {
|
|
277
327
|
this.pendingUpdate.text = text;
|
|
278
328
|
this.pendingUpdate.waiters.push(waiter);
|
|
279
329
|
}
|
|
330
|
+
|
|
280
331
|
return;
|
|
281
332
|
}
|
|
282
|
-
|
|
333
|
+
|
|
334
|
+
this.startUpdate(text, [ waiter ]);
|
|
283
335
|
});
|
|
284
336
|
}
|
|
285
337
|
|
|
286
338
|
async commit(finalText: string): Promise<void> {
|
|
287
|
-
if (!this.canMutate())
|
|
339
|
+
if (!this.canMutate()) {
|
|
340
|
+
throw endedLifecycleError();
|
|
341
|
+
}
|
|
342
|
+
|
|
288
343
|
this.closing = 'commit';
|
|
289
344
|
this.latestText = finalText;
|
|
345
|
+
|
|
290
346
|
if (this.updateInFlight != null) {
|
|
291
347
|
const update = await this.raceWithTerminal(this.updateInFlight);
|
|
348
|
+
|
|
292
349
|
if (update.type === 'terminal') {
|
|
293
350
|
this.requireSuccessfulConsumerCommit(update.result, finalText);
|
|
351
|
+
|
|
294
352
|
return;
|
|
295
353
|
}
|
|
296
|
-
|
|
354
|
+
|
|
355
|
+
if (update.type === 'failed') {
|
|
356
|
+
throw update.error;
|
|
357
|
+
}
|
|
297
358
|
}
|
|
359
|
+
|
|
298
360
|
if (this.terminalResult != null) {
|
|
299
361
|
this.requireSuccessfulConsumerCommit(this.terminalResult, finalText);
|
|
362
|
+
|
|
300
363
|
return;
|
|
301
364
|
}
|
|
302
|
-
|
|
365
|
+
|
|
366
|
+
if (!this.canFinish()) {
|
|
367
|
+
throw endedLifecycleError();
|
|
368
|
+
}
|
|
303
369
|
|
|
304
370
|
const foldedWaiters = this.takePendingWaiters();
|
|
305
371
|
const commit = await this.raceWithTerminal(this.host.commit(this.id, finalText));
|
|
372
|
+
|
|
306
373
|
if (commit.type === 'terminal') {
|
|
307
374
|
const error = this.explicitCommitError(commit.result, finalText);
|
|
308
375
|
settleWaiters(foldedWaiters, error);
|
|
309
|
-
|
|
376
|
+
|
|
377
|
+
if (error) {
|
|
378
|
+
throw error;
|
|
379
|
+
}
|
|
380
|
+
|
|
310
381
|
return;
|
|
311
382
|
}
|
|
383
|
+
|
|
312
384
|
if (commit.type === 'failed') {
|
|
313
385
|
settleWaiters(foldedWaiters, commit.error);
|
|
314
|
-
|
|
386
|
+
|
|
387
|
+
if (!this.terminal && this.host.owns(this)) {
|
|
388
|
+
this.closing = null;
|
|
389
|
+
}
|
|
390
|
+
|
|
315
391
|
throw commit.error;
|
|
316
392
|
}
|
|
393
|
+
|
|
317
394
|
try {
|
|
318
395
|
this.requireOwnership();
|
|
319
396
|
const endResult = requireEndResult(commit.value, this.id);
|
|
320
397
|
const endError = this.explicitCommitError(endResult, finalText);
|
|
321
398
|
this.finish(endResult);
|
|
322
399
|
settleWaiters(foldedWaiters, endError);
|
|
323
|
-
|
|
400
|
+
|
|
401
|
+
if (endError) {
|
|
402
|
+
throw endError;
|
|
403
|
+
}
|
|
324
404
|
} catch (error) {
|
|
325
405
|
settleWaiters(foldedWaiters, error);
|
|
326
|
-
|
|
406
|
+
|
|
407
|
+
if (!this.terminal && this.host.owns(this)) {
|
|
408
|
+
this.closing = null;
|
|
409
|
+
}
|
|
410
|
+
|
|
327
411
|
throw error;
|
|
328
412
|
}
|
|
329
413
|
}
|
|
330
414
|
|
|
331
415
|
cancel(): Promise<void> {
|
|
332
|
-
if (this.terminal)
|
|
333
|
-
|
|
334
|
-
|
|
416
|
+
if (this.terminal) {
|
|
417
|
+
return Promise.resolve();
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (this.cancelPromise != null) {
|
|
421
|
+
return this.cancelPromise;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (!this.canMutate()) {
|
|
425
|
+
return Promise.reject(endedLifecycleError());
|
|
426
|
+
}
|
|
427
|
+
|
|
335
428
|
this.cancelPromise = this.cancelWithCause('consumer');
|
|
429
|
+
|
|
336
430
|
return this.cancelPromise;
|
|
337
431
|
}
|
|
338
432
|
|
|
339
433
|
cancelForDocumentChange(): Promise<void> {
|
|
340
|
-
if (this.terminal)
|
|
341
|
-
|
|
342
|
-
|
|
434
|
+
if (this.terminal) {
|
|
435
|
+
return Promise.resolve();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (this.cancelPromise != null) {
|
|
439
|
+
return this.cancelPromise;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (!this.canMutate()) {
|
|
443
|
+
return Promise.reject(endedLifecycleError());
|
|
444
|
+
}
|
|
445
|
+
|
|
343
446
|
this.cancelPromise = this.cancelWithCause('documentChange');
|
|
447
|
+
|
|
344
448
|
return this.cancelPromise;
|
|
345
449
|
}
|
|
346
450
|
|
|
347
451
|
finish(result: NativeCompositionEndResult): void {
|
|
348
|
-
if (this.terminal)
|
|
452
|
+
if (this.terminal) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
349
456
|
this.terminal = true;
|
|
350
457
|
this.terminalResult = result;
|
|
458
|
+
|
|
351
459
|
const error = result.error
|
|
352
460
|
? nativeEditorV2ErrorToException(result.error)
|
|
353
461
|
: endedLifecycleError();
|
|
462
|
+
|
|
354
463
|
this.terminalError = error;
|
|
464
|
+
|
|
355
465
|
const waiterError =
|
|
356
466
|
this.closing === 'commit' && this.explicitCommitError(result, this.latestText) == null
|
|
357
467
|
? undefined
|
|
358
468
|
: error;
|
|
469
|
+
|
|
359
470
|
settleWaiters(this.inFlightWaiters, waiterError);
|
|
360
471
|
this.inFlightWaiters = [];
|
|
472
|
+
|
|
361
473
|
if (this.pendingUpdate != null) {
|
|
362
474
|
settleWaiters(this.pendingUpdate.waiters, waiterError);
|
|
363
475
|
this.pendingUpdate = null;
|
|
364
476
|
}
|
|
477
|
+
|
|
365
478
|
this.host.release(this);
|
|
366
479
|
this.resolveTerminal(result);
|
|
480
|
+
|
|
367
481
|
this.options.onEnd?.({
|
|
368
482
|
outcome: result.outcome,
|
|
369
483
|
cause: result.cause,
|
|
@@ -394,8 +508,12 @@ class ManagedExternalTextCompositionSession implements ExternalTextCompositionSe
|
|
|
394
508
|
} catch (error) {
|
|
395
509
|
settleWaiters(waiters, error);
|
|
396
510
|
} finally {
|
|
397
|
-
if (this.inFlightWaiters === waiters)
|
|
511
|
+
if (this.inFlightWaiters === waiters) {
|
|
512
|
+
this.inFlightWaiters = [];
|
|
513
|
+
}
|
|
514
|
+
|
|
398
515
|
this.updateInFlight = null;
|
|
516
|
+
|
|
399
517
|
if (!this.terminal && this.closing == null && this.pendingUpdate != null) {
|
|
400
518
|
const pending = this.pendingUpdate;
|
|
401
519
|
this.pendingUpdate = null;
|
|
@@ -407,20 +525,23 @@ class ManagedExternalTextCompositionSession implements ExternalTextCompositionSe
|
|
|
407
525
|
private takePendingWaiters(): Waiter[] {
|
|
408
526
|
const waiters = this.pendingUpdate?.waiters ?? [];
|
|
409
527
|
this.pendingUpdate = null;
|
|
528
|
+
|
|
410
529
|
return waiters;
|
|
411
530
|
}
|
|
412
531
|
|
|
413
532
|
private requireOwnership(): void {
|
|
414
|
-
if (!this.host.owns(this))
|
|
533
|
+
if (!this.host.owns(this)) {
|
|
534
|
+
throw endedLifecycleError();
|
|
535
|
+
}
|
|
415
536
|
}
|
|
416
537
|
|
|
417
538
|
private raceWithTerminal<T>(work: Promise<T>): Promise<TerminalRace<T>> {
|
|
418
539
|
return Promise.race([
|
|
419
540
|
work.then<TerminalRace<T>, TerminalRace<T>>(
|
|
420
|
-
|
|
541
|
+
value => ({ type: 'completed', value }),
|
|
421
542
|
(error: unknown) => ({ type: 'failed', error })
|
|
422
543
|
),
|
|
423
|
-
this.terminalCompletion.then<TerminalRace<T>>(
|
|
544
|
+
this.terminalCompletion.then<TerminalRace<T>>(result => ({
|
|
424
545
|
type: 'terminal',
|
|
425
546
|
result,
|
|
426
547
|
})),
|
|
@@ -439,6 +560,7 @@ class ManagedExternalTextCompositionSession implements ExternalTextCompositionSe
|
|
|
439
560
|
) {
|
|
440
561
|
return undefined;
|
|
441
562
|
}
|
|
563
|
+
|
|
442
564
|
return result.error ? nativeEditorV2ErrorToException(result.error) : endedLifecycleError();
|
|
443
565
|
}
|
|
444
566
|
|
|
@@ -447,7 +569,10 @@ class ManagedExternalTextCompositionSession implements ExternalTextCompositionSe
|
|
|
447
569
|
finalText: string
|
|
448
570
|
): void {
|
|
449
571
|
const error = this.explicitCommitError(result, finalText);
|
|
450
|
-
|
|
572
|
+
|
|
573
|
+
if (error) {
|
|
574
|
+
throw error;
|
|
575
|
+
}
|
|
451
576
|
}
|
|
452
577
|
|
|
453
578
|
private settleTerminalWaiters(waiters: Waiter[]): void {
|
|
@@ -456,41 +581,65 @@ class ManagedExternalTextCompositionSession implements ExternalTextCompositionSe
|
|
|
456
581
|
|
|
457
582
|
private async cancelWithCause(cause: 'consumer' | 'documentChange'): Promise<void> {
|
|
458
583
|
this.closing = 'cancel';
|
|
584
|
+
|
|
459
585
|
if (this.updateInFlight != null) {
|
|
460
586
|
const update = await this.raceWithTerminal(this.updateInFlight);
|
|
461
|
-
|
|
462
|
-
if (update.type === '
|
|
587
|
+
|
|
588
|
+
if (update.type === 'terminal') {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (update.type === 'failed') {
|
|
593
|
+
throw update.error;
|
|
594
|
+
}
|
|
463
595
|
}
|
|
464
|
-
|
|
596
|
+
|
|
597
|
+
if (!this.canFinish()) {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
|
|
465
601
|
const pendingWaiters = this.takePendingWaiters();
|
|
466
602
|
const cancellation = await this.raceWithTerminal(this.host.cancel(this.id, cause));
|
|
603
|
+
|
|
467
604
|
if (cancellation.type === 'terminal') {
|
|
468
605
|
this.settleTerminalWaiters(pendingWaiters);
|
|
606
|
+
|
|
469
607
|
return;
|
|
470
608
|
}
|
|
609
|
+
|
|
471
610
|
if (cancellation.type === 'failed') {
|
|
472
611
|
settleWaiters(pendingWaiters, cancellation.error);
|
|
612
|
+
|
|
473
613
|
if (this.host.owns(this)) {
|
|
474
614
|
this.closing = null;
|
|
475
615
|
this.cancelPromise = null;
|
|
476
616
|
}
|
|
617
|
+
|
|
477
618
|
throw cancellation.error;
|
|
478
619
|
}
|
|
620
|
+
|
|
479
621
|
try {
|
|
480
622
|
this.requireOwnership();
|
|
481
623
|
const endResult = requireEndResult(cancellation.value, this.id);
|
|
624
|
+
|
|
482
625
|
const endError = endResult.error
|
|
483
626
|
? nativeEditorV2ErrorToException(endResult.error)
|
|
484
627
|
: undefined;
|
|
628
|
+
|
|
485
629
|
this.finish(endResult);
|
|
486
630
|
settleWaiters(pendingWaiters, endError);
|
|
487
|
-
|
|
631
|
+
|
|
632
|
+
if (endError) {
|
|
633
|
+
throw endError;
|
|
634
|
+
}
|
|
488
635
|
} catch (error) {
|
|
489
636
|
settleWaiters(pendingWaiters, error);
|
|
637
|
+
|
|
490
638
|
if (!this.terminal && this.host.owns(this)) {
|
|
491
639
|
this.closing = null;
|
|
492
640
|
this.cancelPromise = null;
|
|
493
641
|
}
|
|
642
|
+
|
|
494
643
|
throw error;
|
|
495
644
|
}
|
|
496
645
|
}
|
|
@@ -498,14 +647,21 @@ class ManagedExternalTextCompositionSession implements ExternalTextCompositionSe
|
|
|
498
647
|
|
|
499
648
|
export class ExternalTextCompositionManager {
|
|
500
649
|
private active: ManagedExternalTextCompositionSession | null = null;
|
|
650
|
+
|
|
501
651
|
private pendingBegin: ManagedExternalTextCompositionSession | null = null;
|
|
652
|
+
|
|
502
653
|
private pendingEnd: NativeCompositionEndResult | null = null;
|
|
654
|
+
|
|
503
655
|
private disposed = false;
|
|
656
|
+
|
|
504
657
|
private beginTail: Promise<void> = Promise.resolve();
|
|
658
|
+
|
|
505
659
|
private readonly sessionHost: ManagedExternalTextCompositionHost = {
|
|
506
|
-
owns:
|
|
507
|
-
release:
|
|
508
|
-
if (this.active === session)
|
|
660
|
+
owns: session => !this.disposed && this.active === session,
|
|
661
|
+
release: session => {
|
|
662
|
+
if (this.active === session) {
|
|
663
|
+
this.active = null;
|
|
664
|
+
}
|
|
509
665
|
},
|
|
510
666
|
update: (sessionId, text) => this.invokeUpdate(sessionId, text),
|
|
511
667
|
commit: (sessionId, text) => this.invokeCommit(sessionId, text),
|
|
@@ -515,10 +671,12 @@ export class ExternalTextCompositionManager {
|
|
|
515
671
|
constructor(
|
|
516
672
|
private readonly editorId: string,
|
|
517
673
|
private readonly getNativeHandle: () => NativeExternalTextCompositionHandle | null
|
|
518
|
-
) {
|
|
674
|
+
) {
|
|
675
|
+
}
|
|
519
676
|
|
|
520
677
|
supports(): boolean {
|
|
521
678
|
const handle = this.getNativeHandle();
|
|
679
|
+
|
|
522
680
|
return (
|
|
523
681
|
typeof handle?.beginExternalTextComposition === 'function' &&
|
|
524
682
|
typeof handle.updateExternalTextComposition === 'function' &&
|
|
@@ -529,21 +687,32 @@ export class ExternalTextCompositionManager {
|
|
|
529
687
|
|
|
530
688
|
begin(options: ExternalTextCompositionOptions = {}): Promise<ExternalTextCompositionSession> {
|
|
531
689
|
const pending = this.beginTail.then(() => this.beginSerialized(options));
|
|
690
|
+
|
|
532
691
|
this.beginTail = pending.then(
|
|
533
692
|
() => undefined,
|
|
534
693
|
() => undefined
|
|
535
694
|
);
|
|
695
|
+
|
|
536
696
|
return pending;
|
|
537
697
|
}
|
|
538
698
|
|
|
539
699
|
handleNativeEnd(editorId: string, resultJson: string): void {
|
|
540
|
-
if (editorId !== this.editorId)
|
|
700
|
+
if (editorId !== this.editorId) {
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
|
|
541
704
|
const result = parseNativeCompositionResult(resultJson);
|
|
542
|
-
|
|
705
|
+
|
|
706
|
+
if (result.type !== 'ended') {
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
|
|
543
710
|
if (result.sessionId === this.active?.id) {
|
|
544
711
|
this.active.finish(result);
|
|
712
|
+
|
|
545
713
|
return;
|
|
546
714
|
}
|
|
715
|
+
|
|
547
716
|
if (result.sessionId === this.pendingBegin?.id && this.pendingEnd == null) {
|
|
548
717
|
this.pendingEnd = result;
|
|
549
718
|
this.pendingBegin.finish(result);
|
|
@@ -555,10 +724,17 @@ export class ExternalTextCompositionManager {
|
|
|
555
724
|
}
|
|
556
725
|
|
|
557
726
|
dispose(): void {
|
|
558
|
-
if (this.disposed)
|
|
727
|
+
if (this.disposed) {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
|
|
559
731
|
this.disposed = true;
|
|
560
732
|
const active = this.active;
|
|
561
|
-
|
|
733
|
+
|
|
734
|
+
if (!active) {
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
|
|
562
738
|
active.finish({
|
|
563
739
|
version: 1,
|
|
564
740
|
type: 'ended',
|
|
@@ -567,6 +743,7 @@ export class ExternalTextCompositionManager {
|
|
|
567
743
|
cause: 'lifecycle',
|
|
568
744
|
text: active.latestText,
|
|
569
745
|
});
|
|
746
|
+
|
|
570
747
|
void this.invokeCancel(active.id, 'lifecycle').catch(() => undefined);
|
|
571
748
|
}
|
|
572
749
|
|
|
@@ -580,30 +757,39 @@ export class ExternalTextCompositionManager {
|
|
|
580
757
|
private async invokeBegin(sessionId: string): Promise<NativeCompositionResult> {
|
|
581
758
|
const handle = this.getNativeHandle();
|
|
582
759
|
const method = handle?.beginExternalTextComposition;
|
|
760
|
+
|
|
583
761
|
if (typeof method !== 'function') {
|
|
584
762
|
throw this.unsupportedError();
|
|
585
763
|
}
|
|
764
|
+
|
|
586
765
|
const resultJson = await method.call(handle, sessionId);
|
|
766
|
+
|
|
587
767
|
return parseNativeCompositionResult(resultJson);
|
|
588
768
|
}
|
|
589
769
|
|
|
590
770
|
private async invokeUpdate(sessionId: string, text: string): Promise<NativeCompositionResult> {
|
|
591
771
|
const handle = this.getNativeHandle();
|
|
592
772
|
const method = handle?.updateExternalTextComposition;
|
|
773
|
+
|
|
593
774
|
if (typeof method !== 'function') {
|
|
594
775
|
throw this.unsupportedError();
|
|
595
776
|
}
|
|
777
|
+
|
|
596
778
|
const resultJson = await method.call(handle, sessionId, text);
|
|
779
|
+
|
|
597
780
|
return parseNativeCompositionResult(resultJson);
|
|
598
781
|
}
|
|
599
782
|
|
|
600
783
|
private async invokeCommit(sessionId: string, text: string): Promise<NativeCompositionResult> {
|
|
601
784
|
const handle = this.getNativeHandle();
|
|
602
785
|
const method = handle?.commitExternalTextComposition;
|
|
786
|
+
|
|
603
787
|
if (typeof method !== 'function') {
|
|
604
788
|
throw this.unsupportedError();
|
|
605
789
|
}
|
|
790
|
+
|
|
606
791
|
const resultJson = await method.call(handle, sessionId, text);
|
|
792
|
+
|
|
607
793
|
return parseNativeCompositionResult(resultJson);
|
|
608
794
|
}
|
|
609
795
|
|
|
@@ -613,40 +799,64 @@ export class ExternalTextCompositionManager {
|
|
|
613
799
|
): Promise<NativeCompositionResult> {
|
|
614
800
|
const handle = this.getNativeHandle();
|
|
615
801
|
const method = handle?.cancelExternalTextComposition;
|
|
802
|
+
|
|
616
803
|
if (typeof method !== 'function') {
|
|
617
804
|
throw this.unsupportedError();
|
|
618
805
|
}
|
|
806
|
+
|
|
619
807
|
const resultJson = await method.call(handle, sessionId, cause);
|
|
808
|
+
|
|
620
809
|
return parseNativeCompositionResult(resultJson);
|
|
621
810
|
}
|
|
622
811
|
|
|
623
812
|
private async beginSerialized(
|
|
624
813
|
options: ExternalTextCompositionOptions
|
|
625
814
|
): Promise<ExternalTextCompositionSession> {
|
|
626
|
-
if (this.disposed)
|
|
815
|
+
if (this.disposed) {
|
|
816
|
+
throw endedLifecycleError();
|
|
817
|
+
}
|
|
818
|
+
|
|
627
819
|
if (!this.supports()) {
|
|
628
820
|
throw this.unsupportedError();
|
|
629
821
|
}
|
|
630
|
-
|
|
631
|
-
if (this.
|
|
822
|
+
|
|
823
|
+
if (this.active) {
|
|
824
|
+
await this.active.commit(this.active.latestText);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
if (this.disposed) {
|
|
828
|
+
throw endedLifecycleError();
|
|
829
|
+
}
|
|
632
830
|
|
|
633
831
|
const session = new ManagedExternalTextCompositionSession(
|
|
634
832
|
allocateExternalCompositionId(),
|
|
635
833
|
this.sessionHost,
|
|
636
834
|
options
|
|
637
835
|
);
|
|
836
|
+
|
|
638
837
|
this.pendingBegin = session;
|
|
639
838
|
this.pendingEnd = null;
|
|
839
|
+
|
|
640
840
|
try {
|
|
641
841
|
const result = await this.invokeBegin(session.id);
|
|
842
|
+
|
|
642
843
|
if (this.disposed) {
|
|
643
844
|
void this.invokeCancel(session.id, 'lifecycle').catch(() => undefined);
|
|
644
845
|
throw endedLifecycleError();
|
|
645
846
|
}
|
|
646
|
-
|
|
847
|
+
|
|
848
|
+
if (this.pendingBegin !== session) {
|
|
849
|
+
throw endedLifecycleError();
|
|
850
|
+
}
|
|
851
|
+
|
|
647
852
|
requireActiveResult(result, session.id);
|
|
648
|
-
|
|
853
|
+
|
|
854
|
+
if (this.pendingEnd != null) {
|
|
855
|
+
throw endedLifecycleError();
|
|
856
|
+
}
|
|
857
|
+
|
|
649
858
|
this.active = session;
|
|
859
|
+
|
|
650
860
|
return session;
|
|
651
861
|
} finally {
|
|
652
862
|
if (this.pendingBegin === session) {
|