105 lines
3.0 KiB
TypeScript
105 lines
3.0 KiB
TypeScript
import type { EditorState, Range } from "@codemirror/state";
|
|
import { syntaxTree } from "@codemirror/language";
|
|
import { Decoration } from "@codemirror/view";
|
|
import {
|
|
decoratorStateField,
|
|
hideBlockSource,
|
|
isCursorInRange,
|
|
widgetRenderMode,
|
|
} from "./util.ts";
|
|
import type { Client } from "../client.ts";
|
|
import { renderLuaExpression } from "../space_lua/render_widget.ts";
|
|
import { LuaWidget } from "./lua_widget.ts";
|
|
import { LoadingWidget } from "./loading_widget.ts";
|
|
|
|
export function luaDirectivePlugin(client: Client) {
|
|
return decoratorStateField((state: EditorState) => {
|
|
const widgets: Range<Decoration>[] = [];
|
|
|
|
let shouldRender = true;
|
|
|
|
const renderMode = widgetRenderMode(client);
|
|
if (renderMode === "disabled") {
|
|
return Decoration.none;
|
|
}
|
|
|
|
syntaxTree(state).iterate({
|
|
enter: (node) => {
|
|
// Disable rendering of Lua directives in #meta/template pages
|
|
// Either in frontmatter
|
|
if (node.name === "FrontMatterCode") {
|
|
const text = state.sliceDoc(node.from, node.to);
|
|
try {
|
|
// Very ad-hoc regex to detect if meta/template appears in the tag list
|
|
if (/tags:.*meta\/template/s.exec(text)) {
|
|
shouldRender = false;
|
|
return;
|
|
}
|
|
} catch {
|
|
// Ignore
|
|
}
|
|
}
|
|
// Or with a hash tag
|
|
if (node.name === "Hashtag") {
|
|
const text = state.sliceDoc(node.from, node.to);
|
|
if (text.startsWith("#meta/template")) {
|
|
shouldRender = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (node.name !== "LuaDirective") {
|
|
return;
|
|
}
|
|
|
|
if (isCursorInRange(state, [node.from, node.to])) {
|
|
return;
|
|
}
|
|
|
|
const hideSource = () => {
|
|
if (client.ui.viewState.uiOptions.markdownSyntaxRendering) return;
|
|
hideBlockSource(widgets, state, node.from, node.to, "start");
|
|
};
|
|
|
|
if (renderMode === "loading") {
|
|
widgets.push(
|
|
Decoration.widget({
|
|
widget: new LoadingWidget(false),
|
|
}).range(node.from),
|
|
);
|
|
hideSource();
|
|
return;
|
|
}
|
|
|
|
const codeText = state.sliceDoc(node.from, node.to);
|
|
const expressionText = codeText.slice(2, -1);
|
|
const currentPageMeta = client.currentPageMeta();
|
|
widgets.push(
|
|
Decoration.widget({
|
|
widget: new LuaWidget({
|
|
client,
|
|
cacheKey: `lua:${expressionText}:${currentPageMeta?.name}`,
|
|
expressionText,
|
|
codeText,
|
|
// Only `${…}` directives can be baked into `<!--#lua EXPR -->`.
|
|
bakeable: true,
|
|
callback: (bodyText) =>
|
|
renderLuaExpression(client, bodyText, currentPageMeta),
|
|
renderEmpty: true,
|
|
inPage: true,
|
|
}),
|
|
}).range(node.from),
|
|
);
|
|
|
|
hideSource();
|
|
},
|
|
});
|
|
|
|
if (!shouldRender) {
|
|
return Decoration.set([]);
|
|
}
|
|
|
|
return Decoration.set(widgets, true);
|
|
});
|
|
}
|