diff --git a/packages/plugs/core/core.plug.yaml b/packages/plugs/core/core.plug.yaml index ff7aa3e..889510c 100644 --- a/packages/plugs/core/core.plug.yaml +++ b/packages/plugs/core/core.plug.yaml @@ -187,18 +187,35 @@ functions: path: "./template.ts:insertTemplateText" slashCommand: name: include + description: Include another page value: | - + - insertInlineTemplate: + insertInjectTemplate: path: "./template.ts:insertTemplateText" slashCommand: - name: inline-template + name: inject + description: Inject template value: | - + - + + insertInjectCleanTemplate: + path: "./template.ts:insertTemplateText" + slashCommand: + name: inject-clean + description: Inject template clean + value: | + + + + insertHRTemplate: + path: "./template.ts:insertTemplateText" + slashCommand: + name: hr + description: Insert a horizontal rule + value: "---" quickNoteCommand: path: ./template.ts:quickNoteCommand command: diff --git a/packages/plugs/core/page.ts b/packages/plugs/core/page.ts index 339a5bf..3c280a4 100644 --- a/packages/plugs/core/page.ts +++ b/packages/plugs/core/page.ts @@ -48,6 +48,7 @@ export async function indexLinks({ name, tree }: IndexTreeEvent) { console.log("Now indexing", name); let pageMeta = extractMeta(tree); if (Object.keys(pageMeta).length > 0) { + console.log("Extracted page meta data", pageMeta); await set(name, "meta:", pageMeta); } @@ -88,14 +89,12 @@ export async function linkQueryProvider({ query, pageName, }: QueryProviderEvent): Promise { - let uniqueLinks = new Set(); - for (let { value: name } of await queryPrefix(`pl:${pageName}:`)) { - uniqueLinks.add(name); + let links: any[] = []; + for (let { value: name, key } of await queryPrefix(`pl:${pageName}:`)) { + const [, , pos] = key.split(":"); // Key: pl:page:pos + links.push({ name, pos }); } - return applyQuery( - query, - [...uniqueLinks].map((l) => ({ name: l })) - ); + return applyQuery(query, links); } export async function deletePage() { diff --git a/packages/plugs/core/template.ts b/packages/plugs/core/template.ts index 388e12b..fcefa61 100644 --- a/packages/plugs/core/template.ts +++ b/packages/plugs/core/template.ts @@ -46,9 +46,12 @@ export async function instantiateTemplateCommand() { ); let parseTree = await parseMarkdown(text); - let additionalPageMeta = extractMeta(parseTree, ["PAGENAME"]); + let additionalPageMeta = extractMeta(parseTree, [ + "$name", + "$disableDirectives", + ]); - let pageName = await prompt("Name of new page", additionalPageMeta.PAGENAME); + let pageName = await prompt("Name of new page", additionalPageMeta.$name); if (!pageName) { return; } diff --git a/packages/plugs/query/complete.ts b/packages/plugs/query/complete.ts index 40ff172..33802da 100644 --- a/packages/plugs/query/complete.ts +++ b/packages/plugs/query/complete.ts @@ -18,37 +18,4 @@ export async function queryComplete() { })), }; } - - prefix = await matchBefore('render "[^"]*'); - if (prefix) { - let allPages = await listPages(); - return { - from: prefix.from + 'render "'.length, - options: allPages.map((pageMeta) => ({ - label: pageMeta.name, - })), - }; - } - - prefix = await matchBefore('#template "[^"]*'); - if (prefix) { - let allPages = await listPages(); - return { - from: prefix.from + '#template "'.length, - options: allPages.map((pageMeta) => ({ - label: pageMeta.name, - })), - }; - } - - prefix = await matchBefore('#include "[^"]*'); - if (prefix) { - let allPages = await listPages(); - return { - from: prefix.from + '#include "'.length, - options: allPages.map((pageMeta) => ({ - label: pageMeta.name, - })), - }; - } } diff --git a/packages/plugs/query/engine.test.ts b/packages/plugs/query/engine.test.ts index 1a1e73c..9460446 100644 --- a/packages/plugs/query/engine.test.ts +++ b/packages/plugs/query/engine.test.ts @@ -62,6 +62,12 @@ test("Test parser", () => { ], }); + expect(parseQuery(`something render [[template/table]]`)).toStrictEqual({ + table: "something", + filter: [], + render: "template/table", + }); + expect(parseQuery(`something render "template/table"`)).toStrictEqual({ table: "something", filter: [], diff --git a/packages/plugs/query/engine.ts b/packages/plugs/query/engine.ts index 51ce5be..63de36b 100644 --- a/packages/plugs/query/engine.ts +++ b/packages/plugs/query/engine.ts @@ -27,6 +27,9 @@ export function valueNodeToVal(valNode: ParseTree): any { case "String": let stringVal = valNode.children![0].text!; return stringVal.substring(1, stringVal.length - 1); + case "PageRef": + let pageRefVal = valNode.children![0].text!; + return pageRefVal.substring(2, pageRefVal.length - 2); case "List": return collectNodesOfType(valNode, "Value").map((t) => valueNodeToVal(t.children![0]) diff --git a/packages/plugs/query/materialized_queries.ts b/packages/plugs/query/materialized_queries.ts index ca13b56..6e0de62 100644 --- a/packages/plugs/query/materialized_queries.ts +++ b/packages/plugs/query/materialized_queries.ts @@ -17,7 +17,8 @@ import { jsonToMDTable, queryRegex } from "./util"; import { dispatch } from "@plugos/plugos-syscall/event"; import { replaceAsync } from "../lib/util"; import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown"; -import { nodeAtPos } from "@silverbulletmd/common/tree"; +import { nodeAtPos, renderToText } from "@silverbulletmd/common/tree"; +import { extractMeta } from "./data"; export async function updateMaterializedQueriesCommand() { const currentPage = await getCurrentPage(); @@ -34,7 +35,7 @@ export async function updateMaterializedQueriesCommand() { } export const templateInstRegex = - /()(.+?)()/gs; + /()(.+?)()/gs; async function updateTemplateInstantiations( text: string, @@ -66,8 +67,11 @@ async function updateTemplateInstantiations( templateText = (await readPage(template)).text; } let newBody = templateText; - // if it's a template (note a literal "include") - if (type === "template") { + // if it's a template injection (not a literal "include") + if (type == "inject" || type === "inject-clean") { + let tree = await parseMarkdown(templateText); + extractMeta(tree, ["$disableDirectives"]); + templateText = renderToText(tree); let templateFn = Handlebars.compile( replaceTemplateVars(templateText, pageName), { noEscape: true } @@ -79,6 +83,29 @@ async function updateTemplateInstantiations( ); } +async function cleanTemplateInstantiations(text: string): Promise { + return replaceAsync( + text, + templateInstRegex, + async (fullMatch, startInst, type, template, args, body, endInst) => { + if (type === "inject-clean") { + body = body.replaceAll( + queryRegex, + ( + fullMatch: string, + startQuery: string, + query: string, + body: string + ) => { + return body.trim(); + } + ); + } + return `${startInst}${body}${endInst}`; + } + ); +} + // Called from client, running on server export async function updateMaterializedQueriesOnPage( pageName: string @@ -96,6 +123,12 @@ export async function updateMaterializedQueriesOnPage( } let newText = await updateTemplateInstantiations(text, pageName); let tree = await parseMarkdown(newText); + let metaData = extractMeta(tree, ["$disableDirectives"]); + if (metaData.$disableDirectives) { + console.log("Directives disabled, skipping"); + return false; + } + newText = renderToText(tree); newText = await replaceAsync( newText, @@ -106,7 +139,6 @@ export async function updateMaterializedQueriesOnPage( // If not a comment block, it's likely a code block, ignore return fullMatch; } - // console.log("Text slice", newText.substring(index, index + 100)); let parsedQuery = parseQuery(replaceTemplateVars(query, pageName)); @@ -132,6 +164,7 @@ export async function updateMaterializedQueriesOnPage( } } ); + newText = await cleanTemplateInstantiations(newText); if (text !== newText) { await writePage(pageName, newText); return true; diff --git a/packages/plugs/query/parse-query.js b/packages/plugs/query/parse-query.js index 91eb477..e77c659 100644 --- a/packages/plugs/query/parse-query.js +++ b/packages/plugs/query/parse-query.js @@ -2,14 +2,14 @@ import {LRParser} from "@lezer/lr" export const parser = LRParser.deserialize({ version: 14, - states: "&fOVQPOOOmQQO'#C^QOQPOOOtQPO'#C`OyQQO'#CkO!OQPO'#CmO!TQPO'#CnO!YQPO'#CoOOQO'#Cp'#CpO!_QQO,58xO!fQQO'#CcO#TQQO'#CaOOQO'#Ca'#CaOOQO,58z,58zO#lQPO,59VOOQO,59X,59XO#qQQO'#D`OOQO,59Y,59YOOQO,59Z,59ZOOQO-E6n-E6nO$YQQO,58}OtQPO,58|O$qQQO1G.qO%]QPO'#CrO%bQQO,59zOOQO'#Cg'#CgOOQO'#Ci'#CiO$YQQO'#CjOOQO'#Cd'#CdOOQO1G.i1G.iOOQO1G.h1G.hOOQO'#Cl'#ClOOQO7+$]7+$]OOQO,59^,59^OOQO-E6p-E6pO%yQPO'#C|O&RQPO,59UO$YQQO'#CqO&WQPO,59hOOQO1G.p1G.pOOQO,59],59]OOQO-E6o-E6o", - stateData: "&`~OiOS~ORPO~OjRO|SO!QTO!RUO!TVO~OgQX~P[ORYO~O}^O~OX_O~OR`O~OYbO~OgQa~P[OkdOsdOtdOudOvdOwdOxdOydOzdO~O{eOgTXjTX|TX!QTX!RTX!TTX~ORfO~OqgOg!SXj!SX|!SX!Q!SX!R!SX!T!SX~OXlOYlO[lOliOmiOnjOokO~O!OoO!PoOg_ij_i|_i!Q_i!R_i!T_i~ORqO~OqgOg!Saj!Sa|!Sa!Q!Sa!R!Sa!T!Sa~OquOrpX~OrwO~OquOrpa~O", - goto: "#d!TPP!UP!X!]!`!c!iPP!rP!r!r!X!w!X!X!X!z#Q#WPPPPPPPPP#^PPPPPPPPPPPPPPPPP#aRQOTWPXR]RR[RQZRRneQmdQskRxuVldkuRpfQXPRcXQvsRyvQh`RrhRtkRaU", - nodeNames: "⚠ Program Query Name WhereClause LogicalExpr AndExpr FilterExpr Value Number String Bool Regex Null List OrderClause Order LimitClause SelectClause RenderClause", - maxTerm: 51, + states: "&fOVQPOOOmQQO'#C^QOQPOOOtQPO'#C`OyQQO'#CkO!OQPO'#CmO!TQPO'#CnO!YQPO'#CoOOQO'#Cq'#CqO!bQQO,58xO!iQQO'#CcO#WQQO'#CaOOQO'#Ca'#CaOOQO,58z,58zO#oQPO,59VOOQO,59X,59XO#tQQO'#DaOOQO,59Y,59YOOQO,59Z,59ZOOQO-E6o-E6oO$]QQO,58}OtQPO,58|O$tQQO1G.qO%`QPO'#CsO%eQQO,59{OOQO'#Cg'#CgOOQO'#Ci'#CiO$]QQO'#CjOOQO'#Cd'#CdOOQO1G.i1G.iOOQO1G.h1G.hOOQO'#Cl'#ClOOQO7+$]7+$]OOQO,59_,59_OOQO-E6q-E6qO%|QPO'#C}O&UQPO,59UO$]QQO'#CrO&ZQPO,59iOOQO1G.p1G.pOOQO,59^,59^OOQO-E6p-E6p", + stateData: "&c~OjOS~ORPO~OkRO}SO!RTO!SUO!UVO~OhQX~P[ORYO~O!O^O~OX_O~OR`O~OYbOdbO~OhQa~P[OldOtdOudOvdOwdOxdOydOzdO{dO~O|eOhTXkTX}TX!RTX!STX!UTX~ORfO~OrgOh!TXk!TX}!TX!R!TX!S!TX!U!TX~OXlOYlO[lOmiOniOojOpkO~O!PoO!QoOh_ik_i}_i!R_i!S_i!U_i~ORqO~OrgOh!Tak!Ta}!Ta!R!Ta!S!Ta!U!Ta~OruOsqX~OswO~OruOsqa~O", + goto: "#e!UPP!VP!Y!^!a!d!jPP!sP!s!s!Y!x!Y!Y!YP!{#R#XPPPPPPPPP#_PPPPPPPPPPPPPPPPP#bRQOTWPXR]RR[RQZRRneQmdQskRxuVldkuRpfQXPRcXQvsRyvQh`RrhRtkRaU", + nodeNames: "⚠ Program Query Name WhereClause LogicalExpr AndExpr FilterExpr Value Number String Bool Regex Null List OrderClause Order LimitClause SelectClause RenderClause PageRef", + maxTerm: 52, skippedNodes: [0], repeatNodeCount: 3, - tokenData: "Ap~R}X^$Opq$Oqr$srs%W|}%r}!O%w!P!Q&Y!Q!['P!^!_'X!_!`'f!`!a's!c!}%w!}#O(Q#P#Q(V#R#S%w#T#U([#U#V*q#V#W%w#W#X+m#X#Y%w#Y#Z-i#Z#]%w#]#^/y#^#`%w#`#a0u#a#b%w#b#c3Y#c#d5U#d#f%w#f#g7i#g#h:e#h#i=a#i#k%w#k#l?]#l#o%w#y#z$O$f$g$O#BY#BZ$O$IS$I_$O$Ip$Iq%W$Iq$Ir%W$I|$JO$O$JT$JU$O$KV$KW$O&FU&FV$O~$TYi~X^$Opq$O#y#z$O$f$g$O#BY#BZ$O$IS$I_$O$I|$JO$O$JT$JU$O$KV$KW$O&FU&FV$O~$vP!_!`$y~%OPu~#r#s%R~%WOy~~%ZUOr%Wrs%ms$Ip%W$Ip$Iq%m$Iq$Ir%m$Ir~%W~%rOY~~%wOq~P%|SRP}!O%w!c!}%w#R#S%w#T#o%w~&_V[~OY&YZ]&Y^!P&Y!P!Q&t!Q#O&Y#O#P&y#P~&Y~&yO[~~&|PO~&Y~'UPX~!Q!['P~'^Pk~!_!`'a~'fOs~~'kPt~#r#s'n~'sOx~~'xPw~!_!`'{~(QOv~~(VOo~~([Or~R(aWRP}!O%w!c!}%w#R#S%w#T#b%w#b#c(y#c#g%w#g#h)u#h#o%wR)OURP}!O%w!c!}%w#R#S%w#T#W%w#W#X)b#X#o%wR)iS{QRP}!O%w!c!}%w#R#S%w#T#o%wR)zURP}!O%w!c!}%w#R#S%w#T#V%w#V#W*^#W#o%wR*eS!PQRP}!O%w!c!}%w#R#S%w#T#o%wR*vURP}!O%w!c!}%w#R#S%w#T#m%w#m#n+Y#n#o%wR+aS}QRP}!O%w!c!}%w#R#S%w#T#o%wR+rURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y,U#Y#o%wR,ZURP}!O%w!c!}%w#R#S%w#T#g%w#g#h,m#h#o%wR,rURP}!O%w!c!}%w#R#S%w#T#V%w#V#W-U#W#o%wR-]S!OQRP}!O%w!c!}%w#R#S%w#T#o%wR-nTRP}!O%w!c!}%w#R#S%w#T#U-}#U#o%wR.SURP}!O%w!c!}%w#R#S%w#T#`%w#`#a.f#a#o%wR.kURP}!O%w!c!}%w#R#S%w#T#g%w#g#h.}#h#o%wR/SURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y/f#Y#o%wR/mSmQRP}!O%w!c!}%w#R#S%w#T#o%wR0OURP}!O%w!c!}%w#R#S%w#T#b%w#b#c0b#c#o%wR0iSzQRP}!O%w!c!}%w#R#S%w#T#o%wR0zURP}!O%w!c!}%w#R#S%w#T#]%w#]#^1^#^#o%wR1cURP}!O%w!c!}%w#R#S%w#T#a%w#a#b1u#b#o%wR1zURP}!O%w!c!}%w#R#S%w#T#]%w#]#^2^#^#o%wR2cURP}!O%w!c!}%w#R#S%w#T#h%w#h#i2u#i#o%wR2|S!QQRP}!O%w!c!}%w#R#S%w#T#o%wR3_URP}!O%w!c!}%w#R#S%w#T#i%w#i#j3q#j#o%wR3vURP}!O%w!c!}%w#R#S%w#T#`%w#`#a4Y#a#o%wR4_URP}!O%w!c!}%w#R#S%w#T#`%w#`#a4q#a#o%wR4xSnQRP}!O%w!c!}%w#R#S%w#T#o%wR5ZURP}!O%w!c!}%w#R#S%w#T#f%w#f#g5m#g#o%wR5rURP}!O%w!c!}%w#R#S%w#T#W%w#W#X6U#X#o%wR6ZURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y6m#Y#o%wR6rURP}!O%w!c!}%w#R#S%w#T#f%w#f#g7U#g#o%wR7]S|QRP}!O%w!c!}%w#R#S%w#T#o%wR7nURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y8Q#Y#o%wR8VURP}!O%w!c!}%w#R#S%w#T#b%w#b#c8i#c#o%wR8nURP}!O%w!c!}%w#R#S%w#T#W%w#W#X9Q#X#o%wR9VURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y9i#Y#o%wR9nURP}!O%w!c!}%w#R#S%w#T#f%w#f#g:Q#g#o%wR:XS!TQRP}!O%w!c!}%w#R#S%w#T#o%wR:jURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y:|#Y#o%wR;RURP}!O%w!c!}%w#R#S%w#T#`%w#`#a;e#a#o%wR;jURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y;|#Y#o%wRa#j#o%wR>fURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y>x#Y#o%wR?PSlQRP}!O%w!c!}%w#R#S%w#T#o%wR?bURP}!O%w!c!}%w#R#S%w#T#[%w#[#]?t#]#o%wR?yURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y@]#Y#o%wR@bURP}!O%w!c!}%w#R#S%w#T#f%w#f#g@t#g#o%wR@yURP}!O%w!c!}%w#R#S%w#T#X%w#X#YA]#Y#o%wRAdSjQRP}!O%w!c!}%w#R#S%w#T#o%w", + tokenData: "B[~R}X^$Opq$Oqr$srs%W|}%r}!O%w!P!Q&Y!Q!['P!^!_'X!_!`'f!`!a's!c!}%w!}#O(Q#P#Q(q#R#S%w#T#U(v#U#V+]#V#W%w#W#X,X#X#Y%w#Y#Z.T#Z#]%w#]#^0e#^#`%w#`#a1a#a#b%w#b#c3t#c#d5p#d#f%w#f#g8T#g#h;P#h#i={#i#k%w#k#l?w#l#o%w#y#z$O$f$g$O#BY#BZ$O$IS$I_$O$Ip$Iq%W$Iq$Ir%W$I|$JO$O$JT$JU$O$KV$KW$O&FU&FV$O~$TYj~X^$Opq$O#y#z$O$f$g$O#BY#BZ$O$IS$I_$O$I|$JO$O$JT$JU$O$KV$KW$O&FU&FV$O~$vP!_!`$y~%OPv~#r#s%R~%WOz~~%ZUOr%Wrs%ms$Ip%W$Ip$Iq%m$Iq$Ir%m$Ir~%W~%rOY~~%wOr~P%|SRP}!O%w!c!}%w#R#S%w#T#o%w~&_V[~OY&YZ]&Y^!P&Y!P!Q&t!Q#O&Y#O#P&y#P~&Y~&yO[~~&|PO~&Y~'UPX~!Q!['P~'^Pl~!_!`'a~'fOt~~'kPu~#r#s'n~'sOy~~'xPx~!_!`'{~(QOw~R(VPpQ!}#O(YP(]RO#P(Y#P#Q(f#Q~(YP(iP#P#Q(lP(qOdP~(vOs~R({WRP}!O%w!c!}%w#R#S%w#T#b%w#b#c)e#c#g%w#g#h*a#h#o%wR)jURP}!O%w!c!}%w#R#S%w#T#W%w#W#X)|#X#o%wR*TS|QRP}!O%w!c!}%w#R#S%w#T#o%wR*fURP}!O%w!c!}%w#R#S%w#T#V%w#V#W*x#W#o%wR+PS!QQRP}!O%w!c!}%w#R#S%w#T#o%wR+bURP}!O%w!c!}%w#R#S%w#T#m%w#m#n+t#n#o%wR+{S!OQRP}!O%w!c!}%w#R#S%w#T#o%wR,^URP}!O%w!c!}%w#R#S%w#T#X%w#X#Y,p#Y#o%wR,uURP}!O%w!c!}%w#R#S%w#T#g%w#g#h-X#h#o%wR-^URP}!O%w!c!}%w#R#S%w#T#V%w#V#W-p#W#o%wR-wS!PQRP}!O%w!c!}%w#R#S%w#T#o%wR.YTRP}!O%w!c!}%w#R#S%w#T#U.i#U#o%wR.nURP}!O%w!c!}%w#R#S%w#T#`%w#`#a/Q#a#o%wR/VURP}!O%w!c!}%w#R#S%w#T#g%w#g#h/i#h#o%wR/nURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y0Q#Y#o%wR0XSnQRP}!O%w!c!}%w#R#S%w#T#o%wR0jURP}!O%w!c!}%w#R#S%w#T#b%w#b#c0|#c#o%wR1TS{QRP}!O%w!c!}%w#R#S%w#T#o%wR1fURP}!O%w!c!}%w#R#S%w#T#]%w#]#^1x#^#o%wR1}URP}!O%w!c!}%w#R#S%w#T#a%w#a#b2a#b#o%wR2fURP}!O%w!c!}%w#R#S%w#T#]%w#]#^2x#^#o%wR2}URP}!O%w!c!}%w#R#S%w#T#h%w#h#i3a#i#o%wR3hS!RQRP}!O%w!c!}%w#R#S%w#T#o%wR3yURP}!O%w!c!}%w#R#S%w#T#i%w#i#j4]#j#o%wR4bURP}!O%w!c!}%w#R#S%w#T#`%w#`#a4t#a#o%wR4yURP}!O%w!c!}%w#R#S%w#T#`%w#`#a5]#a#o%wR5dSoQRP}!O%w!c!}%w#R#S%w#T#o%wR5uURP}!O%w!c!}%w#R#S%w#T#f%w#f#g6X#g#o%wR6^URP}!O%w!c!}%w#R#S%w#T#W%w#W#X6p#X#o%wR6uURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y7X#Y#o%wR7^URP}!O%w!c!}%w#R#S%w#T#f%w#f#g7p#g#o%wR7wS}QRP}!O%w!c!}%w#R#S%w#T#o%wR8YURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y8l#Y#o%wR8qURP}!O%w!c!}%w#R#S%w#T#b%w#b#c9T#c#o%wR9YURP}!O%w!c!}%w#R#S%w#T#W%w#W#X9l#X#o%wR9qURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y:T#Y#o%wR:YURP}!O%w!c!}%w#R#S%w#T#f%w#f#g:l#g#o%wR:sS!UQRP}!O%w!c!}%w#R#S%w#T#o%wR;UURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y;h#Y#o%wR;mURP}!O%w!c!}%w#R#S%w#T#`%w#`#aQURP}!O%w!c!}%w#R#S%w#T#f%w#f#g>d#g#o%wR>iURP}!O%w!c!}%w#R#S%w#T#i%w#i#j>{#j#o%wR?QURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y?d#Y#o%wR?kSmQRP}!O%w!c!}%w#R#S%w#T#o%wR?|URP}!O%w!c!}%w#R#S%w#T#[%w#[#]@`#]#o%wR@eURP}!O%w!c!}%w#R#S%w#T#X%w#X#Y@w#Y#o%wR@|URP}!O%w!c!}%w#R#S%w#T#f%w#f#gA`#g#o%wRAeURP}!O%w!c!}%w#R#S%w#T#X%w#X#YAw#Y#o%wRBOSkQRP}!O%w!c!}%w#R#S%w#T#o%w", tokenizers: [0, 1], topRules: {"Program":[0,1]}, tokenPrec: 0 diff --git a/packages/plugs/query/parse-query.terms.js b/packages/plugs/query/parse-query.terms.js index 5e020bb..9d41b67 100644 --- a/packages/plugs/query/parse-query.terms.js +++ b/packages/plugs/query/parse-query.terms.js @@ -18,4 +18,5 @@ export const Order = 16, LimitClause = 17, SelectClause = 18, - RenderClause = 19 + RenderClause = 19, + PageRef = 20 diff --git a/packages/plugs/query/parser.ts b/packages/plugs/query/parser.ts index b9aa2a2..587b4df 100644 --- a/packages/plugs/query/parser.ts +++ b/packages/plugs/query/parser.ts @@ -84,7 +84,10 @@ export function parseQuery(query: string): ParsedQuery { let renderNode = findNodeOfType(queryNode, "RenderClause"); if (renderNode) { - let renderNameNode = findNodeOfType(renderNode, "String"); + let renderNameNode = findNodeOfType(renderNode, "PageRef"); + if (!renderNameNode) { + renderNameNode = findNodeOfType(renderNode, "String"); + } parsedQuery.render = valueNodeToVal(renderNameNode!); } diff --git a/packages/plugs/query/query.grammar b/packages/plugs/query/query.grammar index 3af872a..489bff8 100644 --- a/packages/plugs/query/query.grammar +++ b/packages/plugs/query/query.grammar @@ -12,7 +12,7 @@ WhereClause { "where" LogicalExpr } OrderClause { "order" "by" Name Order? } LimitClause { "limit" Number } SelectClause { "select" commaSep } -RenderClause { "render" String } +RenderClause { "render" (PageRef | String) } Order { "desc" | "asc" @@ -56,6 +56,9 @@ Null { String { ("\"" | "“" | "”") ![\"”“]* ("\"" | "“" | "”") } + PageRef { + "[" "[" ![\]]* "]" "]" + } Regex { "/" ( ![/\\\n\r] | "\\" _ )* "/"? } Number { std.digit+ } diff --git a/packages/plugs/query/util.ts b/packages/plugs/query/util.ts index f4b480e..f100902 100644 --- a/packages/plugs/query/util.ts +++ b/packages/plugs/query/util.ts @@ -8,15 +8,9 @@ import { export const queryRegex = /()(.+?)()/gs; -export const queryStartRegex = //s; +export const directiveStartRegex = //s; -export const queryEndRegex = //s; - -// export function whiteOutQueries(text: string): string { -// return text.replaceAll(queryRegex, (match) => -// new Array(match.length + 1).join(" ") -// ); -// } +export const directiveEndRegex = //s; export function removeQueries(pt: ParseTree) { addParentPointers(pt); @@ -25,9 +19,11 @@ export function removeQueries(pt: ParseTree) { return false; } let text = t.children![0].text!; - if (!queryStartRegex.exec(text)) { + let match = directiveStartRegex.exec(text); + if (!match) { return false; } + let directiveType = match[1]; let parentChildren = t.parent!.children!; let index = parentChildren.indexOf(t); let nodesToReplace: ParseTree[] = []; @@ -35,7 +31,8 @@ export function removeQueries(pt: ParseTree) { let n = parentChildren[i]; if (n.type === "CommentBlock") { let text = n.children![0].text!; - if (queryEndRegex.exec(text)) { + let match = directiveEndRegex.exec(text); + if (match && match[1] === directiveType) { break; } } diff --git a/website/CHANGELOG.md b/website/CHANGELOG.md index 92aa4b5..f6ecbd3 100644 --- a/website/CHANGELOG.md +++ b/website/CHANGELOG.md @@ -1,10 +1,18 @@ An attempt at documenting of the changes/new features introduced in each (pre) release. ## 0.0.31 +* Update to the query language: the `render` clause now uses page reference syntax `[[page]]`. For example `render [[template/task]]` rather than `render "template/task"`. The old syntax still works, but is deprecated, completion for the old syntax has been removed. * Updates to templates: - * For the `Template: Instantiate Page` command, the page meta value `PAGENAME` is now used to configure the page name (was `name` before). Also if `PAGENAME` is the only page meta defined, it will remove the page meta entirely when instantiating. + * For the `Template: Instantiate Page` command, the page meta value `$name` is now used to configure the page name (was `name` before). Also if `$name` is the only page meta defined, it will remove the page meta entirely when instantiating. * You can now configure a daily note prefix with `dailyNotePrefix` in `SETTINGS` and create a template for your daily note under `template/page/Daily Note` (configurable via the `dailyNoteTemplate` setting). * You can now a quick note prefix with `quickNotePrefix` in `SETTINGS`. +* Directives (e.g. `#query`, `#import`, `#inject`) changes: + * Renamed `#template` directive to `#inject` + * New `#inject-clean` directive will clean all the embedded queries and templates in its scope + * All directives now use the page reference syntax `[[page name]]` instead of `"page name"`, this includes `#inject` and `#inject-clean` as well as `#import`. + * The `link` query provider now also returns the `pos` of a link (in addition to the `page`) + * New `$disableDirectives` page meta data attribute can be used to disable directives processing in a page (useful for templates) +* Added a new `/hr` slash command to insert a horizontal rule (`---`) useful for mobile devices (where these are harder to type) ## 0.0.30 * Slash commands now only trigger after a non-word character to avoid "false positives" like "hello/world". diff --git a/website/Silver Bullet.md b/website/Silver Bullet.md index 8dd73bb..9b62881 100644 --- a/website/Silver Bullet.md +++ b/website/Silver Bullet.md @@ -37,7 +37,7 @@ What type of extensions, you ask? Let us demonstrate this in a very meta way: by Here’s a list of (non-built in) plugs documented in this space (note the `#query` ... `/query` notation used): - + * [[🔌 Backlinks]] by **Guillermo Vayá** ([repo](https://github.com/Willyfrog/silverbullet-backlinks)) * [[🔌 Ghost]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-ghost)) * [[🔌 Git]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-github)) diff --git a/website/🔌 Core.md b/website/🔌 Core.md index 67cfd0d..6dae9ef 100644 --- a/website/🔌 Core.md +++ b/website/🔌 Core.md @@ -16,14 +16,14 @@ Page templates, by default, are looked for in the `template/page/` prefix. So cr Page template have one “magic” type of page meta data that is used during instantiation: -* `PAGENAME` is used as a default value for a new page based on this template +* `$name` is used as a default value for a new page based on this template In addition, any standard template placeholders are available (see below) For instance: ```meta - PAGENAME: "📕 " + $name: "📕 " ``` # {{page}} @@ -45,7 +45,7 @@ Will prompt you to pick a page name (defaulting to “📕 “), and then create ### Snippets Snippets are similar to page templates, except you insert them into an existing page with the `/snippet` slash command. The default prefix is `snippet/` which is configurable via the `snippetPrefix` setting in `SETTINGS`. -Snippet templates do not support the `PAGENAME` page meta, because it doesn’t apply. +Snippet templates do not support the `$name` page meta, because it doesn’t apply. However, snippets do support the special `|^|` placeholder for placing the cursor caret after injecting the snippet. If you leave it out, the cursor will simply be placed at the end, but if you like to insert the cursor elsewhere, that position can be set with the `|^|` placeholder. diff --git a/website/🔌 Github.md b/website/🔌 Github.md index acbac40..a016f03 100644 --- a/website/🔌 Github.md +++ b/website/🔌 Github.md @@ -4,7 +4,7 @@ uri: github:silverbulletmd/silverbullet-github/github.plug.json repo: https://github.com/silverbulletmd/silverbullet-github author: Zef Hemel ``` - + # SilverBullet plug for Github Provides Github events, notifications and pull requests as query sources using SB's query mechanism @@ -44,7 +44,7 @@ Example uses: ## Recent PRs - + diff --git a/website/🔌 Plugs.md b/website/🔌 Plugs.md index cd7686c..6d15f4c 100644 --- a/website/🔌 Plugs.md +++ b/website/🔌 Plugs.md @@ -3,7 +3,7 @@ Silver Bullet at its core is bare bones in terms of functionality, most of its p Plugs are an extension mechanism (implemented using a library called `plugos` that runs plug code on the server in a sandboxed v8 node.js process, and in the browser using web workers). Plugs can hook into SB in various ways: plugs can extend the Markdown parser and its syntax, define new commands and keybindings, respond to various events triggered either on the server or client side, as well as run recurring and background tasks. Plugs can even define their own extension mechanisms through custom events. Each plug runs in its own sandboxed environment and communicates with SB via _syscalls_ that expose a vast range of functionality. Plugs can be loaded, unloaded and updated without having to restart SB itself. ## Directory - + * [[🔌 Backlinks]] by **Guillermo Vayá** ([repo](https://github.com/Willyfrog/silverbullet-backlinks)) * [[🔌 Ghost]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-ghost)) * [[🔌 Git]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-github)) diff --git a/website/🔌 Query.md b/website/🔌 Query.md index f6af57e..db366b6 100644 --- a/website/🔌 Query.md +++ b/website/🔌 Query.md @@ -64,10 +64,10 @@ city: Berlin country: Germany ``` -|name|age|city |country|page |pos | -|----|--|-----|-----|---------------|----| -|John|50|Milan|Italy|🔌 Query |2198| -|Jane|53|Rome |Italy|🔌 Query |2244| +|name|age|city |country|page |pos | +|----|--|-----|-----|--------|----| +|John|50|Milan|Italy|🔌 Query|2277| +|Jane|53|Rome |Italy|🔌 Query|2323| #### 4.2 Plugs’ data sources @@ -89,18 +89,18 @@ For the sake of simplicity, we will use `page` data source and limit the results **Result:** Look at the data. This is more than we need. The query even gives us template pages. Lets try to limit it in the next step. -|name |lastModified |perm|tags |type|uri |repo |author | +|name |lastModified |perm|tags |type|uri |repo |author | |--|--|--|--|--|--|--|--| -|index |1659178324000|rw|undefined|undefined|undefined |undefined |undefined | -|Mattermost Plugin|1659108035000|rw|undefined|undefined|undefined |undefined |undefined | -|PLUGS |1659108634000|rw|undefined|undefined|undefined |undefined |undefined | -|Test Data Query |1659179547000|rw|undefined|undefined|undefined |undefined |undefined | -|template/plug |1659108035000|rw|undefined|undefined|undefined |undefined |undefined | -|template/tasks |1659108035000|rw|#each|undefined|undefined |undefined |undefined | -|💡 Inspiration |1659108035000|rw|undefined|undefined|undefined |undefined |undefined | -|🔌 Backlinks |1659108035000|rw|undefined|plug|ghr:Willyfrog/silverbullet-backlinks |https://github.com/Willyfrog/silverbullet-backlinks |Guillermo Vayá| -|🔌 Ghost |1659108035000|rw|undefined|plug|github:silverbulletmd/silverbullet-ghost/ghost.plug.json |https://github.com/silverbulletmd/silverbullet-ghost |Zef Hemel | -|🔌 Git |1659108035000|rw|undefined|plug|github:silverbulletmd/silverbullet-github/github.plug.json|https://github.com/silverbulletmd/silverbullet-github|Zef Hemel | +|SETTINGS |1659437160849|rw|undefined|undefined|undefined |undefined |undefined | +|Silver Bullet |1660051168973|rw|undefined|undefined|undefined |undefined |undefined | +|CHANGELOG |1660050383951|rw|undefined|undefined|undefined |undefined |undefined | +|Mattermost Plugin|1658755340866|rw|undefined|undefined|undefined |undefined |undefined | +|PLUGS |1659437423367|rw|undefined|undefined|undefined |undefined |undefined | +|index |1659440751554|rw|undefined|undefined|undefined |undefined |undefined | +|template/plug |1658751100952|rw|undefined|undefined|undefined |undefined |undefined | +|template/tasks |1657890041936|rw|#each|undefined|undefined |undefined |undefined | +|💡 Inspiration |1658133917441|rw|undefined|undefined|undefined |undefined |undefined | +|🔌 Backlinks |1658760465195|rw|undefined|plug|ghr:Willyfrog/silverbullet-backlinks|https://github.com/Willyfrog/silverbullet-backlinks|Guillermo Vayá| #### 5.2 Simple query with a condition @@ -109,13 +109,13 @@ For the sake of simplicity, we will use `page` data source and limit the results **Result:** Okay, this what we wanted but there are also information such as perm, type and lastModified that we don't need. -|name |lastModified |perm|type|uri |repo |author | +|name |lastModified |perm|type|uri |repo |author | |--|--|--|--|--|--|--| -|🔌 Query |1659194185345|rw|plug|core:query |https://github.com/silverbulletmd/silverbullet |Silver Bullet Authors| -|🔌 Mattermost|1659111156000|rw|plug|github:silverbulletmd/silverbullet-mattermost/mattermost.plug.json|https://github.com/silverbulletmd/silverbullet-mattermost|Zef Hemel | -|🔌 Backlinks |1659108035000|rw|plug|ghr:Willyfrog/silverbullet-backlinks |https://github.com/Willyfrog/silverbullet-backlinks |Guillermo Vayá | -|🔌 Ghost |1659108035000|rw|plug|github:silverbulletmd/silverbullet-ghost/ghost.plug.json |https://github.com/silverbulletmd/silverbullet-ghost |Zef Hemel | -|🔌 Git |1659108035000|rw|plug|github:silverbulletmd/silverbullet-github/github.plug.json |https://github.com/silverbulletmd/silverbullet-github |Zef Hemel | +|🔌 Query |1660051209241|rw|plug|core:query |https://github.com/silverbulletmd/silverbullet |Silver Bullet Authors| +|🔌 Github|1660050280511|rw|plug|github:silverbulletmd/silverbullet-github/github.plug.json|https://github.com/silverbulletmd/silverbullet-github|Zef Hemel | +|🔌 Mount |1658760601369|rw|plug|github:silverbulletmd/silverbullet-mount/mount.plug.json |https://github.com/silverbulletmd/silverbullet-mount |Zef Hemel | +|🔌 Git |1658760545612|rw|plug|github:silverbulletmd/silverbullet-github/github.plug.json|https://github.com/silverbulletmd/silverbullet-github|Zef Hemel | +|🔌 Ghost |1658760515320|rw|plug|github:silverbulletmd/silverbullet-ghost/ghost.plug.json |https://github.com/silverbulletmd/silverbullet-ghost |Zef Hemel | @@ -125,13 +125,13 @@ For the sake of simplicity, we will use `page` data source and limit the results **Result:** Okay, this is much better. However, I believe this needs a touch from a visual perspective. -|name |author |repo | +|name |author |repo | |--|--|--| -|🔌 Query |Silver Bullet Authors|https://github.com/silverbulletmd/silverbullet | -|🔌 Mattermost|Zef Hemel |https://github.com/silverbulletmd/silverbullet-mattermost| -|🔌 Backlinks |Guillermo Vayá |https://github.com/Willyfrog/silverbullet-backlinks | -|🔌 Ghost |Zef Hemel |https://github.com/silverbulletmd/silverbullet-ghost | -|🔌 Git |Zef Hemel |https://github.com/silverbulletmd/silverbullet-github | +|🔌 Query |Silver Bullet Authors|https://github.com/silverbulletmd/silverbullet | +|🔌 Github|Zef Hemel |https://github.com/silverbulletmd/silverbullet-github| +|🔌 Mount |Zef Hemel |https://github.com/silverbulletmd/silverbullet-mount | +|🔌 Git |Zef Hemel |https://github.com/silverbulletmd/silverbullet-github| +|🔌 Ghost |Zef Hemel |https://github.com/silverbulletmd/silverbullet-ghost | #### 5.4 Display the data in a format defined by a template @@ -140,21 +140,21 @@ For the sake of simplicity, we will use `page` data source and limit the results **Result:** Here you go. This is the result we would like to achieve 🎉. Did you see how I used `render` and `template/plug` in a query? 🚀 - + * [[🔌 Query]] by **Silver Bullet Authors** ([repo](https://github.com/silverbulletmd/silverbullet)) -* [[🔌 Mattermost]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-mattermost)) -* [[🔌 Backlinks]] by **Guillermo Vayá** ([repo](https://github.com/Willyfrog/silverbullet-backlinks)) -* [[🔌 Ghost]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-ghost)) +* [[🔌 Github]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-github)) +* [[🔌 Mount]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-mount)) * [[🔌 Git]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-github)) +* [[🔌 Ghost]] by **Zef Hemel** ([repo](https://github.com/silverbulletmd/silverbullet-ghost)) PS: You don't need select only certain fields to use templates. Templates are smart enough to get only the information needed to render the data. Therefore, following queries are same in terms of end result when using the templates. ```yaml - + ``` ```yaml - + ```