@@ -46,17 +46,21 @@ declare const $: any;
4646const SCROLLBACK = 5000 ;
4747const MAX_HISTORY_LENGTH = 100 * SCROLLBACK ;
4848
49- const MAX_DELAY = 15000 ;
49+ const ENABLE_WEBGL = true ;
5050
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 ;
5254
5355interface Path {
5456 file ?: string ;
5557 directory ?: string ;
5658}
5759
60+ type State = "ready" | "closed" ;
61+
5862export class Terminal < T extends CodeEditorState = CodeEditorState > {
59- private state : string = "ready" ;
63+ private state : State = "ready" ;
6064 private actions : Actions < T > | ConnectedTerminalInterface ;
6165 private account_store : any ;
6266 private project_actions : ProjectActions ;
@@ -71,9 +75,9 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
7175 private pauseKeyCount : number = 0 ;
7276 private keyhandler_initialized : boolean = false ;
7377 // last time user typed something
74- private lastSend = 0 ;
78+ // private lastSend = 0;
7579 // last time we received data back from project
76- private lastReceive = 0 ;
80+ // private lastReceive = 0;
7781 /* We initially have to ignore when rendering the initial history.
7882 To TEST this, do this in a terminal, then reconnect:
7983 printf "\E[c\n" ; sleep 1 ; echo
@@ -118,7 +122,6 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
118122 workingDir ?: string ,
119123 ) {
120124 this . actions = actions ;
121-
122125 this . account_store = redux . getStore ( "account" ) ;
123126 this . project_actions = redux . getProjectActions ( actions . project_id ) ;
124127 if ( this . account_store == null ) {
@@ -181,7 +184,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
181184 this . init_settings ( ) ;
182185 this . init_touch ( ) ;
183186 this . set_connection_status ( "disconnected" ) ;
184- this . reconnectIfNotResponding ( ) ;
187+ // this.reconnectIfNotResponding();
185188
186189 // The docs https://xtermjs.org/docs/api/terminal/classes/terminal/#resize say
187190 // "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> {
191194 // this.terminal_resize = debounce(this.terminal_resize, 2000);
192195 }
193196
197+ isClosed = ( ) => ( this . state ?? "closed" ) === "closed" ;
198+
194199 private get_xtermjs_options = ( ) : any => {
195200 const rendererType = this . rendererType ;
196201 const settings = this . account_store . get ( "terminal" ) ;
@@ -216,13 +221,13 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
216221 } ;
217222
218223 private assert_not_closed = ( ) : void => {
219- if ( this . state === "closed" ) {
224+ if ( this . isClosed ( ) ) {
220225 throw Error ( "BUG -- Terminal is closed." ) ;
221226 }
222227 } ;
223228
224229 close = ( ) : void => {
225- if ( this . state === "closed" ) {
230+ if ( this . isClosed ( ) ) {
226231 return ;
227232 }
228233 this . set_connection_status ( "disconnected" ) ;
@@ -279,7 +284,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
279284 connect = async ( ) => {
280285 try {
281286 if ( this . conn != null ) {
282- this . conn . removeListener ( "close " , this . connect ) ; // avoid infinite loop
287+ this . conn . removeListener ( "closed " , this . connect ) ; // avoid infinite loop
283288 this . conn . close ( ) ;
284289 delete this . conn ;
285290 }
@@ -309,15 +314,16 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
309314 cwd : this . workingDir ,
310315 env : this . actions . get_term_env ( ) ,
311316 } ,
317+ ephemeral : EPHEMERAL ,
312318 } ) ;
313319 this . conn = conn as any ;
314- conn . on ( "close ", this . connect ) ;
320+ conn . once ( "closed ", this . connect ) ;
315321 conn . on ( "kick" , this . close_request ) ;
316- conn . on ( "cwd" , ( cwd ) => {
317- this . actions . set_terminal_cwd ( this . id , cwd ) ;
318- } ) ;
319322 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+ } ) ;
321327 conn . once ( "ready" , ( ) => {
322328 delete this . last_geom ;
323329 this . ignore_terminal_data = false ;
@@ -362,19 +368,19 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
362368 return ;
363369 }
364370 this . conn . write ( data ) ;
365- this . lastSend = Date . now ( ) ;
371+ // this.lastSend = Date.now();
366372 } ;
367373
368374 // this should never ever be necessary. It's a just-in-case things
369375 // 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+ // };
378384
379385 private handleDataFromProject = ( data : any ) : void => {
380386 this . assert_not_closed ( ) ;
@@ -390,15 +396,14 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
390396 } ;
391397
392398 private activity = ( ) => {
393- this . lastReceive = Date . now ( ) ;
399+ // this.lastReceive = Date.now();
394400 this . project_actions . flag_file_activity ( this . path ) ;
395401 } ;
396402
397403 private render = async ( data : string ) : Promise < void > => {
398- if ( data == null ) {
404+ if ( data == null || this . isClosed ( ) ) {
399405 return ;
400406 }
401- this . assert_not_closed ( ) ;
402407 this . history += data ;
403408 if ( this . history . length > MAX_HISTORY_LENGTH ) {
404409 this . history = this . history . slice (
@@ -420,7 +425,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
420425 await delay ( 0 ) ;
421426 this . ignoreData -- ;
422427 }
423- if ( this . state == "done" ) return ;
428+ if ( this . isClosed ( ) ) return ;
424429 // tell anyone who waited for output coming back about this
425430 while ( this . render_done . length > 0 ) {
426431 this . render_done . pop ( ) ?.( ) ;
@@ -438,7 +443,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
438443 this . terminal . onTitleChange ( ( title ) => {
439444 if ( title != null ) {
440445 this . actions . set_title ( this . id , title ) ;
441- this . ask_for_cwd ( ) ;
446+ this . update_cwd ( ) ;
442447 }
443448 } ) ;
444449 } ;
@@ -450,7 +455,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
450455 } ;
451456
452457 touch = async ( ) => {
453- if ( this . state === "closed" ) return ;
458+ if ( this . isClosed ( ) ) return ;
454459 if ( Date . now ( ) - this . last_active < 70000 ) {
455460 if ( this . project_actions . isTabClosed ( ) ) {
456461 return ;
@@ -464,7 +469,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
464469 } ;
465470
466471 init_keyhandler = ( ) : void => {
467- if ( this . state === "closed" ) {
472+ if ( this . isClosed ( ) ) {
468473 return ;
469474 }
470475 if ( this . keyhandler_initialized ) {
@@ -575,7 +580,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
575580 // Stop ignoring terminal data... but ONLY once
576581 // the render buffer is also empty.
577582 no_ignore = async ( ) : Promise < void > => {
578- if ( this . state === "closed" ) {
583+ if ( this . isClosed ( ) ) {
579584 return ;
580585 }
581586 const g = ( cb ) => {
@@ -587,7 +592,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
587592 }
588593 // cause render to actually appear now.
589594 await delay ( 0 ) ;
590- if ( this . state === "closed" ) {
595+ if ( this . isClosed ( ) ) {
591596 return ;
592597 }
593598 try {
@@ -720,9 +725,23 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
720725 this . render_buffer = "" ;
721726 } ;
722727
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+ ) ;
726745
727746 kick_other_users_out ( ) : void {
728747 // @ts -ignore
@@ -760,14 +779,14 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
760779 }
761780
762781 focus ( ) : void {
763- if ( this . state === "closed" ) {
782+ if ( this . isClosed ( ) ) {
764783 return ;
765784 }
766785 this . terminal . focus ( ) ;
767786 }
768787
769788 refresh ( ) : void {
770- if ( this . state === "closed" ) {
789+ if ( this . isClosed ( ) ) {
771790 return ;
772791 }
773792 this . terminal . refresh ( 0 , this . terminal . rows - 1 ) ;
@@ -777,7 +796,7 @@ export class Terminal<T extends CodeEditorState = CodeEditorState> {
777796 try {
778797 await open_init_file ( this . actions . _get_project_actions ( ) , this . termPath ) ;
779798 } catch ( err ) {
780- if ( this . state === "closed" ) {
799+ if ( this . isClosed ( ) ) {
781800 return ;
782801 }
783802 this . actions . set_error ( `Problem opening init file -- ${ err } ` ) ;
0 commit comments