1
0

Initial implementation of command link arguments (#573)

Initial implementation of command link arguments

---------

Co-authored-by: prcrst <p-github@prcr.st>
Co-authored-by: Zef Hemel <zef@zef.me>
This commit is contained in:
prcrst 2023-11-25 18:57:00 +01:00 committed by GitHub
parent a03b211dad
commit e6f77b12af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 105 additions and 17 deletions

View File

@ -2,6 +2,7 @@ import { Tag } from "../deps.ts";
export const CommandLinkTag = Tag.define(); export const CommandLinkTag = Tag.define();
export const CommandLinkNameTag = Tag.define(); export const CommandLinkNameTag = Tag.define();
export const CommandLinkArgsTag = Tag.define();
export const WikiLinkTag = Tag.define(); export const WikiLinkTag = Tag.define();
export const WikiLinkPageTag = Tag.define(); export const WikiLinkPageTag = Tag.define();
export const CodeInfoTag = Tag.define(); export const CodeInfoTag = Tag.define();

View File

@ -121,3 +121,40 @@ Deno.test("Test multi-status tasks", () => {
assertEquals(tasks[1].children![0].children![1].text, "x"); assertEquals(tasks[1].children![0].children![1].text, "x");
assertEquals(tasks[2].children![0].children![1].text, "TODO"); assertEquals(tasks[2].children![0].children![1].text, "TODO");
}); });
const commandLinkSample = `
{[Some: Command]}
{[Other: Command|Alias]}
{[Command: Space | Spaces ]}
`;
Deno.test("Test command links", () => {
const lang = buildMarkdown([]);
const tree = parse(lang, commandLinkSample);
const commands = collectNodesOfType(tree, "CommandLink");
console.log("Command links parsed", JSON.stringify(commands, null, 2));
assertEquals(commands.length, 3);
assertEquals(commands[0].children![1].children![0].text, "Some: Command");
assertEquals(commands[1].children![1].children![0].text, "Other: Command");
assertEquals(commands[1].children![3].children![0].text, "Alias");
assertEquals(commands[2].children![1].children![0].text, "Command: Space ");
assertEquals(commands[2].children![3].children![0].text, " Spaces ");
});
const commandLinkArgsSample = `
{[Args: Command]("with", "args")}
{[Othargs: Command|Args alias]("other", "args", 123)}
`;
Deno.test("Test command link arguments", () => {
const lang = buildMarkdown([]);
const tree = parse(lang, commandLinkArgsSample);
const commands = collectNodesOfType(tree, "CommandLink");
assertEquals(commands.length, 2);
const args1 = findNodeOfType(commands[0], "CommandLinkArgs")
assertEquals(args1!.children![0].text, '"with", "args"');
const args2 = findNodeOfType(commands[1], "CommandLinkArgs")
assertEquals(args2!.children![0].text, '"other", "args", 123');
});

View File

@ -68,13 +68,14 @@ const WikiLink: MarkdownConfig = {
], ],
}; };
export const commandLinkRegex = /^\{\[([^\]\|]+)(\|([^\]]+))?\]\}/; export const commandLinkRegex = /^\{\[([^\]\|]+)(\|([^\]]+))?\](\(([^\)]+)\))?\}/;
const CommandLink: MarkdownConfig = { const CommandLink: MarkdownConfig = {
defineNodes: [ defineNodes: [
{ name: "CommandLink", style: { "CommandLink/...": ct.CommandLinkTag } }, { name: "CommandLink", style: { "CommandLink/...": ct.CommandLinkTag } },
{ name: "CommandLinkName", style: ct.CommandLinkNameTag }, { name: "CommandLinkName", style: ct.CommandLinkNameTag },
{ name: "CommandLinkAlias", style: ct.CommandLinkNameTag }, { name: "CommandLinkAlias", style: ct.CommandLinkNameTag },
{ name: "CommandLinkArgs", style: ct.CommandLinkArgsTag },
{ name: "CommandLinkMark", style: t.processingInstruction }, { name: "CommandLinkMark", style: t.processingInstruction },
], ],
parseInline: [ parseInline: [
@ -88,7 +89,7 @@ const CommandLink: MarkdownConfig = {
) { ) {
return -1; return -1;
} }
const [fullMatch, command, pipePart, label] = match; const [fullMatch, command, pipePart, label, argsPart, args] = match;
const endPos = pos + fullMatch.length; const endPos = pos + fullMatch.length;
let aliasElts: any[] = []; let aliasElts: any[] = [];
@ -103,11 +104,26 @@ const CommandLink: MarkdownConfig = {
), ),
]; ];
} }
let argsElts: any[] = [];
if (argsPart) {
const argsStartPos = pos + 2 + command.length + (pipePart?.length ?? 0);
argsElts = [
cx.elt("CommandLinkMark", argsStartPos, argsStartPos + 2),
cx.elt(
"CommandLinkArgs",
argsStartPos + 2,
argsStartPos + 2 + args.length,
),
];
}
return cx.addElement( return cx.addElement(
cx.elt("CommandLink", pos, endPos, [ cx.elt("CommandLink", pos, endPos, [
cx.elt("CommandLinkMark", pos, pos + 2), cx.elt("CommandLinkMark", pos, pos + 2),
cx.elt("CommandLinkName", pos + 2, pos + 2 + command.length), cx.elt("CommandLinkName", pos + 2, pos + 2 + command.length),
...aliasElts, ...aliasElts,
...argsElts,
cx.elt("CommandLinkMark", endPos - 2, endPos), cx.elt("CommandLinkMark", endPos - 2, endPos),
]), ]),
); );

View File

@ -9,8 +9,8 @@ export function invokeFunction(
} }
// Only available on the client // Only available on the client
export function invokeCommand(name: string): Promise<any> { export function invokeCommand(name: string, args?: string[]): Promise<any> {
return syscall("system.invokeCommand", name); return syscall("system.invokeCommand", name, args);
} }
// Only available on the client // Only available on the client

View File

@ -226,3 +226,10 @@ functions:
path: ./upload.ts:uploadFile path: ./upload.ts:uploadFile
command: command:
name: "Upload: File" name: "Upload: File"
customFlashMessage:
path: editor.ts:customFlashMessage
command:
name: "Flash: Custom Message"
contexts:
- internal

View File

@ -50,3 +50,7 @@ export async function moveToPosCommand() {
const pos = +posString; const pos = +posString;
await editor.moveCursor(pos); await editor.moveCursor(pos);
} }
export async function customFlashMessage(_ctx: any, message: string) {
await editor.flashNotification(message);
}

View File

@ -87,7 +87,15 @@ async function actionClickOrActionEnter(
} }
case "CommandLink": { case "CommandLink": {
const commandName = mdTree.children![1]!.children![0].text!; const commandName = mdTree.children![1]!.children![0].text!;
await system.invokeCommand(commandName); const argsNode = findNodeOfType(mdTree, "CommandLinkArgs");
const argsText = argsNode?.children![0]?.text;
// Assume the arguments are can be parsed as the innards of a valid JSON list
try {
const args = argsText ? JSON.parse(`[${argsText}]`) : [];
await system.invokeCommand(commandName, args);
} catch(e: any) {
await editor.flashNotification(`Error parsing command link arguments: ${e.message}`, "error");
}
break; break;
} }
} }

View File

@ -43,7 +43,7 @@ async function saveFile(file: UploadFile) {
editor.insertAtCursor(attachmentMarkdown); editor.insertAtCursor(attachmentMarkdown);
} }
export async function uploadFile() { export async function uploadFile(_ctx: any, accept?: string, capture?: string) {
const uploadFile = await editor.uploadFile(); const uploadFile = await editor.uploadFile(accept, capture);
await saveFile(uploadFile); await saveFile(uploadFile);
} }

View File

@ -879,10 +879,14 @@ export class Client {
} }
} }
async runCommandByName(name: string) { async runCommandByName(name: string, args?: string[]) {
const cmd = this.ui.viewState.commands.get(name); const cmd = this.ui.viewState.commands.get(name);
if (cmd) { if (cmd) {
await cmd.run(); if (args) {
await cmd.run(args);
} else {
await cmd.run();
}
} else { } else {
throw new Error(`Command ${name} not found`); throw new Error(`Command ${name} not found`);
} }

View File

@ -8,6 +8,7 @@ const straightQuoteContexts = [
"FrontMatterCode", "FrontMatterCode",
"DirectiveStart", "DirectiveStart",
"Attribute", "Attribute",
"CommandLink"
]; ];
// TODO: Add support for selection (put quotes around or create blockquote block?) // TODO: Add support for selection (put quotes around or create blockquote block?)

View File

@ -63,7 +63,7 @@ export function createEditorState(
return false; return false;
} }
} }
Promise.resolve() Promise.resolve([])
.then(def.run) .then(def.run)
.catch((e: any) => { .catch((e: any) => {
console.error(e); console.error(e);

View File

@ -14,7 +14,7 @@ export type CommandDef = {
export type AppCommand = { export type AppCommand = {
command: CommandDef; command: CommandDef;
run: () => Promise<void>; run: (args?: string[]) => Promise<void>;
}; };
export type CommandHookT = { export type CommandHookT = {
@ -43,8 +43,8 @@ export class CommandHook extends EventEmitter<CommandHookEvents>
const cmd = functionDef.command; const cmd = functionDef.command;
this.editorCommands.set(cmd.name, { this.editorCommands.set(cmd.name, {
command: cmd, command: cmd,
run: () => { run: (args?: string[]) => {
return plug.invoke(name, [cmd]); return plug.invoke(name, [cmd, ...args??[]]);
}, },
}); });
} }

View File

@ -50,11 +50,11 @@ export function systemSyscalls(
} }
return plug.invoke(name, args); return plug.invoke(name, args);
}, },
"system.invokeCommand": (_ctx, name: string) => { "system.invokeCommand": (_ctx, name: string, args?: string[]) => {
if (!client) { if (!client) {
throw new Error("Not supported"); throw new Error("Not supported");
} }
return client.runCommandByName(name); return client.runCommandByName(name, args);
}, },
"system.listCommands": (): { [key: string]: CommandDef } => { "system.listCommands": (): { [key: string]: CommandDef } => {
if (!client) { if (!client) {

View File

@ -0,0 +1,10 @@
Command links allow you to create buttons in your pages that trigger commands.
# Basic use
{[Stats: Show]} or {[Open Daily Note]}
# Aliasing
{[Stats: Show|Show me stats]}
# Passing arguments
{[Flash: Custom Message|Say hello]("hello there")}

View File

@ -8,7 +8,7 @@ In addition to supporting [[Markdown/Basics|markdown basics]] as standardized by
* [[Live Templates]] * [[Live Templates]]
* [[Anchors]] * [[Anchors]]
* Hashtags, e.g. `#mytag`. * Hashtags, e.g. `#mytag`.
* Command link syntax: `{[Stats: Show]}` rendered into a clickable button {[Stats: Show]}. * [[Markdown/Command links]] syntax
* [Tables](https://www.markdownguide.org/extended-syntax/#tables) * [Tables](https://www.markdownguide.org/extended-syntax/#tables)
* [Task lists](https://www.markdownguide.org/extended-syntax/#task-lists) * [Task lists](https://www.markdownguide.org/extended-syntax/#task-lists)
* [Highlight](https://www.markdownguide.org/extended-syntax/#highlight) * [Highlight](https://www.markdownguide.org/extended-syntax/#highlight)