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 CommentMarkerTag = Tag.define();
export const BulletList = Tag.define(); export const BulletList = Tag.define();
export const OrderedList = 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 { class CommentParser implements LeafBlockParser {
nextLine() { nextLine() {
return false; return false;
@ -88,6 +113,7 @@ export default function buildMarkdown(mdExtensions: MDExt[]): Language {
WikiLink, WikiLink,
TaskList, TaskList,
Comment, Comment,
Strikethrough,
Table, Table,
...mdExtensions.map(mdExtensionSyntaxConfig), ...mdExtensions.map(mdExtensionSyntaxConfig),
// parseCode({ // parseCode({

View File

@ -23,3 +23,7 @@ export async function dispatch(
.catch(reject); .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); 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[]> { async dispatchEvent(eventName: string, data?: any): Promise<any[]> {
if (!this.system) { if (!this.system) {
throw new Error("Event hook is not initialized"); 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) => { "event.dispatch": async (ctx, eventName: string, data: any) => {
return eventHook.dispatchEvent(eventName, data); return eventHook.dispatchEvent(eventName, data);
}, },
"event.list": async () => {
return eventHook.listEvents();
},
}; };
} }

View File

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

View File

@ -1,15 +1,3 @@
import { insertAtCursor } from "@silverbulletmd/plugos-silverbullet-syscall/editor";
export function niceDate(d: Date): string { export function niceDate(d: Date): string {
return d.toISOString().split("T")[0]; 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), 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"; } from "@silverbulletmd/plugos-silverbullet-syscall/space";
import { import {
filterBox, filterBox,
getCurrentPage,
getCursor,
insertAtCursor,
moveCursor, moveCursor,
navigate, navigate,
prompt, prompt,
@ -15,6 +18,7 @@ import { renderToText } from "@silverbulletmd/common/tree";
import { niceDate } from "./dates"; import { niceDate } from "./dates";
const pageTemplatePrefix = `template/page/`; const pageTemplatePrefix = `template/page/`;
const snippetPrefix = `snippet/`;
export async function instantiateTemplateCommand() { export async function instantiateTemplateCommand() {
let allPages = await listPages(); let allPages = await listPages();
@ -48,12 +52,45 @@ export async function instantiateTemplateCommand() {
await navigate(pageName); 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? // TODO: This should probably be replaced with handlebards somehow?
export function replaceTemplateVars(s: string, pageName: string): string { export function replaceTemplateVars(s: string, pageName: string): string {
return s.replaceAll(/\{\{([^\}]+)\}\}/g, (match, v) => { return s.replaceAll(/\{\{([^\}]+)\}\}/g, (match, v) => {
switch (v) { switch (v) {
case "today": case "today":
return niceDate(new Date()); return niceDate(new Date());
case "tomorrow":
let tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
return niceDate(tomorrow);
case "yesterday": case "yesterday":
let yesterday = new Date(); let yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1); yesterday.setDate(yesterday.getDate() - 1);
@ -62,6 +99,8 @@ export function replaceTemplateVars(s: string, pageName: string): string {
let lastWeek = new Date(); let lastWeek = new Date();
lastWeek.setDate(lastWeek.getDate() - 7); lastWeek.setDate(lastWeek.getDate() - 7);
return niceDate(lastWeek); return niceDate(lastWeek);
case "page":
return pageName;
} }
return match; return match;
}); });
@ -75,12 +114,15 @@ export async function quickNoteCommand() {
await navigate(pageName); await navigate(pageName);
} }
export async function quickTaskCommand() { export async function insertTemplateText(cmdDef: any) {
let isoDate = new Date().toISOString(); let cursorPos = await getCursor();
let [date, time] = isoDate.split("T"); let page = await getCurrentPage();
time = time.split(".")[0]; let templateText: string = cmdDef.value;
let pageName = `${date} ${time}`; let carretPos = templateText.indexOf("|^|");
await writePage(pageName, "* [ ] "); templateText = templateText.replace("|^|", "");
await navigate(pageName); templateText = replaceTemplateVars(templateText, page);
await moveCursor(6); 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); await replaceRange(from, selection.to, text);
} }
export function boldCommand() { export function wrapSelection(cmdDef: any) {
return insertMarker("**"); return insertMarker(cmdDef.wrapper);
}
export function italicCommand() {
return insertMarker("_");
} }
async function insertMarker(marker: string) { 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() { export async function updateMaterializedQueriesCommand() {
const currentPage = await getCurrentPage(); const currentPage = await getCurrentPage();
await save(); await save();
// await flashNotification("Updating materialized queries...");
if ( if (
await invokeFunction( await invokeFunction(
"server", "server",
@ -67,8 +66,17 @@ async function updateTemplateInstantiations(
export async function updateMaterializedQueriesOnPage( export async function updateMaterializedQueriesOnPage(
pageName: string pageName: string
): Promise<boolean> { ): 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); let newText = await updateTemplateInstantiations(text, pageName);
newText = await replaceAsync( newText = await replaceAsync(
newText, newText,

View File

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

View File

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

View File

@ -8,9 +8,11 @@ import {
import { slashCommandRegexp } from "../types"; import { slashCommandRegexp } from "../types";
import { safeRun } from "../../common/util"; import { safeRun } from "../../common/util";
import { Editor } from "../editor"; import { Editor } from "../editor";
import { syntaxTree } from "@codemirror/language";
export type SlashCommandDef = { export type SlashCommandDef = {
name: string; name: string;
description?: string;
}; };
export type AppSlashCommand = { export type AppSlashCommand = {
@ -43,7 +45,7 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
this.slashCommands.set(cmd.name, { this.slashCommands.set(cmd.name, {
slashCommand: cmd, slashCommand: cmd,
run: () => { run: () => {
return plug.invoke(name, []); return plug.invoke(name, [cmd]);
}, },
}); });
} }
@ -59,10 +61,16 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
return null; return null;
} }
let options: Completion[] = []; 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()) { for (let [name, def] of this.slashCommands.entries()) {
options.push({ options.push({
label: def.slashCommand.name, label: def.slashCommand.name,
detail: name, detail: def.slashCommand.description,
apply: () => { apply: () => {
// Delete slash command part // Delete slash command part
this.editor.editorView?.dispatch({ this.editor.editorView?.dispatch({
@ -75,6 +83,7 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
// Replace with whatever the completion is // Replace with whatever the completion is
safeRun(async () => { safeRun(async () => {
await def.run(); 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.CodeInfoTag, class: "code-info" },
{ tag: ct.CommentTag, class: "comment" }, { tag: ct.CommentTag, class: "comment" },
{ tag: ct.CommentMarkerTag, class: "comment-marker" }, { tag: ct.CommentMarkerTag, class: "comment-marker" },
{ tag: ct.Highlight, class: "highlight" },
{ tag: t.emphasis, class: "emphasis" }, { tag: t.emphasis, class: "emphasis" },
{ tag: t.strong, class: "strong" }, { tag: t.strong, class: "strong" },
{ tag: t.atom, class: "atom" }, { tag: t.atom, class: "atom" },

View File

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