template

package
v0.13.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 24, 2026 License: MIT Imports: 16 Imported by: 0

README

Template Package

This package provides lexing, parsing, and semantic analysis for Go templates (text/template / html/template). It powers IDE features like diagnostics, go-to-definition, and hover for template files.

Architecture

The package follows a three-stage pipeline:

Source Code → Lexer → Parser → Analyzer → Diagnostics/LSP Features
Package Structure
internal/template/
├── template.go          # Public API: parsing and analysis entry points
├── lexer/               # Tokenization of template syntax
├── parser/              # AST construction from tokens
└── analyzer/            # Semantic analysis and type checking

Sub-packages

lexer/

Tokenizes Go template source code, extracting {{...}} blocks and breaking them into tokens.

Key types:

  • Token - A single lexical element (keyword, variable, operator, etc.)
  • StreamToken - All tokens from one {{...}} block
  • Kind - Token type identifier (Keyword, DollarVariable, DotVariable, etc.)
  • Position / Range - Source locations

Entry point:

func Tokenize(content []byte) ([]*StreamToken, []Error)

Files:

  • lexer.go - Core types and Tokenize() function
  • lexer_tokenize.go - Token stream processing
  • lexer_extract.go - Extracts {{...}} blocks from source
  • lexer_patterns.go - Regex patterns for token recognition
  • lexer_enum.go - Token kind constants
  • lexer_position.go - Position/range utilities
parser/

Builds an Abstract Syntax Tree (AST) from token streams. Handles control flow (if, range, with), template definitions, variable declarations, and expressions.

Key types:

  • AstNode - Interface for all AST nodes
  • GroupStatementNode - Block with nested statements (if, range, with, define)
  • ExpressionNode - Single expression (function call, variable, literal)
  • MultiExpressionNode - Pipeline of expressions (a | b | c)
  • VariableDeclarationNode - Variable declaration ($x := expr)
  • TemplateStatementNode - Template invocation ({{template "name" .}})
  • CommentNode - Template comments, including go:code directives

Entry point:

func Parse(streams []*lexer.StreamToken) (*GroupStatementNode, []lexer.Error)

Files:

  • parser.go - Core Parser type and Parse() function
  • ast.go - AST node type definitions
  • parser_expression.go - Expression parsing
  • parser_keywords.go - Control flow keyword parsing
  • parser_statement.go - Statement-level parsing
  • parser_scope.go - Scope management during parsing
  • parser_util.go - Parser utilities (peek, expect, etc.)
analyzer/

Performs semantic analysis: type checking, variable resolution, template dependency analysis, and provides data for LSP features.

Key types:

  • FileDefinition - Analysis results for a single file
  • VariableDefinition - Variable with its type and scope
  • FunctionDefinition - Template function signature
  • TemplateDefinition - Defined template with input type
  • NodeDefinition - Interface for all symbol definitions

Key concepts:

  • Type inference: Variables declared as any have their types inferred from usage
  • Template dependencies: Tracks which templates call which others
  • go:code directives: Parses Go code in comments to extract type hints

Entry points:

// Single file analysis
func DefinitionAnalysisSingleFile(fileName string, workspace map[string]*parser.GroupStatementNode) (*FileDefinition, []Error)

// Workspace-wide analysis
func DefinitionAnalysisWithinWorkspace(workspace map[string]*parser.GroupStatementNode) []FileAnalysisAndError

Files:

  • analyzer.go - Built-in functions, entry points
  • analyzer_types.go - Type definitions (FileDefinition, VariableDefinition, etc.)
  • analyzer_statements.go - Statement analysis (groups, templates, comments)
  • analyzer_expression.go - Expression analysis
  • analyzer_variables.go - Variable declaration/assignment analysis
  • analyzer_inference.go - Type inference logic
  • analyzer_typecheck.go - Type compatibility checking
  • analyzer_implicit.go - Implicit type tree for inference
  • analyzer_lsp.go - LSP feature support (hover, go-to-definition)
  • template_dependencies_analysis.go - Cross-template dependency tracking
  • funcmap_scanner.go - Scans Go files for custom template functions

Public API (template.go)

The main template package provides high-level functions:

// File operations
func OpenProjectFiles(rootDir string, extensions []string) map[string][]byte

// Parsing
func ParseSingleFile(source []byte) (*parser.GroupStatementNode, []Error)
func ParseFilesInWorkspace(files map[string][]byte) (map[string]*parser.GroupStatementNode, []Error)

// Analysis
func DefinitionAnalysisSingleFile(fileName string, workspace ...) (*FileDefinition, []Error)
func DefinitionAnalysisWithinWorkspace(workspace ...) []FileAnalysisAndError
func DefinitionAnalysisChainTriggeredBySingleFileChange(workspace ..., fileName string) []FileAnalysisAndError

// LSP features
func GoToDefinition(file *FileDefinition, position lexer.Position) ([]string, []lexer.Range, error)
func Hover(file *FileDefinition, position lexer.Position) (string, lexer.Range)
func FoldingRange(root *parser.GroupStatementNode) ([]*parser.GroupStatementNode, []*parser.CommentNode)

// Custom functions
func SetWorkspaceCustomFunctions(funcs map[string]*FunctionDefinition)

Type Inference

The analyzer supports type inference for variables with unknown types (declared as any). When a variable is used in a context that requires a specific type, the analyzer records this constraint and resolves it at scope end.

Example:

{{/* go:code
type Input struct {
    Users []User
}
*/}}

{{range .Users}}        <!-- .Users inferred as []User -->
    {{.Name}}           <!-- . inferred as User, .Name checked against User fields -->
{{end}}

go:code Directives

Templates can include Go code in comments to provide type hints:

{{/* go:code
type Input struct {
    Title string
    Items []Item
}

func formatDate(t time.Time) string
*/}}

The analyzer parses this Go code to:

  1. Set the type of . (the Input type)
  2. Register custom functions with their signatures

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DocumentHighlight

func DocumentHighlight(
	rootNode *parser.GroupStatementNode,
	position lexer.Position,
) []lexer.Range

DocumentHighlight returns the keyword ranges of all linked control flow keywords when the cursor is on one of them. For example, if the cursor is on {{if}}, {{else}}, or {{end}}, all related keywords in the same control flow block are highlighted.

func FoldingRange

func FoldingRange(
	rootNode *parser.GroupStatementNode,
) ([]*parser.GroupStatementNode, []*parser.CommentNode)

func Format added in v0.7.0

func Format(source []byte, opts FormatOptions) []byte

Format re-indents the source based on HTML tag and Go template control block nesting. Only leading whitespace is changed; content within lines is never modified. When PrintWidth > 0, opening HTML tags that exceed the width are wrapped.

func GetWorkspaceCustomFunctions

func GetWorkspaceCustomFunctions() map[string]*checker.FunctionDefinition

GetWorkspaceCustomFunctions returns the currently set custom template functions.

func GoToDefinition

func GoToDefinition(
	file *checker.FileDefinition,
	position lexer.Position,
) (fileNames []string, ranges []lexer.Range, err error)

func HasFileExtension

func HasFileExtension(fileName string, extensions []string) bool

HasFileExtension reports whether fileName's extension is found within extensions.

func Hover

func Hover(file *checker.FileDefinition, position lexer.Position) (string, lexer.Range)

func OpenProjectFiles

func OpenProjectFiles(rootDir string, withFileExtensions []string) map[string][]byte

OpenProjectFiles recursively opens files from 'rootDir'. There is a depth limit for the recursion (current MAX_DEPTH = 5).

func Print

func Print(node ...parser.AstNode)

Print outputs AST nodes as JSON to stdout (use jq for pretty formatting).

func SetWorkspaceCustomFunctions

func SetWorkspaceCustomFunctions(funcs map[string]*checker.FunctionDefinition)

SetWorkspaceCustomFunctions sets the custom template functions for the workspace. These functions are discovered by scanning Go source files for template.FuncMap definitions. Call this once when initializing the workspace.

Types

type DocumentLinkInfo added in v0.10.0

type DocumentLinkInfo struct {
	Range        lexer.Range // range of the template name (without quotes)
	TemplateName string      // the template name
	TargetURI    string      // resolved file URI of the template definition, if known
}

DocumentLinkInfo represents a template call that should be rendered as a clickable link.

func DocumentLinks(rootNode *parser.GroupStatementNode) []DocumentLinkInfo

DocumentLinks returns document links for all template calls in the AST. Each link covers the template name (without quotes) in {{template "name"}} calls.

type Error

type Error = lexer.Error

func DefinitionAnalysisSingleFile

func DefinitionAnalysisSingleFile(
	fileName string,
	parsedFilesInWorkspace map[string]*parser.GroupStatementNode,
) (*checker.FileDefinition, []Error)

DefinitionAnalysisSingleFile performs semantic analysis on a single file. Use DefinitionAnalysisChainTriggeredBySingleFileChange instead for better performance.

func ParseFilesInWorkspace

func ParseFilesInWorkspace(
	workspaceFiles map[string][]byte,
) (map[string]*parser.GroupStatementNode, []Error)

ParseFilesInWorkspace parses all files within a workspace using parallel goroutines. Returns AST nodes and error list. Never returns nil, always an empty 'map' if nothing found. Files are parsed concurrently for improved performance on multi-core systems.

func ParseSingleFile

func ParseSingleFile(source []byte) (*parser.GroupStatementNode, []Error)

ParseSingleFile parses file content (buffer) and returns an AST node and error list. Returned parse tree is never 'nil', even when empty.

type FileAnalysisAndError

type FileAnalysisAndError struct {
	FileName string
	File     *checker.FileDefinition
	Errs     []lexer.Error
}

FileAnalysisAndError pairs analysis results with any errors for a single file.

func DefinitionAnalysisChainTriggeredByBatchFileChange

func DefinitionAnalysisChainTriggeredByBatchFileChange(
	parsedFilesInWorkspace map[string]*parser.GroupStatementNode,
	fileNames ...string,
) []FileAnalysisAndError

DefinitionAnalysisChainTriggeredByBatchFileChange computes semantic analysis for multiple file changes.

func DefinitionAnalysisChainTriggeredBySingleFileChange

func DefinitionAnalysisChainTriggeredBySingleFileChange(
	parsedFilesInWorkspace map[string]*parser.GroupStatementNode,
	fileName string,
) []FileAnalysisAndError

DefinitionAnalysisChainTriggeredBySingleFileChange computes semantic analysis for a file and all affected files.

func DefinitionAnalysisWithinWorkspace

func DefinitionAnalysisWithinWorkspace(
	parsedFilesInWorkspace map[string]*parser.GroupStatementNode,
) []FileAnalysisAndError

DefinitionAnalysisWithinWorkspace performs definition analysis for all files in a workspace.

type FormatOptions added in v0.7.0

type FormatOptions struct {
	TabSize      int
	InsertSpaces bool
	PrintWidth   int    // 0 = disabled, default 120
	AttrWrapMode string // "overflow" or "all"
}

FormatOptions holds configuration for the formatter.

type FunctionDefinition

type FunctionDefinition = checker.FunctionDefinition

FunctionDefinition is an alias to allow external packages to work with custom functions.

type SemanticToken

type SemanticToken struct {
	Line      int
	StartChar int
	Length    int
	TokenType SemanticTokenType
	Modifiers SemanticTokenModifier
}

SemanticToken represents a single semantic token with position and type info.

func SemanticTokens

func SemanticTokens(rootNode *parser.GroupStatementNode) []SemanticToken

SemanticTokens extracts semantic tokens from a parsed template AST. It walks the AST and returns tokens suitable for LSP semantic highlighting.

type SemanticTokenModifier

type SemanticTokenModifier int

SemanticTokenModifier represents semantic token modifiers for LSP.

const (
	SemanticModifierDeclaration SemanticTokenModifier = 1 << iota
	SemanticModifierDefinition
	SemanticModifierReadonly
)

Semantic token modifiers (bit flags, must match the legend in lsp/protocol.go).

type SemanticTokenType

type SemanticTokenType int

SemanticTokenType represents semantic token types for LSP.

const (
	SemanticTokenKeyword SemanticTokenType = iota
	SemanticTokenVariable
	SemanticTokenFunction
	SemanticTokenProperty
	SemanticTokenString
	SemanticTokenNumber
	SemanticTokenOperator
	SemanticTokenComment
)

Semantic token types (must match the legend in lsp/protocol.go).

Directories

Path Synopsis
Package testutil provides shared test helpers for the template package tests.
Package testutil provides shared test helpers for the template package tests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL