diff --git a/src/KeyBindingsDefaults.ts b/src/KeyBindingsDefaults.ts index 0e9d14ea8f..ac9ef1f8cc 100644 --- a/src/KeyBindingsDefaults.ts +++ b/src/KeyBindingsDefaults.ts @@ -161,27 +161,27 @@ const messageComposerBindings = (): KeyBinding[] => { const autocompleteBindings = (): KeyBinding[] => { return [ { - action: AutocompleteAction.ApplySelection, + action: AutocompleteAction.CompleteOrNextSelection, keyCombo: { key: Key.TAB, }, }, { - action: AutocompleteAction.ApplySelection, + action: AutocompleteAction.CompleteOrNextSelection, keyCombo: { key: Key.TAB, ctrlKey: true, }, }, { - action: AutocompleteAction.ApplySelection, + action: AutocompleteAction.CompleteOrPrevSelection, keyCombo: { key: Key.TAB, shiftKey: true, }, }, { - action: AutocompleteAction.ApplySelection, + action: AutocompleteAction.CompleteOrPrevSelection, keyCombo: { key: Key.TAB, ctrlKey: true, diff --git a/src/KeyBindingsManager.ts b/src/KeyBindingsManager.ts index 45ef97b121..d862f10c02 100644 --- a/src/KeyBindingsManager.ts +++ b/src/KeyBindingsManager.ts @@ -52,14 +52,19 @@ export enum MessageComposerAction { /** Actions for text editing autocompletion */ export enum AutocompleteAction { - /** Apply the current autocomplete selection */ - ApplySelection = 'ApplySelection', - /** Cancel autocompletion */ - Cancel = 'Cancel', + /** + * Select previous selection or, if the autocompletion window is not shown, open the window and select the first + * selection. + */ + CompleteOrPrevSelection = 'ApplySelection', + /** Select next selection or, if the autocompletion window is not shown, open it and select the first selection */ + CompleteOrNextSelection = 'CompleteOrNextSelection', /** Move to the previous autocomplete selection */ PrevSelection = 'PrevSelection', /** Move to the next autocomplete selection */ NextSelection = 'NextSelection', + /** Close the autocompletion window */ + Cancel = 'Cancel', } /** Actions for the room list sidebar */ diff --git a/src/components/views/rooms/BasicMessageComposer.tsx b/src/components/views/rooms/BasicMessageComposer.tsx index 5dabd80399..9d9e3a1ba0 100644 --- a/src/components/views/rooms/BasicMessageComposer.tsx +++ b/src/components/views/rooms/BasicMessageComposer.tsx @@ -485,16 +485,14 @@ export default class BasicMessageEditor extends React.Component if (model.autoComplete && model.autoComplete.hasCompletions()) { const autoComplete = model.autoComplete; switch (autocompleteAction) { + case AutocompleteAction.CompleteOrPrevSelection: case AutocompleteAction.PrevSelection: - autoComplete.onUpArrow(event); + autoComplete.selectPreviousSelection(); handled = true; break; + case AutocompleteAction.CompleteOrNextSelection: case AutocompleteAction.NextSelection: - autoComplete.onDownArrow(event); - handled = true; - break; - case AutocompleteAction.ApplySelection: - autoComplete.onTab(event); + autoComplete.selectNextSelection(); handled = true; break; case AutocompleteAction.Cancel: @@ -504,8 +502,10 @@ export default class BasicMessageEditor extends React.Component default: return; // don't preventDefault on anything else } - } else if (autocompleteAction === AutocompleteAction.ApplySelection) { - this.tabCompleteName(event); + } else if (autocompleteAction === AutocompleteAction.CompleteOrPrevSelection + || autocompleteAction === AutocompleteAction.CompleteOrNextSelection) { + // there is no current autocomplete window, try to open it + this.tabCompleteName(); handled = true; } else if (event.key === Key.BACKSPACE || event.key === Key.DELETE) { this.formatBarRef.current.hide(); @@ -517,7 +517,7 @@ export default class BasicMessageEditor extends React.Component } }; - private async tabCompleteName(event: React.KeyboardEvent) { + private async tabCompleteName() { try { await new Promise(resolve => this.setState({showVisualBell: false}, resolve)); const {model} = this.props; @@ -540,7 +540,7 @@ export default class BasicMessageEditor extends React.Component // Don't try to do things with the autocomplete if there is none shown if (model.autoComplete) { - await model.autoComplete.onTab(event); + await model.autoComplete.startSelection(); if (!model.autoComplete.hasSelection()) { this.setState({showVisualBell: true}); model.autoComplete.close(); diff --git a/src/editor/autocomplete.ts b/src/editor/autocomplete.ts index d8cea961d4..2f56494ea0 100644 --- a/src/editor/autocomplete.ts +++ b/src/editor/autocomplete.ts @@ -68,24 +68,24 @@ export default class AutocompleteWrapperModel { this.updateCallback({close: true}); } - public async onTab(e: KeyboardEvent) { + /** + * If there is no current autocompletion, start one and move to the first selection. + */ + public async startSelection() { const acComponent = this.getAutocompleterComponent(); - if (acComponent.countCompletions() === 0) { // Force completions to show for the text currently entered await acComponent.forceComplete(); // Select the first item by moving "down" await acComponent.moveSelection(+1); - } else { - await acComponent.moveSelection(e.shiftKey ? -1 : +1); } } - public onUpArrow(e: KeyboardEvent) { + public selectPreviousSelection() { this.getAutocompleterComponent().moveSelection(-1); } - public onDownArrow(e: KeyboardEvent) { + public selectNextSelection() { this.getAutocompleterComponent().moveSelection(+1); }