diff --git a/public/index.html b/public/index.html index f2e9dec84..489d8706a 100644 --- a/public/index.html +++ b/public/index.html @@ -4120,6 +4120,12 @@

AutoComplete Settings

+
diff --git a/public/scripts/autocomplete/AutoComplete.js b/public/scripts/autocomplete/AutoComplete.js index d1205f6e8..b7ccd6ee1 100644 --- a/public/scripts/autocomplete/AutoComplete.js +++ b/public/scripts/autocomplete/AutoComplete.js @@ -25,6 +25,8 @@ export class AutoComplete { /**@type {boolean}*/ isReplaceable = false; /**@type {boolean}*/ isShowingDetails = false; /**@type {boolean}*/ wasForced = false; + /**@type {boolean}*/ isForceHidden = false; + /**@type {boolean}*/ canBeAutoHidden = false; /**@type {string}*/ text; /**@type {AutoCompleteNameResult}*/ parserResult; @@ -57,6 +59,10 @@ export class AutoComplete { return power_user.stscript.matching ?? 'fuzzy'; } + get autoHide() { + return power_user.stscript.autocomplete.autoHide ?? false; + } + @@ -224,6 +230,16 @@ export class AutoComplete { return a.name.localeCompare(b.name); } + basicAutoHideCheck() { + // auto hide only if at least one char has been typed after the name + space + return this.textarea.selectionStart > this.parserResult.start + + this.parserResult.name.length + + (this.startQuote ? 1 : 0) + + (this.endQuote ? 1 : 0) + + 1 + ; + } + /** * Show the autocomplete. * @param {boolean} isInput Whether triggered by input. @@ -244,6 +260,9 @@ export class AutoComplete { return this.hide(); } + // disable force-hide if trigger was forced + if (isForced) this.isForceHidden = false; + // request provider to get name result (potentially "incomplete", i.e. not an actual existing name) for // cursor position this.parserResult = await this.getNameAt(this.text, this.textarea.selectionStart); @@ -275,12 +294,16 @@ export class AutoComplete { this.name = this.name.slice(0, this.textarea.selectionStart - (this.parserResult.start) - (this.startQuote ? 1 : 0)); this.parserResult.name = this.name; this.isReplaceable = true; + this.isForceHidden = false; + this.canBeAutoHidden = false; } else { this.isReplaceable = false; + this.canBeAutoHidden = this.basicAutoHideCheck(); } } else { // if not forced and no user input -> just show details this.isReplaceable = false; + this.canBeAutoHidden = this.basicAutoHideCheck(); } if (isForced || isInput || isSelect) { @@ -292,8 +315,11 @@ export class AutoComplete { this.secondaryParserResult = result; this.name = this.secondaryParserResult.name; this.isReplaceable = isForced || this.secondaryParserResult.isRequired; + this.isForceHidden = false; + this.canBeAutoHidden = false; } else { this.isReplaceable = false; + this.canBeAutoHidden = this.basicAutoHideCheck(); } } } @@ -314,7 +340,17 @@ export class AutoComplete { // filter the list of options by the partial name according to the matching type .filter(it => this.isReplaceable || it.name == '' ? matchers[this.matchType](it.name) : it.name.toLowerCase() == this.name) // remove aliases - .filter((it,idx,list) => list.findIndex(opt=>opt.value == it.value) == idx) + .filter((it,idx,list) => list.findIndex(opt=>opt.value == it.value) == idx); + + if (this.result.length == 0 && this.effectiveParserResult != this.parserResult && isForced) { + // no matching secondary results and forced trigger -> show current command details + this.secondaryParserResult = null; + this.result = [this.effectiveParserResult.optionList.find(it=>it.name == this.effectiveParserResult.name)]; + this.name = this.effectiveParserResult.name; + this.fuzzyRegex = /(.*)(.*)(.*)/; + } + + this.result // update remaining options .map(option => { // build element @@ -336,6 +372,15 @@ export class AutoComplete { ; + + if (this.isForceHidden) { + // hidden with escape + return this.hide(); + } + if (this.autoHide && this.canBeAutoHidden && !isForced && this.effectiveParserResult == this.parserResult && this.result.length == 1) { + // auto hide user setting enabled and somewhere after name part and would usually show command details + return this.hide(); + } if (this.result.length == 0) { if (!isInput) { // no result and no input? hide autocomplete @@ -683,6 +728,8 @@ export class AutoComplete { if (evt.ctrlKey || evt.altKey || evt.shiftKey) return; evt.preventDefault(); evt.stopPropagation(); + this.isForceHidden = true; + this.wasForced = false; this.hide(); return; } diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index d05160915..93186df20 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -259,6 +259,7 @@ let power_user = { stscript: { matching: 'fuzzy', autocomplete: { + autoHide: false, style: 'theme', font: { scale: 0.8, @@ -1618,6 +1619,7 @@ function loadPowerUserSettings(settings, data) { $('#token_padding').val(power_user.token_padding); $('#aux_field').val(power_user.aux_field); + $('#stscript_autocomplete_autoHide').prop('checked', power_user.stscript.autocomplete.autoHide ?? false).trigger('input'); $('#stscript_matching').val(power_user.stscript.matching ?? 'fuzzy'); $('#stscript_autocomplete_style').val(power_user.stscript.autocomplete_style ?? 'theme'); document.body.setAttribute('data-stscript-style', power_user.stscript.autocomplete_style); @@ -3646,6 +3648,11 @@ $(document).ready(() => { saveSettingsDebounced(); }); + $('#stscript_autocomplete_autoHide').on('input', function () { + power_user.stscript.autocomplete.autoHide = !!$(this).prop('checked'); + saveSettingsDebounced(); + }); + $('#stscript_matching').on('change', function () { const value = $(this).find(':selected').val(); power_user.stscript.matching = String(value); diff --git a/public/scripts/slash-commands/SlashCommandAutoCompleteNameResult.js b/public/scripts/slash-commands/SlashCommandAutoCompleteNameResult.js index c6bd0d1fb..80ed27a37 100644 --- a/public/scripts/slash-commands/SlashCommandAutoCompleteNameResult.js +++ b/public/scripts/slash-commands/SlashCommandAutoCompleteNameResult.js @@ -99,7 +99,7 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult { const result = new AutoCompleteSecondaryNameResult( value, start + name.length, - cmdArg.enumList.map(it=>new SlashCommandEnumAutoCompleteOption(it)), + cmdArg.enumList.map(it=>new SlashCommandEnumAutoCompleteOption(this.executor.command, it)), true, ); result.isRequired = true; @@ -154,7 +154,7 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult { const result = new AutoCompleteSecondaryNameResult( value, start, - cmdArg.enumList.map(it=>new SlashCommandEnumAutoCompleteOption(it)), + cmdArg.enumList.map(it=>new SlashCommandEnumAutoCompleteOption(this.executor.command, it)), false, ); const isCompleteValue = cmdArg.enumList.find(it=>it.value == value); diff --git a/public/scripts/slash-commands/SlashCommandEnumAutoCompleteOption.js b/public/scripts/slash-commands/SlashCommandEnumAutoCompleteOption.js index 9d9a5d40d..12d9c64ad 100644 --- a/public/scripts/slash-commands/SlashCommandEnumAutoCompleteOption.js +++ b/public/scripts/slash-commands/SlashCommandEnumAutoCompleteOption.js @@ -1,16 +1,20 @@ import { AutoCompleteOption } from '../autocomplete/AutoCompleteOption.js'; +import { SlashCommand } from './SlashCommand.js'; import { SlashCommandEnumValue } from './SlashCommandEnumValue.js'; export class SlashCommandEnumAutoCompleteOption extends AutoCompleteOption { + /**@type {SlashCommand}*/ cmd; /**@type {SlashCommandEnumValue}*/ enumValue; /** + * @param {SlashCommand} cmd * @param {SlashCommandEnumValue} enumValue */ - constructor(enumValue) { + constructor(cmd, enumValue) { super(enumValue.value, '◊'); + this.cmd = cmd; this.enumValue = enumValue; } @@ -25,22 +29,6 @@ export class SlashCommandEnumAutoCompleteOption extends AutoCompleteOption { renderDetails() { - const frag = document.createDocumentFragment(); - const specs = document.createElement('div'); { - specs.classList.add('specs'); - const name = document.createElement('div'); { - name.classList.add('name'); - name.classList.add('monospace'); - name.textContent = this.name; - specs.append(name); - } - frag.append(specs); - } - const help = document.createElement('span'); { - help.classList.add('help'); - help.textContent = this.enumValue.description; - frag.append(help); - } - return frag; + return this.cmd.renderHelpDetails(); } } diff --git a/public/scripts/slash-commands/SlashCommandNamedArgumentAutoCompleteOption.js b/public/scripts/slash-commands/SlashCommandNamedArgumentAutoCompleteOption.js index f6b114a25..29f4867fd 100644 --- a/public/scripts/slash-commands/SlashCommandNamedArgumentAutoCompleteOption.js +++ b/public/scripts/slash-commands/SlashCommandNamedArgumentAutoCompleteOption.js @@ -27,22 +27,6 @@ export class SlashCommandNamedArgumentAutoCompleteOption extends AutoCompleteOpt renderDetails() { - const frag = document.createDocumentFragment(); - const specs = document.createElement('div'); { - specs.classList.add('specs'); - const name = document.createElement('div'); { - name.classList.add('name'); - name.classList.add('monospace'); - name.textContent = this.name; - specs.append(name); - } - frag.append(specs); - } - const help = document.createElement('span'); { - help.classList.add('help'); - help.innerHTML = `${this.arg.isRequired ? '' : '(optional) '}${this.arg.description ?? ''}`; - frag.append(help); - } - return frag; + return this.cmd.renderHelpDetails(); } }