|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { AutoCompleteOption } from './AutoCompleteOption.js'; |
|
|
import { |
|
|
formatMacroSignature, |
|
|
createSourceIndicator, |
|
|
createAliasIndicator, |
|
|
renderMacroDetails, |
|
|
} from '../macros/MacroBrowser.js'; |
|
|
import { enumIcons } from '../slash-commands/SlashCommandCommonEnumsProvider.js'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class EnhancedMacroAutoCompleteOption extends AutoCompleteOption { |
|
|
|
|
|
#macro; |
|
|
|
|
|
|
|
|
#context = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor(macro, context = null) { |
|
|
|
|
|
super(macro.name, enumIcons.macro); |
|
|
this.#macro = macro; |
|
|
this.#context = context; |
|
|
|
|
|
this.nameOffset = 2; |
|
|
} |
|
|
|
|
|
|
|
|
get macro() { |
|
|
return this.#macro; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
renderItem() { |
|
|
const li = document.createElement('li'); |
|
|
li.classList.add('item', 'macro-ac-item'); |
|
|
li.setAttribute('data-name', this.name); |
|
|
li.setAttribute('data-option-type', 'macro'); |
|
|
|
|
|
|
|
|
const type = document.createElement('span'); |
|
|
type.classList.add('type', 'monospace'); |
|
|
type.textContent = '{}'; |
|
|
li.append(type); |
|
|
|
|
|
|
|
|
const specs = document.createElement('span'); |
|
|
specs.classList.add('specs'); |
|
|
|
|
|
|
|
|
const nameEl = document.createElement('span'); |
|
|
nameEl.classList.add('name', 'monospace'); |
|
|
|
|
|
|
|
|
const sigText = formatMacroSignature(this.#macro); |
|
|
for (const char of sigText) { |
|
|
const span = document.createElement('span'); |
|
|
span.textContent = char; |
|
|
nameEl.append(span); |
|
|
} |
|
|
specs.append(nameEl); |
|
|
li.append(specs); |
|
|
|
|
|
|
|
|
const stopgap = document.createElement('span'); |
|
|
stopgap.classList.add('stopgap'); |
|
|
li.append(stopgap); |
|
|
|
|
|
|
|
|
const help = document.createElement('span'); |
|
|
help.classList.add('help'); |
|
|
const content = document.createElement('span'); |
|
|
content.classList.add('helpContent'); |
|
|
content.textContent = this.#macro.description || ''; |
|
|
help.append(content); |
|
|
li.append(help); |
|
|
|
|
|
|
|
|
const aliasIcon = createAliasIndicator(this.#macro); |
|
|
if (aliasIcon) { |
|
|
aliasIcon.classList.add('macro-ac-indicator'); |
|
|
li.append(aliasIcon); |
|
|
} |
|
|
|
|
|
|
|
|
const sourceIcon = createSourceIndicator(this.#macro); |
|
|
sourceIcon.classList.add('macro-ac-indicator'); |
|
|
li.append(sourceIcon); |
|
|
|
|
|
return li; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
renderDetails() { |
|
|
const frag = document.createDocumentFragment(); |
|
|
|
|
|
|
|
|
const currentArgIndex = this.#context?.currentArgIndex ?? -1; |
|
|
|
|
|
|
|
|
if (currentArgIndex >= 0) { |
|
|
const hint = this.#renderArgumentHint(); |
|
|
if (hint) frag.append(hint); |
|
|
} |
|
|
|
|
|
|
|
|
const details = renderMacroDetails(this.#macro, { currentArgIndex }); |
|
|
|
|
|
|
|
|
details.classList.add('macro-ac-details'); |
|
|
frag.append(details); |
|
|
|
|
|
return frag; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#renderArgumentHint() { |
|
|
if (!this.#context || this.#context.currentArgIndex < 0) return null; |
|
|
|
|
|
const argIndex = this.#context.currentArgIndex; |
|
|
const isListArg = argIndex >= this.#macro.maxArgs; |
|
|
|
|
|
|
|
|
if (isListArg && !this.#macro.list) return null; |
|
|
|
|
|
const hint = document.createElement('div'); |
|
|
hint.classList.add('macro-ac-arg-hint'); |
|
|
|
|
|
const icon = document.createElement('i'); |
|
|
icon.classList.add('fa-solid', 'fa-arrow-right'); |
|
|
hint.append(icon); |
|
|
|
|
|
if (isListArg) { |
|
|
|
|
|
const listIndex = argIndex - this.#macro.maxArgs + 1; |
|
|
const text = document.createElement('span'); |
|
|
text.innerHTML = `<strong>List item ${listIndex}</strong>`; |
|
|
hint.append(text); |
|
|
} else { |
|
|
|
|
|
const argDef = this.#macro.unnamedArgDefs[argIndex]; |
|
|
let optionalLabel = ''; |
|
|
if (argDef?.optional) { |
|
|
optionalLabel = argDef.defaultValue !== undefined |
|
|
? ` <em>(optional, default: ${argDef.defaultValue === '' ? '<empty string>' : argDef.defaultValue})</em>` |
|
|
: ' <em>(optional)</em>'; |
|
|
} |
|
|
const text = document.createElement('span'); |
|
|
text.innerHTML = `<strong>${argDef?.name || `Argument ${argIndex + 1}`}</strong>${optionalLabel}`; |
|
|
if (argDef?.type) { |
|
|
const typeSpan = document.createElement('code'); |
|
|
typeSpan.classList.add('macro-ac-hint-type'); |
|
|
if (Array.isArray(argDef.type)) { |
|
|
typeSpan.textContent = argDef.type.join(' | '); |
|
|
typeSpan.title = `Accepts: ${argDef.type.join(', ')}`; |
|
|
} else { |
|
|
typeSpan.textContent = argDef.type; |
|
|
} |
|
|
text.append(' ', typeSpan); |
|
|
} |
|
|
hint.append(text); |
|
|
|
|
|
if (argDef?.description) { |
|
|
const descSpan = document.createElement('span'); |
|
|
descSpan.classList.add('macro-ac-hint-desc'); |
|
|
descSpan.textContent = ` — ${argDef.description}`; |
|
|
hint.append(descSpan); |
|
|
} |
|
|
|
|
|
if (argDef?.sampleValue) { |
|
|
const sampleSpan = document.createElement('span'); |
|
|
sampleSpan.classList.add('macro-ac-hint-sample'); |
|
|
sampleSpan.textContent = ` (e.g. ${argDef.sampleValue})`; |
|
|
hint.append(sampleSpan); |
|
|
} |
|
|
} |
|
|
|
|
|
return hint; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function parseMacroContext(macroText, cursorOffset) { |
|
|
const parts = []; |
|
|
let currentPart = ''; |
|
|
let partStart = 0; |
|
|
let i = 0; |
|
|
|
|
|
while (i < macroText.length) { |
|
|
if (macroText[i] === ':' && macroText[i + 1] === ':') { |
|
|
parts.push({ text: currentPart, start: partStart, end: i }); |
|
|
currentPart = ''; |
|
|
i += 2; |
|
|
partStart = i; |
|
|
} else { |
|
|
currentPart += macroText[i]; |
|
|
i++; |
|
|
} |
|
|
} |
|
|
|
|
|
parts.push({ text: currentPart, start: partStart, end: macroText.length }); |
|
|
|
|
|
|
|
|
let currentArgIndex = -1; |
|
|
for (let idx = 0; idx < parts.length; idx++) { |
|
|
const part = parts[idx]; |
|
|
if (cursorOffset >= part.start && cursorOffset <= part.end) { |
|
|
currentArgIndex = idx - 1; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (currentArgIndex === -1 && cursorOffset >= parts[parts.length - 1].end) { |
|
|
currentArgIndex = parts.length - 1; |
|
|
} |
|
|
|
|
|
return { |
|
|
fullText: macroText, |
|
|
cursorOffset, |
|
|
identifier: parts[0]?.text.trim() || '', |
|
|
args: parts.slice(1).map(p => p.text), |
|
|
currentArgIndex, |
|
|
}; |
|
|
} |
|
|
|