@@ -15,7 +15,7 @@ import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBo
15
15
import { SimpleButton , findPreviousMatchIcon , findNextMatchIcon } from 'vs/editor/contrib/find/browser/findWidget' ;
16
16
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey' ;
17
17
import { IContextViewService } from 'vs/platform/contextview/browser/contextView' ;
18
- import { editorWidgetBackground , inputActiveOptionBorder , inputActiveOptionBackground , inputActiveOptionForeground , inputBackground , inputBorder , inputForeground , inputValidationErrorBackground , inputValidationErrorBorder , inputValidationErrorForeground , inputValidationInfoBackground , inputValidationInfoBorder , inputValidationInfoForeground , inputValidationWarningBackground , inputValidationWarningBorder , inputValidationWarningForeground , widgetShadow , editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry' ;
18
+ import { editorWidgetBackground , inputActiveOptionBorder , inputActiveOptionBackground , inputActiveOptionForeground , inputBackground , inputBorder , inputForeground , inputValidationErrorBackground , inputValidationErrorBorder , inputValidationErrorForeground , inputValidationInfoBackground , inputValidationInfoBorder , inputValidationInfoForeground , inputValidationWarningBackground , inputValidationWarningBorder , inputValidationWarningForeground , widgetShadow , editorWidgetForeground , errorForeground } from 'vs/platform/theme/common/colorRegistry' ;
19
19
import { IColorTheme , registerThemingParticipant } from 'vs/platform/theme/common/themeService' ;
20
20
import { ContextScopedFindInput } from 'vs/platform/history/browser/contextScopedHistoryWidget' ;
21
21
import { widgetClose } from 'vs/platform/theme/common/iconRegistry' ;
@@ -26,6 +26,12 @@ const NLS_PREVIOUS_MATCH_BTN_LABEL = nls.localize('label.previousMatchButton', "
26
26
const NLS_NEXT_MATCH_BTN_LABEL = nls . localize ( 'label.nextMatchButton' , "Next Match" ) ;
27
27
const NLS_CLOSE_BTN_LABEL = nls . localize ( 'label.closeButton' , "Close" ) ;
28
28
29
+ interface IFindOptions {
30
+ showOptionButtons ?: boolean ;
31
+ checkImeCompletionState ?: boolean ;
32
+ showResultCount ?: boolean ;
33
+ }
34
+
29
35
export abstract class SimpleFindWidget extends Widget {
30
36
private readonly _findInput : FindInput ;
31
37
private readonly _domNode : HTMLElement ;
@@ -35,16 +41,16 @@ export abstract class SimpleFindWidget extends Widget {
35
41
private readonly _updateHistoryDelayer : Delayer < void > ;
36
42
private readonly prevBtn : SimpleButton ;
37
43
private readonly nextBtn : SimpleButton ;
44
+ private _matchesCount : HTMLElement | undefined ;
38
45
39
46
private _isVisible : boolean = false ;
40
- private foundMatch : boolean = false ;
47
+ private _foundMatch : boolean = false ;
41
48
42
49
constructor (
43
50
@IContextViewService private readonly _contextViewService : IContextViewService ,
44
51
@IContextKeyService contextKeyService : IContextKeyService ,
45
52
private readonly _state : FindReplaceState = new FindReplaceState ( ) ,
46
- showOptionButtons ?: boolean ,
47
- checkImeCompletionState ?: boolean
53
+ private readonly _options : IFindOptions
48
54
) {
49
55
super ( ) ;
50
56
@@ -59,20 +65,23 @@ export abstract class SimpleFindWidget extends Widget {
59
65
new RegExp ( value ) ;
60
66
return null ;
61
67
} catch ( e ) {
62
- this . foundMatch = false ;
63
- this . updateButtons ( this . foundMatch ) ;
68
+ this . _foundMatch = false ;
69
+ this . updateButtons ( this . _foundMatch ) ;
64
70
return { content : e . message } ;
65
71
}
66
72
}
67
- } , contextKeyService , showOptionButtons ) ) ;
73
+ } , contextKeyService , _options . showOptionButtons ) ) ;
68
74
69
75
// Find History with update delayer
70
76
this . _updateHistoryDelayer = new Delayer < void > ( 500 ) ;
71
77
72
- this . _register ( this . _findInput . onInput ( ( e ) => {
73
- if ( ! checkImeCompletionState || ! this . _findInput . isImeSessionInProgress ) {
74
- this . foundMatch = this . _onInputChanged ( ) ;
75
- this . updateButtons ( this . foundMatch ) ;
78
+ this . _register ( this . _findInput . onInput ( async ( e ) => {
79
+ if ( ! _options . checkImeCompletionState || ! this . _findInput . isImeSessionInProgress ) {
80
+ this . _foundMatch = this . _onInputChanged ( ) ;
81
+ if ( this . _options . showResultCount ) {
82
+ await this . updateResultCount ( ) ;
83
+ }
84
+ this . updateButtons ( this . _foundMatch ) ;
76
85
this . focusFindBox ( ) ;
77
86
this . _delayedUpdateHistory ( ) ;
78
87
}
@@ -152,6 +161,14 @@ export abstract class SimpleFindWidget extends Widget {
152
161
this . _register ( dom . addDisposableListener ( this . _innerDomNode , 'click' , ( event ) => {
153
162
event . stopPropagation ( ) ;
154
163
} ) ) ;
164
+
165
+ if ( _options ?. showResultCount ) {
166
+ this . _domNode . classList . add ( 'result-count' ) ;
167
+ this . _register ( this . _findInput . onDidChange ( ( ) => {
168
+ this . updateResultCount ( ) ;
169
+ this . updateButtons ( this . _foundMatch ) ;
170
+ } ) ) ;
171
+ }
155
172
}
156
173
157
174
protected abstract _onInputChanged ( ) : boolean ;
@@ -161,6 +178,7 @@ export abstract class SimpleFindWidget extends Widget {
161
178
protected abstract _onFocusTrackerBlur ( ) : void ;
162
179
protected abstract _onFindInputFocusTrackerFocus ( ) : void ;
163
180
protected abstract _onFindInputFocusTrackerBlur ( ) : void ;
181
+ protected abstract _getResultCount ( ) : Promise < { resultIndex : number ; resultCount : number } | undefined > ;
164
182
165
183
protected get inputValue ( ) {
166
184
return this . _findInput . getValue ( ) ;
@@ -214,7 +232,7 @@ export abstract class SimpleFindWidget extends Widget {
214
232
}
215
233
216
234
this . _isVisible = true ;
217
- this . updateButtons ( this . foundMatch ) ;
235
+ this . updateButtons ( this . _foundMatch ) ;
218
236
219
237
setTimeout ( ( ) => {
220
238
this . _innerDomNode . classList . add ( 'visible' , 'visible-transition' ) ;
@@ -243,7 +261,7 @@ export abstract class SimpleFindWidget extends Widget {
243
261
// Need to delay toggling visibility until after Transition, then visibility hidden - removes from tabIndex list
244
262
setTimeout ( ( ) => {
245
263
this . _isVisible = false ;
246
- this . updateButtons ( this . foundMatch ) ;
264
+ this . updateButtons ( this . _foundMatch ) ;
247
265
this . _innerDomNode . classList . remove ( 'visible' ) ;
248
266
} , 200 ) ;
249
267
}
@@ -281,6 +299,20 @@ export abstract class SimpleFindWidget extends Widget {
281
299
this . nextBtn . focus ( ) ;
282
300
this . _findInput . inputBox . focus ( ) ;
283
301
}
302
+
303
+ async updateResultCount ( ) : Promise < void > {
304
+ const count = await this . _getResultCount ( ) ;
305
+ if ( ! this . _matchesCount ) {
306
+ this . _matchesCount = document . createElement ( 'div' ) ;
307
+ this . _matchesCount . className = 'matchesCount' ;
308
+ }
309
+ this . _matchesCount . innerText = '' ;
310
+ const label = count === undefined || count . resultCount === 0 ? `No Results` : `${ count . resultIndex + 1 } of ${ count . resultCount } ` ;
311
+ this . _matchesCount . appendChild ( document . createTextNode ( label ) ) ;
312
+ this . _matchesCount . classList . toggle ( 'no-results' , ! count || count . resultCount === 0 ) ;
313
+ this . _findInput ?. domNode . insertAdjacentElement ( 'afterend' , this . _matchesCount ) ;
314
+ this . _foundMatch = ! ! count && count . resultCount > 0 ;
315
+ }
284
316
}
285
317
286
318
// theming
@@ -299,4 +331,9 @@ registerThemingParticipant((theme, collector) => {
299
331
if ( widgetShadowColor ) {
300
332
collector . addRule ( `.monaco-workbench .simple-find-part { box-shadow: 0 0 8px 2px ${ widgetShadowColor } ; }` ) ;
301
333
}
334
+
335
+ const error = theme . getColor ( errorForeground ) ;
336
+ if ( error ) {
337
+ collector . addRule ( `.no-results.matchesCount { color: ${ error } ; }` ) ;
338
+ }
302
339
} ) ;
0 commit comments