3
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
- import { Disposable , DisposableStore , MutableDisposable } from 'vs/base/common/lifecycle' ;
6
+ import type { ITerminalAddon , Terminal } from '@xterm/xterm' ;
7
+ import { debounce } from 'vs/base/common/decorators' ;
8
+ import { Event } from 'vs/base/common/event' ;
9
+ import { Disposable , MutableDisposable } from 'vs/base/common/lifecycle' ;
7
10
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility' ;
11
+ import { IConfigurationService } from 'vs/platform/configuration/common/configuration' ;
8
12
import { ITerminalCapabilityStore , TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities' ;
9
13
import { ITerminalLogService , TerminalSettingId } from 'vs/platform/terminal/common/terminal' ;
10
- import type { Terminal , ITerminalAddon } from '@xterm/xterm' ;
11
- import { debounce } from 'vs/base/common/decorators' ;
12
- import { addDisposableListener } from 'vs/base/browser/dom' ;
13
- import { IConfigurationService } from 'vs/platform/configuration/common/configuration' ;
14
-
15
- export interface ITextAreaData {
16
- content : string ;
17
- cursorX : number ;
18
- }
19
14
20
15
export class TextAreaSyncAddon extends Disposable implements ITerminalAddon {
21
16
private _terminal : Terminal | undefined ;
22
- private readonly _listeners = this . _register ( new MutableDisposable < DisposableStore > ( ) ) ;
23
- private _currentCommand : string | undefined ;
24
- private _cursorX : number | undefined ;
17
+ private readonly _listeners = this . _register ( new MutableDisposable ( ) ) ;
25
18
26
19
activate ( terminal : Terminal ) : void {
27
20
this . _terminal = terminal ;
28
- if ( this . _shouldBeActive ( ) ) {
29
- this . _registerSyncListeners ( ) ;
30
- }
21
+ this . _refreshListeners ( ) ;
31
22
}
32
23
33
24
constructor (
@@ -37,22 +28,27 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon {
37
28
@ITerminalLogService private readonly _logService : ITerminalLogService
38
29
) {
39
30
super ( ) ;
40
- this . _register ( this . _accessibilityService . onDidChangeScreenReaderOptimized ( ( ) => {
41
- if ( this . _shouldBeActive ( ) ) {
42
- this . _syncTextArea ( ) ;
43
- this . _registerSyncListeners ( ) ;
44
- } else {
45
- this . _listeners . clear ( ) ;
46
- }
31
+
32
+ this . _register ( Event . runAndSubscribe ( Event . any (
33
+ this . _capabilities . onDidAddCapability ,
34
+ this . _capabilities . onDidRemoveCapability ,
35
+ this . _accessibilityService . onDidChangeScreenReaderOptimized ,
36
+ ) , ( ) => {
37
+ this . _refreshListeners ( ) ;
47
38
} ) ) ;
48
39
}
49
40
50
- private _registerSyncListeners ( ) : void {
51
- if ( this . _shouldBeActive ( ) && this . _terminal ?. textarea ) {
52
- this . _listeners . value = new DisposableStore ( ) ;
53
- this . _listeners . value . add ( this . _terminal . onCursorMove ( ( ) => this . _syncTextArea ( ) ) ) ;
54
- this . _listeners . value . add ( this . _terminal . onData ( ( ) => this . _syncTextArea ( ) ) ) ;
55
- this . _listeners . value . add ( addDisposableListener ( this . _terminal . textarea , 'focus' , ( ) => this . _syncTextArea ( ) ) ) ;
41
+ private _refreshListeners ( ) : void {
42
+ const commandDetection = this . _capabilities . get ( TerminalCapability . CommandDetection ) ;
43
+ if ( this . _shouldBeActive ( ) && commandDetection ) {
44
+ if ( ! this . _listeners . value ) {
45
+ const textarea = this . _terminal ?. textarea ;
46
+ if ( textarea ) {
47
+ this . _listeners . value = Event . runAndSubscribe ( commandDetection . promptInputModel . onDidChangeInput , ( ) => this . _sync ( textarea ) ) ;
48
+ }
49
+ }
50
+ } else {
51
+ this . _listeners . clear ( ) ;
56
52
}
57
53
}
58
54
@@ -61,62 +57,16 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon {
61
57
}
62
58
63
59
@debounce ( 50 )
64
- private _syncTextArea ( ) : void {
65
- this . _logService . debug ( 'TextAreaSyncAddon#syncTextArea' ) ;
66
- const textArea = this . _terminal ?. textarea ;
67
- if ( ! textArea ) {
68
- this . _logService . debug ( `TextAreaSyncAddon#syncTextArea: no textarea` ) ;
60
+ private _sync ( textArea : HTMLTextAreaElement ) : void {
61
+ const commandCapability = this . _capabilities . get ( TerminalCapability . CommandDetection ) ;
62
+ if ( ! commandCapability ) {
69
63
return ;
70
64
}
71
65
72
- this . _updateCommandAndCursor ( ) ;
73
-
74
- if ( this . _currentCommand !== textArea . value ) {
75
- textArea . value = this . _currentCommand || '' ;
76
- this . _logService . debug ( `TextAreaSyncAddon#syncTextArea: text changed to "${ this . _currentCommand } "` ) ;
77
- } else if ( ! this . _currentCommand ) {
78
- textArea . value = '' ;
79
- this . _logService . debug ( `TextAreaSyncAddon#syncTextArea: text cleared` ) ;
80
- }
81
-
82
- if ( this . _cursorX !== textArea . selectionStart ) {
83
- const selection = ! this . _cursorX || this . _cursorX < 0 ? 0 : this . _cursorX ;
84
- textArea . selectionStart = selection ;
85
- textArea . selectionEnd = selection ;
86
- this . _logService . debug ( `TextAreaSyncAddon#syncTextArea: selection start/end changed to ${ selection } ` ) ;
87
- }
88
- }
66
+ textArea . value = commandCapability . promptInputModel . value ;
67
+ textArea . selectionStart = commandCapability . promptInputModel . cursorIndex ;
68
+ textArea . selectionEnd = commandCapability . promptInputModel . cursorIndex ;
89
69
90
- private _updateCommandAndCursor ( ) : void {
91
- if ( ! this . _terminal ) {
92
- return ;
93
- }
94
- const commandCapability = this . _capabilities . get ( TerminalCapability . CommandDetection ) ;
95
- const currentCommand = commandCapability ?. currentCommand ;
96
- if ( ! currentCommand ) {
97
- this . _logService . debug ( `TextAreaSyncAddon#updateCommandAndCursor: no current command` ) ;
98
- return ;
99
- }
100
- const buffer = this . _terminal . buffer . active ;
101
- const lineNumber = currentCommand . commandStartMarker ?. line ;
102
- this . _logService . debug ( 'line number?' , lineNumber ) ;
103
- if ( ! lineNumber ) {
104
- return ;
105
- }
106
- const commandLine = buffer . getLine ( lineNumber ) ?. translateToString ( true ) ;
107
- if ( ! commandLine ) {
108
- this . _logService . debug ( `TextAreaSyncAddon#updateCommandAndCursor: no line` ) ;
109
- return ;
110
- }
111
- // TODO: Support multi-line prompts
112
- if ( currentCommand . commandStartX !== undefined ) {
113
- this . _currentCommand = commandLine . substring ( currentCommand . commandStartX ) ;
114
- const cursorPosition = buffer . cursorX - currentCommand . commandStartX ;
115
- this . _cursorX = cursorPosition >= 0 ? cursorPosition : 0 ;
116
- } else {
117
- this . _currentCommand = undefined ;
118
- this . _cursorX = undefined ;
119
- this . _logService . debug ( `TextAreaSyncAddon#updateCommandAndCursor: no commandStartX` ) ;
120
- }
70
+ this . _logService . debug ( `TextAreaSyncAddon#sync: text changed to "${ textArea . value } "` ) ;
121
71
}
122
72
}
0 commit comments