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:
parent
a03b211dad
commit
e6f77b12af
@ -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();
|
||||||
|
@ -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');
|
||||||
|
});
|
@ -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),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
@ -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
|
||||||
|
@ -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
|
@ -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);
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
@ -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`);
|
||||||
}
|
}
|
||||||
|
@ -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?)
|
||||||
|
@ -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);
|
||||||
|
@ -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??[]]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
10
website/Markdown/Command links.md
Normal file
10
website/Markdown/Command links.md
Normal 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")}
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user