66
77import * as nls from 'vs/nls' ;
88import { ResolvedKeybinding , Keybinding } from 'vs/base/common/keyCodes' ;
9- import { IDisposable , dispose } from 'vs/base/common/lifecycle' ;
9+ import { IDisposable , Disposable } from 'vs/base/common/lifecycle' ;
1010import { ICommandService } from 'vs/platform/commands/common/commands' ;
1111import { KeybindingResolver , IResolveResult } from 'vs/platform/keybinding/common/keybindingResolver' ;
1212import { IKeybindingEvent , IKeybindingService , IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding' ;
@@ -16,18 +16,18 @@ import { Event, Emitter } from 'vs/base/common/event';
1616import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem' ;
1717import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry' ;
1818import { INotificationService } from 'vs/platform/notification/common/notification' ;
19+ import { IntervalTimer } from 'vs/base/common/async' ;
1920
2021interface CurrentChord {
2122 keypress : string ;
2223 label : string ;
2324}
2425
25- export abstract class AbstractKeybindingService implements IKeybindingService {
26+ export abstract class AbstractKeybindingService extends Disposable implements IKeybindingService {
2627 public _serviceBrand : any ;
2728
28- protected toDispose : IDisposable [ ] = [ ] ;
29-
3029 private _currentChord : CurrentChord ;
30+ private _currentChordChecker : IntervalTimer ;
3131 private _currentChordStatusMessage : IDisposable ;
3232 protected _onDidUpdateKeybindings : Emitter < IKeybindingEvent > ;
3333
@@ -44,27 +44,29 @@ export abstract class AbstractKeybindingService implements IKeybindingService {
4444 notificationService : INotificationService ,
4545 statusService ?: IStatusbarService
4646 ) {
47+ super ( ) ;
4748 this . _contextKeyService = contextKeyService ;
4849 this . _commandService = commandService ;
4950 this . _telemetryService = telemetryService ;
5051 this . _statusService = statusService ;
5152 this . _notificationService = notificationService ;
5253
5354 this . _currentChord = null ;
55+ this . _currentChordChecker = new IntervalTimer ( ) ;
5456 this . _currentChordStatusMessage = null ;
55- this . _onDidUpdateKeybindings = new Emitter < IKeybindingEvent > ( ) ;
56- this . toDispose . push ( this . _onDidUpdateKeybindings ) ;
57+ this . _onDidUpdateKeybindings = this . _register ( new Emitter < IKeybindingEvent > ( ) ) ;
5758 }
5859
5960 public dispose ( ) : void {
60- this . toDispose = dispose ( this . toDispose ) ;
61+ super . dispose ( ) ;
6162 }
6263
6364 get onDidUpdateKeybindings ( ) : Event < IKeybindingEvent > {
6465 return this . _onDidUpdateKeybindings ? this . _onDidUpdateKeybindings . event : Event . None ; // Sinon stubbing walks properties on prototype
6566 }
6667
6768 protected abstract _getResolver ( ) : KeybindingResolver ;
69+ protected abstract _documentHasFocus ( ) : boolean ;
6870 public abstract resolveKeybinding ( keybinding : Keybinding ) : ResolvedKeybinding [ ] ;
6971 public abstract resolveKeyboardEvent ( keyboardEvent : IKeyboardEvent ) : ResolvedKeybinding ;
7072 public abstract resolveUserBinding ( userBinding : string ) : ResolvedKeybinding [ ] ;
@@ -114,6 +116,40 @@ export abstract class AbstractKeybindingService implements IKeybindingService {
114116 return this . _getResolver ( ) . resolve ( contextValue , currentChord , firstPart ) ;
115117 }
116118
119+ private _enterChordMode ( firstPart : string , keypressLabel : string ) : void {
120+ this . _currentChord = {
121+ keypress : firstPart ,
122+ label : keypressLabel
123+ } ;
124+ if ( this . _statusService ) {
125+ this . _currentChordStatusMessage = this . _statusService . setStatusMessage ( nls . localize ( 'first.chord' , "({0}) was pressed. Waiting for second key of chord..." , keypressLabel ) ) ;
126+ }
127+ const chordEnterTime = Date . now ( ) ;
128+ this . _currentChordChecker . cancelAndSet ( ( ) => {
129+
130+ if ( ! this . _documentHasFocus ( ) ) {
131+ // Focus has been lost => leave chord mode
132+ this . _leaveChordMode ( ) ;
133+ return ;
134+ }
135+
136+ if ( Date . now ( ) - chordEnterTime > 5000 ) {
137+ // 5 seconds elapsed => leave chord mode
138+ this . _leaveChordMode ( ) ;
139+ }
140+
141+ } , 500 ) ;
142+ }
143+
144+ private _leaveChordMode ( ) : void {
145+ if ( this . _currentChordStatusMessage ) {
146+ this . _currentChordStatusMessage . dispose ( ) ;
147+ this . _currentChordStatusMessage = null ;
148+ }
149+ this . _currentChordChecker . cancel ( ) ;
150+ this . _currentChord = null ;
151+ }
152+
117153 protected _dispatch ( e : IKeyboardEvent , target : IContextKeyServiceTarget ) : boolean {
118154 let shouldPreventDefault = false ;
119155
@@ -135,13 +171,7 @@ export abstract class AbstractKeybindingService implements IKeybindingService {
135171
136172 if ( resolveResult && resolveResult . enterChord ) {
137173 shouldPreventDefault = true ;
138- this . _currentChord = {
139- keypress : firstPart ,
140- label : keypressLabel
141- } ;
142- if ( this . _statusService ) {
143- this . _currentChordStatusMessage = this . _statusService . setStatusMessage ( nls . localize ( 'first.chord' , "({0}) was pressed. Waiting for second key of chord..." , keypressLabel ) ) ;
144- }
174+ this . _enterChordMode ( firstPart , keypressLabel ) ;
145175 return shouldPreventDefault ;
146176 }
147177
@@ -151,11 +181,8 @@ export abstract class AbstractKeybindingService implements IKeybindingService {
151181 shouldPreventDefault = true ;
152182 }
153183 }
154- if ( this . _currentChordStatusMessage ) {
155- this . _currentChordStatusMessage . dispose ( ) ;
156- this . _currentChordStatusMessage = null ;
157- }
158- this . _currentChord = null ;
184+
185+ this . _leaveChordMode ( ) ;
159186
160187 if ( resolveResult && resolveResult . commandId ) {
161188 if ( ! resolveResult . bubble ) {
0 commit comments