1+ import { Pseudoterminal , EventEmitter , Event , Terminal , window } from "vscode" ;
2+
3+ export class LineBufferingPseudoterminal implements Pseudoterminal {
4+ private static instances = new Map < string , LineBufferingPseudoterminal > ( ) ;
5+
6+ private writeEmitter = new EventEmitter < string > ( ) ;
7+ onDidWrite : Event < string > = this . writeEmitter . event ;
8+
9+ private closeEmitter = new EventEmitter < void > ( ) ;
10+ onDidClose ?: Event < void > = this . closeEmitter . event ;
11+
12+ private buffer : string = '' ;
13+ private isOpen = false ;
14+ private readonly name : string ;
15+ private terminal : Terminal | undefined ;
16+
17+ private constructor ( name : string ) {
18+ this . name = name ;
19+ }
20+
21+ open ( ) : void {
22+ this . isOpen = true ;
23+ }
24+
25+ close ( ) : void {
26+ this . isOpen = false ;
27+ this . closeEmitter . fire ( ) ;
28+ }
29+
30+ /**
31+ * Accepts partial input strings and logs complete lines when they are formed.
32+ * Also processes carriage returns (\r) to overwrite the current line.
33+ * @param input The string input to the pseudoterminal.
34+ */
35+ public acceptInput ( input : string ) : void {
36+ if ( ! this . isOpen ) {
37+ return ;
38+ }
39+
40+ for ( const char of input ) {
41+ if ( char === '\n' ) {
42+ // Process a newline: log the current buffer and reset it
43+ this . logLine ( this . buffer . trim ( ) ) ;
44+ this . buffer = '' ;
45+ } else if ( char === '\r' ) {
46+ // Process a carriage return: log the current buffer on the same line
47+ this . logInline ( this . buffer . trim ( ) ) ;
48+ this . buffer = '' ;
49+ } else {
50+ // Append characters to the buffer
51+ this . buffer += char ;
52+ }
53+ }
54+ }
55+
56+ private logLine ( line : string ) : void {
57+ this . writeEmitter . fire ( `${ line } \r\n` ) ;
58+ }
59+
60+ private logInline ( line : string ) : void {
61+ // Clear the current line and move the cursor to the start
62+ this . writeEmitter . fire ( `\x1b[2K\x1b[1G${ line } ` ) ;
63+ }
64+
65+ public flushBuffer ( ) : void {
66+ if ( this . buffer . trim ( ) . length > 0 ) {
67+ this . logLine ( this . buffer . trim ( ) ) ;
68+ this . buffer = '' ;
69+ }
70+ }
71+
72+ public clear ( ) : void {
73+ this . writeEmitter . fire ( '\x1b[2J\x1b[3J\x1b[H' ) ; // Clear screen and move cursor to top-left
74+ }
75+
76+ public show ( ) : void {
77+ if ( ! this . terminal ) {
78+ this . terminal = window . createTerminal ( {
79+ name : this . name ,
80+ pty : this ,
81+ } ) ;
82+ }
83+ this . terminal . show ( true ) ;
84+ }
85+
86+ /**
87+ * Gets an existing instance or creates a new one by the terminal name.
88+ * The terminal is also created and managed internally.
89+ * @param name The name of the pseudoterminal.
90+ * @returns The instance of the pseudoterminal.
91+ */
92+ public static getInstance ( name : string ) : LineBufferingPseudoterminal {
93+ if ( ! this . instances . has ( name ) ) {
94+ const instance = new LineBufferingPseudoterminal ( name ) ;
95+ this . instances . set ( name , instance ) ;
96+ }
97+ const instance = this . instances . get ( name ) ! ;
98+ instance . show ( ) ;
99+ return instance ;
100+ }
101+ }
0 commit comments