IntelliJ Live Templates With Groovy Script
Live Templates have made my programming life a lot easier. There is a lot of boilerplate code that we type every single day. I like using Groovy script to build some powerful Scala live templates.
Unfortunately the documentation for this is almost non existent. Oh well!
I ended up looking at the IntelliJ Scala plugin’s source to find the available variables for live templates. You can see it here - IntelliJ live template variables
Groovy scripts in templates are executed like this - groovyScript(<Script String>, [arg1, arg2...])
Instead of just talking about it, let us work through an example. We will build a live template to convert a simple case class into a Postgres table creation sql script. Imagine you had a case class like this -
case class Person(
firstName: String,
lastName: String,
birthday: LocalDate,
address: String,
zipCode: String,
phoneNumber: String
)
We will come up with an easy way turn this into -
CREATE TABLE person (
first_name VARCHAR(x) NOT NULL,
last_name VARCHAR(x) NOT NULL,
birthday TIMESTAMP NOT NULL,
address VARCHAR(x) NOT NULL,
zip_code VARCHAR(x) NOT NULL,
phone_number VARCHAR(x) NOT NULL
);
- Start by creating a live template: Preferences -> Editor -> Live Templates
- Click on Scala
- Add a new template
- Give it an abbreviation - pgtable
- Give it a description
- Select Scala as the applicable context so that it only shows up in Scala code
- Click Apply just so you don’t lose your changes accidentally
- Add the following template text
CREATE TABLE $TABLENAME$ (
$FIELDS$
);
We have declared two variables - TABLENAME and FIELDS. We’ll have to define what they mean next. From the Scala plugin source above we can see two useful variables: scala_className
and scala_primaryConstructorParams
.
- Click on Edit Variables
- Set snakeCase(scala_className) as the expression for the TABLENAME variable
- This will put the snake cased version of the Scala class name in place of the TABLENAME variable. These are the variables/functions that ship with IntelliJ
- Now for the fun part. Let us build the groovy script that will output the fields with their types. We will use scala_primaryConstructorParams as our input to the groovy script. It outputs all the params with their types in a single string. For example if our case class had i: Int and j: String, it outputs
"i: Int, j: String
- The simple way to achieve this is to
- Split the string on comma
- Map over each item in the list
- Split on
:
to get the field name and type - Lookup the sql type for each Scala type
- Snake case the field
- Write it out as
<field> <sql type> NOT NULL,
- We can work on optional values later - Join the strings with a new line
This will be our groovy script
typesMap = [Int: 'INT', String: 'VARCHAR(x)', LocalDate: 'TIMESTAMP'].withDefault { key ->
key.toLowerCase()
};
def toSnakeCase(s) {
return s.replaceAll( /([A-Z])/, /_$1/ ).toLowerCase().replaceAll( /^_/, '' );
};
def toSql(fieldAndType) {
split = fieldAndType.tokenize(':');
field = split[0].trim();
scalaType = split[1].trim();
sqlType = typesMap[scalaType];
return toSnakeCase(field) + ' ' + sqlType + ' NOT NULL,';
};
tokens = _1.tokenize(',');
dbFields = tokens.collect { token -> return toSql(token.trim()) };
sqlFields = dbFields.join(' \\n ');
return sqlFields;
Now for the FIELDS variable use the groovy script above - `groovyScript(“”, scala_primaryConstructorParams);
Unfortunately the place where you put this string in IntelliJ makes it a single line string. I wish they added a feature to make the editor bigger. So for now you have to end everything with a semicolon to make sure it works as a single line script
Save it. Your cursor needs to be in the context of the case class. So something like
case class Foo() {
<cursor goes here>
}
- Start typing the abbreviation, hit tab and it should put the sql script where your cursor is
- Add more types to the typesMap if you like
- Think about how you would deal with optional values, primary keys, etc
- Write to Jetbrains and ask them to make the script editor larger ¯\_(ツ)_/¯
- Want to work on a better scripting plugin? Write to me :P