0313565610
Complete redo of data store Introduces live queries and live templates
119 lines
3.3 KiB
TypeScript
119 lines
3.3 KiB
TypeScript
import type { CompleteEvent } from "$sb/app_event.ts";
|
|
import { events } from "$sb/syscalls.ts";
|
|
import { queryObjects } from "./api.ts";
|
|
import { ObjectValue, QueryExpression } from "$sb/types.ts";
|
|
import { builtinPseudoPage } from "./builtins.ts";
|
|
|
|
export type AttributeObject = ObjectValue<{
|
|
name: string;
|
|
attributeType: string;
|
|
tag: string;
|
|
page: string;
|
|
}>;
|
|
|
|
export type AttributeCompleteEvent = {
|
|
source: string;
|
|
prefix: string;
|
|
};
|
|
|
|
export type AttributeCompletion = {
|
|
name: string;
|
|
source: string;
|
|
attributeType: string;
|
|
builtin?: boolean;
|
|
};
|
|
|
|
export function determineType(v: any): string {
|
|
const t = typeof v;
|
|
if (t === "object") {
|
|
if (Array.isArray(v)) {
|
|
return "array";
|
|
}
|
|
}
|
|
return t;
|
|
}
|
|
|
|
export async function objectAttributeCompleter(
|
|
attributeCompleteEvent: AttributeCompleteEvent,
|
|
): Promise<AttributeCompletion[]> {
|
|
const attributeFilter: QueryExpression | undefined =
|
|
attributeCompleteEvent.source === ""
|
|
? undefined
|
|
: ["=", ["attr", "tag"], ["string", attributeCompleteEvent.source]];
|
|
const allAttributes = await queryObjects<AttributeObject>("attribute", {
|
|
filter: attributeFilter,
|
|
});
|
|
return allAttributes.map((value) => {
|
|
return {
|
|
name: value.name,
|
|
source: value.tag,
|
|
attributeType: value.attributeType,
|
|
builtin: value.page === builtinPseudoPage,
|
|
} as AttributeCompletion;
|
|
});
|
|
}
|
|
|
|
export async function attributeComplete(completeEvent: CompleteEvent) {
|
|
if (/([\-\*]\s+\[)([^\]]+)$/.test(completeEvent.linePrefix)) {
|
|
// Don't match task states, which look similar
|
|
return null;
|
|
}
|
|
const inlineAttributeMatch = /([^\[\{}]|^)\[(\w+)$/.exec(
|
|
completeEvent.linePrefix,
|
|
);
|
|
if (inlineAttributeMatch) {
|
|
// console.log("Parents", completeEvent.parentNodes);
|
|
let type = "page";
|
|
if (completeEvent.parentNodes.includes("Task")) {
|
|
type = "task";
|
|
} else if (completeEvent.parentNodes.includes("ListItem")) {
|
|
type = "item";
|
|
}
|
|
const completions = (await events.dispatchEvent(
|
|
`attribute:complete:${type}`,
|
|
{
|
|
source: type,
|
|
prefix: inlineAttributeMatch[2],
|
|
} as AttributeCompleteEvent,
|
|
)).flat() as AttributeCompletion[];
|
|
return {
|
|
from: completeEvent.pos - inlineAttributeMatch[2].length,
|
|
options: attributeCompletionsToCMCompletion(
|
|
completions.filter((completion) => !completion.builtin),
|
|
),
|
|
};
|
|
}
|
|
const attributeMatch = /^(\w+)$/.exec(completeEvent.linePrefix);
|
|
if (attributeMatch) {
|
|
if (completeEvent.parentNodes.includes("FrontMatterCode")) {
|
|
const completions = (await events.dispatchEvent(
|
|
`attribute:complete:page`,
|
|
{
|
|
source: "page",
|
|
prefix: attributeMatch[1],
|
|
} as AttributeCompleteEvent,
|
|
)).flat() as AttributeCompletion[];
|
|
return {
|
|
from: completeEvent.pos - attributeMatch[1].length,
|
|
options: attributeCompletionsToCMCompletion(
|
|
completions.filter((completion) => !completion.builtin),
|
|
),
|
|
};
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function attributeCompletionsToCMCompletion(
|
|
completions: AttributeCompletion[],
|
|
) {
|
|
return completions.map(
|
|
(completion) => ({
|
|
label: completion.name,
|
|
apply: `${completion.name}: `,
|
|
detail: `${completion.attributeType} (${completion.source})`,
|
|
type: "attribute",
|
|
}),
|
|
);
|
|
}
|