Skip to content

Commit 83d2400

Browse files
peterflynnAJDBenner
authored andcommitted
Remove smart-auto-complete from codebase. Add unit tests for QuickSearchField.
Remove logging code and old TODOs. Round out documentation. Fix bug where pressing enter in Goto Line mode did nothing if a previous search mode had been used earlier (currentPlugin was stale).
1 parent a374993 commit 83d2400

22 files changed

Lines changed: 340 additions & 4867 deletions

src/brackets.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ define(function (require, exports, module) {
4343
require("widgets/bootstrap-modal");
4444
require("widgets/bootstrap-twipsy-mod");
4545
require("thirdparty/path-utils/path-utils.min");
46-
require("thirdparty/smart-auto-complete-local/jquery.smart_autocomplete");
4746

4847
// Load CodeMirror add-ons--these attach themselves to the CodeMirror module
4948
require("thirdparty/CodeMirror2/addon/fold/xml-fold");

src/search/QuickOpen.js

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,8 @@
2626
/*unittests: QuickOpen*/
2727

2828
/*
29-
* Displays an auto suggest pop-up list of files to allow the user to quickly navigate to a file and lines
30-
* within a file.
31-
*
32-
* TODO (issue 333) - currently jquery smart auto complete is used for the pop-up list. While it mostly works
33-
* it has several issues, so it should be replace with an alternative. Issues:
34-
* - the pop-up position logic has flaws that require CSS workarounds
35-
* - the pop-up properties cannot be modified once the object is constructed
29+
* Displays a search bar where the user can quickly navigate to a different file by searching file names in
30+
* the project, and/or jump to a line number. Providers can plug in to offer additional search modes.
3631
*/
3732

3833

@@ -336,7 +331,6 @@ define(function (require, exports, module) {
336331
* Make sure ModalBar doesn't touch the scroll pos in cases where we're doing our own restoring instead.
337332
*/
338333
QuickNavigateDialog.prototype._handleBeforeClose = function (reason) {
339-
console.log("QuickOpen._handleBeforeClose()", this.isOpen, this.closePromise, reason);
340334
if (reason === ModalBar.CLOSE_ESCAPE) {
341335
// Don't let ModalBar adjust scroll pos: we're going to revert it to its original pos. (In _handleCloseBar(),
342336
// when the editor has been resized back to its original height, matching state when we saved the pos)
@@ -360,7 +354,6 @@ define(function (require, exports, module) {
360354
};
361355

362356
QuickNavigateDialog.prototype._handleCloseBar = function (event, reason, modalBarClosePromise) {
363-
console.log("QuickOpen._handleCloseBar()", this.isOpen, this.closePromise, reason, modalBarClosePromise);
364357
console.assert(!this.closePromise);
365358
this.closePromise = modalBarClosePromise;
366359
this.isOpen = false;
@@ -378,7 +371,6 @@ define(function (require, exports, module) {
378371

379372
// Restore original selection / scroll pos if closed via Escape
380373
if (reason === ModalBar.CLOSE_ESCAPE) {
381-
console.log("QO restoring pos");
382374
// We can reset the scroll position synchronously on the ModalBar "close" event (before the animation
383375
// completes) since the editor has already been resized at this point.
384376
var editor = EditorManager.getCurrentFullEditor();
@@ -507,7 +499,6 @@ define(function (require, exports, module) {
507499
this._updateDialogLabel(null, query);
508500

509501
// No matching plugin: use default file search mode
510-
currentPlugin = null;
511502
return searchFileList(query, this._filenameMatcher);
512503
};
513504

src/search/QuickSearchField.js

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)