Attribute indexing and code completion
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import { index } from "$sb/silverbullet-syscall/mod.ts";
|
||||
import type { CompleteEvent } from "$sb/app_event.ts";
|
||||
|
||||
export type AttributeContext = "page" | "item" | "task";
|
||||
|
||||
type AttributeEntry = {
|
||||
type: string;
|
||||
};
|
||||
|
||||
function determineType(v: any): string {
|
||||
const t = typeof v;
|
||||
if (t === "object") {
|
||||
if (Array.isArray(v)) {
|
||||
return "array";
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
const attributeKeyPrefix = "attr:";
|
||||
|
||||
export async function indexAttributes(
|
||||
pageName: string,
|
||||
attributes: Record<string, any>,
|
||||
context: AttributeContext,
|
||||
) {
|
||||
await index.batchSet(
|
||||
pageName,
|
||||
Object.entries(attributes).map(([k, v]) => {
|
||||
return {
|
||||
key: `${attributeKeyPrefix}${context}:${k}`,
|
||||
value: {
|
||||
type: determineType(v),
|
||||
} as AttributeEntry,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export async function attributeComplete(completeEvent: CompleteEvent) {
|
||||
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 allAttributes = await index.queryPrefix(
|
||||
`${attributeKeyPrefix}${type}:`,
|
||||
);
|
||||
return {
|
||||
from: completeEvent.pos - inlineAttributeMatch[2].length,
|
||||
options: allAttributes.map((attr) => {
|
||||
const [_prefix, _context, name] = attr.key.split(":");
|
||||
return {
|
||||
label: name,
|
||||
apply: `${name}: `,
|
||||
detail: attr.value.type,
|
||||
type: "attribute",
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
const attributeMatch = /^(\w+)$/.exec(completeEvent.linePrefix);
|
||||
if (attributeMatch) {
|
||||
if (completeEvent.parentNodes.includes("FrontMatterCode")) {
|
||||
const allAttributes = await index.queryPrefix(
|
||||
`${attributeKeyPrefix}page:`,
|
||||
);
|
||||
return {
|
||||
from: completeEvent.pos - attributeMatch[1].length,
|
||||
options: allAttributes.map((attr) => {
|
||||
const [_prefix, _context, name] = attr.key.split(":");
|
||||
return {
|
||||
label: name,
|
||||
apply: `${name}: `,
|
||||
detail: attr.value.type,
|
||||
type: "attribute",
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -89,6 +89,11 @@ functions:
|
||||
events:
|
||||
- editor:complete
|
||||
|
||||
attributeComplete:
|
||||
path: "./attributes.ts:attributeComplete"
|
||||
events:
|
||||
- editor:complete
|
||||
|
||||
# Commands
|
||||
commandComplete:
|
||||
path: "./command.ts:commandComplete"
|
||||
@@ -317,7 +322,7 @@ functions:
|
||||
extractToPageCommand:
|
||||
path: ./refactor.ts:extractToPageCommand
|
||||
command:
|
||||
name: "Extract text to new page"
|
||||
name: "Page: Extract"
|
||||
renamePageCommand:
|
||||
path: "./refactor.ts:renamePageCommand"
|
||||
command:
|
||||
@@ -328,7 +333,7 @@ functions:
|
||||
renamePrefixCommand:
|
||||
path: "./refactor.ts:renamePrefixCommand"
|
||||
command:
|
||||
name: "Refactor: Batch Rename Page Prefix"
|
||||
name: "Page: Batch Rename Prefix"
|
||||
|
||||
# Plug manager
|
||||
updatePlugsCommand:
|
||||
|
||||
@@ -5,6 +5,7 @@ import { collectNodesOfType, ParseTree, renderToText } from "$sb/lib/tree.ts";
|
||||
import { applyQuery, removeQueries } from "$sb/lib/query.ts";
|
||||
import { extractAttributes } from "$sb/lib/attribute.ts";
|
||||
import { rewritePageRefs } from "$sb/lib/resolve.ts";
|
||||
import { indexAttributes } from "./attributes.ts";
|
||||
|
||||
export type Item = {
|
||||
name: string;
|
||||
@@ -23,6 +24,8 @@ export async function indexItems({ name, tree }: IndexTreeEvent) {
|
||||
|
||||
const coll = collectNodesOfType(tree, "ListItem");
|
||||
|
||||
const allAttributes: Record<string, any> = {};
|
||||
|
||||
for (const n of coll) {
|
||||
if (!n.children) {
|
||||
continue;
|
||||
@@ -46,8 +49,10 @@ export async function indexItems({ name, tree }: IndexTreeEvent) {
|
||||
}
|
||||
// Extract attributes and remove from tree
|
||||
const extractedAttributes = await extractAttributes(child, true);
|
||||
|
||||
for (const [key, value] of Object.entries(extractedAttributes)) {
|
||||
item[key] = value;
|
||||
allAttributes[key] = value;
|
||||
}
|
||||
textNodes.push(child);
|
||||
}
|
||||
@@ -71,6 +76,7 @@ export async function indexItems({ name, tree }: IndexTreeEvent) {
|
||||
}
|
||||
// console.log("Found", items, "item(s)");
|
||||
await index.batchSet(name, items);
|
||||
await indexAttributes(name, allAttributes, "item");
|
||||
}
|
||||
|
||||
export async function queryProvider({
|
||||
|
||||
@@ -5,6 +5,7 @@ import { extractAttributes } from "$sb/lib/attribute.ts";
|
||||
import { IndexTreeEvent, QueryProviderEvent } from "$sb/app_event.ts";
|
||||
import { applyQuery } from "$sb/lib/query.ts";
|
||||
import { resolvePath } from "$sb/lib/resolve.ts";
|
||||
import { indexAttributes } from "./attributes.ts";
|
||||
|
||||
// Key space:
|
||||
// l:toPage:pos => {name: pageName, inDirective: true, asTemplate: true}
|
||||
@@ -41,6 +42,8 @@ export async function indexLinks({ name, tree }: IndexTreeEvent) {
|
||||
await index.set(name, "meta:", pageMeta);
|
||||
}
|
||||
|
||||
await indexAttributes(name, pageMeta, "page");
|
||||
|
||||
let directiveDepth = 0;
|
||||
traverseTree(tree, (n): boolean => {
|
||||
if (n.type === "DirectiveStart") {
|
||||
|
||||
Reference in New Issue
Block a user