Summary ScriptKit now has snippets built-in, but they're lacking a couple of powerful features at the moment.
So I built this addon to handle the selection, cursor, clipboard, and evil
script eval()
:
Here's an example snippet that's going to replace the selection with a script tag and set variables based on clipboard and ScriptKit input:
// Name: Expand TS<script lang="ts">$SELECTION$$CURSOR$const argument = '$$arg("Anything to say?")$$';const clipboard = '$CLIPBOARD$';const number = '$$arg("What is the magic number?", ['42', '7', '8'])$$';</script>
If you like the script, retweet it 😇
// Name: Snippets on Steroids// Author: pyronaur// Twitter: @pyronaur// Shortcut: cmd+shift+e/*** This script expands a given snippet and replaces placeholders with their respective values.** ## Placeholders* - $CURSOR$ - set the cursor position in the snippet after it's expanded.* - $SELECTION$ - insert the currently selected text within the snippet.* - $CLIPBOARD$ - insert the current clipboard text within the snippet.** ## Code Evaluation* You can place any JavaScript code within $$...$$ and it will be evaluated and replaced with the result.** For example:* The code inside the $$...$$ will be executed and the result will replace the placeholder.* Example:* ```* const clipboard = '$$clipboard.readText()$$';* const date = '$$new Date().toLocaleDateString()$$';* const number = '$$arg("What is the magic number?", ['42', '7', '8'])$$';* ```* Note:* The script execution is potentially dangerous and should be enabled with caution.* You have to enable it by setting the `I_AM_THE_DANGER` variable to `true`.*/import "@johnlindquist/kit"import { Choice } from '@johnlindquist/kit';const { globby } = await npm("globby");const snippet_path = kenvPath('snippets');// 🔴 DANGER 🔴// SETTING THIS TO TRUE ALLOW ANY SCRIPT TO BE EXECUTED BY SNIPPETSconst I_AM_THE_DANGER = false;async function dangerous_evil_parse(content: string) {const evil_regex = /\$\$(.*?)\$\$/g;let match;let matches = [];while ((match = evil_regex.exec(content)) !== null) {matches.push(match);}for (let match of matches) {const script = match[1];const result = I_AM_THE_DANGER ? await eval(script) : ''; // 🎩 😎content = content.replace(`$$${script}$$`, result);}return content;}// Find $CURSOR$ and set cursor positionasync function set_with_cursor(content: string) {const cursor_index = content.indexOf('$CURSOR$');if (cursor_index === -1) {return false;}// Remove $$CURSOR$$content = content.replace('$CURSOR$', '');await setSelectedText(content);// There's some async weirdness here// so we'll just wait 100msawait new Promise(resolve => setTimeout(resolve, 100));const target_cursor_position = content.length - cursor_index;const keystrokes = [];for (let i = 0; i < target_cursor_position; i++) {keystrokes.push(keystroke('left'));}await Promise.all(keystrokes);return true;}function files_to_choices(files: string[]): Choice[] {return files.map(file => ({name: path.basename(file, '').split('.')[0],value: file}));}async function get_snippet_files() {// Untested attempt to fix windows paths (I don't have a windows machine)const snippet_files = await globby(`${snippet_path}/*`);if (process.platform == 'win32') {return snippet_files;}return snippet_files.map(file => file.replace(/\\/g, '/'));}async function get_content(snippet: string) {const snippet_content = await readFile(snippet, 'utf8');const snippet_lines = snippet_content.split('\n')// Remove comments and empty lines until first line of snippetreturn snippet_lines.slice(snippet_lines.findIndex(line => !line.startsWith('//'))).join('\n');}async function insert_selection(content: string) {if (content.includes('$SELECTION$') === false) {return content;}const selection = await getSelectedText();content = content.replace('$SELECTION$', selection);return content;}async function insert_clipboard(content: string) {if (content.includes('$CLIPBOARD$') === false) {return content;}const text = await clipboard.readText();content = content.replace('$CLIPBOARD$', text);return content;}// 🚀 Go!const snippet_file = await arg("Which snippet?", files_to_choices(await get_snippet_files()));let content = await get_content(snippet_file);content = await insert_selection(content);content = await insert_clipboard(content);content = await dangerous_evil_parse(content);if (!(await set_with_cursor(content))) {await setSelectedText(content);}
Props @johnlindquist for helpful pointers 👍