1
0

Template cleanup, autocomplete for more query stuff

This commit is contained in:
Zef Hemel 2022-07-04 15:07:27 +02:00
parent e2e7e2f936
commit 60cfefbc95
18 changed files with 235 additions and 69 deletions

View File

@ -9,3 +9,4 @@ export const CommentTag = Tag.define();
export const CommentMarkerTag = Tag.define();
export const BulletList = Tag.define();
export const OrderedList = Tag.define();
export const Highlight = Tag.define();

View File

@ -53,6 +53,31 @@ const WikiLink: MarkdownConfig = {
],
};
const HighlightDelim = { resolve: "Highlight", mark: "HighlightMark" };
export const Strikethrough: MarkdownConfig = {
defineNodes: [
{
name: "Highlight",
style: { "Highlight/...": ct.Highlight },
},
{
name: "HighlightMark",
style: t.processingInstruction,
},
],
parseInline: [
{
name: "Highlight",
parse(cx, next, pos) {
if (next != 61 /* '=' */ || cx.char(pos + 1) != 61) return -1;
return cx.addDelimiter(HighlightDelim, pos, pos + 2, true, true);
},
after: "Emphasis",
},
],
};
class CommentParser implements LeafBlockParser {
nextLine() {
return false;
@ -88,6 +113,7 @@ export default function buildMarkdown(mdExtensions: MDExt[]): Language {
WikiLink,
TaskList,
Comment,
Strikethrough,
Table,
...mdExtensions.map(mdExtensionSyntaxConfig),
// parseCode({

View File

@ -23,3 +23,7 @@ export async function dispatch(
.catch(reject);
});
}
export async function listEvents(): Promise<string[]> {
return syscall("event.list");
}

View File

@ -21,6 +21,30 @@ export class EventHook implements Hook<EventHookT> {
this.localListeners.get(eventName)!.push(callback);
}
// Pull all events listened to
listEvents(): string[] {
if (!this.system) {
throw new Error("Event hook is not initialized");
}
let eventNames = new Set<string>();
for (const plug of this.system.loadedPlugs.values()) {
for (const [name, functionDef] of Object.entries(
plug.manifest!.functions
)) {
if (functionDef.events) {
for (let eventName of functionDef.events) {
eventNames.add(eventName);
}
}
}
}
for (let eventName of this.localListeners.keys()) {
eventNames.add(eventName);
}
return [...eventNames];
}
async dispatchEvent(eventName: string, data?: any): Promise<any[]> {
if (!this.system) {
throw new Error("Event hook is not initialized");

View File

@ -6,5 +6,8 @@ export function eventSyscalls(eventHook: EventHook): SysCallMapping {
"event.dispatch": async (ctx, eventName: string, data: any) => {
return eventHook.dispatchEvent(eventName, data);
},
"event.list": async () => {
return eventHook.listEvents();
},
};
}

View File

@ -123,34 +123,63 @@ functions:
# Template commands
insertPageMeta:
path: "./page.ts:insertPageMeta"
path: "./template.ts:insertTemplateText"
slashCommand:
name: meta
value: |
```meta
|^|
```
insertTask:
path: "./template.ts:insertTemplateText"
slashCommand:
name: task
value: "* [ ] |^|"
insertQuery:
path: "./template.ts:insertTemplateText"
slashCommand:
name: query
value: |
<!-- #query |^| -->
<!-- /query -->
insertTaskToday:
path: "./template.ts:insertTemplateText"
slashCommand:
name: task-today
value: "* [ ] |^| 📅 {{today}}"
quickNoteCommand:
path: ./template.ts:quickNoteCommand
command:
name: "Template: Quick Note"
key: "Alt-Shift-n"
quickTaskCommand:
path: ./template.ts:quickTaskCommand
command:
name: "Template: Quick Task"
key: "Alt-Shift-t"
instantiateTemplateCommand:
path: ./template.ts:instantiateTemplateCommand
command:
name: "Template: Instantiate for Page"
insertToday:
path: "./dates.ts:insertToday"
name: "Template: Instantiate Page"
insertSnippet:
path: ./template.ts:insertSnippet
command:
name: "Template: Insert Snippet"
slashCommand:
name: snippet
description: Insert a snippet
insertTodayCommand:
path: "./template.ts:insertTemplateText"
slashCommand:
name: today
insertTomorrow:
path: "./dates.ts:insertTomorrow"
description: Insert today's date
value: "{{today}}"
insertTomorrowCommand:
path: "./template.ts:insertTemplateText"
slashCommand:
name: tomorrow
description: Insert tomorrow's date
value: "{{tomorrow}}"
# Text editing commands
quoteSelection:
quoteSelectionCommand:
path: ./text.ts:quoteSelection
command:
name: "Text: Quote Selection"
@ -165,17 +194,25 @@ functions:
command:
name: "Text: Number Listify Selection"
bold:
path: ./text.ts:boldCommand
path: ./text.ts:wrapSelection
command:
name: "Text: Bold"
key: "Ctrl-b"
mac: "Cmd-b"
wrapper: "**"
italic:
path: ./text.ts:italicCommand
path: ./text.ts:wrapSelection
command:
name: "Text: Italic"
key: "Ctrl-i"
mac: "Cmd-i"
wrapper: "_"
marker:
path: ./text.ts:wrapSelection
command:
name: "Text: Marker"
key: "Alt-m"
wrapper: "=="
# Plug manager
updatePlugsCommand:

View File

@ -1,15 +1,3 @@
import { insertAtCursor } from "@silverbulletmd/plugos-silverbullet-syscall/editor";
export function niceDate(d: Date): string {
return d.toISOString().split("T")[0];
}
export async function insertToday() {
await insertAtCursor(niceDate(new Date()));
}
export async function insertTomorrow() {
let d = new Date();
d.setDate(d.getDate() + 1);
await insertAtCursor(niceDate(d));
}

View File

@ -236,9 +236,3 @@ export async function parseIndexTextRepublish({ name, text }: IndexEvent) {
tree: await parseMarkdown(text),
});
}
export async function insertPageMeta() {
let cursorPos = await getCursor();
await insertAtCursor("```meta\n\n```");
await moveCursor(cursorPos + 8);
}

View File

@ -5,6 +5,9 @@ import {
} from "@silverbulletmd/plugos-silverbullet-syscall/space";
import {
filterBox,
getCurrentPage,
getCursor,
insertAtCursor,
moveCursor,
navigate,
prompt,
@ -15,6 +18,7 @@ import { renderToText } from "@silverbulletmd/common/tree";
import { niceDate } from "./dates";
const pageTemplatePrefix = `template/page/`;
const snippetPrefix = `snippet/`;
export async function instantiateTemplateCommand() {
let allPages = await listPages();
@ -48,12 +52,45 @@ export async function instantiateTemplateCommand() {
await navigate(pageName);
}
export async function insertSnippet() {
let allPages = await listPages();
let cursorPos = await getCursor();
let page = await getCurrentPage();
let allSnippets = allPages.filter((pageMeta) =>
pageMeta.name.startsWith(snippetPrefix)
);
let selectedSnippet = await filterBox(
"Snippet",
allSnippets,
`Select the snippet to insert (listing any page starting with <tt>${snippetPrefix}</tt>)`
);
if (!selectedSnippet) {
return;
}
let { text } = await readPage(selectedSnippet.name);
let templateText = replaceTemplateVars(text, page);
let carretPos = templateText.indexOf("|^|");
templateText = templateText.replace("|^|", "");
templateText = replaceTemplateVars(templateText, page);
await insertAtCursor(templateText);
if (carretPos !== -1) {
await moveCursor(cursorPos + carretPos);
}
}
// TODO: This should probably be replaced with handlebards somehow?
export function replaceTemplateVars(s: string, pageName: string): string {
return s.replaceAll(/\{\{([^\}]+)\}\}/g, (match, v) => {
switch (v) {
case "today":
return niceDate(new Date());
case "tomorrow":
let tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
return niceDate(tomorrow);
case "yesterday":
let yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
@ -62,6 +99,8 @@ export function replaceTemplateVars(s: string, pageName: string): string {
let lastWeek = new Date();
lastWeek.setDate(lastWeek.getDate() - 7);
return niceDate(lastWeek);
case "page":
return pageName;
}
return match;
});
@ -75,12 +114,15 @@ export async function quickNoteCommand() {
await navigate(pageName);
}
export async function quickTaskCommand() {
let isoDate = new Date().toISOString();
let [date, time] = isoDate.split("T");
time = time.split(".")[0];
let pageName = `${date} ${time}`;
await writePage(pageName, "* [ ] ");
await navigate(pageName);
await moveCursor(6);
export async function insertTemplateText(cmdDef: any) {
let cursorPos = await getCursor();
let page = await getCurrentPage();
let templateText: string = cmdDef.value;
let carretPos = templateText.indexOf("|^|");
templateText = templateText.replace("|^|", "");
templateText = replaceTemplateVars(templateText, page);
await insertAtCursor(templateText);
if (carretPos !== -1) {
await moveCursor(cursorPos + carretPos);
}
}

View File

@ -56,12 +56,8 @@ export async function numberListifySelection() {
await replaceRange(from, selection.to, text);
}
export function boldCommand() {
return insertMarker("**");
}
export function italicCommand() {
return insertMarker("_");
export function wrapSelection(cmdDef: any) {
return insertMarker(cmdDef.wrapper);
}
async function insertMarker(marker: string) {

View File

@ -1,11 +0,0 @@
import {
getCursor,
insertAtCursor,
moveCursor,
} from "@silverbulletmd/plugos-silverbullet-syscall/editor";
export async function insertQuery() {
let cursorPos = await getCursor();
await insertAtCursor(`<!-- #query -->\n\n<!-- /query -->`);
await moveCursor(cursorPos + 12);
}

View File

@ -0,0 +1,32 @@
import { listEvents } from "@plugos/plugos-syscall/event";
import { matchBefore } from "@silverbulletmd/plugos-silverbullet-syscall/editor";
import { listPages } from "@silverbulletmd/plugos-silverbullet-syscall/space";
export async function queryComplete() {
let prefix = await matchBefore("#query [\\w\\-_]*");
if (prefix) {
let allEvents = await listEvents();
// console.log("All events", allEvents);
return {
from: prefix.from + "#query ".length,
options: allEvents
.filter((eventName) => eventName.startsWith("query:"))
.map((source) => ({
label: source.substring("query:".length),
})),
};
}
prefix = await matchBefore('render "[^"]*');
if (prefix) {
let allPages = await listPages();
return {
from: prefix.from + 'render "'.length,
options: allPages.map((pageMeta) => ({
label: pageMeta.name,
})),
};
}
}

View File

@ -19,7 +19,6 @@ import { replaceAsync } from "../lib/util";
export async function updateMaterializedQueriesCommand() {
const currentPage = await getCurrentPage();
await save();
// await flashNotification("Updating materialized queries...");
if (
await invokeFunction(
"server",
@ -67,8 +66,17 @@ async function updateTemplateInstantiations(
export async function updateMaterializedQueriesOnPage(
pageName: string
): Promise<boolean> {
let { text } = await readPage(pageName);
let text = "";
try {
text = (await readPage(pageName)).text;
} catch {
console.warn(
"Could not read page",
pageName,
"perhaps it doesn't yet exist"
);
return false;
}
let newText = await updateTemplateInstantiations(text, pageName);
newText = await replaceAsync(
newText,

View File

@ -17,7 +17,7 @@ functions:
path: ./data.ts:queryProvider
events:
- query:data
insertQueryCommand:
path: ./command.ts:insertQuery
slashCommand:
name: query
queryComplete:
path: ./complete.ts:queryComplete
events:
- page:complete

View File

@ -59,6 +59,7 @@ import { FilterList } from "./components/filter";
import { FilterOption, PageMeta } from "@silverbulletmd/common/types";
import { syntaxTree } from "@codemirror/language";
import sandboxSyscalls from "@plugos/plugos/syscalls/sandbox";
import { eventSyscalls } from "@plugos/plugos/syscalls/event";
// import globalModules from "../common/dist/global.plug.json";
class PageState {
@ -148,6 +149,7 @@ export class Editor {
this.system.registerSyscalls(
[],
eventSyscalls(this.eventHook),
editorSyscalls(this),
spaceSyscalls(this),
indexerSyscalls(this.space),
@ -630,9 +632,15 @@ export class Editor {
editor.focus();
if (cmd) {
dispatch({ type: "command-run", command: cmd.command.name });
cmd.run().catch((e) => {
console.error("Error running command", e.message);
});
cmd
.run()
.catch((e) => {
console.error("Error running command", e.message);
})
.then(() => {
// Always be focusing the editor after running a command
editor.focus();
});
}
}}
commands={viewState.commands}

View File

@ -8,9 +8,11 @@ import {
import { slashCommandRegexp } from "../types";
import { safeRun } from "../../common/util";
import { Editor } from "../editor";
import { syntaxTree } from "@codemirror/language";
export type SlashCommandDef = {
name: string;
description?: string;
};
export type AppSlashCommand = {
@ -43,7 +45,7 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
this.slashCommands.set(cmd.name, {
slashCommand: cmd,
run: () => {
return plug.invoke(name, []);
return plug.invoke(name, [cmd]);
},
});
}
@ -59,10 +61,16 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
return null;
}
let options: Completion[] = [];
// No slash commands in comment blocks (queries and such)
let currentNode = syntaxTree(ctx.state).resolveInner(ctx.pos);
if (currentNode.type.name === "CommentBlock") {
return null;
}
for (let [name, def] of this.slashCommands.entries()) {
options.push({
label: def.slashCommand.name,
detail: name,
detail: def.slashCommand.description,
apply: () => {
// Delete slash command part
this.editor.editorView?.dispatch({
@ -75,6 +83,7 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
// Replace with whatever the completion is
safeRun(async () => {
await def.run();
this.editor.focus();
});
},
});

View File

@ -20,6 +20,7 @@ export default function highlightStyles(mdExtension: MDExt[]) {
{ tag: ct.CodeInfoTag, class: "code-info" },
{ tag: ct.CommentTag, class: "comment" },
{ tag: ct.CommentMarkerTag, class: "comment-marker" },
{ tag: ct.Highlight, class: "highlight" },
{ tag: t.emphasis, class: "emphasis" },
{ tag: t.strong, class: "strong" },
{ tag: t.atom, class: "atom" },

View File

@ -81,6 +81,10 @@
background-color: rgba(72, 72, 72, 0.1);
}
.highlight {
background-color: rgba(255, 255, 0, 0.5);
}
.line-fenced-code {
background-color: rgba(72, 72, 72, 0.1);