Custom Postfix Templates for Intellij IDEA
Custom Postfix Templates is an Intellij IDEA plugin that allows you to define your own custom postfix completion templates. At the moment it supports the following programming languages with : Java, Scala, SQL, PHP, Go, Groovy, Python, LaTeX, Kotlin (untyped templates), Dart (untyped templates), JavaScript (untyped templates), and Rust (untyped templates).
So what is the difference to IDEA's postfix templates?
Since IDEA 2018 you are now able to define your own postfix templates in the settings UI (Editor β General β Postfix Templates). However, this is a pretty new feature and it's less functional than this plugin. Here are some of the advantages of this plugin:
- You can define different template rules for the same template name, e.g. .toList should behave differently for arrays and for sets.
- You can use template variables (e.g.
$varName$
) which are filled by the user while applying the template. - You can use live template macros to automatically fill some of the template variables (e.g.
$var:suggestVariableName()$
) as well as you can define default values. - You can restrict the availability of templates or template rules to the availability of certain classes or libraries (e.g. expand
"test".val
toval s = "test"
if Lombok is available). - It allows you to use static imports instead of class imports (e.g.
array.toList
can be expanded toasList(array)
instead ofArrays.asList(array)
if you add[USE_STATIC_IMPORTS]
to the rule). - It comes with more than 500 editable postfix templates with more than 700 template rules, e.g.
string.toInt
βInteger.parse(string)
array.toList
βArrays.asList(array)
file.lines
βFiles.readAllLines(file.toPath(), Charset.forName("UTF-8"))
file.getName().val
βfinal String name = file.getName();
- The text based format for defining your templates allows you to easily share them via copy and paste.
Screencast
Download
You can download the plugin Custom Postfix Templates via Settings β Plugins β Browse Repositories.
Usage
The plugin comes with a predefined set of templates for Java and Scala (see below) which can be immediatly applied in Java/Scala files. For instance, write
"1".toInt
in a Java file. If the completion popup does not automatically show up, press Ctrl+SPACE.
Select the .toInt
template and see how it is expanded.
And if you want to see the template definition, just press Alt+ENTER in the completiion popup and select Edit '.toInt' template.
Kinds of template files
There are three different types of template files:
- User template files: use them to define your own templates and/or override local or web template rules
- Local template files: loaded from the local file system, read-only, and updated automatically when an IDEA project is opened
- Web template files: loaded from the web, read-only, and updated automatically once a day
Order of template files/rules
Template rules are applied in a first-come-first-serve manner, i.e., more specific rules/files should be placed above more general rules/files. Reorder files in the tree by selecting them and by using the up/down buttons.
Predefined web templates files
The plugin comes with a set of so-called "web template files" which provide in total more than 200 useful templates. While web template files are read-only and shall not be edited by the user because of automatic updates, you can still edit or deactivate templates of these files.
To change or deactivate a predefined template you just have to start the template name completion with Ctrl+Space and then press ALT+Enter and select the third item (Edit .TEMPLATE_NAME template). The corresponding web template file is opened and you see the definition of the template rule. Since you cannot this template file directly you have to override the template rule by pressing Alt+Enter and selecting Override template rule. This overriding works in a way that your template rule needs to be loaded before the predefined template gets loaded. This is done by adding your rule to a user template file which is placed above the predefined web template file in the plugin settings. In case that you don't have a user template file which is loaded before, you are offered to create one. After you selected an existing user template or created a new one the template rule to override is automatically added to this file and you can start adapting it. To deactivate a template rule, replace the rigth side of the rule with [SKIP].
Edit the templates
Press Shift+Alt+P (or go to menu Tools β Custom Postfix Templates β Edit Templates of Current Language) to open the custom postfix templates for the programming language in your current editor. Here you can easily change, remove, or add new templates matching your needs. Note that you have to save the template file explicitly (via Ctrl+S) in order to update the postfix templates in the IDE.
Template definitions
The file may contain multiple template definitions of the form:
.TEMPLATE_NAME : TEMPLATE_DESCRIPTION
TEMPLATE_RULE1
TEMPLATE_RULE2
...
Each template definition consists of a template name, a template description and an arbitrary number of template rules. The template name is used as key in the code completion and the template description is shown as hint in the code completion popup. The template rules define on which types the template can be applied and how the application is performed.
Simple template rules
A simple template rule has the form
MATCHING_TYPE β TEMPLATE_CODE
whereas
MATCHING_TYPE
defines the type the template can be applied to, andTEMPLATE_CODE
defines how the template is applied (how the expression is replaced).
MATCHING_TYPE
The options for MATCHING_TYPE may differ from programming language to programming language:
- In Java the MATCHING_TYPE can be either a Java class name or one of the following special types:
ANY
- any expressionVOID
- any void expressionNON_VOID
- any non-void expressionARRAY
- any Java arrayBOOLEAN
- boxed or unboxed boolean expressionsITERABLE_OR_ARRAY
- any iterable or arrayNOT_PRIMITIVE
- any non-primitive valueNUMBER
- any boxed or unboxed numberBYTE
- a boxed or unboxed byte valueSHORT
- a boxed or unboxed short valueCHAR
- a boxed or unboxed char valueINT
- a boxed or unboxed int valueLONG
- a boxed or unboxed long valueFLOAT
- a boxed or unboxed float valueDOUBLE
- a boxed or unboxed double valueNUMBER_LITERAL
- any number literalBYTE_LITERAL
- a byte literalSHORT_LITERAL
- a short literalCHAR_LITERAL
- a char literalINT_LITERAL
- an int literalLONG_LITERAL
- a long literalFLOAT_LITERAL
- a float literalDOUBLE_LITERAL
- a double literalSTRING_LITERAL
- a String literalCLASS
- any class reference
- In Scala the MATCHING_TYPE can be either a Java class name or one of the following special types:
ANY
- any expressionVOID
- any void (Unit) expressionNON_VOID
- any non-void (non-Unit) expressionBOOLEAN
- scala.Boolean or java.lang.BooleanNUMBER
- any Scala or Java number valueBYTE
- scala.Byte or java.lang.ByteSHORT
- scala.Short or java.lang.ShortCHAR
- scala.Char or java.lang.CharINT
- scala.Int or java.lang.IntegerLONG
- scala.Long or java.lang.LongFLOAT
- scala.Float or java.lang.FloatDOUBLE
- scala.Double or java.lang.Double
- In SQL the MATCHING_TYPE can be either a Java class name or one of the following special types:
ANY
- any expressionUNKNOWN
- unknown expressionDEFAULT
- ?INTEGER
- integer expressionREAL
- real expressionSTRING
- string expressionBOOLEAN
- boolean expressionDATE_TIME
- date-time expressionDATE
- date expressionTIME
- time expressionTIMESTAMP
- timestamp expressionINTERVAL
- interval expressionBYTES
- bytes expressionREFERENCE
- ?ARRAY
- array expressionCOLLECTION
- collection expressionTABLE
- table referenceRECORD
- ?SETO
- ?
- In PHP the MATCHING_TYPE can be either a PHP class name or one of the following special types:
ANY
- any expressionempty
null
string
boolean
int
float
object
callable
resource
array
iterable
number
void
unset
static
\Closure
\Exception
\Throwable
- In Go the MATCHING_TYPE can be one of the following special types:
ANY
- any expressionARRAY
- any arrayBOOLEAN
- any boolean expressionSTRING
- any string expressionINT
- any integer expressionINT64
- any 64 bit integer expressionUINT
- any unsigned integer expressionFLOAT
- any floating point expressionFLOAT32
- any 32 bit floating point expressionFLOAT64
- any 64 bit floating point expressionBYTESLICE
- any byte slice expressionERROR
- any error expressionCOMPLEX
- ???NIL
- any expression of type Nil
- In Groovy the MATCHING_TYPE can be either a Java/Groovy class name or one of the following special types:
ANY
- any expressionARRAY
- any Java arrayBOOLEAN
- boxed or unboxed boolean expressionsITERABLE_OR_ARRAY
- any iterable or arrayNUMBER
- any boxed or unboxed numberBYTE
- a boxed or unboxed byte valueSHORT
- a boxed or unboxed short valueCHAR
- a boxed or unboxed char valueINT
- a boxed or unboxed int valueLONG
- a boxed or unboxed long valueFLOAT
- a boxed or unboxed float valueDOUBLE
- a boxed or unboxed double valueCLASS
- any class reference
- In Python the MATCHING_TYPE can be one of the following special types:
ANY
- any expressionobject
list
dict
set
tuple
int
float
complex
str
unicode
bytes
bool
classmethod
staticmethod
type
- In Kotlin the MATCHING_TYPE has to be
ANY
. - In Dart the MATCHING_TYPE has to be
ANY
. - In JavaScript the MATCHING_TYPE has to be
ANY
. - In Rust the MATCHING_TYPE has to be
ANY
- In Latex the MATCHING_TYPE can be one of the following types:
ANY
- any expression in any contextTEXT
- any expression that is not within a math environmentMATH
- any expression that is within a math environment
TEMPLATE_CODE
The TEMPLATE_CODE can be any text which may also contain template variables used as placeholder.
- Simple template variables have the format
$NAME$
. - The following template variables have a special meaning:
$expr$
- the expression the template shall be applied to$END$
- the final cursor position after the template application
- All other variables will be replaced interactively during the template expansion.
- If you want to change the order of variables, set default values or use live template macros for filling the variables automatically, you can use the following variable format:
$NAME#NO:EXPRESSION:DEFAULT_VALUE$
- NAME - name of the variable; use a
*
at the end of the name to skip user interaction - NO (optional) - number of the variable (defining in which order the variables are expanded)
- EXPRESSION (optional) - a live template macro used to generate a replacement (e.g.
suggestVariableName()
) - DEFAULT_VALUE (optional) - a default value that may be used by the macro
- NAME - name of the variable; use a
- If you want to create multi-line templates you can use a backslash (
\
) at the end of a line to indicate that the template code continues at the next line.
Template Examples
- Artificial example showing variable reordering, variable reusage, interaction skipping, macros, and default values:
.test : test NON_VOID β "$user*#1:user()$: $second#3:className()$ + $first#2::"1st"$ + $first$" + $expr$
- Real world example: Write a variable to the debug log, including the developer name, the class name, and method name:
.logd : log a variable NON_VOID β Log.d("$user*:user():"MyTag"$", "$className*:className()$ :: $methodName*:methodName()$): $expr$="+$expr$);
- Multi-line template:
.for : iterate over ... ITERABLE_OR_ARRAY β for ($ELEMENT_TYPE:iterableComponentType(expr):"java.lang.Object"$ $VAR:suggestVariableName()$ : $expr$) {\ $END$\ }
While writing the templates you can use the code completion for completing class names, variable names, template macros and arrows (β).
Advanced template rules
In the chapter above some options have been omitted for simplicity. If you need more functionality here is the full format of template rules including two optional parameters:
MATCHING_TYPE [REQUIRED_CLASS] β TEMPLATE_CODE [FLAG]
- REQUIRED_CLASS (optional) is a name of a class that needs to be available in the module to activate the template rule (see next section for a detailed explaination)
- FLAG (optional) can be one of the following flags:
SKIP
- skips the ruleUSE_STATIC_IMPORTS
- adds static method imports automatically if possible (Java only)IMPORT
... - adds an import to the file header (Scala only)
Writing library specific template rules via REQUIRED_CLASS
Sometimes you may want to write library specific template rules, i.e. rules that shall be only applied when a certain library is included in the project. For instance, take a look at the .val
template provided with this plugin:
.val : extract as value
NON_VOID [lombok.val] β val $var:suggestVariableName()$ = $expr$;
NON_VOID β final $type*:expressionType(expr))$ $var:suggestVariableName()$ = $expr$;
It can be applied to any non-void expression and expands either to
val myVar = myExpression;
if lombok is available, or to
final MyType myVar = myExpression;
if you're using Java without lombok.
In this exmaple template the [lombok.val]
part after the matching type is used to restrict the rule appliction to those cases where the class lombok.val
is available in the class path.
In general you can use any class name between the square brackets you want to define a restriction on.
FLAGs
SKIP
You can use the [SKIP]
flag for deactivating the template rule for a given matching type.
Example:
.sort : sort naturally
de.endrullis.lazyseq.LazySeq β [SKIP]
java.util.List β java.util.Collections.sort($expr$)
In this example a postfix template .sort
is defined.
The first rule tells the plugin that there shall be no completition for expressions of type LazySeq
.
The second rule defines how List
expressions shall be completed.
USE_STATIC_IMPORTS
If you tag a template rule for Java with [USE_STATIC_IMPORTS]
all static methods that are used will be automatically imported and your code gets more compact. For instance, lets take the following template rule:
.toList : convert to List
ARRAY β java.util.Arrays.asList($expr$) [USE_STATIC_IMPORTS]
Since the rule is tagged with [USE_STATIC_IMPORTS]
expanding of array.toList
does not lead to Arrays.asList(array)
but to asList(array)
and the following line is added to your import statements:
import static java.util.Arrays.asList;
IMPORT
If you tag a template rule for Scala with [IMPORT FULLY_QUALIFIED_CLASSNAME]
the given class (or method) import is automatically added to the file header when the template gets applied:
.printStream : get PrintStream
java.io.File β new PrintStream($expr$) [IMPORT java.io.PrintStream]
Note that you can use the IMPORT
flag multiple times.
Update templates and open plugin settings
Go to Settings β Editor β Custom Postfix Templates or Tools β Custom Postfix Templates β Open Settings / Upgrade Templates. There you can chose between two different lambda styles and check/uncheck the template files you want to enable/disable.
Contribute
Any contributions are welcome. Just fork the project, make your changes and create a pull request.
Here are some guides: