mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-18 12:52:42 +01:00
Hide the autocompleter intelligently when you ignore it and keep typing
Summary: Ref T10163. When we think the user has finished typing a word (because they typed a space, period, or other similar characters) and nothing else they might type could possibly change the outcome (usually because the words they have typed already match nothing), just deactivate the autocomplete. As a special case, if the word they have typed already select exactly one result, //and// they have already typed exactly that result, assume they just typed it from memory and deactivate. Test Plan: - Typed `@dog qwer zxcv` and saw autocomplete deactivate on the space before `z` (on my local install, `@dog` is ambiguous but `@dog qwer` matches nothing). - Typed `@epriestley ` and saw autocomplete deactivate on space. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10163 Differential Revision: https://secure.phabricator.com/D15039
This commit is contained in:
parent
849b4c765a
commit
75b8d3312b
4 changed files with 152 additions and 33 deletions
|
@ -8,7 +8,7 @@
|
||||||
return array(
|
return array(
|
||||||
'names' => array(
|
'names' => array(
|
||||||
'core.pkg.css' => '3a97c8b9',
|
'core.pkg.css' => '3a97c8b9',
|
||||||
'core.pkg.js' => '5813273d',
|
'core.pkg.js' => '573e6664',
|
||||||
'darkconsole.pkg.js' => 'e7393ebb',
|
'darkconsole.pkg.js' => 'e7393ebb',
|
||||||
'differential.pkg.css' => '2de124c9',
|
'differential.pkg.css' => '2de124c9',
|
||||||
'differential.pkg.js' => 'f83532f8',
|
'differential.pkg.js' => 'f83532f8',
|
||||||
|
@ -249,9 +249,9 @@ return array(
|
||||||
'rsrc/externals/javelin/lib/control/typeahead/Typeahead.js' => '70baed2f',
|
'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/normalizer/TypeaheadNormalizer.js' => 'e6e25838',
|
||||||
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '503e17fd',
|
'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/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/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '6c0e62fa',
|
||||||
'rsrc/externals/raphael/g.raphael.js' => '40dde778',
|
'rsrc/externals/raphael/g.raphael.js' => '40dde778',
|
||||||
'rsrc/externals/raphael/g.raphael.line.js' => '40da039e',
|
'rsrc/externals/raphael/g.raphael.line.js' => '40da039e',
|
||||||
|
@ -507,7 +507,7 @@ return array(
|
||||||
'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836',
|
'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836',
|
||||||
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
||||||
'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262',
|
'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/PHUIXDropdownMenu.js' => 'bd4c8dca',
|
||||||
'rsrc/js/phuix/PHUIXFormControl.js' => '8fba1997',
|
'rsrc/js/phuix/PHUIXFormControl.js' => '8fba1997',
|
||||||
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
||||||
|
@ -709,9 +709,9 @@ return array(
|
||||||
'javelin-typeahead' => '70baed2f',
|
'javelin-typeahead' => '70baed2f',
|
||||||
'javelin-typeahead-composite-source' => '503e17fd',
|
'javelin-typeahead-composite-source' => '503e17fd',
|
||||||
'javelin-typeahead-normalizer' => 'e6e25838',
|
'javelin-typeahead-normalizer' => 'e6e25838',
|
||||||
'javelin-typeahead-ondemand-source' => '8b3fd187',
|
'javelin-typeahead-ondemand-source' => '013ffff9',
|
||||||
'javelin-typeahead-preloaded-source' => '54f314a0',
|
'javelin-typeahead-preloaded-source' => '54f314a0',
|
||||||
'javelin-typeahead-source' => '2818f5ce',
|
'javelin-typeahead-source' => '1bc11c4a',
|
||||||
'javelin-typeahead-static-source' => '6c0e62fa',
|
'javelin-typeahead-static-source' => '6c0e62fa',
|
||||||
'javelin-uri' => 'c989ade3',
|
'javelin-uri' => 'c989ade3',
|
||||||
'javelin-util' => '93cc50d6',
|
'javelin-util' => '93cc50d6',
|
||||||
|
@ -836,7 +836,7 @@ return array(
|
||||||
'phui-workpanel-view-css' => 'adec7699',
|
'phui-workpanel-view-css' => 'adec7699',
|
||||||
'phuix-action-list-view' => 'b5c256b8',
|
'phuix-action-list-view' => 'b5c256b8',
|
||||||
'phuix-action-view' => '8cf6d262',
|
'phuix-action-view' => '8cf6d262',
|
||||||
'phuix-autocomplete' => '5582787f',
|
'phuix-autocomplete' => '0abdd4a8',
|
||||||
'phuix-dropdown-menu' => 'bd4c8dca',
|
'phuix-dropdown-menu' => 'bd4c8dca',
|
||||||
'phuix-form-control-view' => '8fba1997',
|
'phuix-form-control-view' => '8fba1997',
|
||||||
'phuix-icon-view' => 'bff6884b',
|
'phuix-icon-view' => 'bff6884b',
|
||||||
|
@ -863,6 +863,12 @@ return array(
|
||||||
'unhandled-exception-css' => '4c96257a',
|
'unhandled-exception-css' => '4c96257a',
|
||||||
),
|
),
|
||||||
'requires' => array(
|
'requires' => array(
|
||||||
|
'013ffff9' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-request',
|
||||||
|
'javelin-typeahead-source',
|
||||||
|
),
|
||||||
'01774ab2' => array(
|
'01774ab2' => array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
|
@ -911,6 +917,12 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-router',
|
'javelin-router',
|
||||||
),
|
),
|
||||||
|
'0abdd4a8' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-dom',
|
||||||
|
'phuix-icon-view',
|
||||||
|
'phabricator-prefab',
|
||||||
|
),
|
||||||
'0b7a4f6e' => array(
|
'0b7a4f6e' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-typeahead-ondemand-source',
|
'javelin-typeahead-ondemand-source',
|
||||||
|
@ -950,6 +962,12 @@ return array(
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
'phabricator-keyboard-shortcut-manager',
|
'phabricator-keyboard-shortcut-manager',
|
||||||
),
|
),
|
||||||
|
'1bc11c4a' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-typeahead-normalizer',
|
||||||
|
),
|
||||||
'1d298e3a' => array(
|
'1d298e3a' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
|
@ -1009,12 +1027,6 @@ return array(
|
||||||
'phabricator-drag-and-drop-file-upload',
|
'phabricator-drag-and-drop-file-upload',
|
||||||
'phabricator-draggable-list',
|
'phabricator-draggable-list',
|
||||||
),
|
),
|
||||||
'2818f5ce' => array(
|
|
||||||
'javelin-install',
|
|
||||||
'javelin-util',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-typeahead-normalizer',
|
|
||||||
),
|
|
||||||
'2926fff2' => array(
|
'2926fff2' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -1198,12 +1210,6 @@ return array(
|
||||||
'javelin-request',
|
'javelin-request',
|
||||||
'javelin-typeahead-source',
|
'javelin-typeahead-source',
|
||||||
),
|
),
|
||||||
'5582787f' => array(
|
|
||||||
'javelin-install',
|
|
||||||
'javelin-dom',
|
|
||||||
'phuix-icon-view',
|
|
||||||
'phabricator-prefab',
|
|
||||||
),
|
|
||||||
'558829c2' => array(
|
'558829c2' => array(
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
|
@ -1489,12 +1495,6 @@ return array(
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
'javelin-vector',
|
'javelin-vector',
|
||||||
),
|
),
|
||||||
'8b3fd187' => array(
|
|
||||||
'javelin-install',
|
|
||||||
'javelin-util',
|
|
||||||
'javelin-request',
|
|
||||||
'javelin-typeahead-source',
|
|
||||||
),
|
|
||||||
'8bdb2835' => array(
|
'8bdb2835' => array(
|
||||||
'phui-fontkit-css',
|
'phui-fontkit-css',
|
||||||
),
|
),
|
||||||
|
|
|
@ -38,7 +38,7 @@ JX.install('TypeaheadOnDemandSource', {
|
||||||
lastChange : null,
|
lastChange : null,
|
||||||
haveData : null,
|
haveData : null,
|
||||||
|
|
||||||
didChange : function(raw_value) {
|
didChange : function(raw_value, force) {
|
||||||
this.lastChange = JX.now();
|
this.lastChange = JX.now();
|
||||||
var value = this.normalize(raw_value);
|
var value = this.normalize(raw_value);
|
||||||
|
|
||||||
|
@ -59,10 +59,19 @@ JX.install('TypeaheadOnDemandSource', {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.waitForResults();
|
this.waitForResults();
|
||||||
setTimeout(
|
|
||||||
JX.bind(this, this.sendRequest, this.lastChange, value, raw_value),
|
var send_request = JX.bind(
|
||||||
this.getQueryDelay()
|
this,
|
||||||
);
|
this.sendRequest,
|
||||||
|
this.lastChange,
|
||||||
|
value,
|
||||||
|
raw_value);
|
||||||
|
|
||||||
|
if (force) {
|
||||||
|
send_request();
|
||||||
|
} else {
|
||||||
|
setTimeout(send_request, this.getQueryDelay());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -289,7 +289,7 @@ JX.install('TypeaheadSource', {
|
||||||
this.filterAndSortHits(value, hits);
|
this.filterAndSortHits(value, hits);
|
||||||
|
|
||||||
var nodes = this.renderNodes(value, hits);
|
var nodes = this.renderNodes(value, hits);
|
||||||
this.invoke('resultsready', nodes, value);
|
this.invoke('resultsready', nodes, value, partial);
|
||||||
if (!partial) {
|
if (!partial) {
|
||||||
this.invoke('complete');
|
this.invoke('complete');
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ JX.install('PHUIXAutocomplete', {
|
||||||
this._map = {};
|
this._map = {};
|
||||||
this._datasources = {};
|
this._datasources = {};
|
||||||
this._listNodes = [];
|
this._listNodes = [];
|
||||||
|
this._resultMap = {};
|
||||||
},
|
},
|
||||||
|
|
||||||
members: {
|
members: {
|
||||||
|
@ -35,6 +36,7 @@ JX.install('PHUIXAutocomplete', {
|
||||||
_x: null,
|
_x: null,
|
||||||
_y: null,
|
_y: null,
|
||||||
_visible: false,
|
_visible: false,
|
||||||
|
_resultMap: null,
|
||||||
|
|
||||||
setArea: function(area) {
|
setArea: function(area) {
|
||||||
this._area = area;
|
this._area = area;
|
||||||
|
@ -205,7 +207,30 @@ JX.install('PHUIXAutocomplete', {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_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) {
|
if (code !== this._active) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -214,6 +239,13 @@ JX.install('PHUIXAutocomplete', {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._isTerminatedString(value)) {
|
||||||
|
if (this._hasUnrefinableResults(value)) {
|
||||||
|
this._deactivate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var list = this._getListNode();
|
var list = this._getListNode();
|
||||||
JX.DOM.setContent(list, nodes);
|
JX.DOM.setContent(list, nodes);
|
||||||
|
|
||||||
|
@ -291,6 +323,59 @@ JX.install('PHUIXAutocomplete', {
|
||||||
return ['#', '@', ',', '!', '?'];
|
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) {
|
_trim: function(str) {
|
||||||
var suffixes = this._getSuffixes();
|
var suffixes = this._getSuffixes();
|
||||||
for (var ii = 0; ii < suffixes.length; ii++) {
|
for (var ii = 0; ii < suffixes.length; ii++) {
|
||||||
|
@ -416,7 +501,32 @@ JX.install('PHUIXAutocomplete', {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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._x = x;
|
||||||
this._y = y;
|
this._y = y;
|
||||||
|
|
Loading…
Reference in a new issue