Sunday, March 15, 2009

Creating a TextMate Bundle

I spend a fair amount of time dealing with code written in SQR. Since this isn't a very popular language there isn't a good editor with syntax highlighting for it. I use TextMate a good deal and so it seemed natural to write a bundle for SQR. Step one is to open TextMate and click Bundle then Bundle Editor and then Show Bundle Editor. Once there click the plus at the bottom of the screen and add new bundle.


Now name the bundle something appropriate, in my case SQR. Now you need to add a new language to your bundle, the language is what controls syntax highlighting.



This will generate the following code.

{ scopeName = 'source.untitled';
fileTypes = ( );
foldingStartMarker = '/\*\*|\{\s*$';
foldingStopMarker = '\*\*/|^\s*\}';
patterns = (
{ name = 'keyword.control.untitled';
match = '\b(if|while|for|return)\b';
},
{ name = 'string.quoted.double.untitled';
begin = '"';
end = '"';
patterns = (
{ name = 'constant.character.escape.untitled';
match = '\\.';
},
);
},
);
}

Lets go through this in detail.
  • scopeName (line 1) — this should be a unique name for the language. The convention is for these to be dot separated where the left most piece is the most specific. In my case I used source.SQR. (TextMate 1)
  • fileTypes (line 2) — this is an array of file type extensions that the language should (by default) be used with. This is referenced when TextMate does not know what grammar to use for a file the user opens. If however the user selects a grammar from the language pop-up in the status bar, TextMate will remember that choice. (TextMate 1)
  • foldingStartMarker / foldingStopMarker (line 3-4) — these are regular expressions that lines (in the document) are matched against. If a line matches one of the patterns (but not both), it becomes a folding marker. The means you will get the arrow in the left margin that will allow you to expand and collapse large sections of code (TextMate 1)
  • patterns (line 5-18) — this is an array with the actual rules used to parse the document. In this example there are two rules (line 6-8 and 9-17). (TextMate 1)


For example this is the beginning of my definition for the SQR language.


{ scopeName = 'source.SQR';
fileTypes = ( 'sqr', 'sqc', 'SQR', 'SQC' );
foldingStartMarker = '(?i)^\s*+(Begin-Procedure|If|Begin-Report|Begin-Heading|begin-select|begin-sql|#ifdef).*';
foldingStopMarker = '(?i)^\s*+(End-Procedure|End-If|End-Report|End-Heading|end-select|end-sql|#end-if).*';
patterns = (
{ name = 'comment.line.double-slash.SQR';
match = '!.*\n';
},
...
);
}
The name for the scope is source.SQR and it operates by default on files with the following extensions sqr, sqc, SQR, and SQC. Lets look at just one of the folding markers, Begin-Procedure/End-Procedure. The regex match I set up does a case insensitive match looking for a line that starts with any number of spaces then Begin-Procedure and basically the same for End-Procedure. The result:


Yeah for little arrow thingys!

So the other you might note is the lines with '!' are grayed out, this is because they are set to be comments by our pattern. The name of the pattern is a special name that matches a TextMate property, for a full list see here under 12.4 Naming Conventions. In this case I choose one that is for comments, "comment.line.double-slash" then added ".SQR" for the right scope. The second part of the pattern is the match in this case "!.*\n." This will watch and '!' and any characters after it.

You can go on to add more patterns for reserved words, variables, strings, etc. In not too long you can have syntax highlighting all set. Then you can go on to add snippets and other commands, but we while have to save that for another time.

You can download what I have so far here.

(TextMate 1, http://manual.macromates.com/en/language_grammars#naming_conventions.html)