Auto-complete clicked suggestions

This commit is contained in:
Kegan Dougal 2015-12-22 11:14:36 +00:00
parent 0dbb8d5294
commit e541ddb060
5 changed files with 44 additions and 15 deletions

View file

@ -29,6 +29,7 @@ class TabComplete {
opts.wordSuffix = opts.wordSuffix || "";
opts.allowLooping = opts.allowLooping || false;
opts.autoEnterTabComplete = opts.autoEnterTabComplete || false;
opts.onClickCompletes = opts.onClickCompletes || false;
this.opts = opts;
this.completing = false;
this.list = []; // full set of tab-completable things
@ -45,6 +46,14 @@ class TabComplete {
*/
setCompletionList(completeList) {
this.list = completeList;
if (this.opts.onClickCompletes) {
// assign onClick listeners for each entry to complete the text
this.list.forEach((l) => {
l.onClick = () => {
this.completeTo(l.getText());
}
});
}
}
/**
@ -73,6 +82,17 @@ class TabComplete {
this._calculateCompletions();
}
/**
* Do an auto-complete with the given word. This terminates the tab-complete.
* @param {string} someVal
*/
completeTo(someVal) {
this.textArea.value = this._replaceWith(someVal, true);
this.stopTabCompleting();
// keep focus on the text area
this.textArea.focus();
}
/**
* @param {Number} numAheadToPeek Return *up to* this many elements.
* @return {Entry[]}
@ -184,15 +204,10 @@ class TabComplete {
}
var looped = this.currentIndex === 0; // catch forward and backward looping
var suffix = "";
if (this.currentIndex !== 0) { // don't suffix the original text!
suffix = this.isFirstWord ? this.opts.startingWordSuffix : this.opts.wordSuffix;
}
// set textarea to this new value
this.textArea.value = this._replaceWith(
this.matchedList[this.currentIndex].text + suffix
this.matchedList[this.currentIndex].text,
this.currentIndex !== 0 // don't suffix the original text!
);
// visual display to the user that we looped - TODO: This should be configurable
@ -211,8 +226,15 @@ class TabComplete {
}
}
_replaceWith(newVal) {
return this.originalText.replace(MATCH_REGEX, newVal);
_replaceWith(newVal, includeSuffix) {
var replacementText = (
newVal + (
includeSuffix ?
(this.isFirstWord ? this.opts.startingWordSuffix : this.opts.wordSuffix) :
""
)
);
return this.originalText.replace(MATCH_REGEX, replacementText);
}
_calculateCompletions() {

View file

@ -41,6 +41,13 @@ class Entry {
getKey() {
return null;
}
/**
* Called when this entry is clicked.
*/
onClick() {
// NOP
}
}
class MemberEntry extends Entry {

View file

@ -93,6 +93,7 @@ module.exports = React.createClass({
wordSuffix: " ",
allowLooping: false,
autoEnterTabComplete: true,
onClickCompletes: true,
onStateChange: (isCompleting) => {
this.forceUpdate();
}

View file

@ -201,8 +201,8 @@ module.exports = React.createClass({
this.onEnter(ev);
}
else if (ev.keyCode === KeyCode.TAB) {
if (this.props.tabComplete && this.props.room) {
var memberList = [];
if (this.props.room) {
// TODO: We should cache this list and only update it when the
// member list changes. It's also horrendous that this is done here.
memberList = this.props.room.getJoinedMembers().sort(function(a, b) {
@ -231,8 +231,6 @@ module.exports = React.createClass({
}).map(function(m) {
return new MemberEntry(m);
});
}
if (this.props.tabComplete) {
this.props.tabComplete.setCompletionList(memberList);
}
}

View file

@ -31,10 +31,11 @@ module.exports = React.createClass({
<div className="mx_TabCompleteBar">
{this.props.entries.map(function(entry, i) {
return (
<div key={entry.getKey() || i + ""} className="mx_TabCompleteBar_item">
<div key={entry.getKey() || i + ""} className="mx_TabCompleteBar_item"
onClick={entry.onClick.bind(entry)} >
{entry.getImageJsx()}
<span className="mx_TabCompleteBar_text">
{entry.text}
{entry.getText()}
</span>
</div>
);