Inserting CDATA into a Sublime Text snippet

sublime text

Sublime Text Snippets are XML documents, with the body of the snippet is inside a CDATA node. So what happens if you need the body of the snippet to have a CDATA of its own?

XML documents cannot have nested CDATA blocks, so any CDATA you put in there is illegal. The problem is not the opening tag itself, the <![CDATA[, which is just treated as part of the data, i.e. ignored. The problem is the closing tag, the ]]> - it will close the CDATA before you need to, so any XML after that will likely be invalid.

Sublime insertion points

Snippets can have insertion points: ${1} is where the cursor will be when you create the snippet, ${2} is where it will go to when you tab, and so on. Incidentally, ${1:DEFAULT} will select the word "DEFAULT" and leave the cursor there, so you can just tab away or type something to overwrite it. Anyway.

It turns out you can use the insertion point ${-1} too. It does absolutely nothing at all, which is just what we need - something that can be used to break up the ]]> without doing anything at all.

Let's say we want to create a snippet with this body, and we want "TITLE" to be selectable, with one insertion point in each CDATA

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <title><![CDATA[TITLE]]></title>
  <description><![CDATA[]]></description>
  <source><![CDATA[]]></source>

Here's how to use the ${-1} trick to break up the CDATA blocks - note how it goes in between the ]] and the closing >

<snippet>
    <content><![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <title><![CDATA[${1:TITLE}]]${-1}></title>
  <description><![CDATA[$2]]${-1}></description>
  <source><![CDATA[$3]]${-1}></source>
]]></content>
    <!-- Optional: Set a tabTrigger to define how to trigger the snippet -->
    <!-- <tabTrigger>hello</tabTrigger> -->
    <!-- Optional: Set a scope to limit where the snippet will trigger -->
    <!-- <scope>source.python</scope> -->
</snippet>

That's rather simple once you know how.