@@ -14,15 +14,23 @@ import { throttle } from 'vs/base/common/decorators';
14
14
import type { Terminal , IMarker , IBufferCell , IBufferLine , IBuffer } from '@xterm/headless' ;
15
15
16
16
const enum PromptInputState {
17
- Unknown ,
18
- Input ,
19
- Execute ,
17
+ Unknown = 0 ,
18
+ Input = 1 ,
19
+ Execute = 2 ,
20
20
}
21
21
22
+ /**
23
+ * A model of the prompt input state using shell integration and analyzing the terminal buffer. This
24
+ * may not be 100% accurate but provides a best guess.
25
+ */
22
26
export interface IPromptInputModel {
23
27
readonly onDidStartInput : Event < IPromptInputModelState > ;
24
28
readonly onDidChangeInput : Event < IPromptInputModelState > ;
25
29
readonly onDidFinishInput : Event < IPromptInputModelState > ;
30
+ /**
31
+ * Fires immediately before {@link onDidFinishInput} when a SIGINT/Ctrl+C/^C is detected.
32
+ */
33
+ readonly onDidInterrupt : Event < IPromptInputModelState > ;
26
34
27
35
readonly value : string ;
28
36
readonly cursorIndex : number ;
@@ -48,6 +56,8 @@ export class PromptInputModel extends Disposable implements IPromptInputModel {
48
56
private _commandStartX : number = 0 ;
49
57
private _continuationPrompt : string | undefined ;
50
58
59
+ private _lastUserInput : string = '' ;
60
+
51
61
private _value : string = '' ;
52
62
get value ( ) { return this . _value ; }
53
63
@@ -63,6 +73,8 @@ export class PromptInputModel extends Disposable implements IPromptInputModel {
63
73
readonly onDidChangeInput = this . _onDidChangeInput . event ;
64
74
private readonly _onDidFinishInput = this . _register ( new Emitter < IPromptInputModelState > ( ) ) ;
65
75
readonly onDidFinishInput = this . _onDidFinishInput . event ;
76
+ private readonly _onDidInterrupt = this . _register ( new Emitter < IPromptInputModelState > ( ) ) ;
77
+ readonly onDidInterrupt = this . _onDidInterrupt . event ;
66
78
67
79
constructor (
68
80
private readonly _xterm : Terminal ,
@@ -72,14 +84,27 @@ export class PromptInputModel extends Disposable implements IPromptInputModel {
72
84
) {
73
85
super ( ) ;
74
86
75
- this . _register ( this . _xterm . onData ( e => this . _handleInput ( e ) ) ) ;
76
87
this . _register ( Event . any (
77
- this . _xterm . onWriteParsed ,
78
88
this . _xterm . onCursorMove ,
89
+ this . _xterm . onData ,
90
+ this . _xterm . onWriteParsed ,
79
91
) ( ( ) => this . _sync ( ) ) ) ;
92
+ this . _register ( this . _xterm . onData ( e => this . _handleUserInput ( e ) ) ) ;
80
93
81
94
this . _register ( onCommandStart ( e => this . _handleCommandStart ( e as { marker : IMarker } ) ) ) ;
82
95
this . _register ( onCommandExecuted ( ( ) => this . _handleCommandExecuted ( ) ) ) ;
96
+
97
+ this . _register ( this . onDidStartInput ( ( ) => this . _logCombinedStringIfTrace ( 'PromptInputModel#onDidStartInput' ) ) ) ;
98
+ this . _register ( this . onDidChangeInput ( ( ) => this . _logCombinedStringIfTrace ( 'PromptInputModel#onDidChangeInput' ) ) ) ;
99
+ this . _register ( this . onDidFinishInput ( ( ) => this . _logCombinedStringIfTrace ( 'PromptInputModel#onDidFinishInput' ) ) ) ;
100
+ this . _register ( this . onDidInterrupt ( ( ) => this . _logCombinedStringIfTrace ( 'PromptInputModel#onDidInterrupt' ) ) ) ;
101
+ }
102
+
103
+ private _logCombinedStringIfTrace ( message : string ) {
104
+ // Only generate the combined string if trace
105
+ if ( this . _logService . getLevel ( ) === LogLevel . Trace ) {
106
+ this . _logService . trace ( message , this . getCombinedString ( ) ) ;
107
+ }
83
108
}
84
109
85
110
setContinuationPrompt ( value : string ) : void {
@@ -112,20 +137,24 @@ export class PromptInputModel extends Disposable implements IPromptInputModel {
112
137
this . _value = '' ;
113
138
this . _cursorIndex = 0 ;
114
139
this . _onDidStartInput . fire ( this . _createStateObject ( ) ) ;
140
+ this . _onDidChangeInput . fire ( this . _createStateObject ( ) ) ;
115
141
}
116
142
117
143
private _handleCommandExecuted ( ) {
118
144
if ( this . _state === PromptInputState . Execute ) {
119
145
return ;
120
146
}
121
147
122
- this . _state = PromptInputState . Execute ;
123
148
this . _cursorIndex = - 1 ;
124
- this . _onDidFinishInput . fire ( this . _createStateObject ( ) ) ;
125
- }
149
+ this . _ghostTextIndex = - 1 ;
150
+ const event = this . _createStateObject ( ) ;
151
+ if ( this . _lastUserInput === '\u0003' ) {
152
+ this . _onDidInterrupt . fire ( event ) ;
153
+ }
126
154
127
- private _handleInput ( data : string ) {
128
- this . _sync ( ) ;
155
+ this . _state = PromptInputState . Execute ;
156
+ this . _onDidFinishInput . fire ( event ) ;
157
+ this . _onDidChangeInput . fire ( event ) ;
129
158
}
130
159
131
160
@throttle ( 0 )
@@ -207,6 +236,10 @@ export class PromptInputModel extends Disposable implements IPromptInputModel {
207
236
}
208
237
}
209
238
239
+ private _handleUserInput ( e : string ) {
240
+ this . _lastUserInput = e ;
241
+ }
242
+
210
243
/**
211
244
* Detect ghost text by looking for italic or dim text in or after the cursor and
212
245
* non-italic/dim text in the cell closest non-whitespace cell before the cursor.
0 commit comments