Context sensitive slashCommands (with onlyContexts and exceptContexts)

main
Zef Hemel 2025-01-26 07:35:41 +01:00
parent 236b2a7fdd
commit a28c3fd5b8
20 changed files with 95 additions and 23 deletions

View File

@ -53,8 +53,10 @@ export type SlashCommandDef = {
name: string; name: string;
description?: string; description?: string;
boost?: number; boost?: number;
// Parent AST nodes in which this slash command is available // Parent AST nodes in which this slash command is available, defaults to everywhere
contexts?: string[]; onlyContexts?: string[];
// Parent AST nodes in which this slash command is not available
exceptContexts?: string[];
}; };
export type SlashCommandHookT = { export type SlashCommandHookT = {
slashCommand?: SlashCommandDef; slashCommand?: SlashCommandDef;

View File

@ -29,20 +29,37 @@ export async function snippetSlashComplete(
// where hooks.snippet.slashCommand exists // where hooks.snippet.slashCommand exists
filter: ["attr", ["attr", ["attr", "hooks"], "snippet"], "slashCommand"], filter: ["attr", ["attr", ["attr", "hooks"], "snippet"], "slashCommand"],
}, 5); }, 5);
return { const options: SlashCompletionOption[] = [];
options: allTemplates.map((template) => { for (const template of allTemplates) {
const snippetTemplate = template.hooks!.snippet!; const snippetTemplate = template.hooks!.snippet!;
return { if (
snippetTemplate.onlyContexts && !snippetTemplate.onlyContexts.some(
(context) =>
completeEvent.parentNodes.some((node) => node.startsWith(context)),
)
) {
continue;
}
if (
snippetTemplate.exceptContexts && snippetTemplate.exceptContexts.some(
(context) =>
completeEvent.parentNodes.some((node) => node.startsWith(context)),
)
) {
continue;
}
options.push({
label: snippetTemplate.slashCommand, label: snippetTemplate.slashCommand,
detail: template.description, detail: template.description,
order: snippetTemplate.order || 0, order: snippetTemplate.order || 0,
templatePage: template.ref, templatePage: template.ref,
pageName: completeEvent.pageName, pageName: completeEvent.pageName,
invoke: "template.insertSnippetTemplate", invoke: "template.insertSnippetTemplate",
}; });
}), }
}; return { options };
} }
export async function insertSnippetTemplate( export async function insertSnippetTemplate(

View File

@ -31,6 +31,8 @@ export type SnippetConfig = CommandConfig & {
// Deprecated: use matchRegex instead (for backwards compatibility) // Deprecated: use matchRegex instead (for backwards compatibility)
match?: string; match?: string;
insertAt?: "cursor" | "line-start" | "line-end" | "page-start" | "page-end"; // defaults to cursor insertAt?: "cursor" | "line-start" | "line-end" | "page-start" | "page-end"; // defaults to cursor
onlyContexts?: string[];
exceptContexts?: string[];
}; };
export type WidgetConfig = { export type WidgetConfig = {
@ -97,6 +99,12 @@ export const SnippetConfigSchema: JSONSchemaType<SnippetConfig> = {
order: { type: "number", nullable: true }, order: { type: "number", nullable: true },
matchRegex: { type: "string", nullable: true }, matchRegex: { type: "string", nullable: true },
match: { type: "string", nullable: true }, match: { type: "string", nullable: true },
onlyContexts: { type: "array", items: { type: "string" }, nullable: true },
exceptContexts: {
type: "array",
items: { type: "string" },
nullable: true,
},
insertAt: { insertAt: {
type: "string", type: "string",
enum: [ enum: [

View File

@ -110,10 +110,16 @@ export class SlashCommandHook implements Hook<SlashCommandHookT> {
// Check if the slash command is available in the current context // Check if the slash command is available in the current context
const parentNodes = this.editor.extractParentNodes(ctx.state, currentNode); const parentNodes = this.editor.extractParentNodes(ctx.state, currentNode);
// console.log("Parent nodes", parentNodes);
for (const def of this.slashCommands.values()) { for (const def of this.slashCommands.values()) {
if ( if (
def.slashCommand.contexts && !def.slashCommand.contexts.some( def.slashCommand.onlyContexts && !def.slashCommand.onlyContexts.some(
(context) => parentNodes.some((node) => node.startsWith(context)),
)
) {
continue;
}
if (
def.slashCommand.exceptContexts && def.slashCommand.exceptContexts.some(
(context) => parentNodes.some((node) => node.startsWith(context)), (context) => parentNodes.some((node) => node.startsWith(context)),
) )
) { ) {

View File

@ -1,7 +1,10 @@
--- ---
description: Insert a fenced code block description: Insert a fenced code block
tags: template tags: template
hooks.snippet.slashCommand: code hooks.snippet:
slashCommand: code
exceptContexts:
- FencedCode
--- ---
```|^| ```|^|

View File

@ -4,6 +4,8 @@ tags: template
hooks.snippet: hooks.snippet:
slashCommand: "#each" slashCommand: "#each"
order: 10 order: 10
onlyContexts:
- FencedCode:template
--- ---
{{escapeDirective("#each |^|")}} {{escapeDirective("#each |^|")}}

View File

@ -4,5 +4,7 @@ description: Make this a level 1 heading
hooks.snippet: hooks.snippet:
slashCommand: h1 slashCommand: h1
matchRegex: "^#*\\s*" matchRegex: "^#*\\s*"
exceptContexts:
- FencedCode
--- ---
# #

View File

@ -4,5 +4,7 @@ description: Make this a level 2 heading
hooks.snippet: hooks.snippet:
slashCommand: h2 slashCommand: h2
matchRegex: "^#*\\s*" matchRegex: "^#*\\s*"
exceptContexts:
- FencedCode
--- ---
## ##

View File

@ -4,5 +4,7 @@ description: Make this a level 3 heading
hooks.snippet: hooks.snippet:
slashCommand: h3 slashCommand: h3
matchRegex: "^#*\\s*" matchRegex: "^#*\\s*"
exceptContexts:
- FencedCode
--- ---
### ###

View File

@ -4,5 +4,7 @@ description: Make this a level 4 heading
hooks.snippet: hooks.snippet:
slashCommand: h4 slashCommand: h4
matchRegex: "^#*\\s*" matchRegex: "^#*\\s*"
exceptContexts:
- FencedCode
--- ---
#### ####

View File

@ -1,6 +1,9 @@
--- ---
tags: template tags: template
description: Insert a horizontal rule description: Insert a horizontal rule
hooks.snippet.slashCommand: hr hooks.snippet:
slashCommand: hr
exceptContexts:
- FencedCode
--- ---
--- ---

View File

@ -4,6 +4,9 @@ tags: template
hooks.snippet: hooks.snippet:
slashCommand: "#if" slashCommand: "#if"
order: 10 order: 10
onlyContexts:
- FencedCode:template
--- ---
{{escapeDirective("#if |^|")}} {{escapeDirective("#if |^|")}}

View File

@ -4,6 +4,8 @@ tags: template
hooks.snippet: hooks.snippet:
slashCommand: "#if-else" slashCommand: "#if-else"
order: 10 order: 10
onlyContexts:
- FencedCode:template
--- ---
{{escapeDirective("#if |^|")}} {{escapeDirective("#if |^|")}}

View File

@ -1,7 +1,10 @@
--- ---
tags: template tags: template
description: Add a live preview of a page description: Add a live preview of a page
hooks.snippet.slashCommand: include-page hooks.snippet:
slashCommand: include-page
exceptContexts:
- FencedCode
--- ---
```include ```include
raw: "[[|^|]]" raw: "[[|^|]]"

View File

@ -6,5 +6,7 @@ hooks.snippet:
matchRegex: "^(\\s*)[\\-\\*]?\\s*" matchRegex: "^(\\s*)[\\-\\*]?\\s*"
command: "Text: Turn into a bullet item" command: "Text: Turn into a bullet item"
key: "Ctrl-q i" key: "Ctrl-q i"
exceptContexts:
- FencedCode
--- ---
$1* $1*

View File

@ -4,6 +4,8 @@ tags: template
hooks.snippet: hooks.snippet:
slashCommand: "#let" slashCommand: "#let"
order: 10 order: 10
onlyContexts:
- FencedCode:template
--- ---
{{escapeDirective("#let @|^| = ")}} {{escapeDirective("#let @|^| = ")}}

View File

@ -1,7 +1,10 @@
--- ---
description: Insert a live query description: Insert a live query
tags: template tags: template
hooks.snippet.slashCommand: query hooks.snippet:
slashCommand: query
exceptContexts:
- FencedCode
--- ---
```query ```query
|^| |^|

View File

@ -1,7 +1,10 @@
--- ---
tags: template tags: template
description: Insert a table description: Insert a table
hooks.snippet.slashCommand: table hooks.snippet:
slashCommand: table
exceptContexts:
- FencedCode
--- ---
| Header A | Header B | | Header A | Header B |
|----------|----------| |----------|----------|

View File

@ -6,5 +6,7 @@ hooks.snippet:
matchRegex: "^(\\s*)[\\-\\*]?\\s*(\\[[ xX]\\])?\\s*" matchRegex: "^(\\s*)[\\-\\*]?\\s*(\\[[ xX]\\])?\\s*"
command: "Text: Turn into task" command: "Text: Turn into task"
key: "Ctrl-q t" key: "Ctrl-q t"
exceptContexts:
- FencedCode
--- ---
$1* [ ] $1* [ ]

View File

@ -1,7 +1,10 @@
--- ---
description: Insert a template description: Insert a template
tags: template tags: template
hooks.snippet.slashCommand: "template" hooks.snippet:
slashCommand: "template"
exceptContexts:
- FencedCode
--- ---
```template ```template
|^| |^|