Page MenuHomePhabricator

D15039.id36320.diff
No OneTemporary

D15039.id36320.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -8,7 +8,7 @@
return array(
'names' => array(
'core.pkg.css' => '3a97c8b9',
- 'core.pkg.js' => '5813273d',
+ 'core.pkg.js' => '573e6664',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '2de124c9',
'differential.pkg.js' => 'f83532f8',
@@ -249,9 +249,9 @@
'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => '70baed2f',
'rsrc/externals/javelin/lib/control/typeahead/normalizer/TypeaheadNormalizer.js' => 'e6e25838',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '503e17fd',
- 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '8b3fd187',
+ 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '013ffff9',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0',
- 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => '2818f5ce',
+ 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => '1bc11c4a',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '6c0e62fa',
'rsrc/externals/raphael/g.raphael.js' => '40dde778',
'rsrc/externals/raphael/g.raphael.line.js' => '40da039e',
@@ -507,7 +507,7 @@
'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836',
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262',
- 'rsrc/js/phuix/PHUIXAutocomplete.js' => '5582787f',
+ 'rsrc/js/phuix/PHUIXAutocomplete.js' => '0abdd4a8',
'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca',
'rsrc/js/phuix/PHUIXFormControl.js' => '8fba1997',
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
@@ -709,9 +709,9 @@
'javelin-typeahead' => '70baed2f',
'javelin-typeahead-composite-source' => '503e17fd',
'javelin-typeahead-normalizer' => 'e6e25838',
- 'javelin-typeahead-ondemand-source' => '8b3fd187',
+ 'javelin-typeahead-ondemand-source' => '013ffff9',
'javelin-typeahead-preloaded-source' => '54f314a0',
- 'javelin-typeahead-source' => '2818f5ce',
+ 'javelin-typeahead-source' => '1bc11c4a',
'javelin-typeahead-static-source' => '6c0e62fa',
'javelin-uri' => 'c989ade3',
'javelin-util' => '93cc50d6',
@@ -836,7 +836,7 @@
'phui-workpanel-view-css' => 'adec7699',
'phuix-action-list-view' => 'b5c256b8',
'phuix-action-view' => '8cf6d262',
- 'phuix-autocomplete' => '5582787f',
+ 'phuix-autocomplete' => '0abdd4a8',
'phuix-dropdown-menu' => 'bd4c8dca',
'phuix-form-control-view' => '8fba1997',
'phuix-icon-view' => 'bff6884b',
@@ -863,6 +863,12 @@
'unhandled-exception-css' => '4c96257a',
),
'requires' => array(
+ '013ffff9' => array(
+ 'javelin-install',
+ 'javelin-util',
+ 'javelin-request',
+ 'javelin-typeahead-source',
+ ),
'01774ab2' => array(
'javelin-dom',
'javelin-util',
@@ -911,6 +917,12 @@
'javelin-dom',
'javelin-router',
),
+ '0abdd4a8' => array(
+ 'javelin-install',
+ 'javelin-dom',
+ 'phuix-icon-view',
+ 'phabricator-prefab',
+ ),
'0b7a4f6e' => array(
'javelin-behavior',
'javelin-typeahead-ondemand-source',
@@ -950,6 +962,12 @@
'javelin-util',
'phabricator-keyboard-shortcut-manager',
),
+ '1bc11c4a' => array(
+ 'javelin-install',
+ 'javelin-util',
+ 'javelin-dom',
+ 'javelin-typeahead-normalizer',
+ ),
'1d298e3a' => array(
'javelin-install',
'javelin-util',
@@ -1009,12 +1027,6 @@
'phabricator-drag-and-drop-file-upload',
'phabricator-draggable-list',
),
- '2818f5ce' => array(
- 'javelin-install',
- 'javelin-util',
- 'javelin-dom',
- 'javelin-typeahead-normalizer',
- ),
'2926fff2' => array(
'javelin-behavior',
'javelin-dom',
@@ -1198,12 +1210,6 @@
'javelin-request',
'javelin-typeahead-source',
),
- '5582787f' => array(
- 'javelin-install',
- 'javelin-dom',
- 'phuix-icon-view',
- 'phabricator-prefab',
- ),
'558829c2' => array(
'javelin-stratcom',
'javelin-behavior',
@@ -1489,12 +1495,6 @@
'javelin-stratcom',
'javelin-vector',
),
- '8b3fd187' => array(
- 'javelin-install',
- 'javelin-util',
- 'javelin-request',
- 'javelin-typeahead-source',
- ),
'8bdb2835' => array(
'phui-fontkit-css',
),
diff --git a/webroot/rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js b/webroot/rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js
--- a/webroot/rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js
+++ b/webroot/rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js
@@ -38,7 +38,7 @@
lastChange : null,
haveData : null,
- didChange : function(raw_value) {
+ didChange : function(raw_value, force) {
this.lastChange = JX.now();
var value = this.normalize(raw_value);
@@ -59,10 +59,19 @@
}
this.waitForResults();
- setTimeout(
- JX.bind(this, this.sendRequest, this.lastChange, value, raw_value),
- this.getQueryDelay()
- );
+
+ var send_request = JX.bind(
+ this,
+ this.sendRequest,
+ this.lastChange,
+ value,
+ raw_value);
+
+ if (force) {
+ send_request();
+ } else {
+ setTimeout(send_request, this.getQueryDelay());
+ }
}
},
diff --git a/webroot/rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js b/webroot/rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js
--- a/webroot/rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js
+++ b/webroot/rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js
@@ -289,7 +289,7 @@
this.filterAndSortHits(value, hits);
var nodes = this.renderNodes(value, hits);
- this.invoke('resultsready', nodes, value);
+ this.invoke('resultsready', nodes, value, partial);
if (!partial) {
this.invoke('complete');
}
diff --git a/webroot/rsrc/js/phuix/PHUIXAutocomplete.js b/webroot/rsrc/js/phuix/PHUIXAutocomplete.js
--- a/webroot/rsrc/js/phuix/PHUIXAutocomplete.js
+++ b/webroot/rsrc/js/phuix/PHUIXAutocomplete.js
@@ -12,6 +12,7 @@
this._map = {};
this._datasources = {};
this._listNodes = [];
+ this._resultMap = {};
},
members: {
@@ -35,6 +36,7 @@
_x: null,
_y: null,
_visible: false,
+ _resultMap: null,
setArea: function(area) {
this._area = area;
@@ -205,7 +207,30 @@
}
},
- _onresults: function(code, nodes, value) {
+ _onresults: function(code, nodes, value, partial) {
+ // Even if these results are out of date, we still want to fill in the
+ // result map so we can terminate things later.
+ if (!partial) {
+ if (!this._resultMap[code]) {
+ this._resultMap[code] = {};
+ }
+
+ var hits = [];
+ for (var ii = 0; ii < nodes.length; ii++) {
+ var result = this._datasources[code].getResult(nodes[ii].rel);
+ if (!result) {
+ hits = null;
+ break;
+ }
+
+ hits.push(result.autocomplete);
+ }
+
+ if (hits !== null) {
+ this._resultMap[code][value] = hits;
+ }
+ }
+
if (code !== this._active) {
return;
}
@@ -214,6 +239,13 @@
return;
}
+ if (this._isTerminatedString(value)) {
+ if (this._hasUnrefinableResults(value)) {
+ this._deactivate();
+ return;
+ }
+ }
+
var list = this._getListNode();
JX.DOM.setContent(list, nodes);
@@ -291,6 +323,59 @@
return ['#', '@', ',', '!', '?'];
},
+ _getTerminators: function() {
+ return [' ', ':', ',', '.', '!', '?'];
+ },
+
+ _isTerminatedString: function(string) {
+ var terminators = this._getTerminators();
+ for (var ii = 0; ii < terminators.length; ii++) {
+ var term = terminators[ii];
+ if (string.substring(string.length - term.length) == term) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ _hasUnrefinableResults: function(query) {
+ if (!this._resultMap[this._active]) {
+ return false;
+ }
+
+ var map = this._resultMap[this._active];
+
+ for (var ii = 1; ii < query.length; ii++) {
+ var prefix = query.substring(0, ii);
+ if (map.hasOwnProperty(prefix)) {
+ var results = map[prefix];
+
+ // If any prefix of the query has no results, the full query also
+ // has no results so we can not refine them.
+ if (!results.length) {
+ return true;
+ }
+
+ // If there is exactly one match and the it is a prefix of the query,
+ // we can safely assume the user just typed out the right result
+ // from memory and doesn't need to refine it.
+ if (results.length == 1) {
+ // Strip the first character off, like a "#" or "@".
+ var result = results[0].substring(1);
+
+ if (query.length >= result.length) {
+ if (query.substring(0, result.length) === result) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ },
+
_trim: function(str) {
var suffixes = this._getSuffixes();
for (var ii = 0; ii < suffixes.length; ii++) {
@@ -416,7 +501,32 @@
}
}
- this._datasource.didChange(trim);
+ // If the input is terminated by a space or another word-terminating
+ // punctuation mark, we're going to deactivate if the results can not
+ // be refined by addding more words.
+
+ // The idea is that if you type "@alan ab", you're allowed to keep
+ // editing "ab" until you type a space, period, or other terminator,
+ // since you might not be sure how to spell someone's last name or the
+ // second word of a project.
+
+ // Once you do terminate a word, if the words you have have entered match
+ // nothing or match only one exact match, we can safely deactivate and
+ // assume you're just typing text because further words could never
+ // refine the result set.
+
+ var force;
+ if (this._isTerminatedString(text)) {
+ if (this._hasUnrefinableResults(text)) {
+ this._deactivate();
+ return;
+ }
+ force = true;
+ } else {
+ force = false;
+ }
+
+ this._datasource.didChange(trim, force);
this._x = x;
this._y = y;

File Metadata

Mime Type
text/plain
Expires
Wed, May 22, 8:24 PM (1 w, 4 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6290928
Default Alt Text
D15039.id36320.diff (10 KB)

Event Timeline