From fcf712ccc7640034697ac9a0125fd29f8d576db4 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Wed, 6 Jul 2022 12:18:47 +0200 Subject: [PATCH] Query code refactor --- docs/🗺️ Roadmap.md | 22 ----- packages/plugs/core/template.ts | 7 ++ packages/plugs/query/engine.test.ts | 3 +- packages/plugs/query/engine.ts | 97 +------------------- packages/plugs/query/materialized_queries.ts | 3 +- packages/plugs/query/parser.ts | 93 +++++++++++++++++++ 6 files changed, 107 insertions(+), 118 deletions(-) delete mode 100644 docs/🗺️ Roadmap.md create mode 100644 packages/plugs/query/parser.ts diff --git a/docs/🗺️ Roadmap.md b/docs/🗺️ Roadmap.md deleted file mode 100644 index 9db9a4b..0000000 --- a/docs/🗺️ Roadmap.md +++ /dev/null @@ -1,22 +0,0 @@ -Some things I want to work on: - -* [ ] Persistent recent commands (saved between sessions) -* [ ] Add ==marker== syntax -* [ ] Two finger tap gesture to bring up command palette -* [ ] Change indent level command -* [ ] Keyboard shortcuts for specific notes (e.g. `index` note) -* [ ] RevealJS slides plug -* [ ] Pinned notes and actions? -* [ ] Template for deadline, with 📅 emoji and perhaps defaulting to today? -* [ ] Use webauthn https://www.npmjs.com/package/webauthn -* [ ] Proper sign up and login -* [ ] Data store pagination API -* [ ] Hashtag plug: - * Higlighting - * Page indexing/item indexing - * Tag completion - * Query providers: ht-page ht-item -* [ ] Extract `MarkdownEditor` component. -* REST API safeguards: - * [ ] PUT page with `If-Last-Modified-Before` type header. Rejects if not matching. Client creates a revision, navigates to it. - * [ ] Put retries exponential back off \ No newline at end of file diff --git a/packages/plugs/core/template.ts b/packages/plugs/core/template.ts index cf1fe10..c622bb9 100644 --- a/packages/plugs/core/template.ts +++ b/packages/plugs/core/template.ts @@ -114,6 +114,13 @@ export async function quickNoteCommand() { await navigate(pageName); } +export async function dailyNoteCommand() { + let isoDate = new Date().toISOString(); + let date = isoDate.split("T")[0]; + let pageName = `📅 ${date}`; + await navigate(pageName); +} + export async function insertTemplateText(cmdDef: any) { let cursorPos = await getCursor(); let page = await getCurrentPage(); diff --git a/packages/plugs/query/engine.test.ts b/packages/plugs/query/engine.test.ts index dfed8c3..1a1e73c 100644 --- a/packages/plugs/query/engine.test.ts +++ b/packages/plugs/query/engine.test.ts @@ -1,5 +1,6 @@ import { expect, test } from "@jest/globals"; -import { applyQuery, parseQuery } from "./engine"; +import { applyQuery } from "./engine"; +import { parseQuery } from "./parser"; test("Test parser", () => { let parsedBasicQuery = parseQuery(`page`); diff --git a/packages/plugs/query/engine.ts b/packages/plugs/query/engine.ts index 7e01c50..9c2dc92 100644 --- a/packages/plugs/query/engine.ts +++ b/packages/plugs/query/engine.ts @@ -1,108 +1,17 @@ -import { - collectNodesOfType, - findNodeOfType, - ParseTree, - replaceNodesMatching, -} from "@silverbulletmd/common/tree"; -import { lezerToParseTree } from "@silverbulletmd/common/parse_tree"; +import { collectNodesOfType, ParseTree } from "@silverbulletmd/common/tree"; import Handlebars from "handlebars"; import YAML from "yaml"; -// @ts-ignore -import { parser } from "./parse-query"; import { readPage } from "@silverbulletmd/plugos-silverbullet-syscall/space"; import { niceDate } from "../core/dates"; +import { ParsedQuery } from "./parser"; export type QueryProviderEvent = { query: ParsedQuery; pageName: string; }; -export type Filter = { - op: string; - prop: string; - value: any; -}; - -export type ParsedQuery = { - table: string; - orderBy?: string; - orderDesc?: boolean; - limit?: number; - filter: Filter[]; - select?: string[]; - render?: string; -}; - -export function parseQuery(query: string): ParsedQuery { - let n = lezerToParseTree(query, parser.parse(query).topNode); - // Clean the tree a bit - replaceNodesMatching(n, (n) => { - if (!n.type) { - let trimmed = n.text!.trim(); - if (!trimmed) { - return null; - } - n.text = trimmed; - } - }); - - // console.log("Parsed", JSON.stringify(n, null, 2)); - - let queryNode = n.children![0]; - let parsedQuery: ParsedQuery = { - table: queryNode.children![0].children![0].text!, - filter: [], - }; - let orderByNode = findNodeOfType(queryNode, "OrderClause"); - if (orderByNode) { - let nameNode = findNodeOfType(orderByNode, "Name"); - parsedQuery.orderBy = nameNode!.children![0].text!; - let orderNode = findNodeOfType(orderByNode, "Order"); - parsedQuery.orderDesc = orderNode - ? orderNode.children![0].text! === "desc" - : false; - } - let limitNode = findNodeOfType(queryNode, "LimitClause"); - if (limitNode) { - let nameNode = findNodeOfType(limitNode, "Number"); - parsedQuery.limit = valueNodeToVal(nameNode!); - } - - let filterNodes = collectNodesOfType(queryNode, "FilterExpr"); - for (let filterNode of filterNodes) { - let val: any = undefined; - let valNode = filterNode.children![2].children![0]; - val = valueNodeToVal(valNode); - let f: Filter = { - prop: filterNode.children![0].children![0].text!, - op: filterNode.children![1].text!, - value: val, - }; - parsedQuery.filter.push(f); - } - let selectNode = findNodeOfType(queryNode, "SelectClause"); - if (selectNode) { - // console.log("Select node", JSON.stringify(selectNode)); - parsedQuery.select = []; - collectNodesOfType(selectNode, "Name").forEach((t) => { - parsedQuery.select!.push(t.children![0].text!); - }); - // let nameNode = findNodeOfType(selectNode, "Number"); - // parsedQuery.limit = +nameNode!.children![0].text!; - } - - let renderNode = findNodeOfType(queryNode, "RenderClause"); - if (renderNode) { - let renderNameNode = findNodeOfType(renderNode, "String"); - parsedQuery.render = valueNodeToVal(renderNameNode!); - } - - // console.log(JSON.stringify(queryNode, null, 2)); - return parsedQuery; -} - -function valueNodeToVal(valNode: ParseTree): any { +export function valueNodeToVal(valNode: ParseTree): any { switch (valNode.type) { case "Number": return +valNode.children![0].text!; diff --git a/packages/plugs/query/materialized_queries.ts b/packages/plugs/query/materialized_queries.ts index 39b63ef..53fdd03 100644 --- a/packages/plugs/query/materialized_queries.ts +++ b/packages/plugs/query/materialized_queries.ts @@ -10,7 +10,8 @@ import { writePage, } from "@silverbulletmd/plugos-silverbullet-syscall/space"; import { invokeFunction } from "@silverbulletmd/plugos-silverbullet-syscall/system"; -import { parseQuery, renderQuery } from "./engine"; +import { renderQuery } from "./engine"; +import { parseQuery } from "./parser"; import { replaceTemplateVars } from "../core/template"; import { jsonToMDTable, queryRegex } from "./util"; import { dispatch } from "@plugos/plugos-syscall/event"; diff --git a/packages/plugs/query/parser.ts b/packages/plugs/query/parser.ts new file mode 100644 index 0000000..b9aa2a2 --- /dev/null +++ b/packages/plugs/query/parser.ts @@ -0,0 +1,93 @@ +import { + collectNodesOfType, + findNodeOfType, + replaceNodesMatching, +} from "@silverbulletmd/common/tree"; +import { lezerToParseTree } from "@silverbulletmd/common/parse_tree"; +import { valueNodeToVal } from "./engine"; + +// @ts-ignore +import { parser } from "./parse-query"; + +export type Filter = { + op: string; + prop: string; + value: any; +}; + +export type ParsedQuery = { + table: string; + orderBy?: string; + orderDesc?: boolean; + limit?: number; + filter: Filter[]; + select?: string[]; + render?: string; +}; + +export function parseQuery(query: string): ParsedQuery { + let n = lezerToParseTree(query, parser.parse(query).topNode); + // Clean the tree a bit + replaceNodesMatching(n, (n) => { + if (!n.type) { + let trimmed = n.text!.trim(); + if (!trimmed) { + return null; + } + n.text = trimmed; + } + }); + + // console.log("Parsed", JSON.stringify(n, null, 2)); + let queryNode = n.children![0]; + let parsedQuery: ParsedQuery = { + table: queryNode.children![0].children![0].text!, + filter: [], + }; + let orderByNode = findNodeOfType(queryNode, "OrderClause"); + if (orderByNode) { + let nameNode = findNodeOfType(orderByNode, "Name"); + parsedQuery.orderBy = nameNode!.children![0].text!; + let orderNode = findNodeOfType(orderByNode, "Order"); + parsedQuery.orderDesc = orderNode + ? orderNode.children![0].text! === "desc" + : false; + } + let limitNode = findNodeOfType(queryNode, "LimitClause"); + if (limitNode) { + let nameNode = findNodeOfType(limitNode, "Number"); + parsedQuery.limit = valueNodeToVal(nameNode!); + } + + let filterNodes = collectNodesOfType(queryNode, "FilterExpr"); + for (let filterNode of filterNodes) { + let val: any = undefined; + let valNode = filterNode.children![2].children![0]; + val = valueNodeToVal(valNode); + let f: Filter = { + prop: filterNode.children![0].children![0].text!, + op: filterNode.children![1].text!, + value: val, + }; + parsedQuery.filter.push(f); + } + let selectNode = findNodeOfType(queryNode, "SelectClause"); + if (selectNode) { + // console.log("Select node", JSON.stringify(selectNode)); + parsedQuery.select = []; + collectNodesOfType(selectNode, "Name").forEach((t) => { + parsedQuery.select!.push(t.children![0].text!); + }); + // let nameNode = findNodeOfType(selectNode, "Number"); + // parsedQuery.limit = +nameNode!.children![0].text!; + } + + let renderNode = findNodeOfType(queryNode, "RenderClause"); + if (renderNode) { + let renderNameNode = findNodeOfType(renderNode, "String"); + parsedQuery.render = valueNodeToVal(renderNameNode!); + } + + // console.log(JSON.stringify(queryNode, null, 2)); + return parsedQuery; +}