@@ -84,6 +84,8 @@ define(function (require, exports, module) {
8484 this . queryInfo = null ;
8585 this . foundAny = false ;
8686 this . marked = [ ] ;
87+ this . resultSet = [ ] ;
88+ this . matchIndex = - 1 ;
8789 this . markedCurrent = null ;
8890 }
8991
@@ -139,30 +141,71 @@ define(function (require, exports, module) {
139141 state . parsedQuery = parseQuery ( queryInfo ) ;
140142 }
141143 }
142-
144+
145+ /**
146+ * @private
147+ * Show the current match index by finding matchRange in the resultSet stored
148+ * in the search state if this is the first call for a new search query. For
149+ * subsequent calls, just compare matchRange with the next match in the resultSet
150+ * based on the search direction and show the next match if they are the same.
151+ * If not, then find the match index by searching matchRange in the entire resultSet.
152+ *
153+ * @param {!SearchState } state The search state that has the array of search result
154+ * @param {!{from: {line: number, ch: number}, to: {line: number, ch: number}} } matchRange - the range of current match
155+ * @param {!boolean } searchBackwards true if searching backwards
156+ */
157+ function _updateFindBarWithMatchInfo ( state , matchRange , searchBackwards ) {
158+ // Bail if there is no result set.
159+ if ( ! state . foundAny ) {
160+ return ;
161+ }
162+
163+ if ( findBar ) {
164+ if ( state . matchIndex === - 1 ) {
165+ state . matchIndex = _ . findIndex ( state . resultSet , matchRange ) ;
166+ } else {
167+ state . matchIndex = searchBackwards ? state . matchIndex - 1 : state . matchIndex + 1 ;
168+ // Adjust matchIndex for modulo wraparound
169+ state . matchIndex = ( state . matchIndex + state . resultSet . length ) % state . resultSet . length ;
170+
171+ // Confirm that we find the right matchIndex. If not, then search
172+ // matchRange in the entire resultSet.
173+ if ( ! _ . isEqual ( state . resultSet [ state . matchIndex ] , matchRange ) ) {
174+ state . matchIndex = _ . findIndex ( state . resultSet , matchRange ) ;
175+ }
176+ }
177+
178+ if ( state . matchIndex !== - 1 ) {
179+ // Convert to 1-based by adding one before showing the index.
180+ findBar . showFindCount ( StringUtils . format ( Strings . FIND_MATCH_INDEX ,
181+ state . matchIndex + 1 , state . marked . length ) ) ;
182+ }
183+ }
184+ }
185+
143186 /**
144187 * @private
145188 * Returns the next match for the current query (from the search state) before/after the given position. Wraps around
146189 * the end of the document if no match is found before the end.
147190 *
148191 * @param {!Editor } editor The editor to search in
149- * @param {boolean } rev True to search backwards
192+ * @param {boolean } searchBackwards true to search backwards
150193 * @param {{line: number, ch: number}= } pos The position to start from. Defaults to the current primary selection's
151194 * head cursor position.
152195 * @param {boolean= } wrap Whether to wrap the search around if we hit the end of the document. Default true.
153196 * @return {?{start: {line: number, ch: number}, end: {line: number, ch: number}} } The range for the next match, or
154197 * null if there is no match.
155198 */
156- function _getNextMatch ( editor , rev , pos , wrap ) {
199+ function _getNextMatch ( editor , searchBackwards , pos , wrap ) {
157200 var cm = editor . _codeMirror ;
158201 var state = getSearchState ( cm ) ;
159- var cursor = getSearchCursor ( cm , state , pos || editor . getCursorPos ( false , rev ? "start" : "end" ) ) ;
202+ var cursor = getSearchCursor ( cm , state , pos || editor . getCursorPos ( false , searchBackwards ? "start" : "end" ) ) ;
160203
161- state . lastMatch = cursor . find ( rev ) ;
204+ state . lastMatch = cursor . find ( searchBackwards ) ;
162205 if ( ! state . lastMatch && wrap !== false ) {
163206 // If no result found before hitting edge of file, try wrapping around
164- cursor = getSearchCursor ( cm , state , rev ? { line : cm . lineCount ( ) - 1 } : { line : 0 , ch : 0 } ) ;
165- state . lastMatch = cursor . find ( rev ) ;
207+ cursor = getSearchCursor ( cm , state , searchBackwards ? { line : cm . lineCount ( ) - 1 } : { line : 0 , ch : 0 } ) ;
208+ state . lastMatch = cursor . find ( searchBackwards ) ;
166209 }
167210 if ( ! state . lastMatch ) {
168211 // No result found, period: clear selection & bail
@@ -377,24 +420,26 @@ define(function (require, exports, module) {
377420 }
378421
379422 /**
380- * Selects the next match (or prev match, if rev ==true) starting from either the current position
423+ * Selects the next match (or prev match, if searchBackwards ==true) starting from either the current position
381424 * (if pos unspecified) or the given position (if pos specified explicitly). The starting position
382425 * need not be an existing match. If a new match is found, sets to state.lastMatch either the regex
383426 * match result, or simply true for a plain-string match. If no match found, sets state.lastMatch
384427 * to false.
385428 * @param {!Editor } editor
386- * @param {?boolean } rev
429+ * @param {?boolean } searchBackwards
387430 * @param {?boolean } preferNoScroll
388431 * @param {?Pos } pos
389432 */
390- function findNext ( editor , rev , preferNoScroll , pos ) {
433+ function findNext ( editor , searchBackwards , preferNoScroll , pos ) {
391434 var cm = editor . _codeMirror ;
392435 cm . operation ( function ( ) {
393436 var state = getSearchState ( cm ) ;
394437 clearCurrentMatchHighlight ( cm , state ) ;
395438
396- var nextMatch = _getNextMatch ( editor , rev , pos ) ;
439+ var nextMatch = _getNextMatch ( editor , searchBackwards , pos ) ;
397440 if ( nextMatch ) {
441+ _updateFindBarWithMatchInfo ( getSearchState ( editor . _codeMirror ) ,
442+ { from : nextMatch . start , to : nextMatch . end } , searchBackwards ) ;
398443 _selectAndScrollTo ( editor , [ nextMatch ] , true , preferNoScroll ) ;
399444 state . markedCurrent = cm . markText ( nextMatch . start , nextMatch . end ,
400445 { className : "searching-current-match" , startStyle : "searching-first" , endStyle : "searching-last" } ) ;
@@ -416,6 +461,9 @@ define(function (require, exports, module) {
416461 state . markedCurrent = null ;
417462
418463 ScrollTrackMarkers . clear ( ) ;
464+
465+ state . resultSet = [ ] ;
466+ state . matchIndex = - 1 ;
419467 }
420468
421469 function clearSearch ( cm ) {
@@ -477,37 +525,36 @@ define(function (require, exports, module) {
477525 var cursor = getSearchCursor ( cm , state ) ;
478526 if ( cm . getValue ( ) . length <= FIND_MAX_FILE_SIZE ) {
479527 // FUTURE: if last query was prefix of this one, could optimize by filtering last result set
480- var resultSet = [ ] ;
528+ state . resultSet = [ ] ;
481529 while ( cursor . findNext ( ) ) {
482- resultSet . push ( cursor . pos ) ; // pos is unique obj per search result
530+ state . resultSet . push ( cursor . pos ) ; // pos is unique obj per search result
483531 }
484532
485533 // Highlight all matches if there aren't too many
486- if ( resultSet . length <= FIND_HIGHLIGHT_MAX ) {
534+ if ( state . resultSet . length <= FIND_HIGHLIGHT_MAX ) {
487535 toggleHighlighting ( editor , true ) ;
488536
489- resultSet . forEach ( function ( result ) {
537+ state . resultSet . forEach ( function ( result ) {
490538 state . marked . push ( cm . markText ( result . from , result . to ,
491539 { className : "CodeMirror-searching" , startStyle : "searching-first" , endStyle : "searching-last" } ) ) ;
492540 } ) ;
493- var scrollTrackPositions = resultSet . map ( function ( result ) {
541+ var scrollTrackPositions = state . resultSet . map ( function ( result ) {
494542 return result . from ;
495543 } ) ;
496544
497545 ScrollTrackMarkers . addTickmarks ( editor , scrollTrackPositions ) ;
498546 }
499547
500- var countInfo ;
501- if ( resultSet . length === 0 ) {
502- countInfo = Strings . FIND_NO_RESULTS ;
503- } else if ( resultSet . length === 1 ) {
504- countInfo = Strings . FIND_RESULT_COUNT_SINGLE ;
505- } else {
506- countInfo = StringUtils . format ( Strings . FIND_RESULT_COUNT , resultSet . length ) ;
548+ // Here we only update find bar with no result. In the case of a match
549+ // a findNext() call is guaranteed to be followed by this function call,
550+ // and findNext() in turn calls _updateFindBarWithMatchInfo() to show the
551+ // match index.
552+ if ( state . resultSet . length === 0 ) {
553+ findBar . showFindCount ( Strings . FIND_NO_RESULTS ) ;
507554 }
508- findBar . showFindCount ( countInfo ) ;
509- state . foundAny = ( resultSet . length > 0 ) ;
510- indicateHasMatches ( resultSet . length ) ;
555+
556+ state . foundAny = ( state . resultSet . length > 0 ) ;
557+ indicateHasMatches ( state . resultSet . length ) ;
511558
512559 } else {
513560 // On huge documents, just look for first match & then stop
@@ -593,8 +640,8 @@ define(function (require, exports, module) {
593640 . on ( "queryChange.FindReplace" , function ( e ) {
594641 handleQueryChange ( editor , state ) ;
595642 } )
596- . on ( "doFind.FindReplace" , function ( e , rev ) {
597- findNext ( editor , rev ) ;
643+ . on ( "doFind.FindReplace" , function ( e , searchBackwards ) {
644+ findNext ( editor , searchBackwards ) ;
598645 } )
599646 . on ( "close.FindReplace" , function ( e ) {
600647 // Clear highlights but leave search state in place so Find Next/Previous work after closing
@@ -612,12 +659,12 @@ define(function (require, exports, module) {
612659
613660 /**
614661 * If no search pending, opens the Find dialog. If search bar already open, moves to
615- * next/prev result (depending on 'rev ')
662+ * next/prev result (depending on 'searchBackwards ')
616663 */
617- function doSearch ( editor , rev ) {
664+ function doSearch ( editor , searchBackwards ) {
618665 var state = getSearchState ( editor . _codeMirror ) ;
619666 if ( state . parsedQuery ) {
620- findNext ( editor , rev ) ;
667+ findNext ( editor , searchBackwards ) ;
621668 return ;
622669 }
623670
0 commit comments