@@ -36,6 +36,116 @@ export async function waitForIdle(onData: Event<unknown>, idleDurationMs: number
36
36
return deferred . p . finally ( ( ) => store . dispose ( ) ) ;
37
37
}
38
38
39
+ /**
40
+ * Detects if the given text content appears to end with a common prompt pattern.
41
+ * This is used as a heuristic to determine if a command has finished executing.
42
+ */
43
+ export function detectsCommonPromptPattern ( content : string ) : boolean {
44
+ if ( ! content || content . trim ( ) . length === 0 ) {
45
+ return false ;
46
+ }
47
+
48
+ // Split content into lines and check the last non-empty line
49
+ const lines = content . split ( '\n' ) ;
50
+ let lastLine = '' ;
51
+ for ( let i = lines . length - 1 ; i >= 0 ; i -- ) {
52
+ const line = lines [ i ] . trim ( ) ;
53
+ if ( line . length > 0 ) {
54
+ lastLine = line ;
55
+ break ;
56
+ }
57
+ }
58
+
59
+ if ( ! lastLine ) {
60
+ return false ;
61
+ }
62
+
63
+ // PowerShell prompt: PS C:\> or similar patterns
64
+ if ( / P S \s + [ A - Z ] : \\ .* > \s * $ / . test ( lastLine ) ) {
65
+ return true ;
66
+ }
67
+
68
+ // Command Prompt: C:\path>
69
+ if ( / ^ [ A - Z ] : \\ .* > \s * $ / . test ( lastLine ) ) {
70
+ return true ;
71
+ }
72
+
73
+ // Bash-style prompts ending with $
74
+ if ( / \$ \s * $ / . test ( lastLine ) ) {
75
+ return true ;
76
+ }
77
+
78
+ // Root prompts ending with #
79
+ if ( / # \s * $ / . test ( lastLine ) ) {
80
+ return true ;
81
+ }
82
+
83
+ // Python REPL prompt
84
+ if ( / ^ > > > \s * $ / . test ( lastLine ) ) {
85
+ return true ;
86
+ }
87
+
88
+ // Custom prompts ending with the starship character ❯ (\u276f)
89
+ if ( / \u276f \s * $ / . test ( lastLine ) ) {
90
+ return true ;
91
+ }
92
+
93
+ // Generic prompts ending with common prompt characters
94
+ if ( / [ > % ] \s * $ / . test ( lastLine ) ) {
95
+ return true ;
96
+ }
97
+
98
+ return false ;
99
+ }
100
+
101
+ /**
102
+ * Enhanced version of waitForIdle that uses prompt detection heuristics.
103
+ * After the initial timeout, checks if the terminal content looks like a common prompt.
104
+ * If not, extends the timeout to give the command more time to complete.
105
+ */
106
+ export async function waitForIdleWithPromptHeuristics (
107
+ onData : Event < unknown > ,
108
+ instance : ITerminalInstance ,
109
+ initialTimeoutMs : number ,
110
+ extendedTimeoutMs : number = 2000
111
+ ) : Promise < void > {
112
+ // First, wait for the initial timeout period
113
+ await waitForIdle ( onData , initialTimeoutMs ) ;
114
+
115
+ // Get the current terminal content to check for prompt patterns
116
+ try {
117
+ const xterm = await instance . xtermReadyPromise ;
118
+ if ( xterm ) {
119
+ // Get the current visible content from the terminal
120
+ const buffer = xterm . raw . buffer . active ;
121
+ const viewportHeight = xterm . raw . rows ;
122
+ let content = '' ;
123
+
124
+ // Read the last few lines of the terminal to detect prompt patterns
125
+ const startLine = Math . max ( 0 , buffer . baseY + buffer . cursorY - viewportHeight + 1 ) ;
126
+ const endLine = buffer . baseY + buffer . cursorY + 1 ;
127
+
128
+ for ( let i = startLine ; i < endLine ; i ++ ) {
129
+ const line = buffer . getLine ( i ) ;
130
+ if ( line ) {
131
+ content += line . translateToString ( true ) + '\n' ;
132
+ }
133
+ }
134
+
135
+ // If we detect a common prompt pattern, we're done
136
+ if ( detectsCommonPromptPattern ( content ) ) {
137
+ return ;
138
+ }
139
+
140
+ // Otherwise, wait for the extended timeout period
141
+ await waitForIdle ( onData , extendedTimeoutMs ) ;
142
+ }
143
+ } catch ( error ) {
144
+ // If there's an error getting terminal content, fall back to extended timeout
145
+ await waitForIdle ( onData , extendedTimeoutMs ) ;
146
+ }
147
+ }
148
+
39
149
/**
40
150
* Tracks the terminal for being idle on a prompt input. This must be called before `executeCommand`
41
151
* is called.
0 commit comments