import type { Completion, CompletionContext, CompletionSource } from '@codemirror/autocomplete';
|
|
import type { FormulaFunction, ParameterSlot } from '#/api/x/eln/formulas';
|
|
import { StreamLanguage } from '@codemirror/language';
|
|
export type FormulaResourceKind = 'all' | 'function' | 'parameter';
|
|
const PARAMETER_SECTION = { name: '参数', rank: 1 } as const;
|
const FUNCTION_SECTION = { name: '函数', rank: 2 } as const;
|
const PARAMETER_TYPE_LABELS: Record<string, string> = {
|
BOOLEAN: '布尔值',
|
DECIMAL: '数值',
|
DECIMAL_LIST: '数值集合',
|
TEXT: '文本',
|
};
|
|
export const formulaLanguage = StreamLanguage.define({
|
startState: () => ({}),
|
token(stream) {
|
if (stream.eatSpace()) return null;
|
/* eslint-disable unicorn/prefer-regexp-test -- StringStream.match advances the CodeMirror tokenizer */
|
if (stream.match(/\$\{[^}]*\}/)) return 'variableName';
|
if (stream.match(/'(?:\\.|[^'\\\r\n])*'|"(?:\\.|[^"\\\r\n])*"/)) return 'string';
|
if (stream.match(/[A-Z_]\w*/i)) return 'variableName';
|
if (stream.match(/\d+(?:\.\d*)?|\.\d+/)) return 'number';
|
if (stream.match(/&&|\|\||[!<>=]=?|[()+\-*/^,%]/)) return 'operator';
|
/* eslint-enable unicorn/prefer-regexp-test */
|
stream.next();
|
return null;
|
},
|
});
|
|
export function createFormulaCompletions(parameters: ParameterSlot[], functions: FormulaFunction[], kind: FormulaResourceKind = 'all'): Completion[] {
|
const parameterItems: Completion[] = parameters.map((parameter) => ({
|
apply: parameter.canshuBianma,
|
detail: [parameter.canshuBianma, PARAMETER_TYPE_LABELS[parameter.shujuLeixing], parameter.danwei].filter(Boolean).join(' · '),
|
displayLabel: parameter.canshuMingcheng,
|
label: `${parameter.canshuMingcheng} ${parameter.canshuBianma}`,
|
section: PARAMETER_SECTION,
|
type: 'variable',
|
}));
|
const functionItems: Completion[] = functions.map((formula) => ({
|
apply: `${formula.code}(`,
|
detail: formula.signature,
|
displayLabel: formula.chineseName,
|
info: [formula.description, formula.example && `示例:${formula.example}`].filter(Boolean).join('\n'),
|
label: `${formula.chineseName} ${formula.code}`,
|
section: FUNCTION_SECTION,
|
type: 'function',
|
}));
|
if (kind === 'parameter') return parameterItems;
|
if (kind === 'function') return functionItems;
|
return [...parameterItems, ...functionItems];
|
}
|
|
export function formulaCompletionSource(
|
parameters: ParameterSlot[],
|
functions: FormulaFunction[],
|
kind: (() => FormulaResourceKind) | FormulaResourceKind = 'all',
|
): CompletionSource {
|
return (context: CompletionContext) => {
|
const word = context.matchBefore(/[\w\u3400-\u9FFF]+/);
|
if (!word && !context.explicit) return null;
|
const activeKind = typeof kind === 'function' ? kind() : kind;
|
return { from: word ? word.from : context.pos, options: createFormulaCompletions(parameters, functions, activeKind) };
|
};
|
}
|
|
export function insertFormulaText(value: string, from: number, to: number, text: string): string {
|
return `${value.slice(0, from)}${text}${value.slice(to)}`;
|
}
|