Posts RSS Comments RSS 32 Posts and 1,017 Comments till now

TextMate Tip – Using PHP for Commands

Not many people realise that all the commands given to you by TextMate are written as simple scripts, editable by anyone, and in any language. PHP is a widespread language, but not many people are accustomed to using PHP for writing shell scripts, so they don’t know how to start writing a command – here I’ll explain how.

Let’s pretend we’re going to be writing some FAQ lists, so we’ll have a list of questions, like this:

This is the first question 
This is the second question 
This is the *third* question
And we want to convert it to a list of links to the questions, like this:
<a href="faqlist.html#this-is-the-first-question">This is the first question</a>
<a href="faqlist.html#this-is-the-second-question">This is the second question</a>
<a href="faqlist.html#this-is-the-third-question">This is the third question</a>
First, you’ll need to make a new command – open the bundle editor (Bundles → Bundle Editor → Show Bundle Editor… or ⌃⌥⌘B) and use the bottom left button (with a plus sign and an arrow) to create a “New Command”, and name it “Create FAQ Index”. The large text field on the right is where you’ll be entering the code for the command.

To start, you’ll need to tell TextMate that the command needs to run with PHP, which is done with a shebang:

#!/usr/bin/env php 
<?php
//Our code will go here
?>
TextMate commands basically have 3 factors:

  • Reading input (provided on standard input, and some information is given in environment variables)
  • Processing the input
  • Doing something with the result (generally either inserting some text into the current document, or showing a HTML window or tooltip)

So first we need to get the input from the document – if you look at the pop-up below the big text field you can see that the input for our command will come from the current selection (or the entire document if there is no selection). This is perfect for what we want.

We need to read the selection from standard input, the easiest way to do this is with:

$input = file_get_contents('php://stdin');
We also need to know the name of the current file, which is given to us as an environment variable TM_FILENAME (Use the Show TM_* Variables command in the Bundle Development bundle to see a full list)– these are accessible from PHP with either the getenv() function or the $_ENV superglobal.
$filename = $_ENV['TM_FILENAME']
Now we want to split the input into lines and then print out the link for each line:
$lines = explode("\n", $document); 
foreach ($lines as $index => $line) { 
  $link = preg_replace('/\W+/', '-', strtolower($line)); 
  echo "<a href=\"$link\">$line</a>\n"; 
}
And that’s it! It’s not very interesting though, so let’s make the titles in the result into snippets:
foreach ($lines as $index => $line) { 
   $tabstop = $index + 1; 
   echo '<a href="$filename#${' . $tabstop . '/\W+/-/g}">' .
      '${' . $tabstop . ':' . e_sn($line) . '}</a>' . "\n"; 
}   

// escape text for use in a TextMate snippet function e_sn($str) {    return preg_replace('/[$`\\]/', '\', $str); }

This will echo some lines like

<a href="#${1:/\W+/-/g}>${1:title}</a>

Which is TextMate’s syntax for snippets. e_sn() is used to escape the characters which are special inside a snippet.

The completed command is repeated below (with an extended transformation to handle lower-casing the anchor name):

#!/usr/bin/env php 
<?php
$document = file_get_contents('php://stdin'); 
$lines    = explode("\n", $document); 
$filename = $_ENV['TM_FILENAME']   

foreach ($lines as $index => $line) { 
    $tabstop = $index + 1; 
    echo '<a href="$filename#${' . $tabstop . '/(?:(\W+)|(.))/(?1:-:\L$2)/g}">' .
       '${' . $tabstop . ':' . e_sn($line) . '}</a>' . "\n"; 
}   

// escape text for use in a TextMate snippet 
function e_sn($str) { 
   return preg_replace('/[$`\\\\]/', '\\', $str); 
}
?>

13 Responses to “TextMate Tip – Using PHP for Commands”

  1. on 04 Apr 2008 at 12:31 am/dev/random › links for 2008-04-03

    [...] Ciarán Walsh’s Blog » TextMate Tip – Using PHP for Commands Not many people realise that all the commands given to you by TextMate are written as simple scripts, editable by anyone, and in any language. PHP is a widespread language, but not many people are accustomed to using PHP for writing shell scripts (tags: textmate php) [...]

  2. on 04 Apr 2008 at 5:22 pmMatt

    Very helpful – I’ve just starting writing my own TM commands, and will definitely try this one out.

  3. on 06 Apr 2008 at 11:49 amZettt

    Very great! Some code is missing in the echo command but i get the point. Didn’t even know that it’s possible to echo Tab Stops in a command which TextMate then uses. Great!

    Oh and by the way. You forgot “;” in “$filename = $ENV['TMFILENAME']” ;)

  4. on 21 Apr 2008 at 1:59 amseyDoggy

    Oh this is way too cool. Thanks a ton!

  5. [...] Link. [...]

  6. Ciarán Walsh’s Blog » TextMate Tip – Using PHP for Commands…

    Ciarán Walsh’s Blog » TextMate Tip – Using PHP for Commands…

  7. on 13 May 2008 at 8:38 pmmitch

    Thanks ciarán. This is exactly what I was looking for.

  8. on 21 Jun 2008 at 10:30 pmmarios

    Very cool, thanks. Note, that the output needs to be inserted as a snippet.

  9. [...] Ciarán Walsh’s Blog » TextMate Tip – Using PHP for Commands [...]

  10. on 22 Jul 2008 at 5:22 pmgeorge tziralis

    Apologies for adding this here, but I didn’t find a mail or contact page or sthng. I’m a textmate lover and I’ve written my PhD dissertation in latex using textmate. Now I’ve learned that I can’t submit it in english (i’m studying in Athens), so I need to translate it in Greek. The only package that works seamlessly with greek in latex requires an ISO 8859-7 encoding and I do feel pretty unlucky that there is no support yet for separate than utf-8 encodings (even if textmate’s arguments on it are pretty strong). Or is there any hope/hack for my beloved software at the end of the tunnel? Thanx!

  11. on 14 Aug 2009 at 12:16 amAnonymouse

    Hi Ciarán

    Thanks for the tutorial.

    $input = filegetcontents(‘php://stdin’);

    should be

    $document = filegetcontents(‘php://stdin’);

    to be consistent with the code block that follows?

  12. [...] Ciarán Walsh’s Blog TextMate Tip – Using PHP for Commands [...]

  13. on 09 Apr 2010 at 5:24 pmbuy steroids

    Thanks ciarán. This is exactly what I was looking for.

Fork me on GitHub