I'm using vscode to edit latex (with the latex workshop plugin), and I recently started creating my own snippets and really like the feature. However, I find the syntax a bit heavy for creating "small" snippets that just abbreviate frequent sequences of words. In particular, I find it cumbersome to have to give a 'name' to each snippet.
Is there a mechanism for "slim" snippets/aliases, that would e.g. take as input a file where each snippet is one line -- the first word being the abbreviation and the rest what is abbreviated?
You have a couple of options. One is to write an extension that could do this - I'll show code that works like an extension that'll work - it looks complicated but use is pretty simple.
Second, you can come close using the Hyper Snips extension where your snippet file (say latex.hsnips
) could like like this:
snippet dategreeting "Gives you the current date!"
Hello from your hsnip on ``rv = new Date().toDateString()``!
endsnippet
snippet // "Fraction simple" A
\frac{$1}{$2}$0
endsnippet
snippet stte A
some text to expand
endsnippet
The descriptions in "" are not necessary and I eliminated it in the last snippet. The A
flag will immediately insert your replacement text, without it you would Tab to insert the replacement text. As the examples here show you can use javascript within a snippet if you want.
The gif doesn't show it well bit here is a demo of auto-expansion with Hyper Snips:
Alternatively, download the extension macro-commander. It allows you to use vscode extension commands in a macro within your settings. This macro would go into your settings.json
file:
"macros": {
"slimSnippetsInsertion" : [
{
"javascript": [
"const editor = vscode.window.activeTextEditor;",
"const document = editor.document;",
"const we = new vscode.WorkspaceEdit();",
"const cursorPosition = editor.selection.active;", // use whether an actual selection or not, returns a Position
"let keyWordRange = document.getWordRangeAtPosition(cursorPosition);", // returns a Range of start/end Positions or undefined
"if (keyWordRange === undefined) {",
"await window.showInformationMessage(`cursor must be in or immediately after word to be replaced`);",
"return;",
"}",
"let wordAtCursor = document.getText(keyWordRange);", // this is the key word to find in slimSnippets.txt
"const thisWorkspace = vscode.workspace.workspaceFolders[0].uri.toString();",
// file:///c:/Users/Mark/OneDrive/Test Bed
"const snippetFileContent = await vscode.workspace.fs.readFile(vscode.Uri.parse(`${thisWorkspace}/.vscode/slimSnippets.txt`));",
"const snippets = snippetFileContent.toString();",
// ignore leading spaces/tabs before keys
// using a named capturing group for the replacement text
"const regex = new RegExp(`\\r?(?<=\\n|^)[\\t ]*(?<key>${wordAtCursor})[\\t ]+?(?<replacementText>.*?)(?=\\r?\\n|$)`);",
"let found = snippets.match(regex);", // returns null if no matches
// matched a key but only spaces as replacement text, so do nothing and exit
"if (found && found.groups.replacementText.trimStart().length === 0) {",
"await window.showInformationMessage(`replacement text is only spaces, not replacing`);",
"return;",
"}",
"if (found) {", // found at least a matching key
"if (found.groups.replacementText) {", // found non-space replacement text
// replace `\n` and `\t` with unicode values for newline and tab
"let replace = found.groups.replacementText.replace(/\\\\n/g, '\\u000A').replace(/\\\\t/g, '\\u0009');",
"let snippet = new vscode.SnippetString(replace)",
"if (editor.selections.length === 1) editor.insertSnippet(snippet, keyWordRange);", // if zero or one selection"
// if multiple selections, uses first key and replacement text"
"else editor.insertSnippet(snippet);",
"}",
"else await window.showInformationMessage(`matching key found but with no replacement text in slimSnippets.txt`);",
"}",
"else await window.showInformationMessage(`no matching key found in slimSnippets.txt`);",
]
}
You can see where I made it to read a simpleSnippets.txt
file located in the .vscode
folder in the workspace - but you can change the location as long as you alter the path info in the command: vscode.workspace.fs.readFile
above.
The slimSnippets.txt
file is just a simple text file where the first word in each line is the key
and the rest of the line is the replacement.
howdy1 $1 first $2 sentence with tabstops
howdy1 this won't be used, duplicate key above
howdy2 second sentence with variable $TM_FILENAME
key3 videos 111111 // one space necessary between key and replacement text
// it will be removed, others retained
key1 222222
stte some text to expand
mt2e more text to expand
[replacement text can have placeholders, tabstops and choices just like regular snippets]
[join multiple-lines snippets into one string with newlines as below]
[some text\nsome more text] [\t can be used for tabs]
key5 line 1\n\tline 2\n\t\tline 3
Keys are single words and if there is no replacement text (or there are only spaces in the file after a key) nothing will happen - the key will not be replaced.
The text actually inserted can be plain text or use vscode's snippet format - see the sample text file above.
The cursor must be immediately after the word or in the word and the word can be selected or not. It must be a word in the regex sense - not continuous text adjoining the word before or after it - just a standalone word, it can be anywhere on the line.
If you have duplicate keys, the first will be used. There can be empty line spaces between key/replacement lines or not.
You will not get intellisense for the keys. I may work on that.
Finally, you will need a keybinding to trigger this macro (in keybindings.json
):
{
"key": "ctrl+;", // whatever keybinding you wish
"command": "macros.slimSnippetsInsertion"
},