Benjamin Sago / ogham / cairnrefinery / etc…

Technical notes Quickly create files from templates

An automation path I’ve seen few people reach for is having an easy ability to create a file from a template, automatically filling in its contents before opening it in your editor. I haven’t run the statistics on this, but probably something like 99% of the times I need to create a file in some folder, I already have a terminal open in that directory, so a command-line function is a nice and easy way to do it.

Since writing this function, I’ve started using it for all sorts of things:

  • Starting new projects with the configuration files I like. For example, when starting a Rust project, I’ll use my own Cargo.toml template rather than running cargo init, because my template includes the common dependencies, options, and profiles I’m going to be setting anyway.

  • Initialising small playground scripts for when I want to test something. For example, I have a “Ruby + WEBrick” template that imports WEBrick and starts it listening on a port, so if I want to test something in isolation without all my existing code getting in the way, I can quickly spin up a new Web server.

  • A bunch of uncategorisable stuff such as “HTML5 page” which has the usual structure and common <head> tags in it.

My name is Newt

Because this creates new files from templates, I called my function newt:

functions/newt.fish
function newt -d "Creates a new file from a template" -a template destination

    # check that all args are given
    if test -z "$template"
        echo "newt: Template argument not given" >&2
        return 3
    else if test -z "$destination"
        echo "newt: Destination argument not given" >&2
        return 3
    end

    # use template name if target is a directory
    if test -d "$destination"
        set destination "$destination/"(basename $template)
    end

    # check that target is available
    if test -e "$destination"
        echo "newt: ‘$destination’ already exists" >&2
        return 1
    end

    # check that the chosen template exists
    set -l tempeh "$HOME/.config/templates/$template"
    if test ! -f "$tempeh"
        echo "newt: No such template ‘$template’" >&2
        return 2
    end

    # apply the template and start editing it
    cp $tempeh $destination
    and eval $EDITOR $destination
end

The script takes two arguments: template, the relative path to the template file, and destination, where it should put it. It’ll then do a simple cp to copy the template into the destination, and then open it in whichever editor you have $EDITOR set to.

Shell completions

One of the great things about having all the templates as just files in a folder is that it’s dead simple to write a small shell completion function for newt, letting you tab-complete the names of the templates:

completions/newt.fish
function __fish_list_newt_templates
    pushd $HOME/.config/templates
    fd -t f | sed -e 's,^\./,,'
    popd
end

complete -c newt -n __fish_is_first_arg -a "(__fish_list_newt_templates)" --no-files

These completions work by listing the templates directory using fd to list all files, skipping directories, and then stripping their prefix. (There’s probably a better way to do this, but I failed to find it.) We only want the first argument to be completed, so the completion is guarded with __fish_is_first_arg.

Editor integration

You might find that your text editor already comes with a similar feature. If you’re lucky, it’ll use the same simple-tree-of-text-files set of templates used here, making it nice and easy to integrate the two together.

My primary text editor, BBEdit, calls this feature “Stationery” — that name being the very, very old Mac terminology for such a thing — and so I’ve simply symlinked ~/.config/templates to the BBEdit Stationery folder, meaning I have access to the same set of templates from both within the application and from the command-line.