mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
clean up autocomplete
This commit is contained in:
@ -0,0 +1,30 @@
|
||||
import { SlashCommand } from './SlashCommand.js';
|
||||
|
||||
|
||||
|
||||
/**@readonly*/
|
||||
/**@enum {Number}*/
|
||||
export const OPTION_TYPE = {
|
||||
'COMMAND': 1,
|
||||
'QUICK_REPLY': 2,
|
||||
'VARIABLE_NAME': 3,
|
||||
};
|
||||
|
||||
|
||||
export class SlashCommandAutoCompleteOption {
|
||||
/**@type {OPTION_TYPE}*/ type;
|
||||
/**@type {string|SlashCommand}*/ value;
|
||||
/**@type {string}*/ name;
|
||||
|
||||
|
||||
/**
|
||||
* @param {OPTION_TYPE} type
|
||||
* @param {string|SlashCommand} value
|
||||
* @param {string} name
|
||||
*/
|
||||
constructor(type, value, name) {
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
import { SlashCommand } from './SlashCommand.js';
|
||||
import { OPTION_TYPE, SlashCommandAutoCompleteOption } from './SlashCommandAutoCompleteOption.js';
|
||||
import { SlashCommandClosure } from './SlashCommandClosure.js';
|
||||
import { SlashCommandClosureExecutor } from './SlashCommandClosureExecutor.js';
|
||||
import { SlashCommandExecutor } from './SlashCommandExecutor.js';
|
||||
import { SlashCommandParserError } from './SlashCommandParserError.js';
|
||||
import { NAME_RESULT_TYPE, SlashCommandParserNameResult } from './SlashCommandParserNameResult.js';
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import { SlashCommandScope } from './SlashCommandScope.js';
|
||||
|
||||
@ -17,9 +18,12 @@ export class SlashCommandParser {
|
||||
/**@type {Number}*/ index;
|
||||
/**@type {SlashCommandScope}*/ scope;
|
||||
|
||||
/**@type {{start:number, end:number}[]}*/ closureIndex;
|
||||
/**@type {SlashCommandExecutor[]}*/ commandIndex;
|
||||
/**@type {SlashCommandScope[]}*/ scopeIndex;
|
||||
|
||||
get userIndex() { return this.index - 2; }
|
||||
|
||||
get ahead() {
|
||||
return this.text.slice(this.index + 1);
|
||||
}
|
||||
@ -170,11 +174,10 @@ export class SlashCommandParser {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} text
|
||||
* @param {*} index
|
||||
* @returns {SlashCommandExecutor|String[]}
|
||||
* @param {*} text The text to parse.
|
||||
* @param {*} index Index to check for names (cursor position).
|
||||
*/
|
||||
getCommandAt(text, index) {
|
||||
getNameAt(text, index) {
|
||||
try {
|
||||
this.parse(text, false);
|
||||
} catch (e) {
|
||||
@ -187,9 +190,32 @@ export class SlashCommandParser {
|
||||
.slice(-1)[0]
|
||||
?? null
|
||||
;
|
||||
const scope = this.scopeIndex[this.commandIndex.indexOf(executor)];
|
||||
if (executor && executor.name == ':') return [executor, scope?.allVariableNames];
|
||||
return executor;
|
||||
|
||||
if (executor) {
|
||||
const childClosure = this.closureIndex
|
||||
.find(it=>it.start <= index && (it.end >= index || it.end == null) && it.start > executor.start)
|
||||
?? null
|
||||
;
|
||||
if (childClosure !== null) return null;
|
||||
if (executor.name == ':') {
|
||||
return new SlashCommandParserNameResult(
|
||||
NAME_RESULT_TYPE.CLOSURE,
|
||||
executor.value.toString(),
|
||||
executor.start,
|
||||
this.scopeIndex[this.commandIndex.indexOf(executor)]
|
||||
?.allVariableNames
|
||||
?.map(it=>new SlashCommandAutoCompleteOption(OPTION_TYPE.VARIABLE_NAME, it, it))
|
||||
?? []
|
||||
,
|
||||
);
|
||||
}
|
||||
return new SlashCommandParserNameResult(
|
||||
NAME_RESULT_TYPE.COMMAND,
|
||||
executor.name,
|
||||
executor.start,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -260,6 +286,7 @@ export class SlashCommandParser {
|
||||
this.keptText = '';
|
||||
this.index = 0;
|
||||
this.scope = null;
|
||||
this.closureIndex = [];
|
||||
this.commandIndex = [];
|
||||
this.scopeIndex = [];
|
||||
const closure = this.parseClosure();
|
||||
@ -271,10 +298,12 @@ export class SlashCommandParser {
|
||||
return this.testSymbol('{:');
|
||||
}
|
||||
testClosureEnd() {
|
||||
if (this.ahead.length < 1) throw new SlashCommandParserError(`Unclosed closure at position ${this.index - 2}`, this.text, this.index);
|
||||
if (this.ahead.length < 1) throw new SlashCommandParserError(`Unclosed closure at position ${this.userIndex}`, this.text, this.index);
|
||||
return this.testSymbol(':}');
|
||||
}
|
||||
parseClosure() {
|
||||
const closureIndexEntry = { start:this.index + 1, end:null };
|
||||
this.closureIndex.push(closureIndexEntry);
|
||||
let injectPipe = true;
|
||||
this.take(2); // discard opening {:
|
||||
let closure = new SlashCommandClosure(this.scope);
|
||||
@ -316,6 +345,7 @@ export class SlashCommandParser {
|
||||
this.take(2); // discard ()
|
||||
closure.executeNow = true;
|
||||
}
|
||||
closureIndexEntry.end = this.index - 1;
|
||||
this.discardWhitespace(); // discard trailing whitespace
|
||||
this.scope = closure.scope.parent;
|
||||
return closure;
|
||||
@ -352,12 +382,12 @@ export class SlashCommandParser {
|
||||
return cmd;
|
||||
} else {
|
||||
console.warn(this.behind, this.char, this.ahead);
|
||||
throw new SlashCommandParserError(`Unexpected end of command at position ${this.index - 2}: "/${cmd.name}"`, this.text, this.index);
|
||||
throw new SlashCommandParserError(`Unexpected end of command at position ${this.userIndex}: "/${cmd.name}"`, this.text, this.index);
|
||||
}
|
||||
}
|
||||
|
||||
testCommand() {
|
||||
return this.testSymbol('/') && !this.testSymbol('//') && !this.testSymbol('/#') && !this.testSymbol(':}', 1);
|
||||
return this.testSymbol('/') && !this.testSymbol('//') && !this.testSymbol('/#');
|
||||
}
|
||||
testCommandEnd() {
|
||||
return this.testClosureEnd() || this.testSymbol('|');
|
||||
@ -396,7 +426,7 @@ export class SlashCommandParser {
|
||||
return cmd;
|
||||
} else {
|
||||
console.warn(this.behind, this.char, this.ahead);
|
||||
throw new SlashCommandParserError(`Unexpected end of command at position ${this.index - 2}: "/${cmd.name}"`, this.text, this.index);
|
||||
throw new SlashCommandParserError(`Unexpected end of command at position ${this.userIndex}: "/${cmd.name}"`, this.text, this.index);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,32 @@
|
||||
import { SlashCommandAutoCompleteOption } from './SlashCommandAutoCompleteOption.js';
|
||||
|
||||
|
||||
/**@readonly*/
|
||||
/**@enum {number}*/
|
||||
export const NAME_RESULT_TYPE = {
|
||||
'COMMAND': 1,
|
||||
'CLOSURE': 2,
|
||||
};
|
||||
|
||||
|
||||
|
||||
export class SlashCommandParserNameResult {
|
||||
/**@type {NAME_RESULT_TYPE} */ type;
|
||||
/**@type {string} */ name;
|
||||
/**@type {number} */ start;
|
||||
/**@type {SlashCommandAutoCompleteOption[]} */ optionList = [];
|
||||
|
||||
|
||||
/**
|
||||
* @param {NAME_RESULT_TYPE} type Type of the name at the requested index.
|
||||
* @param {string} name Name (potentially partial) of the name at the requested index.
|
||||
* @param {number} start Index where the name starts.
|
||||
* @param {SlashCommandAutoCompleteOption[]} optionList A list of autocomplete options found in the current scope.
|
||||
*/
|
||||
constructor(type, name, start, optionList = []) {
|
||||
this.type = type;
|
||||
this.name = name,
|
||||
this.start = start;
|
||||
this.optionList = optionList;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user