1
- import { Dispatcher } from './Dispatcher' ;
1
+ import { Dispatcher , CommandParams } from './Dispatcher' ;
2
2
import { JWPlugin , JWPluginConfig } from './JWPlugin' ;
3
3
import { Core } from './Core' ;
4
4
import { ContextManager } from './ContextManager' ;
5
5
import { VSelection } from './VSelection' ;
6
+ import { VRange } from './VRange' ;
6
7
import { isConstructor } from '../../utils/src/utils' ;
7
8
import { Keymap } from '../../plugin-keymap/src/Keymap' ;
8
9
import { StageError } from '../../utils/src/errors' ;
9
10
import { ContainerNode } from './VNodes/ContainerNode' ;
10
11
import { AtomicNode } from './VNodes/AtomicNode' ;
11
12
import { SeparatorNode } from './VNodes/SeparatorNode' ;
12
13
import { ModeIdentifier , ModeDefinition , Mode } from './Mode' ;
14
+ import { Memory , ChangesLocations } from './Memory/Memory' ;
15
+ import { makeVersionable } from './Memory/Versionable' ;
16
+ import { VersionableArray } from './Memory/VersionableArray' ;
17
+ import { Point } from './VNodes/VNode' ;
13
18
14
19
export enum EditorStage {
15
20
CONFIGURATION = 'configuration' ,
@@ -26,9 +31,13 @@ export type Loadables<T extends JWPlugin = JWPlugin> = {
26
31
} ;
27
32
28
33
type Commands < T extends JWPlugin > = Extract < keyof T [ 'commands' ] , string > ;
29
- type CommandParams < T extends JWPlugin , K extends string > = K extends Commands < T >
34
+ type CommandParamsType < T extends JWPlugin , K extends string > = K extends Commands < T >
30
35
? Parameters < T [ 'commands' ] [ K ] [ 'handler' ] > [ 0 ]
31
36
: never ;
37
+ export interface CommitParams extends CommandParams {
38
+ changesLocations : ChangesLocations ;
39
+ commandNames : string [ ] ;
40
+ }
32
41
33
42
export interface JWEditorConfig {
34
43
/**
@@ -68,7 +77,10 @@ export class JWEditor {
68
77
plugins : [ ] ,
69
78
loadables : { } ,
70
79
} ;
71
- selection = new VSelection ( this ) ;
80
+ memory : Memory ;
81
+ memoryInfo : { commandNames : string [ ] } ;
82
+ private _memoryID = 0 ;
83
+ selection : VSelection ;
72
84
loaders : Record < string , Loader > = { } ;
73
85
private mutex = Promise . resolve ( ) ;
74
86
// Use a set so that when asynchronous functions are called we ensure that
@@ -86,6 +98,7 @@ export class JWEditor {
86
98
constructor ( ) {
87
99
this . dispatcher = new Dispatcher ( this ) ;
88
100
this . plugins = new Map ( ) ;
101
+ this . selection = new VSelection ( this ) ;
89
102
this . contextManager = new ContextManager ( this ) ;
90
103
91
104
this . nextEventMutex = this . nextEventMutex . bind ( this ) ;
@@ -130,9 +143,18 @@ export class JWEditor {
130
143
this . setMode ( this . configuration . mode ) ;
131
144
}
132
145
133
- for ( const plugin of this . plugins . values ( ) ) {
134
- await plugin . start ( ) ;
135
- }
146
+ // create memory
147
+ this . memoryInfo = makeVersionable ( { commandNames : [ ] } ) ;
148
+ this . memory = new Memory ( ) ;
149
+ this . memory . attach ( this . memoryInfo ) ;
150
+ this . memory . create ( this . _memoryID . toString ( ) ) ;
151
+
152
+ // Start all plugins in the first memory slice.
153
+ return this . execCommand ( async ( ) => {
154
+ for ( const plugin of this . plugins . values ( ) ) {
155
+ await plugin . start ( ) ;
156
+ }
157
+ } ) ;
136
158
}
137
159
138
160
//--------------------------------------------------------------------------
@@ -303,13 +325,12 @@ export class JWEditor {
303
325
}
304
326
}
305
327
306
- async execBatch ( callback : ( ) => Promise < void > ) : Promise < void > {
307
- this . preventRenders . add ( callback ) ;
308
- await callback ( ) ;
309
- this . preventRenders . delete ( callback ) ;
310
- await this . dispatcher . dispatchHooks ( '@batch' ) ;
311
- }
312
-
328
+ /**
329
+ * Execute arbitrary code in `callback`, then dispatch the commit event.
330
+ *
331
+ * @param callback
332
+ */
333
+ async execCommand ( callback : ( ) => Promise < void > | void ) : Promise < void > ;
313
334
/**
314
335
* Execute the given command.
315
336
*
@@ -318,28 +339,101 @@ export class JWEditor {
318
339
*/
319
340
async execCommand < P extends JWPlugin , C extends Commands < P > = Commands < P > > (
320
341
commandName : C ,
321
- params ?: CommandParams < P , C > ,
342
+ params ?: CommandParamsType < P , C > ,
343
+ ) : Promise < void > ;
344
+ /**
345
+ * Execute the command or arbitrary code in `callback` in memory.
346
+ *
347
+ * TODO: create memory for each plugin who use the command then use
348
+ * squashInto(winnerSliceKey, winnerSliceKey, newMasterSliceKey)
349
+ *
350
+ * @param commandName name identifier of the command to execute or callback
351
+ * @param params arguments object of the command to execute
352
+ */
353
+ async execCommand < P extends JWPlugin , C extends Commands < P > = Commands < P > > (
354
+ commandName : C | ( ( ) => Promise < void > | void ) ,
355
+ params ?: CommandParamsType < P , C > ,
322
356
) : Promise < void > {
323
- return await this . dispatcher . dispatch ( commandName , params ) ;
357
+ const isFrozen = this . memory . isFrozen ( ) ;
358
+
359
+ let memorySlice : string ;
360
+ if ( isFrozen ) {
361
+ // Switch to the next memory slice (unfreeze the memory).
362
+ memorySlice = this . _memoryID . toString ( ) ;
363
+ this . memory . switchTo ( memorySlice ) ;
364
+ this . memoryInfo . commandNames = new VersionableArray ( ) ;
365
+ }
366
+
367
+ // Execute command.
368
+ if ( typeof commandName === 'function' ) {
369
+ this . memoryInfo . commandNames . push ( '@custom' ) ;
370
+ await commandName ( ) ;
371
+ } else {
372
+ this . memoryInfo . commandNames . push ( commandName ) ;
373
+ await this . dispatcher . dispatch ( commandName , params ) ;
374
+ }
375
+
376
+ if ( isFrozen ) {
377
+ // Check if it's frozen for calling execCommand inside a call of
378
+ // execCommand Create the next memory slice (and freeze the
379
+ // current memory).
380
+ this . _memoryID ++ ;
381
+ const nextMemorySlice = this . _memoryID . toString ( ) ;
382
+ this . memory . create ( nextMemorySlice ) ;
383
+
384
+ // Send the commit message with a froozen memory.
385
+ const changesLocations = this . memory . getChangesLocations (
386
+ memorySlice ,
387
+ this . memory . sliceKey ,
388
+ ) ;
389
+ await this . dispatcher . dispatchHooks ( '@commit' , {
390
+ changesLocations : changesLocations ,
391
+ commandNames : this . memoryInfo . commandNames ,
392
+ } ) ;
393
+ }
324
394
}
325
395
326
396
/**
327
- * Execute arbitrary code in `callback`, then dispatch the event.
397
+ * Create a temporary range corresponding to the given boundary points and
398
+ * call the given callback with the newly created range as argument. The
399
+ * range is automatically destroyed after calling the callback.
400
+ *
401
+ * @param bounds The points corresponding to the range boundaries.
402
+ * @param callback The callback to call with the newly created range.
403
+ * @param mode
328
404
*/
329
- async execCustomCommand < P extends JWPlugin , C extends Commands < P > = Commands < P > > (
330
- callback : ( ) => Promise < void > ,
405
+ async withRange (
406
+ bounds : [ Point , Point ] ,
407
+ callback : ( range : VRange ) => Promise < void > | void ,
408
+ mode ?: Mode ,
331
409
) : Promise < void > {
332
- await callback ( ) ;
333
- await this . dispatcher . dispatchHooks ( '@custom' ) ;
410
+ return this . execCommand ( async ( ) => {
411
+ this . memoryInfo . commandNames . push ( '@withRange' ) ;
412
+ const range = new VRange ( this , bounds , mode ) ;
413
+ await callback ( range ) ;
414
+ range . remove ( ) ;
415
+ } ) ;
334
416
}
335
417
336
418
/**
337
419
* Stop this editor instance.
338
420
*/
339
421
async stop ( ) : Promise < void > {
422
+ if ( this . memory ) {
423
+ this . memory . create ( 'stop' ) ;
424
+ this . memory . switchTo ( 'stop' ) ; // Unfreeze the memory.
425
+ }
340
426
for ( const plugin of this . plugins . values ( ) ) {
341
427
await plugin . stop ( ) ;
342
428
}
429
+ if ( this . memory ) {
430
+ this . memory . create ( 'stopped' ) ; // Freeze the memory.
431
+ this . memory = null ;
432
+ }
433
+ this . plugins . clear ( ) ;
434
+ this . dispatcher = new Dispatcher ( this ) ;
435
+ this . selection = new VSelection ( this ) ;
436
+ this . contextManager = new ContextManager ( this ) ;
343
437
// Clear loaders.
344
438
this . loaders = { } ;
345
439
this . _stage = EditorStage . CONFIGURATION ;
0 commit comments