56 lines
1.3 KiB
TypeScript
56 lines
1.3 KiB
TypeScript
import type * as esprima from 'esprima-next';
|
|
import type { Completion } from '@codemirror/autocomplete';
|
|
import type { Node } from 'estree';
|
|
import type { RangeNode } from './types';
|
|
import { sanitizeHtml } from '@/utils/htmlUtils';
|
|
|
|
export function walk<T extends RangeNode>(
|
|
node: Node | esprima.Program,
|
|
test: (node: Node) => boolean,
|
|
found: Node[] = [],
|
|
) {
|
|
// @ts-ignore
|
|
if (test(node)) found.push(node);
|
|
|
|
for (const key in node) {
|
|
if (!(key in node)) continue;
|
|
|
|
// @ts-ignore
|
|
const child = node[key];
|
|
|
|
if (child === null || typeof child !== 'object') continue;
|
|
|
|
if (Array.isArray(child)) {
|
|
child.forEach((node) => walk(node, test, found));
|
|
} else {
|
|
walk(child, test, found);
|
|
}
|
|
}
|
|
|
|
return found as T[];
|
|
}
|
|
|
|
export const escape = (str: string) =>
|
|
str
|
|
.replace('$', '\\$')
|
|
.replace('(', '\\(')
|
|
.replace(')', '\\)')
|
|
.replace('[', '\\[')
|
|
.replace(']', '\\]');
|
|
|
|
export const toVariableOption = (label: string) => ({ label, type: 'variable' });
|
|
|
|
export const addVarType = (option: Completion) => ({ ...option, type: 'variable' });
|
|
|
|
export const addInfoRenderer = (option: Completion): Completion => {
|
|
const { info } = option;
|
|
if (typeof info === 'string') {
|
|
option.info = () => {
|
|
const wrapper = document.createElement('span');
|
|
wrapper.innerHTML = sanitizeHtml(info);
|
|
return wrapper;
|
|
};
|
|
}
|
|
return option;
|
|
};
|