diff --git a/plugs/index/index.plug.yaml b/plugs/index/index.plug.yaml index fd3f4ebe..79fea678 100644 --- a/plugs/index/index.plug.yaml +++ b/plugs/index/index.plug.yaml @@ -25,11 +25,11 @@ functions: objectSourceProvider: path: api.ts:objectSourceProvider events: - - query:* + - query:* discoverSources: path: api.ts:discoverSources events: - - query_ + - query_ clearIndex: path: api.ts:clearIndex env: server @@ -40,7 +40,7 @@ functions: events: - page:saved - page:deleted - + parseIndexTextRepublish: path: "./command.ts:parseIndexTextRepublish" env: server @@ -55,9 +55,9 @@ functions: processIndexQueue: path: ./command.ts:processIndexQueue mqSubscriptions: - - queue: indexQueue - batchSize: 10 - autoAck: true + - queue: indexQueue + batchSize: 10 + autoAck: true reindexSpace: path: "./command.ts:reindexSpace" env: server @@ -70,45 +70,51 @@ functions: indexParagraphs: path: "./paragraph.ts:indexParagraphs" events: - - page:index + - page:index # Backlinks indexLinks: path: "./page_links.ts:indexLinks" events: - - page:index + - page:index attributeComplete: path: "./attributes.ts:attributeComplete" events: - - editor:complete + - editor:complete objectAttributeCompleter: path: ./attributes.ts:objectAttributeCompleter events: - - attribute:complete:* + - attribute:complete:* # Item indexing indexItem: path: "./item.ts:indexItems" events: - - page:index + - page:index # Anchors indexAnchors: path: "./anchor.ts:indexAnchors" events: - - page:index + - page:index anchorComplete: path: "./anchor.ts:anchorComplete" events: - editor:complete + # Tables + indexTables: + path: "table.ts:indexTables" + events: + - page:index + # Headers indexHeaders: path: header.ts:indexHeaders events: - - page:index + - page:index headerComplete: path: header.ts:headerComplete events: @@ -118,20 +124,19 @@ functions: indexData: path: data.ts:indexData events: - - page:index + - page:index # Script indexSpaceScript: path: script.ts:indexSpaceScript events: - - page:index - + - page:index # Hashtags indexTags: path: tags.ts:indexTags events: - - page:index + - page:index tagComplete: path: tags.ts:tagComplete events: @@ -181,7 +186,7 @@ functions: lintYAML: path: lint.ts:lintYAML events: - - editor:lint + - editor:lint # Tag file system readFileTag: @@ -205,9 +210,9 @@ functions: syscallSourceProvider: path: builtins.ts:syscallSourceProvider events: - - query:syscall + - query:syscall commandSourceProvider: path: builtins.ts:commandSourceProvider events: - - query:command + - query:command diff --git a/plugs/index/table.ts b/plugs/index/table.ts new file mode 100644 index 00000000..ff61d505 --- /dev/null +++ b/plugs/index/table.ts @@ -0,0 +1,76 @@ +import { CompleteEvent, IndexTreeEvent, ObjectValue } from "$type/types.ts"; +import { collectNodesMatching, ParseTree } from "$lib/tree.ts"; +import { indexObjects } from "./api.ts"; +import { collectNodesOfType } from "$lib/tree.ts"; + +type TableRowObject = + & ObjectValue<{ + tableref: string; + page: string; + pos: number; + }> + & Record; + +/** + * Replace any invalid characters in a string so it can serve as indexed field name in a query + * @param str the input string + * @returns All lowercase string with special chars replaced with underscore + */ +function cleanHeaderFieldName(str: string): string { + return str.replace(/[\W_]+/g, "_").toLowerCase(); +} + +/** + * Concat text properties of all child nodes + * @param nodes + * @returns + */ +function concatChildrenTexts(nodes: ParseTree[]): string { + return nodes.map((c) => c.text).join("").trim(); +} + +export async function indexTables({ name: pageName, tree }: IndexTreeEvent) { + console.log(`Indexing tables in '${pageName}'`); + const result: ObjectValue[] = []; + + collectNodesMatching(tree, (t) => !!t.type?.startsWith("Table")).forEach( + (table) => { + const rows = collectNodesOfType(table, "TableRow"); + const header = collectNodesOfType(table, "TableHeader")[0]; //Use first header. As per markdown spec there can only be exactly one + const headerLabels = collectNodesOfType(header, "TableCell").map((cell) => + concatChildrenTexts(cell.children!) + ).map(cleanHeaderFieldName); + //console.log("Header labels", headerLabels); + + for (const row of rows) { + const tags = new Set(); + collectNodesOfType(row, "Hashtag").forEach((h) => { + // Push tag to the list, removing the initial # + tags.add(h.children![0].text!.substring(1)); + }); + + const cells = collectNodesOfType(row, "TableCell"); + + const tableRow: TableRowObject = { + tableref: `${pageName}@${table.from}`, + ref: `${pageName}@${row.from}`, + tag: "table", + tags: [...tags], + page: pageName, + pos: row.from!, + }; + cells.forEach((c, i) => { + const content = concatChildrenTexts(c.children!); + const label = headerLabels[i]; + tableRow[label!] = content; + }); + result.push(tableRow); + + //console.log("Cells", cells); + } + }, + ); + + console.log("Found", result.length, "row(s)"); + await indexObjects(pageName, result); +} diff --git a/website/Objects.md b/website/Objects.md index 865fa5d4..91e88026 100644 --- a/website/Objects.md +++ b/website/Objects.md @@ -37,6 +37,22 @@ Note that you can also query this page using the `example-tag` directly: example-tag ``` +## table +Markdown table rows are indexed using the `table` tag, any additional tags can be added using [[Tags]] in any of its cells. + +| Title | Description Text | +| --- | ----- | +| This is some key | The value contains a #table-tag | +| Some Row | This is an example row in between two others | +| Another key | This time without a tag | + + +```query +table +``` + +Table headers will be normalized by converting them to lowercase and replacing all non alphanumeric characters with `_`. + ## task task