@@ -36,77 +36,77 @@ export async function waitForIdle(onData: Event<unknown>, idleDurationMs: number
36
36
return deferred . p . finally ( ( ) => store . dispose ( ) ) ;
37
37
}
38
38
39
+ export interface IPromptDetectionResult {
40
+ /**
41
+ * Whether a prompt was detected.
42
+ */
43
+ detected : boolean ;
44
+ /**
45
+ * The reason for logging.
46
+ */
47
+ reason ?: string ;
48
+ }
49
+
39
50
/**
40
51
* Detects if the given text content appears to end with a common prompt pattern.
41
52
* This is used as a heuristic to determine if a command has finished executing.
42
- *
53
+ *
43
54
* The idea is to do basic regex checks after the initial timeout to extend the
44
55
* timeout if it doesn't look like a common prompt format, giving commands more
45
56
* time to complete before assuming they're done.
46
57
*/
47
- export function detectsCommonPromptPattern ( content : string ) : boolean {
48
- if ( ! content || content . trim ( ) . length === 0 ) {
49
- return false ;
50
- }
51
-
52
- // Split content into lines and check the last non-empty line
53
- const lines = content . split ( '\n' ) ;
54
- let lastLine = '' ;
55
- for ( let i = lines . length - 1 ; i >= 0 ; i -- ) {
56
- const line = lines [ i ] . trim ( ) ;
57
- if ( line . length > 0 ) {
58
- lastLine = line ;
59
- break ;
60
- }
58
+ export function detectsCommonPromptPattern ( cursorLine : string ) : IPromptDetectionResult {
59
+ if ( ! cursorLine || cursorLine . trim ( ) . length === 0 ) {
60
+ return { detected : false , reason : 'Content is empty or contains only whitespace' } ;
61
61
}
62
62
63
- if ( ! lastLine ) {
64
- return false ;
63
+ if ( ! cursorLine ) {
64
+ return { detected : false , reason : 'No non-empty lines found' } ;
65
65
}
66
66
67
67
// PowerShell prompt: PS C:\> or similar patterns
68
- if ( / P S \s + [ A - Z ] : \\ .* > \s * $ / . test ( lastLine ) ) {
69
- return true ;
68
+ if ( / P S \s + [ A - Z ] : \\ .* > \s * $ / . test ( cursorLine ) ) {
69
+ return { detected : true , reason : `PowerShell prompt pattern detected: " ${ cursorLine } "` } ;
70
70
}
71
71
72
72
// Command Prompt: C:\path>
73
- if ( / ^ [ A - Z ] : \\ .* > \s * $ / . test ( lastLine ) ) {
74
- return true ;
73
+ if ( / ^ [ A - Z ] : \\ .* > \s * $ / . test ( cursorLine ) ) {
74
+ return { detected : true , reason : `Command Prompt pattern detected: " ${ cursorLine } "` } ;
75
75
}
76
76
77
- // Bash-style prompts ending with $
78
- if ( / \$ \s * $ / . test ( lastLine ) ) {
79
- return true ;
77
+ // Bash-style prompts ending with $
78
+ if ( / \$ \s * $ / . test ( cursorLine ) ) {
79
+ return { detected : true , reason : `Bash-style prompt pattern detected: " ${ cursorLine } "` } ;
80
80
}
81
81
82
82
// Root prompts ending with #
83
- if ( / # \s * $ / . test ( lastLine ) ) {
84
- return true ;
83
+ if ( / # \s * $ / . test ( cursorLine ) ) {
84
+ return { detected : true , reason : `Root prompt pattern detected: " ${ cursorLine } "` } ;
85
85
}
86
86
87
87
// Python REPL prompt
88
- if ( / ^ > > > \s * $ / . test ( lastLine ) ) {
89
- return true ;
88
+ if ( / ^ > > > \s * $ / . test ( cursorLine ) ) {
89
+ return { detected : true , reason : `Python REPL prompt pattern detected: " ${ cursorLine } "` } ;
90
90
}
91
91
92
- // Custom prompts ending with the starship character ❯ (\u276f)
93
- if ( / \u276f \s * $ / . test ( lastLine ) ) {
94
- return true ;
92
+ // Custom prompts ending with the starship character (\u276f)
93
+ if ( / \u276f \s * $ / . test ( cursorLine ) ) {
94
+ return { detected : true , reason : `Starship prompt pattern detected: " ${ cursorLine } "` } ;
95
95
}
96
96
97
97
// Generic prompts ending with common prompt characters
98
- if ( / [ > % ] \s * $ / . test ( lastLine ) ) {
99
- return true ;
98
+ if ( / [ > % ] \s * $ / . test ( cursorLine ) ) {
99
+ return { detected : true , reason : `Generic prompt pattern detected: " ${ cursorLine } "` } ;
100
100
}
101
101
102
- return false ;
102
+ return { detected : false , reason : `No common prompt pattern found in last line: " ${ cursorLine } "` } ;
103
103
}
104
104
105
105
/**
106
106
* Enhanced version of waitForIdle that uses prompt detection heuristics.
107
107
* After the initial timeout, checks if the terminal content looks like a common prompt.
108
108
* If not, extends the timeout to give the command more time to complete.
109
- *
109
+ *
110
110
* This addresses the need for better heuristics around prompt characters for evaluating
111
111
* no shell integration command finished state, as requested in the issue.
112
112
*/
@@ -115,44 +115,36 @@ export async function waitForIdleWithPromptHeuristics(
115
115
instance : ITerminalInstance ,
116
116
initialTimeoutMs : number ,
117
117
extendedTimeoutMs : number = 2000
118
- ) : Promise < void > {
119
- // First, wait for the initial timeout period
118
+ ) : Promise < IPromptDetectionResult > {
120
119
await waitForIdle ( onData , initialTimeoutMs ) ;
121
120
122
- // Get the current terminal content to check for prompt patterns
123
121
try {
124
122
const xterm = await instance . xtermReadyPromise ;
125
123
if ( xterm ) {
126
- // Get the current visible content from the terminal
127
- const buffer = xterm . raw . buffer . active ;
128
124
let content = '' ;
129
-
130
- // Read the current line and a few lines above to detect prompt patterns
131
- // Focus on the area around the cursor, which is most likely to contain the prompt
132
- const currentLine = buffer . baseY + buffer . cursorY ;
133
- const startLine = Math . max ( 0 , currentLine - 2 ) ; // Read up to 2 lines above
134
- const endLine = Math . min ( buffer . length , currentLine + 1 ) ; // Read current line
135
-
136
- for ( let i = startLine ; i < endLine ; i ++ ) {
137
- const line = buffer . getLine ( i ) ;
138
- if ( line ) {
139
- content += line . translateToString ( true ) + '\n' ;
140
- }
125
+ const buffer = xterm . raw . buffer . active ;
126
+ const line = buffer . getLine ( buffer . baseY + buffer . cursorY ) ;
127
+ if ( line ) {
128
+ content = line . translateToString ( true ) + '\n' ;
141
129
}
142
130
143
131
// If we detect a common prompt pattern, we're done
144
- // Note: Logging is handled by the calling strategy (e.g., noneExecuteStrategy)
145
- if ( detectsCommonPromptPattern ( content ) ) {
146
- return ;
132
+ const promptResult = detectsCommonPromptPattern ( content ) ;
133
+ if ( promptResult . detected ) {
134
+ return promptResult ;
147
135
}
148
136
149
137
// Otherwise, wait for the extended timeout period
150
138
await waitForIdle ( onData , extendedTimeoutMs ) ;
139
+ return { detected : false , reason : 'Extended timeout reached without prompt detection' } ;
151
140
}
152
141
} catch ( error ) {
153
142
// If there's an error getting terminal content, fall back to extended timeout
154
143
await waitForIdle ( onData , extendedTimeoutMs ) ;
144
+ return { detected : false , reason : `Error reading terminal content: ${ error } ` } ;
155
145
}
146
+
147
+ return { detected : false , reason : 'Xterm not available' } ;
156
148
}
157
149
158
150
/**
0 commit comments