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$
);

Step 1

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); Step 2

  • 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