Template Guide
This guide provides a detailed overview of writing and using templates in the Next project, based on the actual .npl
files provided.
Introduction to NPL Files
NPL (Next Template Language based on Go's text/template) files are used to define templates for code generation in the Next project. These templates control how different programming constructs are translated into various target languages.
See Builtin Template Files for more information.
Basic Template Structure
A typical npl file consists of the following elements:
- Metadata definitions (for entrypoint template file via command line flags
-T
) - Template definitions or overrides
- Main content generation
Example:
{{/* Define metadata in entrypoint template file */}}
{{- define "meta/this"}}file{{end -}}
{{- define "meta/path"}}{{this.Package.Name}}/{{this.Name}}.next.h{{end -}}
{{/* Overrides next/cpp/import template */}}
{{- define "cpp/import" -}}
#include "../{{.File.Package.Name}}/{{.File.Name}}.next.h"
{{- end -}}
{{head}}
{{next this}}
Metadata Definitions
Metadata is crucial for controlling the template behavior. Common metadata includes:
{{- define "meta/this"}}file{{end -}}
{{- define "meta/path"}}{{this.Package.Name}}/{{this.Name}}.next.h{{end -}}
{{- define "meta/skip"}}{{exist meta.path}}{{end -}}
meta/this
: Specifies the type of entity being generated (e.g., "file", "struct", "enum", see API/Context/this for details)meta/path
: Defines the output path for the generated filemeta/skip
: Provides a condition to skip generation
Template Definitions and Overrides
Templates are defined or overridden using the define
keyword:
{{- define "next/cpp/struct" -}}
{{next .Doc}}class {{next .Type}} {
public:
{{next .Type}}() = default;
~{{next .Type}}() = default;
{{next .Fields}}
};
{{- end}}
Template name starting with next/
is reserved for builtin language supporting. For example, if you plan to write a extension for a new language LANG
,you should define templates like next/LANG/struct
.
To extend an existing template, use the super
keyword:
{{- define "cpp/struct" -}}
{{super .}}
{{- with .Annotations.message.type}}
static int message_type() { return {{.}}; }
{{- end}}
{{- end -}}
Language-Specific Templates
Each supported language typically has its own npl file (e.g., go.npl, cpp.npl, java.npl). These files contain language-specific template definitions.
Example from go.npl:
{{- define "next/go/file" -}}
package {{.Package.Name}}
{{super . -}}
{{- end}}
{{- define "next/go/struct" -}}
{{next .Doc}}type {{next .Type}} struct {
{{- next .Fields}}
}
{{- end}}
Example from cpp.npl:
{{- define "next/cpp/file" -}}
#pragma once
{{next .Imports -}}
{{render "file:namespace.begin" . -}}
{{render "file:forward.declarations" . -}}
{{next .Decls -}}
{{render "file:namespace.end" . -}}
{{- end}}
Common Template Patterns
-
File Generation:
{{- define "next/go/file" -}}
package {{.Package.Name}}
{{super . -}}
{{- end}} -
Struct Generation:
{{- define "next/go/struct" -}}
{{next .Doc}}type {{next .Type}} struct {
{{- next .Fields}}
}
{{- end}} -
Enum Generation:
{{- define "next/go/enum" -}}
{{next .Doc}}type {{next .Type}} {{render "enum:member.type" .}}
const (
{{- next .Members}}
)
{{- end}} -
Interface Generation:
{{- define "next/go/interface" -}}
{{next .Doc}}type {{next .Type}} interface {
{{- next .Methods}}
}
{{- end}}
Advanced Techniques
-
Conditional Logic:
{{- if .Annotations.message.type}}
static int message_type() { return {{.Annotations.message.type}}; }
{{- end}} -
Custom Rendering:
{{render "file:namespace.begin" . -}}
{{render "file:forward.declarations" . -}} -
Nested Templates:
{{- define "next/go/imports:decl" -}}
{{_}}
import "strconv"
{{- if .File.Annotations.next.go_imports}}
{{- range (.File.Annotations.next.go_imports | split "," | map (trim | split "." | first | trimPrefix "*") | sort | uniq)}}
import "{{.}}"
{{- end}}
{{- end}}
{{- end}} -
Using Built-in Functions:
{{.Name | camelCase}}
{{.Name | snakeCase | upper}}
Best Practices
- Use consistent naming conventions for templates (e.g.,
<lang>/<element>
,next/<lang>/<element>
) - Leverage metadata to control template behavior
- Create modular, reusable template components
- Use comments to explain complex logic
- Handle language-specific differences in separate files
- Use
super
when extending templates to maintain base functionality - Utilize built-in functions for string manipulations and other common operations
By following these guidelines and patterns, you can create efficient, maintainable, and powerful templates for the Next project. Remember to always consider the specific requirements of each target language when writing your templates.