composer: cycle through auto-completions

Closes: https://todo.sr.ht/~emersion/gamja/42
This commit is contained in:
Simon Ser 2021-06-30 22:20:40 +02:00
parent 08aefc9dc5
commit 00eebc9859
2 changed files with 58 additions and 35 deletions

View File

@ -1007,13 +1007,10 @@ export default class App extends Component {
autocomplete(prefix) { autocomplete(prefix) {
function fromList(l, prefix) { function fromList(l, prefix) {
prefix = prefix.toLowerCase(); prefix = prefix.toLowerCase();
let repl = null; let repl = [];
for (let item of l) { for (let item of l) {
if (item.toLowerCase().startsWith(prefix)) { if (item.toLowerCase().startsWith(prefix)) {
if (repl) { repl.push(item);
return null;
}
repl = item;
} }
} }
return repl; return repl;
@ -1021,15 +1018,12 @@ export default class App extends Component {
if (prefix.startsWith("/")) { if (prefix.startsWith("/")) {
let repl = fromList(Object.keys(commands), prefix.slice(1)); let repl = fromList(Object.keys(commands), prefix.slice(1));
if (repl) { return repl.map(cmd => "/" + cmd);
repl = "/" + repl;
}
return repl;
} }
let buf = this.state.buffers.get(this.state.activeBuffer); let buf = this.state.buffers.get(this.state.activeBuffer);
if (!buf || !buf.members) { if (!buf || !buf.members) {
return null; return [];
} }
return fromList(buf.members.keys(), prefix); return fromList(buf.members.keys(), prefix);
} }

View File

@ -5,6 +5,7 @@ export default class Composer extends Component {
text: "", text: "",
}; };
textInput = createRef(); textInput = createRef();
lastAutocomplete = null;
constructor(props) { constructor(props) {
super(props); super(props);
@ -42,48 +43,76 @@ export default class Composer extends Component {
event.preventDefault(); event.preventDefault();
let carretIndex = input.selectionStart; let carretPos = input.selectionStart;
let text = this.state.text; let text = this.state.text;
let wordStart; let autocomplete;
for (wordStart = carretIndex - 1; wordStart >= 0; wordStart--) { if (this.lastAutocomplete && this.lastAutocomplete.text === text && this.lastAutocomplete.carretPos === carretPos) {
if (text[wordStart] === " ") { autocomplete = this.lastAutocomplete;
break; } else {
this.lastAutocomplete = null;
let wordStart;
for (wordStart = carretPos - 1; wordStart >= 0; wordStart--) {
if (text[wordStart] === " ") {
break;
}
} }
} wordStart++;
wordStart++;
let wordEnd; let wordEnd;
for (wordEnd = carretIndex; wordEnd < text.length; wordEnd++) { for (wordEnd = carretPos; wordEnd < text.length; wordEnd++) {
if (text[wordEnd] === " ") { if (text[wordEnd] === " ") {
break; break;
}
} }
let word = text.slice(wordStart, wordEnd);
if (!word) {
return;
}
let replacements = this.props.autocomplete(word);
if (replacements.length === 0) {
return;
}
autocomplete = {
text,
carretPos: input.selectionStart,
prefix: text.slice(0, wordStart),
suffix: text.slice(wordEnd),
replacements,
replIndex: -1,
};
} }
let word = text.slice(wordStart, wordEnd); let n = autocomplete.replacements.length;
if (!word) { if (event.shiftKey) {
return; autocomplete.replIndex--;
} else {
autocomplete.replIndex++;
} }
autocomplete.replIndex = (autocomplete.replIndex + n) % n;
let repl = this.props.autocomplete(word); let repl = autocomplete.replacements[autocomplete.replIndex];
if (!repl) { if (!autocomplete.prefix && !autocomplete.suffix) {
return; if (repl.startsWith("/")) {
}
if (wordStart === 0 && wordEnd === text.length) {
if (word.startsWith("/")) {
repl += " "; repl += " ";
} else { } else {
repl += ": "; repl += ": ";
} }
} }
text = text.slice(0, wordStart) + repl + text.slice(wordEnd); autocomplete.text = autocomplete.prefix + repl + autocomplete.suffix;
autocomplete.carretPos = autocomplete.prefix.length + repl.length;
input.value = text; input.value = autocomplete.text;
input.selectionStart = wordStart + repl.length; input.selectionStart = autocomplete.carretPos;
input.selectionEnd = input.selectionStart; input.selectionEnd = input.selectionStart;
this.setState({ text }); this.lastAutocomplete = autocomplete;
this.setState({ text: autocomplete.text });
} }
handleWindowKeyDown(event) { handleWindowKeyDown(event) {