This guide is for developers using the GoTemplate Support extension. It covers what the extension does, how to get the most out of its features, and how to configure it.
- Obtain the
.vsixpackage from a project release (or build it yourself withnpm run package:vscode). - Open the Extensions sidebar (Ctrl+Shift+X).
- Click the
…menu in the top-right corner of the sidebar and choose Install from VSIX… - Select the
.vsixfile and confirm. VS Code will activate the extension immediately.
- Obtain the
.zipplugin archive from a project release (or build it yourself with./gradlew buildinsideclients/JetBrains/go-text-template/). - Open Settings (Ctrl+Alt+S) → Plugins.
- Click the gear icon ⚙ → Install Plugin from Disk…
- Select the
.zipfile and restart the IDE when prompted.
The extension activates for files with a double extension ending in .tmpl, for example:
page.html.tmpl
query.sql.tmpl
config.yaml.tmplSingle-extension .tmpl files are also supported. The language ID used internally is gotmpl.
Template tags ({{ }}) and their contents are highlighted separately from the surrounding text, regardless of what the host language is (HTML, SQL, YAML, etc.).
Completions trigger automatically when you type $ (variables) or . (dot fields/methods). You can also invoke them manually with Ctrl+Space.
What you get depends on where the cursor is:
| Cursor position | Suggestions |
|---|---|
{{ $ }} |
All variables in scope ($, $i, $v, …) |
{{ . }} |
The . item, or all fields/methods of the current dot type (if a type hint is present) |
{{ .Address. }} |
Fields and methods of the element type (chained access) |
{{ .Items | len | }} |
Only functions that accept int input |
{{ .IsAdmin | not | }} |
Only functions that accept bool input |
Inside {{ if … }}, {{ range … }}, etc. |
All in-scope variables, dot fields/methods, and global functions |
Pipe-aware filtering means the completion list is narrowed based on what the previous command in the pipe produces, so you won't be offered functions with incompatible input types.
Hovering over any node in a template file shows a tooltip. Examples:
| Hovered token | Tooltip |
|---|---|
if .Cond |
If the value of the pipeline is empty, no output is generated; Otherwise, inside is executed. |
range .Items |
Branch executed for each item in a collection. |
with .Value |
Branch executed with a new context. |
$i in range $i, $v |
var $i int - Serves as the index variable in the range loop, representing the current iteration count. |
$x |
var $x Type - variable declaration/type (or (unknown) if not resolvable) |
. |
Returns the current context. |
.Name |
field .Name - Accesses the Name field of the . context. |
end |
From `if` / `range` / `with` at line N. |
else |
From `if` / `range` / `with` at line N. |
len |
A built-in function that returns the length of its argument. |
and |
A built-in function that returns the first argument if it is false, and the last argument otherwise. |
or |
A built-in function that returns the first argument if it is true, and the last argument otherwise. |
not |
A built-in function that returns the boolean negation of its argument. |
nil |
nil is a predeclared identifier representing the zero value for a pointer, channel, func, interface, map, or slice type. |
| Other identifiers | Represents an identifier in a command or action. |
Ctrl+Click (or F12) on a symbol jumps to its definition:
| Symbol | Behaviour |
|---|---|
$x (variable) |
Jumps to all := declarations of $x in the file (VS Code shows all; JetBrains navigates to the first - see client differences) |
. inside range or with |
Jumps to the range/with pipe that redefines dot |
.FieldName |
Jumps to the field or method declaration in the Go source file (requires a type hint) |
.Address.City |
Jumps to whichever identifier the cursor is on |
upper, formatDate, etc. (user-defined function) |
Jumps to the function declaration in the Go source file (requires a function hint) |
Right-click -> Find All References (or Shift+F12) lists every occurrence of a variable or identifier within the current file.
Supported symbols:
| Symbol | Behaviour |
|---|---|
$x (variable) |
All uses of $x in the file |
upper, len, etc. (identifier) |
All uses of that identifier name in the file |
Field access nodes (.FieldName) are not supported by find references.
Errors appear as squiggly underlines as you type. The extension reports:
| Situation | Message |
|---|---|
| Invalid character in a tag | undefined variable: bad character U+003F '?' |
| Empty action | missing value for command |
| Undeclared variable | undefined variable: $x |
| Unknown function | unsupported function or unregistered command: foo |
| Variable redeclared in the same scope | variable $x already declared in this scope |
Variable redeclared inside a range body |
variable $x already declared in this scope |
range over a struct or other non-rangeable |
cannot range over value of type models.Order |
Invalid gotype hint (type not found) |
could not load type "pkg.NoSuchType": … |
| Template called with wrong type | template "T" expects argument of type models.User, but got models.Order |
- Snippets - common template patterns available via the completion menu
- Wrap in comment - wraps the selected text in
{{- /* … */ -}} - Wrap in block - wraps the selection in a block (
{{- if … }} … {{- end }}), using snippets
By default the extension knows the Go built-in template functions and the variables in scope, but it does not know the type of . (the dot context). Adding a type hint unlocks field and method completions, hover types, go-to-definition for fields, and type checking on template calls.
Place a comment on the first line of the template file (or immediately after {{define "name"}} for named blocks):
{{- /*gotype: github.com/example/myapp/models.User*/ -}}The format is gotype: <import-path>.<TypeName>. For a type in the same module you can use the package name directly:
{{/*gotype: models.User*/}}After saving, the extension loads the type from the Go source using go list. This takes a second or two on the first load; subsequent opens are instant because the result is cached.
With the hint in place:
{{ . }}completes with all exported fields and methods ofUser{{ .Address. }}completes with the fields of whateverAddressis- Hovering
.Nameshows the field type - Ctrl+Click on
.Namejumps to theNamefield in the Go source
Each {{define}} block can carry its own independent hint:
{{- /*gotype: models.Address*/ -}}
Street: {{ .Street }}, {{ .City }}
{{define "OrderBlock"}}
{{- /*gotype: models.Order*/ -}}
Order #{{ .ID }} by {{ .Customer.Name }}
{{end}}
{{define "NoHintBlock"}}
{{ . }} {{/* no type-aware features here */}}
{{end}}If a called template has a type hint, the extension checks that the argument you pass matches the expected type:
{{- /*gotype: models.Order*/ -}}
{{/* correct - passing the whole Order to OrderBlock */}}
{{ template "OrderBlock" . }}
{{/* error - UserBlock expects models.User, not models.Order */}}
{{ template "UserBlock" . }}
{{/* correct - passing the User field */}}
{{ template "UserBlock" .Customer }}The error appears as a diagnostic underline on the bad {{ template … }} call.
If your Go code registers custom functions into a template.FuncMap, you can expose them to the extension by adding a //tmpl:func "global" comment above the function that returns the map:
//tmpl:func "global"
func TemplateFuncs() template.FuncMap {
return template.FuncMap{
"upper": strings.ToUpper,
"repeat": strings.Repeat,
"formatDate": formatDate,
}
}Once annotated, upper, repeat, and formatDate will:
- appear in the completion list alongside built-in functions
- not produce "unsupported function" diagnostics
- be usable in pipe filtering (if the signature is resolvable)
The extension picks up changes automatically when any .go file in the workspace is saved - no restart needed.
Create a gotmpl.config.json file in the root of your project to configure the language server for everyone working in that repo, regardless of which IDE they use:
{
"enableHover": true,
"enableDefinition": true,
"enableDiagnostics": true,
"diagnostics": {
"syntaxError": "error",
"doubleDeclaredVariable": "warning",
"invalidFunction": "warning"
},
"enableAutocompletion": true,
"trace": {
"server": "messages"
}
}This file takes precedence over any IDE-specific settings. A server restart is required for changes to take effect.
Open Settings (Ctrl+,) and search for Go Template, or add entries directly to .vscode/settings.json:
{
"goTmplSupport.enableDiagnostics": true,
"goTmplSupport.diagnostics": {
"invalidFunction": "disabled"
},
"goTmplSupport.trace.server": "off"
}Go to Settings -> Tools -> Go Text Template. Application-level settings apply globally; project-level settings (stored in .idea/) override them for the current project.
Common options:
| Option | Type | Default | Description |
|---|---|---|---|
enableHover |
boolean | true |
Show hover tooltips |
enableDefinition |
boolean | true |
Enable go-to-definition |
enableDiagnostics |
boolean | true |
Enable all diagnostics |
enableAutocompletion |
boolean | true |
Enable completions |
pipeChainCompletion |
string | "full" |
Nested field completions: "off", "full", or "step" |
trace.server |
string | "messages" |
LSP trace level: "off", "messages", or "verbose" |
Setting trace.server to "verbose" logs the full LSP traffic and is useful when debugging why a feature isn't working. The output appears in the Output panel (VS Code) or the LSP4IJ console (JetBrains).
The diagnostics object controls severity per check. For example:
"diagnostics": {
"invalidFunction": "warning",
"doubleDeclaredVariable": "disabled"
}For the full list of diagnostic keys and their defaults see docs/configuration.md.
When a specific type in the template should be provided, pipeChainCompletion allows users to see valid fields/methods differently. For example, if a string should be provided, when pipeChainCompletion is set to full, we see all the available children fields/methods like .Address.City or .Address.Info.Desc.Info1 up to the depth of 8. If it is set to step, only immediate fields are suggested in case they or their children match the type. When it is off, nested fields are not suggested.
If you encounter issues while using the extension please report them to us by creating a GitHub issue! You can create one easily here: https://github.com/allseas/go-template-lsp/issues/new?template=bug.yml.
The following sections explain how to find the relevant server logs.
To find the server logs you should go to the Output tab, you can do so by opening the command pallet (Ctrl+Shift+P) and then selecting Output: Focus on Output View. Then select Go Template Language Server and copy the logs into the bug report.
To find the server logs you need to go the Language Servers tab, which you can open in the bottom-left menu. Then, you can navigate to go-text-template-lsp and click on the server process (it will be called started pid:...). You can copy the logs from Traces and Logs.
In order to see the logs you might need to not only change the options in the UI or config, but also in the Language Servers tab. To enable verbose logging here you need to select go-text-template-lsp and go into the Debug tab. Then select the Trace level to verbose and save.

