Change tab completion in the UI to prefer common prefix (#6759)

* add common-prefix util and use it in the list controller

* add test

* browser js for in-repo dirs

* address PR feedback
This commit is contained in:
Matthew Irish 2019-05-22 09:28:34 -05:00 committed by GitHub
parent 1a72f14201
commit 8cdba298cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 12 deletions

View file

@ -1,6 +1,7 @@
import { computed } from '@ember/object';
import Mixin from '@ember/object/mixin';
import escapeStringRegexp from 'escape-string-regexp';
import commonPrefix from 'vault/utils/common-prefix';
export default Mixin.create({
queryParams: {
@ -16,21 +17,27 @@ export default Mixin.create({
isLoading: false,
filterMatchesKey: computed('filter', 'model', 'model.[]', function() {
var filter = this.get('filter');
var content = this.get('model');
let { filter, model: content } = this;
return !!(content.length && content.findBy('id', filter));
}),
firstPartialMatch: computed('filter', 'model', 'model.[]', 'filterMatchesKey', function() {
var filter = this.get('filter');
var content = this.get('model');
var filterMatchesKey = this.get('filterMatchesKey');
var re = new RegExp('^' + escapeStringRegexp(filter));
return filterMatchesKey
? null
: content.find(function(key) {
return re.test(key.get('id'));
});
let { filter, filterMatchesKey, model: content } = this;
let re = new RegExp('^' + escapeStringRegexp(filter));
let matchSet = content.filter(key => re.test(key.id));
let match = matchSet[0];
if (filterMatchesKey || !match) {
return null;
}
let sharedPrefix = commonPrefix(content);
// if we already are filtering the prefix, then next we want
// the exact match
if (filter === sharedPrefix || matchSet.length === 1) {
return match;
}
return { id: sharedPrefix };
}),
actions: {

View file

@ -0,0 +1,24 @@
export default function(arr = [], attribute = 'id') {
if (!arr.length) {
return '';
}
// this assumes an already sorted array
// if the array is sorted, we want to compare the first and last
// item in the array - if they share a prefix, all of the items do
let firstString = arr[0][attribute];
let lastString = arr[arr.length - 1][attribute];
// the longest the shared prefix could be is the length of the match
let targetLength = firstString.length;
let prefixLength = 0;
// walk the two strings, and if they match at the current length,
// increment the prefixLength and try again
while (
prefixLength < targetLength &&
firstString.charAt(prefixLength) === lastString.charAt(prefixLength)
) {
prefixLength++;
}
// slice the prefix from the first item
return firstString.substring(0, prefixLength);
}

View file

@ -1,6 +1,6 @@
module.exports = {
env: {
node: true,
browser: false,
browser: true,
},
};

View file

@ -0,0 +1,32 @@
import commonPrefix from 'vault/utils/common-prefix';
import { module, test } from 'qunit';
module('Unit | Util | common prefix', function() {
test('it returns empty string if called with no args or an empty array', function(assert) {
let returned = commonPrefix();
assert.equal(returned, '', 'returns an empty string');
returned = commonPrefix([]);
assert.equal(returned, '', 'returns an empty string for an empty array');
});
test('it returns empty string if there are no common prefixes', function(assert) {
let secrets = ['asecret', 'secret2', 'secret3'].map(s => ({ id: s }));
let returned = commonPrefix(secrets);
assert.equal(returned, '', 'returns an empty string');
});
test('it returns the longest prefix', function(assert) {
let secrets = ['secret1', 'secret2', 'secret3'].map(s => ({ id: s }));
let returned = commonPrefix(secrets);
assert.equal(returned, 'secret', 'finds secret prefix');
let greetings = ['hello-there', 'hello-hi', 'hello-howdy'].map(s => ({ id: s }));
returned = commonPrefix(greetings);
assert.equal(returned, 'hello-', 'finds hello- prefix');
});
test('it can compare an attribute that is not "id" to calculate the longest prefix', function(assert) {
let secrets = ['secret1', 'secret2', 'secret3'].map(s => ({ name: s }));
let returned = commonPrefix(secrets, 'name');
assert.equal(returned, 'secret', 'finds secret prefix from name attribute');
});
});