@@ -56,7 +56,8 @@ define(function (require, exports, module) {
5656 * the provider yields a null error string, input is not decorated.
5757 *
5858 * @param {!function(*, string):string } options.formatter
59- * Converts one result object to a string of HTML text. Passed the item and the current query.
59+ * Converts one result object to a string of HTML text. Passed the item and the current query. The
60+ * outermost element must be <li>. The ".highlight" class can be ignored as it is applied automatically.
6061 * @param {!function(?*, string):void } options.onCommit
6162 * Called when an item is selected by clicking or pressing Enter. Passed the item and the current
6263 * query. If the current result list is not up to date with the query text at the time Enter is
@@ -66,7 +67,6 @@ define(function (require, exports, module) {
6667 * Called when an item is highlighted via the arrow keys. Passed the item and the current query.
6768 * Always called once with the top item in the result list, each time the list is updated (because
6869 * the top item is always initially highlighted).
69- * TODO: only if query not blank - fix IN THE HANDLER by no-oping if query.length===1 !!!
7070 * @param {?number } options.maxResults
7171 * Maximum number of items from resultProvider() to display in the popup.
7272 * @param {?number } options.verticalAdjust
@@ -78,6 +78,8 @@ define(function (require, exports, module) {
7878 this . $input = $input ;
7979 this . options = options ;
8080
81+ options . maxResults = options . maxResults || 10 ;
82+
8183 this . _handleInput = this . _handleInput . bind ( this ) ;
8284 this . _handleKeyDown = this . _handleKeyDown . bind ( this ) ;
8385
@@ -90,45 +92,49 @@ define(function (require, exports, module) {
9092 /** @type {!Object } */
9193 QuickSearchField . prototype . options = null ;
9294
93- /** @type {?$.Promise } */
95+ /** @type {?$.Promise } Promise corresponding to latest resultProvider call. Any earlier promises ignored */
9496 QuickSearchField . prototype . _pending = null ;
9597
96- /** @type {boolean } */
98+ /** @type {boolean } True if Enter already pressed & just waiting for results to arrive before committing */
9799 QuickSearchField . prototype . _commitPending = false ;
98100
99- /** @type {?string } */
101+ /** @type {?string } Value of $input corresponding to the _displayedResults list */
100102 QuickSearchField . prototype . _displayedQuery = null ;
101103
102- /** @type {?Array.<*> } */
104+ /** @type {?Array.<*> } Latest resultProvider result */
103105 QuickSearchField . prototype . _displayedResults = null ;
104106
105107 /** @type {?number } */
106108 QuickSearchField . prototype . _highlightIndex = null ;
107109
108- /** @type {?jQueryObject } */
110+ /** @type {?jQueryObject } Dropdown's <ol>, while open; null while closed */
109111 QuickSearchField . prototype . _$dropdown = null ;
110112
111113 /** @type {!jQueryObject } */
112- QuickSearchField . prototype . $input = null ; // TODO: _ prefix?
114+ QuickSearchField . prototype . $input = null ;
113115
114- /**
115- */
116+
117+ /** When text field changes, update results list */
116118 QuickSearchField . prototype . _handleInput = function ( ) {
117119 this . _pending = null ; // immediately invalidate any previous Promise
118120
119121 var valueAtEvent = this . $input . val ( ) ;
120122 var self = this ;
123+ // The timeout lets us skip over a backlog of multiple keyboard events when the provider is responding
124+ // so slowly that JS execution can't keep up. All the remaining input events are serviced before the
125+ // first timeout runs; then all the queued-up timeouts run in a row. All except the last one can no-op.
121126 setTimeout ( function ( ) {
122127 if ( self . $input . val ( ) === valueAtEvent ) {
123128 self . updateResults ( ) ;
124129 }
125130 } , 0 ) ;
126131 } ;
127132
128- /**
129- */
133+ /** Handle special keys: Enter, Up/Down */
130134 QuickSearchField . prototype . _handleKeyDown = function ( event ) {
131135 if ( event . keyCode === KeyEvent . DOM_VK_RETURN ) {
136+ // Enter should always act on the latest results. If input has changed and we're still waiting for
137+ // new results, just flag the 'commit' for later
132138 if ( this . _displayedQuery === this . $input . val ( ) ) {
133139 event . preventDefault ( ) ; // prevents keyup from going to someone else after we close
134140 this . _doCommit ( ) ;
@@ -138,7 +144,7 @@ define(function (require, exports, module) {
138144 }
139145 } else if ( event . keyCode === KeyEvent . DOM_VK_DOWN ) {
140146 // Highlight changes are always done synchronously on the currently shown result list. If the list
141- // later changes, the highlight is reset
147+ // later changes, the highlight is reset to the top
142148 if ( this . _displayedResults && this . _displayedResults . length ) {
143149 if ( this . _highlightIndex === null || this . _highlightIndex === this . _displayedResults . length - 1 ) {
144150 this . _highlightIndex = 0 ;
@@ -161,19 +167,24 @@ define(function (require, exports, module) {
161167 event . preventDefault ( ) ; // treated as End key otherwise
162168 }
163169 } ;
164- QuickSearchField . prototype . _doCommit = function ( clickedIndex ) {
170+
171+ /** Call onCommit() immediately */
172+ QuickSearchField . prototype . _doCommit = function ( index ) {
165173 var item ;
166174 if ( this . _displayedResults && this . _displayedResults . length ) {
167- var committedIndex = clickedIndex !== undefined ? clickedIndex : ( this . _highlightIndex || 0 ) ;
175+ var committedIndex = index !== undefined ? index : ( this . _highlightIndex || 0 ) ;
168176 item = this . _displayedResults [ committedIndex ] ;
169177 }
170178 this . options . onCommit ( item , this . _displayedQuery ) ;
171179 } ;
180+
181+ /** Update display to reflect value of _highlightIndex, & call onHighlight() */
172182 QuickSearchField . prototype . _updateHighlight = function ( ) {
173183 var $items = this . _$dropdown . find ( "li" ) ;
174184 $items . removeClass ( "highlight" ) ;
175185 if ( this . _highlightIndex !== null ) {
176186 $items . eq ( this . _highlightIndex ) . addClass ( "highlight" ) ;
187+
177188 this . options . onHighlight ( this . _displayedResults [ this . _highlightIndex ] , this . $input . val ( ) ) ;
178189 }
179190 } ;
@@ -188,6 +199,8 @@ define(function (require, exports, module) {
188199 var query = this . $input . val ( ) ;
189200 var results = this . options . resultProvider ( query ) ;
190201 if ( results . done && results . fail ) {
202+ // Provider returned an async result - mark it as the latest Promise and if it's still latest when
203+ // it resolves, render the results then
191204 this . _pending = results ;
192205 var self = this ;
193206 this . _pending . done ( function ( realResults ) {
@@ -203,17 +216,24 @@ define(function (require, exports, module) {
203216 }
204217 } ) ;
205218 } else {
219+ // Synchronous result - render immediately
206220 this . _render ( results , query ) ;
207221 }
208222 } ;
209223
210- QuickSearchField . prototype . _$dropdown = null ;
224+
225+ /** Close dropdown result list if visible */
211226 QuickSearchField . prototype . _closeDropdown = function ( ) {
212227 if ( this . _$dropdown ) {
213228 this . _$dropdown . remove ( ) ;
214229 this . _$dropdown = null ;
215230 }
216231 } ;
232+
233+ /**
234+ * Open dropdown result list & populate with the given content
235+ * @param {!string } htmlContent
236+ */
217237 QuickSearchField . prototype . _openDropdown = function ( htmlContent ) {
218238 if ( ! this . _$dropdown ) {
219239 var self = this ;
@@ -235,18 +255,23 @@ define(function (require, exports, module) {
235255 this . _$dropdown . html ( htmlContent ) ;
236256 } ;
237257
258+ /**
259+ * Given finished provider result, format it into HTML and show in dropdown, and update "no-results" style.
260+ * If an Enter key commit was pending from earlier, process it now.
261+ * @param {!Array.<*> } results
262+ * @param {!string } query
263+ */
238264 QuickSearchField . prototype . _render = function ( results , query ) {
239265 this . _displayedQuery = query ;
240266 this . _displayedResults = results ;
241267 this . _highlightIndex = 0 ;
242268 // TODO: fixup to match prev value's item if possible?
243- // (Sublime moves arrowed highlight to stay on same item as list is filters, as long as it's
244- // still visible; then jumps back to top when not. Do we need a equals(*, *):boolean to track this?)
245269
246270 if ( results . error || results . length === 0 ) {
247271 this . _closeDropdown ( ) ;
248272 this . $input . addClass ( "no-results" ) ;
249273 } else if ( results . hasOwnProperty ( "error" ) ) {
274+ // Error present but falsy - no results to show, but don't decorate with error style
250275 this . _closeDropdown ( ) ;
251276 this . $input . removeClass ( "no-results" ) ;
252277 } else {
@@ -261,33 +286,26 @@ define(function (require, exports, module) {
261286 this . _openDropdown ( html ) ;
262287
263288 // Highlight top item and trigger highlight callback
264- // TODO: if we had equals(), could avoid running callback when topmost item is same as before
265- // (though master doesn't do this either)
266289 this . _updateHighlight ( ) ;
267290 }
268291
292+ // If Enter key was pressed earlier, handle it now that we've gotten results back
269293 if ( this . _commitPending ) {
270294 this . _commitPending = false ;
271295 this . _doCommit ( ) ;
272296 }
273297 } ;
274298
299+
275300 /**
276301 * Programmatically changes the search text and updates the results.
302+ * @param {!string } value
277303 */
278304 QuickSearchField . prototype . setText = function ( value ) {
279305 this . $input . val ( value ) ;
280306 this . updateResults ( ) ; // programmatic changes don't trigger "input" event
281307 } ;
282308
283- /**
284- * Returns the currently highlighted item. Returns null if there is no result popup open (i.e. no text has
285- * been entered yet; the provider returned zero results; or one of those was previously true and we are
286- * still waiting for the provider to return newer results).
287- */
288- QuickSearchField . prototype . getSelectedItem = function ( ) {
289- } ;
290-
291309 /**
292310 * Closes the dropdown, and discards any pending Promises.
293311 */
0 commit comments