Archive for the ‘text editing’ Category
Why vi?
July 3rd, 2008
You may have seen in the past week or so links to Beeswax, a new terminal-based productivity application inspired by Lotus Agenda. I’m not looking for a new productivity app—OmniFocus is working pretty well for me now—but I did find it interesting that Beeswax uses vi keybindings for adding and editing tasks.
Vi may be the oldest text editor still in wide use. The original vi was written by Bill Joy while he was a member of the BSD development group in the ’70s. It’s been rewritten several times since then; the most popular version nowadays is probably vim.
Text editing in vi is characterized by modes. Text is added to a file while in insert mode, where every keystroke results in a character appearing on the screen, just like the most familiar editors of today. Text is edited in command mode, where keystrokes do things to the text in the file. For example:
- x deletes the character at the cursor;
- 5x deletes five characters, starting at the cursor;
- w moves the cursor forward one word;
- d3w deletes three words, starting at the cursor.
As you can see, vi commands can be combined and multiplied. Also, the command mode eliminates the need for Command- and Control-key combinations.
Modal editing may seem weird now, but it was the norm in the old days. In fact, vi was a big hit because it was a “screen editor,” allowing you to look at and work on an entire screenful of text at a time. Most editors back then were “line editors,” which restricted you to just one line at a time.
Vi is intriguing to productivity geeks because its ability to combine commands gives users the leverage to do a lot of editing with very little typing. Merlin Mann at 43 Folders has mentioned vi several times, both on the blog and on the MacBreak Weekly podcast. I’ve gone through my vi phases, too.
Back in my Linux days, I would periodically use vi as my primary text editor for a week or two at a time, but the modes always defeated me. I can’t tell you how many times I had to delete “:w” from my work1. I’m glad I spent the time to learn the basics, because vi can be very useful when I need to ssh into a remote computer and do a quick file edit. If the remote computer is Unix-like, some form of vi will certainly be there. But I was never able stay with vi as my regular text editor because of how I write.
As I see it, there are three types of writer:
First, there’s the kind that write final copy directly. Words, sentences, and paragraphs flow out in a smooth stream with no need for editing. While I don’t think any professional writing gets done this way, I know many people who can do correspondence like this. I’ve often gotten mail from clients with the notation at the bottom that the letter was “dictated but not reviewed.” Since these people don’t edit, the text editor they use is immaterial.
Second, there’s the kind that write in drafts. When I was a kid in the pre-PC era, school essays were always written as a longhand draft first, then edited and typed up as a final copy. Even today, when everyone writes on a computer, many writing guides suggest that you “get everything out” in a first draft and then clean things up with a good editing session. Vi’s modal editing can be great for this kind of writer. All of the first draft is done in insert mode and all the later editing in command mode.
Finally, there’s the kind that constantly fiddles with the text. This is me, and this is why my experiments with vi failed. I tend to rewrite sentences as I’m typing, stopping in the middle to go back and reset the tense or flip clauses around. It’s rare that I can type an entire paragraph without stopping to edit it. The need to keep shifting in vi from command to insert mode makes it a very clumsy editor for this type of writer.
I’ve tried to change my writing habits. I’ve tried to turn my brain’s internal editor off and just let the words come out, trusting myself to fix them later on. It would make writing much easier, regardless of the text editor I use. But I can’t do it.
One of the few advantages of reaching middle age is coming to recognize your strengths and weaknesses and knowing what you can change and what you can’t. This is something I can’t.
I wish the Beeswax developers well; I’m sure their system will be just the thing for many people. But I know my limitations.
-
That’s the command for saving. ↩
Window wrapping in TextMate
January 26th, 2008
Three years ago, when I moved back to Macintosh from 7-8 years of Linux, I carried along a few habits. Some of these, like avoiding proprietary formats and using plain text files almost exclusively, still make sense to me. But other habits are losing their grip and falling into disuse. Today I may have lost another: setting my text editor to linewrap after a fixed number of characters.
Wrapping after a fixed number of characters is the norm in the Unix/Linux world. Text editors like Emacs, vi, and my personal favorite, NEdit, all wrap that way. It makes sense in that world because the Unix tradition is to put a newline character at the end of each line. Paragraphs are separated by double newlines, i.e., a blank line.
Part of the usual set of preferences for a Unix text editor is the column number to wrap at. Column 80 is a common choice because of the tradition of 80-column displays back in the pre-GUI days, but many people choose to wrap at 72 columns so their lines can be “quoted” by “> ” prefixes in emails without exceeding the historical 80-column barrier. I remember trying out a few values in the 72-78 range, hoping to find the perfect balance between “quotability” and having enough room to hold reasonable-length lines of code.
What, you may wonder, would happen when you were editing a paragraph and your insertions and deletions took you far away from the original line lengths? It turns out that all these editors had a reformat command that would pluck out the newlines from the current paragraph, rewrap the lines to the chosen length, and stick newlines back in at the end of each rewrapped line. In Emacs, the reformat command is Meta-q (let’s not get into what the “meta” key was), it’s Control-j in NEdit, and it’s some variant on “gq” in vim.
So anyway, wrapping at a particular column was ingrained in my text-editor work habits, and I set up both BBEdit (my editor of choice in 2005) and TextMate (my editor since 2006) to work that way, even though neither of them put newline characters at the end of each line. But yesterday I was reading about Scrivener and how it holds off on formatting the line lengths and page breaks until you’re ready to print. I don’t seem myself switching to Scrivener, but it did get me thinking that even though I write in plain text only, wrapping to a particular column was kind of unnecessary formatting. There are times when I’d like to have a narrow TextMate window on one side of the screen and a window with what I’m writing about on the other side. Wrapping at a given column forces me—for no good reason—to keep my TM window at least that wide and limits how big the other window can be.
The upshot is that today I set TextMate to wrap at the window width and another vestigial habit bit the dust.
Tags from keywords in TextMate
December 6th, 2007
I’ve been using TextMate’s blogging bundle for quite a while. I’m sure MarsEdit and Ecto have their advantages, but I’m so used to the TM environment, I like to write everything in it. (It should go without saying that Movable Type’s builtin text entry page is just awful, but I’ll say it anyway: Movable Type’s builtin text entry page is just awful.)
I try to add Technorati-style tags to the bottom of each post, and I wrote a couple of snippets to make that easier. But now I have a better way. I use the Keywords header line at the top of my post and run a TM command to extract the keywords from that line to construct an HTML stanza of Technorati tags. Here’s what the command looks like in the Bundle Editor (you can click on the image to see a full-sized version):
The command is pretty straightforward:
1: #!/usr/bin/env python
2:
3: import sys, re
4:
5: # Get the list of keywords.
6: kwline = re.compile(r'Keywords:')
7: for line in sys.stdin:
8: if kwline.match(line):
9: keywords = line.split(":")[1].strip().split()
10: break
11:
12: # Make a list of tag anchors from the keywords.
13: tag_template = '<a href="http://technorati.com/tag/%s" rel="tag">%s</a>'
14: tags = [tag_template % (x, x) for x in keywords]
15:
16: # Wrap the tags in a paragraph.
17: group_template = """<!-- technorati tags start -->
18: <p class="tags">Tags: %s</p>
19: <!-- technorati tags end -->"""
20:
21: print group_template % "\n".join(tags)
The only complex part is line 9, where I’ve clumped three or four things together: splitting the Keywords line at the colon, stripping the leading and trailing whitespace from the part after the colon, and splitting the resulting string into a list of words.
As you can see in the screenshot, the command’s input is set to Entire Document and its ouput is Insert as Text. I activate it through a Tab Trigger of “kw.” The scope is set to “text.blog” so it can work in either HTML or Markdown blog formats (I have no idea whether the Text or Textile formats accept HTML—if they do, this command will also work with those formats).
As an example, this post (written in Markdown) has a Keywords line of
Keywords: mac textmate blogging movabletype technorati markdown
which leads to a tags stanza of
<!-- technorati tags start -->
<p class="tags">Tags: <a href="http://technorati.com/tag/mac" rel="tag">mac</a>
<a href="http://technorati.com/tag/textmate" rel="tag">textmate</a>
<a href="http://technorati.com/tag/blogging" rel="tag">blogging</a>
<a href="http://technorati.com/tag/movabletype" rel="tag">movabletype</a>
<a href="http://technorati.com/tag/technorati" rel="tag">technorati</a>
<a href="http://technorati.com/tag/markdown" rel="tag">markdown</a></p>
<!-- technorati tags end -->
GPG and Vim
October 22nd, 2007
In my previous post, I mentioned that I used to use Vim to automatically encrypt and decrypt my file of user names and passwords. A reader emailed to ask for the configuration file that allowed this transparent behavior, so here it is. It’s called gpg.vim and is kept in the ~/.vim/plugin directory.
" Transparent editing of gpg encrypted files.
" By Wouter Hanegraaff <wouter@blub.net>
augroup encrypted
au!
" First make sure nothing is written to ~/.viminfo while editing
" an encrypted file.
autocmd BufReadPre,FileReadPre *.asc set viminfo=
" We don't want a swap file, as it writes unencrypted data to disk
autocmd BufReadPre,FileReadPre *.asc set noswapfile
" Switch to binary mode to read the encrypted file
autocmd BufReadPre,FileReadPre *.asc set bin
autocmd BufReadPre,FileReadPre *.asc let ch_save = &ch|set ch=2
autocmd BufReadPost,FileReadPost *.asc '[,']!gpg -qd 2> /dev/null
" Switch to normal mode for editing
autocmd BufReadPost,FileReadPost *.asc set nobin
autocmd BufReadPost,FileReadPost *.asc let &ch = ch_save|unlet ch_save
autocmd BufReadPost,FileReadPost *.asc execute ":doautocmd BufReadPost " . expand("%:r")
autocmd BufReadPost,FileReadPost *.asc set ff=unix
" Convert all text to encrypted text before writing
autocmd BufWritePre,FileWritePre *.asc '[,']!gpg --default-recipient-self -ae 2>/dev/null
" Undo the encryption so we are back in the normal text, directly
" after the file has been written.
autocmd BufWritePost,FileWritePost *.asc u
" First make sure nothing is written to ~/.viminfo while editing
" an encrypted file.
autocmd BufReadPre,FileReadPre *.gpg set viminfo=
" We don't want a swap file, as it writes unencrypted data to disk
autocmd BufReadPre,FileReadPre *.gpg set noswapfile
" Switch to binary mode to read the encrypted file
autocmd BufReadPre,FileReadPre *.gpg set bin
autocmd BufReadPre,FileReadPre *.gpg let ch_save = &ch|set ch=2
autocmd BufReadPost,FileReadPost *.gpg '[,']!gpg -qd 2> /dev/null
" Switch to normal mode for editing
autocmd BufReadPost,FileReadPost *.gpg set nobin
autocmd BufReadPost,FileReadPost *.gpg let &ch = ch_save|unlet ch_save
autocmd BufReadPost,FileReadPost *.gpg execute ":doautocmd BufReadPost " . expand("%:r")
autocmd BufReadPost,FileReadPost *.gpg set ff=unix
" Convert all text to encrypted text before writing
autocmd BufWritePre,FileWritePre *.gpg '[,']!gpg --default-recipient-self -e 2>/dev/null
" Undo the encryption so we are back in the normal text, directly
" after the file has been written.
autocmd BufWritePost,FileWritePost *.gpg u
augroup END
As you can tell from the top line, this file is not original with me but was downloaded from somewhere lost in the mists of memory and modified. Googling “Wouter Hanegraaff gpg” will get you a shorter version that handles only binary encrypted files, not ASCII-armored files. I guess the lines that work with *.asc files are mine, although I can’t take much credit for them—they are nothing more than an obvious rewrite of the lines that handle *.gpg files.
If you are a Vim user, you may want to look into another GPG configuration file, written by Markus Braun. You can find it here (thanks to Karsten Hokamp for the tip).
GPG and TextMate
October 9th, 2007
Note: see update at bottom.
Several years ago, when it became clear that I was going to have far more login names and passwords than I could comfortably keep in my head, I created a password file in my home directory, put in all the logins and passwords I had at the time, and encrypted the file with GPG. Thus, one passphrase gave me access to all my other secret information, and I could add new logins and passwords as the need arose. This simple “password locker” was hardly an original idea, but because it was simple I was able to carry it with me when I switched from Linux to Mac a few years ago—same password file, same GPG passphrase and keyring.
I even carried along a transparent way of encrypting and decrypting the password file: a vim configuration file that would ask me for my passphrase when I opened the file, show and allow editing of the plaintext, then re-encrypt the file when I saved it. Vim was never my editor of choice, but this file was more read than edited, and the editing that was done to it was pretty rudimentary. So the convenience of transparency outweighed the inconvenience of using a less-than-favorite editor. So for three years I’ve been using the very unMaclike vim to view and edit my password file, because I didn’t know of a simple way to do it in (initially) BBEdit or (now) TextMate.
But yesterday I saw the “AES Encrypt Document/Selection With Password…” and “AES Decrypt Document/Selection With Password…” commands in TextMate’s Text Bundle and decided to adapt them to GPG. Here’s my encryption command, called “Encrypt GPG” and given a Key Equivalent of Control-Option-Command-G (⌃⌥⌘G):
1: get_r () {
2: res=$(CocoaDialog 2>/dev/null inputbox --float --no-newline \
3: --title 'Encrypt with GPG' \
4: --informative-text "$1" \
5: --text "$2" \
6: --button1 "$3" --button2 Cancel)
7: [[ ${res:0:1} == 1 ]] && echo -n "${res:2}"
8: }
9:
10: r=$(get_r 'Who is this going to?' drang OK)
11: [[ -z "$r" ]] && exit_discard
12:
13: if ! gpg -qear "$r" --no-tty; then
14: exit_show_tool_tip
15: fi
It uses the very nice CocoaDialog application to ask the user for the recipient of the encrypted file (the idea of public key encryption, like GPG, is to encrypt the file so that only the recipient can decrypt it). Because I usually encrypt things for my own use, the default recipient is me, but the command is general enough that I can encrypt the file for anyone in my GPG keyring. The encryption itself is done on line 13: the -qea options tell GPG to be relatively quiet in what it prints out, encrypt the file, and ASCII-armor it. ASCII-armoring makes the encrypted file come out in printable characters, which is helpful when working in a text editor. The -r option is for the recipient, which has been saved in the $r variable. Finally, the --no-tty option tells GPG that it’s not working through a terminal.
The decryption command is called “Decrypt GPG” and is given the same Key Equivalent, ⌃⌥⌘G.
1: get_pw () {
2: res=$(CocoaDialog 2>/dev/null secure-inputbox --float --no-newline \
3: --title 'Decrypt with GPG' \
4: --informative-text "$1" \
5: --button1 "$2" --button2 Cancel)
6: [[ ${res:0:1} == 1 ]] && echo -n "${res:2}"
7: }
8:
9: pw=$(get_pw 'GPG passphrase?' Decrypt)
10: [[ -z "$pw" ]] && exit_discard
11:
12: if ! gpg -qd --passphrase "$pw" --no-tty; then
13: exit_show_tool_tip
14: fi
It gets the passphrase using CocoaDialog and decrypts the file with the GPG command on line 12. The -d option is for decryption, the passphrase is saved in the $pw variable, and the other options are as before.
Both commands get their input from either the selected text or the entire document and their output replaces the input.
So now I can open my encrypted password file in TextMate, which looks like this:
-----BEGIN PGP MESSAGE-----
Version: GnuPG v1.4.6 (Darwin)
hQIOA7gA+xUvP1efEAgAiFrp4J95LeBzgnHfv6J+A9pGIUE4g0nL1V/fG93FOUw0
YoAABdV8TnlR6P5CLVdNdx9b30WtpaOinX9WNLAiP8Sy1cGmS+OSL/QJu1oaIIi2
.
[snip]
.
WFZ6WBmm/dbt+aO97qCcZKEgZehU2ONe5YCrTq/8JvkPorIFnxqP73wy
=x4Cj
-----END PGP MESSAGE-----
I then hit ⌃⌥⌘G, select the “Decrypt GPG” choice from the little popup menu, enter my passphrase in the CocoaDialog box, and the plaintext file appears. If I’m just looking up a password, I can just close the file when I’m done and decline to save the changes. If I add or change a password, I re-encrypt the file by hitting ⌃⌥⌘G, selecting the “Encrypt GPG” choice, and hitting Return to accept the default recipient. The file is now encrypted and ASCII-armored, ready for saving.
This is not as transparent as the automatic encrypting and decrypting available with vim, but it keeps me from having to open a Terminal window and typing in vim seekret.asc. And I can navigate and edit in a more comfortable editor.
Update
If you’re interested in my old Vim setup, I’ve posted the configuration file here.
Last entry on line numbering code in blog posts
June 29th, 2007
One thing I forgot to do in the last post was show how to filter a set of lines through my line numbering program, anl, from within TextMate. I did mention that you use the Filter Through Command… item in the Text menu, but didn’t show how to set the options in the item’s dialog box. Here are the options you should select:

I do this before adding the 4-space indentation that tells Markdown to wrap the lines in pre and code tags. After anl adds the line numbers, colons, and spaces, I do the 4-space indentation.
OK, on with the last two items I need to put nicely formatted line numbers in my blog’s code blocks: some JavaScript to wrap the line numbers in span tags, and CSS to style the numbers.
I put the following JavaScript function in the head sections of the various blog templates. (Movable Type has templates for the main page, the date-based archives, the category-based archives, and the individual post archives and I want the same line numbering no matter how you’re viewing the page.)
1: function styleLN() {
2: var preElems = document.getElementsByTagName('pre');
3: if (0 == preElems.length) { // no pre elements; stop
4: return;
5: }
6: for (var i = 0; i < preElems.length; i++) {
7: var pre = preElems[i];
8: var code = pre.getElementsByTagName('code')[0];
9: if (null == code) { // no code; move on
10: continue;
11: }
12: var oldContent = code.innerHTML;
13: var newContent = oldContent.replace(/^( *)(\d+):( )/mg,
14: '<span class="ln">$1$2<' + '/span>$3');
15: code.innerHTML = newContent;
16: }
17: }
Line 2 grabs all the pre elements in the document. If there aren’t any, Lines 3-4 exit the function and no changes are made. If there are pre elements, the loop that starts on Line 6 goes through all of them, looking for those that contain code elements. The contents of each code element is filtered through a search-and-replace operation on Lines 13-14. The operation searches for lines that start with zero or more spaces, one or more digits, a colon, and two spaces. If found, it replaces that with an opening span tag with a class attribute of “ln”, the leading spaces (if any), the digits, a closing span tag, and the two spaces. Line 15 then stuffs the altered text back into the document. The function is invoked by adding onLoad="styleLN()" to the template’s body tag.
If you’re wondering why the replacement string on Line 14 is the concatenation of two strings, it’s because HTML Tidy complains when it sees a string with </ followed by a letter. I don’t know of any current browsers that fail when they encounter such a string, but I like to keep HTML Tidy happy nonetheless.
The colon is removed (and you know how painful that can be) because I’m going to style the numbers to make them distinct from the following code lines and no longer need the extra separation the colon provided in the Markdown source. Here’s the CSS that styles the line numbers:
span.ln {
color: #888;
font-size: 75%;
}
A medium gray text with a slightly smaller size than the code itself. I’ve played around with different background colors and padding to get more of a “gutter” look like you might see in TextMate and BBEdit (and vi and Emacs and any number of other editors), but the extra styling didn’t add anything.
(By the way, if you want to see the code with the colons, just tell your browser to display the source of this page.)
One problem with displaying the line numbers this way is that readers of the blog can no longer just copy code from my posts and expect it to run—the line numbers will get selected along with the code itself. The line numbers and separation space are easily deleted by doing a global search on the regular expression
/^ *\d+ /
(that’s one space between the caret and the asterisk and two spaces after the plus sign) and replacing it with nothing. Depending on the features of your text editor, you may also be able to do a “column” or “rectangular” selection to grab all the line numbers and leading space and delete them with a single keystroke. (TextMate does column selection if you drag while holding down the Option key.) This can be much faster than search and replace if the code isn’t very long and you don’t have to do much scrolling.
I did spend some time thinking about whether the clearer explanations that line numbers allow are worth the extra effort needed to delete them and, obviously, decided that they are. If you think otherwise, let me know.
Lucky linking
July 26th, 2006
Today I saw the TextMate Search and Hyperlink screencast, and was impressed with how the URLs for Markdown links were automatically inserted using Google’s “I’m Feeling Lucky” magic. “How the hell did they do that?” I thought. So I went to Haris Skiadas’s blog and saw what a clever bit of coding he’d done to make it work (Haris and clever are redundant).
Unfortunately, Haris used his genius for evil instead of good, as he had his code create inline links instead of reference-style links. I hate Markdown’s inline style of linking, because the inserted URL breaks up the flow of the text. I’ve already done three separate posts on creating reference-style links in TextMate, but now feel an acute need to do a fourth. incorporating an automatic URL. Here goes.
First, I made a command-line program, which I call glucky, taken almost verbatim from Haris’s post (I just broke up a couple of the long strings):
#!/usr/bin/env ruby
require 'net/http'
word =STDIN.read
prefix = "http://www.google.com/search?q="
suffix = "&btnI=I'm+Feeling+Lucky"
goodURL = URI.escape(prefix + word + suffix)
response = Net::HTTP.get_response(URI.parse(goodURL))
print response.to_hash['location']
I saved this file in my ~/bin directory, which is in my $PATH, and made it executable. Now if I type echo markdown | glucky into a Terminal session, it happily spits back http://daringfireball.net/markdown/, which means my little edits didn’t mess up Haris’s code. Good.
In TextMate, I opened the Bundle Editor and made three commands, one for running the glucky program on the selection and putting the result on the clipboard, one for bracketing the selection, and one for inserting a snippet.
Here’s the command for putting the “I’m Feeling Lucky” URL into the clipboard:
echo -n "$TM_SELECTED_TEXT" | glucky | pbcopy
In the Bundle Editor window, this command is set to take the Selected Text or Nothing as its Input and Discards its Output. I called it “glucky,” but its name isn’t important.
Here’s the TextMate command for bracketing the selection:
echo -n "[$TM_SELECTED_TEXT]"
As you might have guessed, I called this command “bracket,” set its Input to Selected Text or Nothing and its Output to Replace Selected Text.
Here’s the command for inserting a snippet:
#!/usr/bin/perl
use List::Util qw(max);
$text = $ENV{'TM_SELECTED_TEXT'};
# Get the highest-numbered reference.
@nums = $text =~ /^\[(\d+)\]: /mg;
$n = max(@nums) + 1;
# Escape special characters.
$text =~ s/([\$\\`])/\\$1/g;
# Insert the snippet.
print '[${1:' . $n . '}]$0'.
$text . '[$1]: ' . `pbpaste` . "\n";
I called it “Reference link selection lucky.” It has an Input of Selected Text or Nothing and an Output of Insert as Snippet.
If you looked at my previous posts on reference-style links, you’ll see that what I’ve done here is just a small variation on my previous work.
These commands are tied together with a macro. I opened a new text file, typed in the word that will act as the link (I chose “markdown”), added a few newlines to the end, and selected the link word. I then started recording a macro and did the following:
- executed the “glucky” command;
- executed the “bracket” command;
- pressed the rightarrow key to move the caret to the right of the closing bracket;
- pressed Command-Shift-downarrow to select from the caret to the end of the document;
- executed the “Reference link selection lucky” command.
I then stopped the recording and saved the macro in my Markdown bundle with the name “Reference link selection lucky” (yes, the same name as the command—it’s OK). I set its Scope Selector to “text.html.markdown” and its Key Equivalent to Control-Option-Shift-L.
Now I can select the text I want to turn into a link, hit Control-Option-Shift-L, and the text is turned into a reference-style link with the “I’m Feeling Lucky” URL. Wheeee!
Markdown links in TextMate: The Final Frontier
March 12th, 2006
OK, let’s hope this is my final posting on making Markdown reference links in TextMate. When we last left our intrepid hero, I had refined my command/snippet/macro system to automate the numbering of the references, while still allowing the references to be overridden by, say, words or abbreviations.
The problem with that system in real-world use is that it assumes that I know I’m going to make a link as I’m typing along. For example, if I am writing about wombats and want to insert a link, I have to know, a priori, that a certain word or phrase is going to be a link and invoke the macro just as I get to that point in the text. Unfortunately, I often find myself typing along without any links and then deciding later which words and phrases should be links. So I also need a system that allows me to selected a string of text and turn that into a reference link.
My new command/snippet/macro combination does just that. It consists of two commands and a macro. The first command takes the selected text, puts brackets around it, and leaves the caret just after the closing bracket. I cleverly call it “Brackets,” and it consists of this bit of Perl
#!/usr/bin/perl
$text = $ENV{'TM_SELECTED_TEXT'};
$text =~ s/([\$\\`])/\\$1/g;
print "[$text]" . '$0';
which looks like this when entered in the Bundle Editor window:
(Click on screenshot images to see them at full size.)
The other command is much like the earlier reference link command. It assumes that the text from the end of the bracketed link text to the end of the document is selected, and it inserts a snippet with placeholders (“tab stops” in TM-speak) for the reference number and the URL. The Perl code is this
#!/usr/bin/perl
use List::Util qw(max);
$text = $ENV{'TM_SELECTED_TEXT'};
# Get the highest-numbered reference.
@nums = $text =~ /^\[(\d+)\]: /mg;
$n = max(@nums) + 1;
# Escape special characters.
$text =~ s/([\$\\`])/\\$1/g;
# Insert the snippet.
print '[${1:' . $n . '}]$0'.
$text . '[$1]: ${2:http://}' . "\n";
and it looks like this in the Bundle Editor:
To make the macro that puts everything together, I select some text and start the macro recording ( Automation:Start Macro Recording ). I then
- invoke the Bracketing command from the little gear-like menu at the bottom of the window, which leaves the caret at the end of the closing bracket;
- select the text from the caret to the end of the document with Command-Shift-Downarrow;
- invoke the snippet-inserting command from the little gear menu, which pops the snippet and placeholders into the document;
- stop the macro from recording and Undo a few times to get the document back to where it was before I started.
At this point I save the macro, name it (I chose “Reference link for selection”), and make sure it’s in the Markdown bundle. The Scope Selector should be “text.html.markdown,” and because it’s to be used when text is selected, the Activation has to be a Key Equivalent rather than a Tab Trigger. I chose Control-Option-L. The macro looks like this in the Bundle Editor:
So now I have two macros for making reference-style links in Markdown documents:
- One called “Reference link” that I use when making links “on the fly” as I’m typing. It’s run by pressing Control-L. (This is the system described in these two posts, but with a new name and new Key Equivalent.)
- On called “Reference link from selection” that I use when I go back through a document and decide that some words should be links. It’s run by pressing Control-Option-L.
Thanks again to Haris Skiadas, Allan Odgaard, and Fred B. from the TextMate mailing list for their help and suggestions.
New and improved TextMate and Markdown links
March 6th, 2006
As a followup to this post about making reference-style links for Markdown documents in TextMate, I’ve made an improvement to the command so that the reference numbers increment as you add links to the document.
The new command was “inspired” (i.e., stolen from) Fred B. of the TextMate mailing list. He rewrote my original command in Ruby and added the incrementing reference number. I, in turn, have retranslated back to Perl and made one small change: Fred’s command uses the number of the last reference-style link in the document as the number to increment; mine uses the highest-numbered reference-style link. In most cases, the two commands will work the same way, but mine is more forgiving of people like me who go back and make additions and deletions that get the reference numbers out of sequence.
To make the distinction a bit more concrete, suppose a series of cuts and pastes left you with a reference section that looked like this:
[1]: http://www.leancrew.com/all-this
[3]: http://www.macromates.com
[2]: http://www.daringfireball.net/markdown
My command will create a reference-style link with a default reference number of 4 rather than another 3. (I’m pretty sure Fred’s would give you a 3, although my Ruby interpretation may be faulty.)
So how do you create and use this spectacular piece of code? Basically, you follow all the steps given in my previous post on this topic, but you use the following command instead of the one given there.
#!/usr/bin/perl
use List::Util qw(max);
$text = $ENV{'TM_SELECTED_TEXT'};
# Get the highest-numbered reference.
@nums = $text =~ /^\[(\d+)\]: /mg;
$n = max(@nums) + 1;
# Escape special characters.
$text =~ s/([\$\\`])/\\$1/g;
# Insert the snippet.
print '[${1:description}][${2:' . $n . '}]$0'.
$text . '[$2]: ${3:http://}' . "\n";
Happy linking!
Update (March 7): I cleaned up the code a bit and used the List::Utils max function instead of my own.
I’d like to make it so selected text could be turned into a reference-style link, but I’m worried about how much that will complicate the code. At present, we have a macro calling a command that inserts a snippet; adding the functionality I want will probably add at least two more levels of indirection.
What good is ecto?
March 4th, 2006
I know that at least one of the BoingBoing crew uses it. And lots of people have good things to say about it. But when you write your posts in Markdown, does ecto really have any advantages over just writing your post in a text editor and pasting it into Movable Type’s “New Entry” field? I downloaded the ecto trial today and will be giving it a spin for a couple of weeks.
This is my first ecto-handled post. I’m writing it in a TextMate window, which popped open when I chose ecto’s Edit:Edit With:TextMate command. The temporary file that is used to communicate between the two programs has an “.html” extension, which is no doubt why TM started out in HTML mode instead of Markdown. There’s probably a way to change that, and I’ll look into it when this post is done.
Another post about blogging. How tiresome!







