@@ -2,6 +2,7 @@ import { AfterViewInit, Component, EventEmitter, Input, OnChanges, OnDestroy, On
2
2
import { FormBuilder } from '@angular/forms' ;
3
3
import { NgTerminal } from 'ng-terminal' ;
4
4
import { Observable , Subscription } from 'rxjs' ;
5
+ import { IDisposable } from 'xterm' ;
5
6
import { InstanceDirectoryEntry , StringEntryChunkDto } from '../../../../models/gen.dtos' ;
6
7
import { InstanceService } from '../../../instance/services/instance.service' ;
7
8
@@ -46,6 +47,7 @@ export class FileViewerComponent implements OnInit, AfterViewInit, OnChanges, On
46
47
private offset = 0 ;
47
48
48
49
private stdinSubscription : Subscription ;
50
+ private resizeDisposable : IDisposable ;
49
51
buffer : string = '' ;
50
52
bufferCursorPos : number = 0 ;
51
53
initialCursorX : number ; // cursor posX after terminal output (input field origin X)
@@ -59,6 +61,12 @@ export class FileViewerComponent implements OnInit, AfterViewInit, OnChanges, On
59
61
}
60
62
61
63
ngAfterViewInit ( ) {
64
+ this . term . underlying . attachCustomKeyEventHandler ( event => {
65
+ // prevent default handling of Ctrl-C/Ctrl-X (otherwise it resets the selections
66
+ // and default Ctrl-C/X has nothing to copy) and Ctrl-V
67
+ return ! event . ctrlKey || 'cxv' . indexOf ( event . key . toLowerCase ( ) ) === - 1 ;
68
+ } ) ;
69
+
62
70
if ( this . supportsStdin ) {
63
71
this . setupStdin ( this . hasStdin ) ;
64
72
}
@@ -146,7 +154,7 @@ export class FileViewerComponent implements OnInit, AfterViewInit, OnChanges, On
146
154
147
155
setupStdin ( connect : boolean ) {
148
156
if ( connect ) {
149
- this . term . underlying . onResize ( data => {
157
+ this . resizeDisposable = this . term . underlying . onResize ( data => {
150
158
if ( this . supportsStdin && this . hasStdin && this . follow ) {
151
159
setTimeout ( dim => {
152
160
this . clearInput ( ) ;
@@ -156,6 +164,7 @@ export class FileViewerComponent implements OnInit, AfterViewInit, OnChanges, On
156
164
} ) ;
157
165
158
166
this . stdinSubscription = this . term . keyInput . subscribe ( input => {
167
+ // keyboard input usually is a single charactor or CSI sequence, but clipboard content is a string
159
168
if ( ! this . follow ) {
160
169
return ;
161
170
}
@@ -164,43 +173,58 @@ export class FileViewerComponent implements OnInit, AfterViewInit, OnChanges, On
164
173
this . initialCursorX = this . cursorX ;
165
174
}
166
175
167
- if ( input === '\r' ) {
168
- this . sendStdin ( ) ;
169
- } else if ( input . charCodeAt ( 0 ) === 0x7f ) {
170
- if ( this . bufferCursorPos > 0 ) {
176
+ let idx = 0 ;
177
+ while ( idx < input . length ) {
178
+ if ( input [ idx ] === '\r' ) {
179
+ idx ++ ;
180
+ this . sendStdin ( ) ;
181
+ } else if ( input . charCodeAt ( idx ) === 0x7f ) {
182
+ idx ++ ;
183
+ if ( this . bufferCursorPos > 0 ) {
184
+ this . clearInput ( ) ;
185
+ this . buffer = this . buffer . substring ( 0 , this . bufferCursorPos - 1 ) + this . buffer . substring ( this . bufferCursorPos ) ;
186
+ this . bufferCursorPos -- ;
187
+ this . updateInput ( ) ;
188
+ }
189
+ } else {
171
190
this . clearInput ( ) ;
172
- this . buffer = this . buffer . substring ( 0 , this . bufferCursorPos - 1 ) + this . buffer . substring ( this . bufferCursorPos ) ;
173
- this . bufferCursorPos -- ;
174
- this . updateInput ( ) ;
175
- }
176
- } else {
177
- this . clearInput ( ) ;
178
- if ( input . length === 1 ) {
179
- this . buffer = [ this . buffer . slice ( 0 , this . bufferCursorPos ) , input , this . buffer . slice ( this . bufferCursorPos ) ] . join ( '' ) ;
180
- this . bufferCursorPos ++ ;
181
- } else if ( input . startsWith ( FileViewerComponent . CSI ) ) {
182
- const seq = input . substr ( FileViewerComponent . CSI . length ) ;
183
- switch ( seq ) {
184
- case 'C' : // cursor right
185
- this . bufferCursorPos = this . bufferCursorPos + ( this . buffer . length > this . bufferCursorPos ? 1 : 0 ) ;
186
- break ;
187
- case 'D' : // cursor left
188
- this . bufferCursorPos = this . bufferCursorPos - ( this . bufferCursorPos > 0 ? 1 : 0 ) ;
189
- break ;
190
- case 'H' : // pos 1
191
- this . bufferCursorPos = 0 ;
192
- break ;
193
- case 'F' : // end
194
- this . bufferCursorPos = this . buffer . length ;
195
- break ;
191
+ if ( input . charCodeAt ( idx ) >= 32 ) {
192
+ this . buffer = [ this . buffer . slice ( 0 , this . bufferCursorPos ) , input [ idx ] , this . buffer . slice ( this . bufferCursorPos ) ] . join ( '' ) ;
193
+ this . bufferCursorPos ++ ;
194
+ idx ++ ;
195
+ } else if ( input . substr ( idx ) . startsWith ( FileViewerComponent . CSI ) ) {
196
+ idx += FileViewerComponent . CSI . length ;
197
+ if ( idx < input . length ) {
198
+ switch ( input [ idx ] ) {
199
+ case 'C' : // cursor right
200
+ this . bufferCursorPos = this . bufferCursorPos + ( this . buffer . length > this . bufferCursorPos ? 1 : 0 ) ;
201
+ break ;
202
+ case 'D' : // cursor left
203
+ this . bufferCursorPos = this . bufferCursorPos - ( this . bufferCursorPos > 0 ? 1 : 0 ) ;
204
+ break ;
205
+ case 'H' : // pos 1
206
+ this . bufferCursorPos = 0 ;
207
+ break ;
208
+ case 'F' : // end
209
+ this . bufferCursorPos = this . buffer . length ;
210
+ break ;
211
+ }
212
+ idx ++ ;
213
+ }
214
+ } else {
215
+ // consume and ignore input character
216
+ idx ++ ;
196
217
}
218
+ this . updateInput ( ) ;
197
219
}
198
- this . updateInput ( ) ;
199
- }
220
+ } ;
221
+
200
222
} )
201
223
} else if ( this . stdinSubscription ) {
202
224
this . stdinSubscription . unsubscribe ( ) ;
203
225
this . stdinSubscription = null ;
226
+ this . resizeDisposable . dispose ( ) ;
227
+ this . resizeDisposable = null ;
204
228
}
205
229
}
206
230
0 commit comments