The VS Code extension connects the editor to the language server via LSP. It handles:
- Launching the appropriate server binary for the platform
- Watching template files for changes
- Forwarding editor events to the server
- Displaying results (completions, hover info, etc.) to the user
VS Code Extension (TypeScript)
│
├─ extension.ts .................. Entry point, LSP client setup
├─ package.json .................. Manifest, scripts, dependencies
├─ language-configuration.json ... Bracket matching, commenting
├─ snippets/
│ └─ snippets.json ............ Code snippets
├─ syntaxes/
│ └─ gotmpl.tmLanguage.json ... Syntax highlighting definition
└─ src/
├─ extension.ts ............ Main extension code
└─ test/
└─ sample.test.ts ...... Test files
The extension supports multiple platforms by shipping with all server binaries:
let binaryName: string;
if (process.platform === "win32") {
binaryName = process.arch === "arm64"
? "gotmpl-server-arm64.exe"
: "gotmpl-server.exe";
} else if (process.platform === "darwin") {
binaryName = process.arch === "arm64"
? "gotmpl-server-darwin-arm64"
: "gotmpl-server-darwin-amd64";
} else {
binaryName = process.arch === "arm64"
? "gotmpl-server-arm64"
: "gotmpl-server";
}The extension watches for changes to .*.tmpl files (e.g., template.html.tmpl):
for (const folder of workspace.workspaceFolders) {
const watcher = workspace.createFileSystemWatcher(
new RelativePattern(folder, "**/*.*.tmpl"),
);
watcher.onDidCreate((uri) => console.log(`Created: ${uri.fsPath}`));
watcher.onDidChange((uri) => console.log(`Changed: ${uri.fsPath}`));
watcher.onDidDelete((uri) => console.log(`Deleted: ${uri.fsPath}`));
}These events are forwarded to the server so it can track document changes.
The extension activates when:
- A workspace folder is opened
- The first template file is edited
This is controlled by activationEvents in package.json.
If you're adding a feature that needs a command, add it to extension.ts:
// In activate()
const hoverCommand = vscode.commands.registerCommand(
'goTmplSupport.showHover',
async () => {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return;
}
const document = editor.document;
const position = editor.selection.active;
// Server handles this if registered
// Results are displayed automatically
}
);
context.subscriptions.push(hoverCommand);In language-configuration.json, define editor behavior:
{
"comments": {
"blockComment": ["{{/*", "*/}}"]
},
"brackets": [
["{{" ,"}}"],
["[", "]"],
["{", "}"]
]
}If you're adding new syntax, update syntaxes/gotmpl.tmLanguage.json:
{
"name": "Go text/template",
"scopeName": "text.template.gotmpl",
"patterns": [
{
"include": "#template"
}
],
"repository": {
"template": {
"patterns": [
{
"match": "{{.*?}}",
"name": "meta.template.gotmpl"
}
]
}
}
}In package.json, register the command:
{
"contributes": {
"commands": [
{
"command": "goTmplSupport.showHover",
"title": "Show Hover Information",
"category": "Go Template"
}
],
"keybindings": [
{
"command": "goTmplSupport.showHover",
"key": "ctrl+k ctrl+i",
"mac": "cmd+k cmd+i",
"when": "editorLangId == gotmpl"
}
]
}
}// src/test/hover.test.ts
import * as assert from "assert";
import { after, before } from "mocha";
import * as vscode from "vscode";
suite("Hover Support", () => {
test("Should show hover information", async () => {
const uri = vscode.Uri.file("/path/to/test.tmpl");
// Create test file
const edit = new vscode.WorkspaceEdit();
edit.createFile(uri, { overwrite: true });
edit.insert(uri, new vscode.Position(0, 0), "{{ .Name }}");
await vscode.workspace.applyEdit(edit);
// Open document
const document = await vscode.workspace.openTextDocument(uri);
const editor = await vscode.window.showTextDocument(document);
// Verify hover works
editor.selection = new vscode.Selection(
new vscode.Position(0, 3),
new vscode.Position(0, 3)
);
// Command would be executed here
// Results would be verified
assert.ok(true);
});
});Snippets provide quick template generation. Add to snippets/snippets.json:
{
"If Block": {
"prefix": "if",
"body": [
"{{- if ${1:.condition} }}",
"${2:content}",
"{{- end }}"
],
"description": "Create an if block"
},
"Range Block": {
"prefix": "range",
"body": [
"{{- range ${1:.items} }}",
"${2:content}",
"{{- end }}"
],
"description": "Create a range block"
}
}Read vscode-config.md for documentation about config in the VS Code extension.
Read vscode-testing.md for a guide on testing the VS Code extension.
Read the main README section about how to run and build the extension.