(archive 'newLISPer)

March 3, 2007

The end of the line for this service

Filed under: newLISP — newlisper @ 18:14

I’ve been having a few problems with the services that I’ve been writing for use with ThisService.

The problem is basically that, on the Mac, applications use either carriage returns or line feeds to terminate the lines of a text file. In the days of the Classic Mac OS (which was basically the last decade of the previous century), each text line ended with a Carriage Return (CR) – hex 0D, “\r”. In the current MacOS X, like other Unix flavours, lines end with a Line Feed (LF) – hex 0A, “\n”.

Perhaps uniquely, BBEdit and TextWrangler use CRs for ending lines while your document is being edited, and convert to and from your preferred line ending (probably LF, but could also be CRLF or CR) when you open or save a document. This isn’t normally a problem, but it becomes one when you write scripts intended to be used as services. The service reads the line endings from the application rather than from the file on disk, and therefore reads CRs from BBEdit rather than LFs from the file. So the basic number-lines service to number lines:

(set 'counter 0)
(while (read-line)
  (println (inc 'counter) {: } (current-line)))

will, when run in BBEdit, generate only a single number (1), and remove all the line endings from the file as well, thus turning this:

one two three
four five six
seven eight nine

into this:

1:   one two three  four five six  seven eight nine

Here, read-line isn’t looking for “\r”, and so reads the entire selection at once.

This service works fine in most applications because the line endings are LFs. (The reason it doesn’t work in Mail is because Apple haven’t fixed a long-standing bug in its support for services, and the reason it doesn’t work in Microsoft Word escapes me.)

It shouldn’t be too hard to write a service that works with both CR- and LF- delimited text, though. Here’s what I’ve got so far, with some help from Lutz (of course):

(set 'counter 0 'contents "")
(while (read-buffer 0 'buff 1000000 "\r")
  (set 'contents (append contents buff)))
; buffer might have a bit left over...
(unless (nil? buff)
  (set 'contents (append contents buff)))
(replace "\r" contents "\n")
; number lines
(dolist (l (parse contents "\n"))
  (println (inc 'counter) { } l))

This reads in characters either in chunks delimited by CR, or a million characters at a time, and stores the lines in a big string, which can then be changed to use LFs. This, so far, seems to work with all the applications I’ve tried it with, including BBEdit. I’ll be testing it some more in due course.

I should say here that BBEdit has its own Unix tools menus, so strictly speaking you could ignore this problem altogether and keep a separate version of each script you write, one for use in BBEdit and a global one for use with all LF-using applications. But, to be honest, that seems an untidy solution.


Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: