@@ -46,17 +46,21 @@ declare const $: any;
46
46
const SCROLLBACK = 5000 ;
47
47
const MAX_HISTORY_LENGTH = 100 * SCROLLBACK ;
48
48
49
- const MAX_DELAY = 15000 ;
49
+ const ENABLE_WEBGL = true ;
50
50
51
- const ENABLE_WEBGL = false ;
51
+ // ephemeral = faster, less load on servers, but if project and browser all
52
+ // close, the history is gone... which may be good and less confusing.
53
+ const EPHEMERAL = true ;
52
54
53
55
interface Path {
54
56
file ?: string ;
55
57
directory ?: string ;
56
58
}
57
59
60
+ type State = "ready" | "closed" ;
61
+
58
62
export class Terminal < T extends CodeEditorState = CodeEditorState > {
59
- private state : string = "ready" ;
63
+ private state : State = "ready" ;
60
64
private actions : Actions < T > | ConnectedTerminalInterface ;
61
65
private account_store : any ;
62
66
private project_actions : ProjectActions ;
@@ -71,9 +75,9 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
71
75
private pauseKeyCount : number = 0 ;
72
76
private keyhandler_initialized : boolean = false ;
73
77
// last time user typed something
74
- private lastSend = 0 ;
78
+ // private lastSend = 0;
75
79
// last time we received data back from project
76
- private lastReceive = 0 ;
80
+ // private lastReceive = 0;
77
81
/* We initially have to ignore when rendering the initial history.
78
82
To TEST this, do this in a terminal, then reconnect:
79
83
printf "\E[c\n" ; sleep 1 ; echo
@@ -118,7 +122,6 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
118
122
workingDir ?: string ,
119
123
) {
120
124
this . actions = actions ;
121
-
122
125
this . account_store = redux . getStore ( "account" ) ;
123
126
this . project_actions = redux . getProjectActions ( actions . project_id ) ;
124
127
if ( this . account_store == null ) {
@@ -181,7 +184,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
181
184
this . init_settings ( ) ;
182
185
this . init_touch ( ) ;
183
186
this . set_connection_status ( "disconnected" ) ;
184
- this . reconnectIfNotResponding ( ) ;
187
+ // this.reconnectIfNotResponding();
185
188
186
189
// The docs https://xtermjs.org/docs/api/terminal/classes/terminal/#resize say
187
190
// "It’s best practice to debounce calls to resize, this will help ensure that
@@ -191,6 +194,8 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
191
194
// this.terminal_resize = debounce(this.terminal_resize, 2000);
192
195
}
193
196
197
+ isClosed = ( ) => ( this . state ?? "closed" ) === "closed" ;
198
+
194
199
private get_xtermjs_options = ( ) : any => {
195
200
const rendererType = this . rendererType ;
196
201
const settings = this . account_store . get ( "terminal" ) ;
@@ -216,13 +221,13 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
216
221
} ;
217
222
218
223
private assert_not_closed = ( ) : void => {
219
- if ( this . state === "closed" ) {
224
+ if ( this . isClosed ( ) ) {
220
225
throw Error ( "BUG -- Terminal is closed." ) ;
221
226
}
222
227
} ;
223
228
224
229
close = ( ) : void => {
225
- if ( this . state === "closed" ) {
230
+ if ( this . isClosed ( ) ) {
226
231
return ;
227
232
}
228
233
this . set_connection_status ( "disconnected" ) ;
@@ -279,7 +284,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
279
284
connect = async ( ) => {
280
285
try {
281
286
if ( this . conn != null ) {
282
- this . conn . removeListener ( "close " , this . connect ) ; // avoid infinite loop
287
+ this . conn . removeListener ( "closed " , this . connect ) ; // avoid infinite loop
283
288
this . conn . close ( ) ;
284
289
delete this . conn ;
285
290
}
@@ -309,15 +314,16 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
309
314
cwd : this . workingDir ,
310
315
env : this . actions . get_term_env ( ) ,
311
316
} ,
317
+ ephemeral : EPHEMERAL ,
312
318
} ) ;
313
319
this . conn = conn as any ;
314
- conn . on ( "close ", this . connect ) ;
320
+ conn . once ( "closed ", this . connect ) ;
315
321
conn . on ( "kick" , this . close_request ) ;
316
- conn . on ( "cwd" , ( cwd ) => {
317
- this . actions . set_terminal_cwd ( this . id , cwd ) ;
318
- } ) ;
319
322
conn . on ( "data" , this . handleDataFromProject ) ;
320
- conn . on ( "init" , this . render ) ;
323
+ conn . once ( "initialize" , ( data ) => {
324
+ this . terminal . clear ( ) ;
325
+ this . render ( data ) ;
326
+ } ) ;
321
327
conn . once ( "ready" , ( ) => {
322
328
delete this . last_geom ;
323
329
this . ignore_terminal_data = false ;
@@ -362,19 +368,19 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
362
368
return ;
363
369
}
364
370
this . conn . write ( data ) ;
365
- this . lastSend = Date . now ( ) ;
371
+ // this.lastSend = Date.now();
366
372
} ;
367
373
368
374
// this should never ever be necessary. It's a just-in-case things
369
375
// were myseriously totally broken measure...
370
- private reconnectIfNotResponding = async ( ) => {
371
- while ( this . state != "closed" ) {
372
- if ( this . lastSend - this . lastReceive >= MAX_DELAY ) {
373
- await this . connect ( ) ;
374
- }
375
- await delay ( MAX_DELAY / 2 ) ;
376
- }
377
- } ;
376
+ // private reconnectIfNotResponding = async () => {
377
+ // while (this.state != "closed") {
378
+ // if (this.lastSend - this.lastReceive >= MAX_DELAY) {
379
+ // await this.connect();
380
+ // }
381
+ // await delay(MAX_DELAY / 2);
382
+ // }
383
+ // };
378
384
379
385
private handleDataFromProject = ( data : any ) : void => {
380
386
this . assert_not_closed ( ) ;
@@ -390,15 +396,14 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
390
396
} ;
391
397
392
398
private activity = ( ) => {
393
- this . lastReceive = Date . now ( ) ;
399
+ // this.lastReceive = Date.now();
394
400
this . project_actions . flag_file_activity ( this . path ) ;
395
401
} ;
396
402
397
403
private render = async ( data : string ) : Promise < void > => {
398
- if ( data == null ) {
404
+ if ( data == null || this . isClosed ( ) ) {
399
405
return ;
400
406
}
401
- this . assert_not_closed ( ) ;
402
407
this . history += data ;
403
408
if ( this . history . length > MAX_HISTORY_LENGTH ) {
404
409
this . history = this . history . slice (
@@ -420,7 +425,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
420
425
await delay ( 0 ) ;
421
426
this . ignoreData -- ;
422
427
}
423
- if ( this . state == "done" ) return ;
428
+ if ( this . isClosed ( ) ) return ;
424
429
// tell anyone who waited for output coming back about this
425
430
while ( this . render_done . length > 0 ) {
426
431
this . render_done . pop ( ) ?.( ) ;
@@ -438,7 +443,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
438
443
this . terminal . onTitleChange ( ( title ) => {
439
444
if ( title != null ) {
440
445
this . actions . set_title ( this . id , title ) ;
441
- this . ask_for_cwd ( ) ;
446
+ this . update_cwd ( ) ;
442
447
}
443
448
} ) ;
444
449
} ;
@@ -450,7 +455,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
450
455
} ;
451
456
452
457
touch = async ( ) => {
453
- if ( this . state === "closed" ) return ;
458
+ if ( this . isClosed ( ) ) return ;
454
459
if ( Date . now ( ) - this . last_active < 70000 ) {
455
460
if ( this . project_actions . isTabClosed ( ) ) {
456
461
return ;
@@ -464,7 +469,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
464
469
} ;
465
470
466
471
init_keyhandler = ( ) : void => {
467
- if ( this . state === "closed" ) {
472
+ if ( this . isClosed ( ) ) {
468
473
return ;
469
474
}
470
475
if ( this . keyhandler_initialized ) {
@@ -575,7 +580,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
575
580
// Stop ignoring terminal data... but ONLY once
576
581
// the render buffer is also empty.
577
582
no_ignore = async ( ) : Promise < void > => {
578
- if ( this . state === "closed" ) {
583
+ if ( this . isClosed ( ) ) {
579
584
return ;
580
585
}
581
586
const g = ( cb ) => {
@@ -587,7 +592,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
587
592
}
588
593
// cause render to actually appear now.
589
594
await delay ( 0 ) ;
590
- if ( this . state === "closed" ) {
595
+ if ( this . isClosed ( ) ) {
591
596
return ;
592
597
}
593
598
try {
@@ -720,9 +725,23 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
720
725
this . render_buffer = "" ;
721
726
} ;
722
727
723
- ask_for_cwd = debounce ( ( ) : void => {
724
- this . conn_write ( { cmd : "cwd" } ) ;
725
- } ) ;
728
+ update_cwd = debounce (
729
+ async ( ) => {
730
+ if ( this . isClosed ( ) ) return ;
731
+ let cwd ;
732
+ try {
733
+ cwd = await this . conn ?. api . cwd ( ) ;
734
+ } catch {
735
+ return ;
736
+ }
737
+ if ( this . isClosed ( ) ) return ;
738
+ if ( cwd != null ) {
739
+ this . actions . set_terminal_cwd ( this . id , cwd ) ;
740
+ }
741
+ } ,
742
+ 1000 ,
743
+ { leading : true , trailing : true } ,
744
+ ) ;
726
745
727
746
kick_other_users_out ( ) : void {
728
747
// @ts -ignore
@@ -760,14 +779,14 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
760
779
}
761
780
762
781
focus ( ) : void {
763
- if ( this . state === "closed" ) {
782
+ if ( this . isClosed ( ) ) {
764
783
return ;
765
784
}
766
785
this . terminal . focus ( ) ;
767
786
}
768
787
769
788
refresh ( ) : void {
770
- if ( this . state === "closed" ) {
789
+ if ( this . isClosed ( ) ) {
771
790
return ;
772
791
}
773
792
this . terminal . refresh ( 0 , this . terminal . rows - 1 ) ;
@@ -777,7 +796,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
777
796
try {
778
797
await open_init_file ( this . actions . _get_project_actions ( ) , this . termPath ) ;
779
798
} catch ( err ) {
780
- if ( this . state === "closed" ) {
799
+ if ( this . isClosed ( ) ) {
781
800
return ;
782
801
}
783
802
this . actions . set_error ( `Problem opening init file -- ${ err } ` ) ;
0 commit comments