From d5f1ba658398d339c685d6048d0ee743171688cd Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Sat, 24 Feb 2024 09:08:10 +0100 Subject: [PATCH] Support carets in matchregex snippets --- plugs/template/snippet.ts | 37 +++++++++++++++----- website/CHANGELOG.md | 1 + website/Snippets.md | 71 +++++++++++++++++++++++++-------------- 3 files changed, 75 insertions(+), 34 deletions(-) diff --git a/plugs/template/snippet.ts b/plugs/template/snippet.ts index e74008bd..5b635d0c 100644 --- a/plugs/template/snippet.ts +++ b/plugs/template/snippet.ts @@ -110,6 +110,7 @@ export async function insertSnippetTemplate(slashCompletion: SlashCompletion) { if (snippetTemplate.match || snippetTemplate.matchRegex) { const pageText = await editor.getText(); + // Regex matching mode const matchRegex = new RegExp( (snippetTemplate.match || snippetTemplate.matchRegex)!, @@ -119,21 +120,41 @@ export async function insertSnippetTemplate(slashCompletion: SlashCompletion) { while (startOfLine > 0 && pageText[startOfLine - 1] !== "\n") { startOfLine--; } - let currentLine = pageText.slice(startOfLine, cursorPos); + let endOfLine = cursorPos; + while (endOfLine < pageText.length && pageText[endOfLine] !== "\n") { + endOfLine++; + } + let currentLine = pageText.slice(startOfLine, endOfLine); + const caretParts = replacementText.split("|^|"); const emptyLine = !currentLine; - currentLine = currentLine.replace(matchRegex, replacementText); + currentLine = currentLine.replace(matchRegex, caretParts[0]); + + let newSelection = emptyLine + ? { + anchor: startOfLine + currentLine.length, + } + : undefined; + + if (caretParts.length === 2) { + // The semantics of a caret in a replacement are: + // 1. It's a caret, so we need to move the cursor there + // 2. It's a placeholder, so we need to remove it + // 3. Any text after the caret should be inserted after the caret + const caretPos = currentLine.length; + // Now add the text after the caret + currentLine += caretParts[1]; + newSelection = { + anchor: startOfLine + caretPos, + }; + } await editor.dispatch({ changes: { from: startOfLine, - to: cursorPos, + to: endOfLine, insert: currentLine, }, - selection: emptyLine - ? { - anchor: startOfLine + currentLine.length, - } - : undefined, + selection: newSelection, }); } else { const carretPos = replacementText.indexOf("|^|"); diff --git a/website/CHANGELOG.md b/website/CHANGELOG.md index 077feb80..ba2b7071 100644 --- a/website/CHANGELOG.md +++ b/website/CHANGELOG.md @@ -20,6 +20,7 @@ _These features are not yet properly released, you need to use [the edge builds] * There are also two very notable new plugs you may be interested in trying: * [[Plugs/TreeView]]: a sidebar showing (and allowing you to manipulate) your space’s folder tree (at long last) * [[Plugs/AI]]: various clever AI integrations (supporting many different LLMs, including locally hosted ones) +* [[Snippets]] using `matchRegex` can now use the `|^|` caret to wrap text around the replacement, see the [[Snippets#Examples]] * [[Space Script]] is now indexed in templates too (so you can put space script in template tagged pages) * Changed the signature of `silverbullet.registerFunction` to make the first argument an object, see [[Space Script#Custom functions]]. Old string-based scripts still work, for backwards compatibility. * The [[Functions#replace(str, match, replacement)]] function now supports multiple replacements diff --git a/website/Snippets.md b/website/Snippets.md index c2cf3129..36279af5 100644 --- a/website/Snippets.md +++ b/website/Snippets.md @@ -8,45 +8,64 @@ You define a snippet by creating a [[Templates|template]] with a `hooks.snippet` * `command`: expose the snippet as a [[Commands|command]]. * `key`: Bind the snippet to a keyboard shortcut (note: this requires to _also_ specify the `command` configuration). * `mac`: Bind the snippet to a Mac-specific keyboard shortcut. -* `matchRegex` (advanced use only): match the current line against a regular expression, and replace the match with the template’s body. +* `matchRegex` (advanced use only): match the _current line_ against a regular expression, and replace the match with the template’s body. If a caret placeholder (`|^|`) appears in the template’s body, the replacement body _before_ the caret will be the replacement of the matchRegex match, and the part _after_ that carret will be appended to the end of the line. This enables text wrapping behavior, see the example below. * `insertAt`: by default a snippet is inserted at the cursor position, but alternatively it can be inserted at: `line-start`, `line-end`, `page-start` or `page-end`. -Minimal example: - - --- - tags: template - hooks.snippet: - slashCommand: meeting-notes - --- - ## Meeting notes for {{today}}! - - |^| - -## Frontmatter +# Frontmatter A template’s [[Frontmatter]] is interpreted by SilverBullet’s [[Templates|template]] engine and removed when instantiated. However, to inject frontmatter after instantiation, you can use the `frontmatter` attribute. Example: - --- - tags: template - hooks.snippet.slashCommand: meeting-notes - frontmatter: | - date: {{today}} - --- - ## Meeting notes for {{today}}! +``` +--- +tags: template +hooks.snippet.slashCommand: meeting-notes +frontmatter: | + date: {{today}} +--- +## Meeting notes for {{today}}! - |^| +|^| +``` Which will expand into e.g. - --- - date: 2023-11-11 - --- - ## Meeting notes for 2023-11-11 +``` +--- +date: 2023-11-11 +--- +## Meeting notes for 2023-11-11 - . +. +``` When the page already contained frontmatter before inserting the snippet, it will be augmented with the additional frontmatter specified by the template. +# Examples +A minimal example using a caret placeholder (to position the cursor after snippet insertion): + +``` +--- +tags: template +hooks.snippet.slashCommand: meeting-notes +--- +## Meeting notes for {{today}}! + +|^| +``` + +A more advanced example using `matchRegex`: a variant of the [[Library/Core/Snippet/Task]] template which adds a `creationDate` [[Attributes|attribute]] at the end: + +``` +--- +tags: template +description: Make this a task with a creation date +hooks.snippet: + slashCommand: task-created + matchRegex: "^(\\s*)[\\-\\*]?\\s*(\\[[ xX]\\])?\\s*" +--- +$1* [ ] |^| [creationDate: {{today}}] +``` + # Use A snippet can be _triggered_ via the specified `slashCommand` via `/slashCommand` or via {[Open Command Palette]} and/or its associate key bindings when `command`, `key`/`mac` are specified.