@
- Creating and inserting code snippets
- List of code snippets
- Code snippet preview
- Code snippet editing
- Custom mapping
- Default Mapping
- autocomplete
- Project Address
Creating and inserting code snippets
The VS Code extension provides data storage, where globalState is a Key-Value way of saving user state using global storage, supporting the retention of certain user states on different computers, for more information seeofficial document
If there is selected text in the editor area, click Create Snippet in the context menu to invoke thecommand to execute the create code snippet.
Creating Service ClassesThe code is as follows
export async function AddSnipp(context: ExtensionContext, state: Partial<ISnipp>) {
const content = await getSnippText();
const trimmedName = content?.text?.trim().substring(0, 20) || '';
await _addOrUpdateSnipp(context, { ...state, name: trimmedName }, content)
}
exist_addOrUpdateSnipp
method of thesnipps
Perform the update operation
async function _addOrUpdateSnipp(context: ExtensionContext, state: Partial<ISnipp>, content?: {
text: string | undefined;
type: string | undefined;
}, snippIndex?: number) {
...
("snipps", updatedSnipps);
If you click Insert Snippet in the right-click menu of the editor area or click on the entry in the code snippet view, call thecommand, which invokes the
InsertSnipp
method performs the insert code snippet operation.
In the service category, insert the following code
export async function InsertSnipp(context: ExtensionContext, snipp: ISnipp) {
const editor = ;
if (editor && (snipp)) {
const position = editor?.;
(async (edit) => {
(position, || '');
});
}
}
List of code snippets
Code snippets are displayed as a tree structure, with code snippet entries grouped according to the type of file content at the time of creation
Interface type for creating code snippets and grouped entries
import * as vscode from "vscode";
export interface ISnipp {
name: string;
content: string;
contentType: string;
created: Date;
lastUsed: Date;
}
export interface IGroup {
name: string;
contentType: string | undefined;
}
Create get accessors in SnippItem to get all grouping types, and getChildren methods to get entries under the grouping
export class SnippItem {
constructor(
readonly view: string,
private context:
) { }
public get roots(): Thenable<IGroup[]> {
const snipps = ?.globalState?.get("snipps", []);
const types = snipps
.map((snipp: ISnipp) => )
.filter((value, index, self) => (value) === index)
.map((type) => ({ name: type, contentType: undefined }));
return (types);
}
public getChildren(node: IGroup): Thenable<ISnipp[]> {
const snipps = ?.globalState
?.get("snipps", [])
.filter((snipp: ISnipp) => {
return === ;
})
.sort((a: ISnipp, b: ISnipp) => ());
return (snipps);
}
export class GroupItem { }
The content displayed in the sidebar of the VS Code extension needs to be tree-structured, by implementing theTreeDataProvider
To provide data for the content, please refer toofficial account
Implement the getChildren method
export class SnippDataProvider
implements
<ISnipp | IGroup>
{
public getChildren(
element?: ISnipp | IGroup
): ISnipp[] | Thenable<ISnipp[]> | IGroup[] | Thenable<IGroup[]> {
return element ? (element) : ;
}
}
Code snippet preview
Implement the getTreeItem method to show previews
Called on clickThe command implements the insertion of code snippets, the COMMAND section is described in the previous section.
Show tooltip preview when mouse over code snippet entry
The code is as follows:
public getTreeItem(element: ISnipp | IGroup): {
const t = ;
const isSnip = (element);
const snippcomm = {
command: "",
title: '',
arguments: [element],
};
let snippetInfo: string = `[${}] ${}`;
return {
// @ts-ignore
label: isSnip ? : ,
command: isSnip ? snippcomm : undefined,
iconPath:isSnip ? new ThemeIcon("code"):new ThemeIcon("folder"),
tooltip: isSnip
? new (
// @ts-ignore
`**caption:**${snippetInfo}\n\n**Modify Time:**${}\n\n**Recent use:**${}\n\n**previews:**\n\`\`\`${}\n${}\n\`\`\``
)
: undefined,
collapsibleState: !isSnip
?
: undefined,
};
}
Code snippet editing
The editor is an input box. Since VS Code's input box does not support multi-line input, it needs to use a webview to realize multi-line input. Also need submit button and cancel button
First create a WebView with a multi-line text box.
In the service categoryto create a function
getWebviewContent
, returns an HTML string used to create a multi-line input box.
function getWebviewContent(placeholder: string, initialValue: string): string {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Multiline Input</title>
</head>
<body>
<textarea rows="10" cols="50" placeholder="${placeholder}">${initialValue}</textarea>
<br>
<button onclick="submitText()">submit (a report etc)</button>
<button onclick="cancel()">abolish</button>
<script>
const vscode = acquireVsCodeApi();
function submitText() {
const text = ('inputBox').value;
({ command: 'submit', text: text });
}
function cancel() {
({ command: 'cancel' });
}
</script>
</body>
</html>
`;
}
Add a handler to return the contents of the text input box when the user clicks "Submit" and close the input box window.
async function showInputBoxWithMultiline(context: ExtensionContext, placeholder: string, initialValue: string): Promise<string | undefined> {
const panel = (
'multilineInput',
'Multiline Input',
,
{
enableScripts: true
}
);
= getWebviewContent(placeholder, initialValue);
return new Promise<string | undefined>((resolve) => {
(
message => {
switch () {
case 'submit':
resolve();
();
return;
case 'cancel':
resolve(undefined);
();
return;
}
},
undefined,
);
});
}
Functions triggered when adding code snippets and editing code snippets
export async function AddSnippFromEditor(context: ExtensionContext, state: Partial<ISnipp>) {
const content = await showInputBoxWithMultiline(context, 'Please enterSnippetelement', '');
if (content) {
_addOrUpdateSnipp(context, state, { text: content, type: "TEXT" })
}
}
export async function EditSnipp(context: ExtensionContext, state: Partial<ISnipp>, snippIndex: number) {
const content = await showInputBoxWithMultiline(context, 'Please enterSnippetelement', ?? '');
if (content) {
_addOrUpdateSnipp(context, state, { text: content, type: ?? "TEXT" }, snippIndex)
}
}
Custom mapping
Mappings are variables that are automatically replaced when a code snippet is inserted, and they are stored in globalState via Key-Value form.
code snippet by setting placeholders (such as the${AUTHOR}
), will be automatically replaced with the value in the global variable when the code snippet is inserted.
When a custom mapping value is not set or is not available, a variable placeholder will be displayed directly
When the extension is initialized, three commonly used custom mappings are inserted and you are free to change or add custom mappings.
-
${AUTHOR}
:: Author's name -
${COMPANY}
:: Company name -
${MAIL}
: E-mail address
All custom mappings in the extension are presented in the Mapping Table tree view.
Example:
Code snippet content
value of 'AUTHOR' is: ${AUTHOR}
value of 'COMPANY' is: ${COMPANY}
value of 'MAIL' is: ${MAIL}
value of 'FOOBAR' (non-exist) is: ${FOOBAR}
After inserting the code snippet, the following is displayed:
value of 'AUTHOR' is: Lin Xiao (1969-), Chinese-American physicist, astronomer and mathematicianlx
value of 'COMPANY' is: my-company
value of 'MAIL' is: jevonsflash@
value of 'FOOBAR' (non-exist) is: ${FOOBAR}
First define the KVItem class:
export class KVItem extends {
constructor(
public readonly key: string,
public readonly value: string | undefined
) {
super(key, );
= `${}: ${}`;
= ;
= 'kvItem';
}
}
The content displayed in the "Mapping Table" tree view needs to be in a tree structure, which also needs to be defined.KVTreeDataProvider
, where methods such as refresh, add, delete, get child nodes, etc. are implemented.
export class KVTreeDataProvider implements <KVItem> {
private _onDidChangeTreeData: <KVItem | undefined> = new <KVItem | undefined>();
readonly onDidChangeTreeData: <KVItem | undefined> = this._onDidChangeTreeData.event;
constructor(private globalState: ) {}
getTreeItem(element: KVItem): {
return element;
}
getChildren(element?: KVItem): Thenable<KVItem[]> {
if (element) {
return ([]);
} else {
const kvObject = <{ [key: string]: string }>('key-value', {});
const keys = (kvObject);
return ((key => new KVItem(key, kvObject[key])));
}
}
refresh(): void {
this._onDidChangeTreeData.fire(undefined);
}
addOrUpdateKey(key: string, value: string): void {
const kvObject = <{ [key: string]: string }>('key-value', {});
kvObject[key] = value;
('key-value', kvObject);
();
}
deleteKey(key: string): void {
const kvObject = <{ [key: string]: string }>('key-value', {});
delete kvObject[key];
('key-value', kvObject);
();
}
}
Default Mapping
The default mapping is the extension's built-in mapping feature, and the available mappings are as follows
Documentation and editor related:
- TM_SELECTED_TEXT: currently selected text or empty string
- TM_CURRENT_LINE: content of the current line
- TM_CURRENT_WORD: the content of the word or empty string under the cursor
- TM_LINE_INDEX: Zero-index based line numbering
- TM_LINE_NUMBER: line number based on an index
- TM_FILENAME: file name of the current document
- TM_FILENAME_BASE: file name of the current document (without extension)
- TM_DIRECTORY: directory of the current document
- TM_FILEPATH: full file path of the current document
- RELATIVE_FILEPATH: relative file path of the current document (relative to the open workspace or folder)
- CLIPBOARD: contents of the clipboard
- WORKSPACE_NAME: name of the open workspace or folder
- WORKSPACE_FOLDER: Path to the open workspace or folder
- CURSOR_INDEX: Cursor number based on zero-indexing
- CURSOR_NUMBER: Cursor number based on a single index
Time Related:
- CURRENT_YEAR: Current year
- CURRENT_YEAR_SHORT: The last two digits of the current year.
- CURRENT_MONTH: two-digit month (e.g. "02")
- CURRENT_MONTH_NAME: full name of the month (e.g. "July")
- CURRENT_MONTH_NAME_SHORT: short name of the month (e.g. "Jul")
- CURRENT_DATE: The day of the month in two digits (e.g. "08").
- CURRENT_DAY_NAME: name of the date (e.g. "Monday")
- CURRENT_DAY_NAME_SHORT: short name of the day (e.g. "Mon")
- CURRENT_HOUR24: current hour in hourly format
- CURRENT_MINUTE: two-digit current minute number
- CURRENT_SECOND: current second in two digits
- CURRENT_SECONDS_UNIX: number of seconds since the Unix epoch
- CURRENT_TIMEZONE_OFFSET The current UTC time zone offset is +HH:MM or -HH:MM (e.g. "-07:00").
Other:
- RANDOM6: Random Base-10 Number
- RANDOM_HEX6: A random Base-16 number.
- UUID: Fourth Edition UUID
These items are referenced to VS Code snippet variables, check out theOfficial VSCode Documentation
As with custom mappings, when the default mapping value is not set or is not available, variable placeholders will be displayed directly
The method of realization is as follows:
export async function ReplacePlaceholders(text: string, context: ExtensionContext): Promise<string> {
const editor = ;
const clipboard = await ();
const workspaceFolders = ;
const currentDate = new Date();
const kvObject = <{ [key: string]: string }>('key-value', {});
const replacements: { [key: string]: string } = {
'${TM_SELECTED_TEXT}': editor?.() || '',
'${TM_CURRENT_LINE}': editor?.().text || '',
'${TM_CURRENT_WORD}': editor?.(()) || '',
'${TM_LINE_INDEX}': (editor?. ?? 0).toString(),
'${TM_LINE_NUMBER}': ((editor?. ?? 0) + 1).toString(),
'${TM_FILENAME}': editor ? () : '',
'${TM_FILENAME_BASE}': editor ? (, ()) : '',
'${TM_DIRECTORY}': editor ? () : '',
'${TM_FILEPATH}': editor?. || '',
'${RELATIVE_FILEPATH}': editor && workspaceFolders ? (workspaceFolders[0]., ) : '',
'${CLIPBOARD}': clipboard,
'${WORKSPACE_NAME}': workspaceFolders ? workspaceFolders[0].name : '',
'${WORKSPACE_FOLDER}': workspaceFolders ? workspaceFolders[0]. : '',
'${CURSOR_INDEX}': (editor?.() ?? 0).toString(),
'${CURSOR_NUMBER}': ((editor?.() ?? 0) + 1).toString(),
'${CURRENT_YEAR}': ().toString(),
'${CURRENT_YEAR_SHORT}': ().toString().slice(-2),
'${CURRENT_MONTH}': (() + 1).toString().padStart(2, '0'),
'${CURRENT_MONTH_NAME}': ('default', { month: 'long' }),
'${CURRENT_MONTH_NAME_SHORT}': ('default', { month: 'short' }),
'${CURRENT_DATE}': ().toString().padStart(2, '0'),
'${CURRENT_DAY_NAME}': ('default', { weekday: 'long' }),
'${CURRENT_DAY_NAME_SHORT}': ('default', { weekday: 'short' }),
'${CURRENT_HOUR}': ().toString().padStart(2, '0'),
'${CURRENT_MINUTE}': ().toString().padStart(2, '0'),
'${CURRENT_SECOND}': ().toString().padStart(2, '0'),
'${CURRENT_SECONDS_UNIX}': (() / 1000).toString(),
'${CURRENT_TIMEZONE_OFFSET}': formatTimezoneOffset(()),
'${RANDOM}': ().toString().slice(2, 8),
'${RANDOM_HEX}': (() * 0xffffff).toString(16).padStart(6, '0'),
'${UUID}': generateUUID()
};
(kvObject).forEach(key => {
replacements[`$\{${key}\}`] = kvObject[key];
});
return (/\$\{(\w+)\}/g, (match, key) => {
return replacements[match] || match;
});
}
autocomplete
Auto-completion is a feature provided by the VS Code editor for displaying auto-prompts and completions in the editor. The extension provides auto-completion based on code snippets.
CompletionItemProvider
The rule used to register for auto-completion, the provider agrees that the auto-completion context menu will appear when the characters entered match under the specified document type.
The context menu lists all available autocomplete entries, each of which is defined by theCompletionItem
definition, clicking on the corresponding entry returns the processed string to be filled in at the editor's current cursor.
Rule provider used to register for auto-completion.
existAll autocomplete entries when registering initialization in the
const providers = contentTypes
.filter((value, index, self) => (value) === index)
.map(type =>
(type, {
provideCompletionItems(
document: TextDocument,
position: Position,
token: CancellationToken,
context: CompletionContext
) {
return new Promise<CompletionItem[]>((resolve, reject) => {
var result = snipps
.filter((snipp: ISnipp) => {
return === type;
})
.map(async (snipp: ISnipp) => {
const replacedContentText = await ReplacePlaceholders(, extensionContext);
const commandCompletion = new CompletionItem();
= replacedContentText || '';
return commandCompletion;
});
(result).then(resolve);
});
}
})
);
(...providers);
Configure modified or new autocomplete entries in the _addOrUpdateSnipp method
if (content?.type && ) {
(, {
provideCompletionItems(
document: TextDocument,
position: Position,
token: CancellationToken,
context: CompletionContext
) {
return new Promise<CompletionItem[]>((resolve, reject) => {
ReplacePlaceholders( || '', extensionContext).then(res => {
const replacedContentText = res;
const commandCompletion = new CompletionItem( || '');
= replacedContentText || '';
resolve([commandCompletion]);
});
});
}
});
}
Project Address
Github:snippet-craft