Query code refactor
This commit is contained in:
parent
05edbfe305
commit
fcf712ccc7
@ -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
|
|
@ -114,6 +114,13 @@ export async function quickNoteCommand() {
|
|||||||
await navigate(pageName);
|
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) {
|
export async function insertTemplateText(cmdDef: any) {
|
||||||
let cursorPos = await getCursor();
|
let cursorPos = await getCursor();
|
||||||
let page = await getCurrentPage();
|
let page = await getCurrentPage();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { expect, test } from "@jest/globals";
|
import { expect, test } from "@jest/globals";
|
||||||
import { applyQuery, parseQuery } from "./engine";
|
import { applyQuery } from "./engine";
|
||||||
|
import { parseQuery } from "./parser";
|
||||||
|
|
||||||
test("Test parser", () => {
|
test("Test parser", () => {
|
||||||
let parsedBasicQuery = parseQuery(`page`);
|
let parsedBasicQuery = parseQuery(`page`);
|
||||||
|
@ -1,108 +1,17 @@
|
|||||||
import {
|
import { collectNodesOfType, ParseTree } from "@silverbulletmd/common/tree";
|
||||||
collectNodesOfType,
|
|
||||||
findNodeOfType,
|
|
||||||
ParseTree,
|
|
||||||
replaceNodesMatching,
|
|
||||||
} from "@silverbulletmd/common/tree";
|
|
||||||
import { lezerToParseTree } from "@silverbulletmd/common/parse_tree";
|
|
||||||
import Handlebars from "handlebars";
|
import Handlebars from "handlebars";
|
||||||
import YAML from "yaml";
|
import YAML from "yaml";
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import { parser } from "./parse-query";
|
|
||||||
import { readPage } from "@silverbulletmd/plugos-silverbullet-syscall/space";
|
import { readPage } from "@silverbulletmd/plugos-silverbullet-syscall/space";
|
||||||
import { niceDate } from "../core/dates";
|
import { niceDate } from "../core/dates";
|
||||||
|
import { ParsedQuery } from "./parser";
|
||||||
|
|
||||||
export type QueryProviderEvent = {
|
export type QueryProviderEvent = {
|
||||||
query: ParsedQuery;
|
query: ParsedQuery;
|
||||||
pageName: string;
|
pageName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Filter = {
|
export function valueNodeToVal(valNode: ParseTree): any {
|
||||||
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 {
|
|
||||||
switch (valNode.type) {
|
switch (valNode.type) {
|
||||||
case "Number":
|
case "Number":
|
||||||
return +valNode.children![0].text!;
|
return +valNode.children![0].text!;
|
||||||
|
@ -10,7 +10,8 @@ import {
|
|||||||
writePage,
|
writePage,
|
||||||
} from "@silverbulletmd/plugos-silverbullet-syscall/space";
|
} from "@silverbulletmd/plugos-silverbullet-syscall/space";
|
||||||
import { invokeFunction } from "@silverbulletmd/plugos-silverbullet-syscall/system";
|
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 { replaceTemplateVars } from "../core/template";
|
||||||
import { jsonToMDTable, queryRegex } from "./util";
|
import { jsonToMDTable, queryRegex } from "./util";
|
||||||
import { dispatch } from "@plugos/plugos-syscall/event";
|
import { dispatch } from "@plugos/plugos-syscall/event";
|
||||||
|
93
packages/plugs/query/parser.ts
Normal file
93
packages/plugs/query/parser.ts
Normal file
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user