Showing posts with label Mac OS X. Show all posts
Showing posts with label Mac OS X. Show all posts

Wednesday, February 17, 2010

Ctags in SubEthaEdit

We've now looked at how to locate the right tags file and match a tag against it by working in the shell. But our goal is to connect Ctags to an editor, SubEthaEdit (SEE) in this case. We thus will need to switch from the world of the shell to the world of AppleScript. In this post, I'll just focus on getting the path to the tags file and a tag for which to search from SEE.

I'll not be working directly with SubEthaEdit's AppleScript dictionary, instead using my SubEthaEditTools handlers as a basis. Should anyone be interested in connecting Ctags to another Mac OS X editor that supports AppleScript, it would probably be better to port the SubEthaEditTools handlers to work with the editor and directly use the scripts I'll present here.

As a general design strategy, I'll identify two AppleScript error numbers with expected behaviors. First, I'll use number 901 to indicate that tag processing should be abandoned. Second, I'll use number 902 to indicate that an error of known type has occurred. This lets me handle a broad class of troubles by either quietly exiting, or beeping then exiting. Any other errors will just be unhandled, causing SubEthaEdit to show a sheet with details of the error.

Additionally, I'll need to define a search path for shell tools. Rather than using a customizable environment as I've done before, I'll just define one as an AppleScript property:
property UnixPath : "export PATH=\"$HOME/Library/Application Support/SubEthaEdit/bin:/Library/Application Support/SubEthaEdit/bin:$HOME/Library/bin:/usr/local/bin:/opt/local/bin:/usr/bin:/bin:/usr/local/sbin:/opt/local/sbin:/usr/sbin:/sbin\";"


To find the tags file, I first need to make sure a document is available to use as the starting point for the search. Second, I just need to call out to the shell with an appropriate command. Encapsulating these in handlers, I define:
on requireValidDocumentForCtags()
if not documentIsAvailable() then
error "No document open" number 902
end if
checkSaveStatus without updating
end requireValidDocumentForCtags

to findTagFile()
set findTagfileScript to (join of {UnixPath, "climb", "-b \"$(dirname", quoted form of documentPath(), ")\"", "tags"} by space)
try
do shell script findTagfileScript
on error
error "Unable to locate tags file"
end try
end findTagFile


Getting the candidate tag is harder than getting the path to the tag file, mostly because it is not as well-defined of a task. Since Ctags can index lots of different languages, it won't be easy to get a solution that is right for every language. Instead, I'll define a handler that works reasonably for a lot of languages, and maintains the possibility for the user to specify the candidate precisely. This latter case is straightforward: if there is text selected in SEE, we'll search for that tag.

When no text is selected, we need to get a candidate tag in some other way. To me, it makes sense that finding symbol definitions should let the user give a term in a dialog, and that text completion should work by using the text preceding the cursor. But how much text should be used? I don't think that the longest possible tag makes sense, as that would mean, e.g., a method invocation in Python of form obj.method would use the whole thing, even though that full term is unlikely to be indexed in the tag file. Instead, it would be better to just use method as the candidate tag. A reasonable choice for many languages would then be to take the longest string of alphanumeric characters and underscores, right to left from the insertion point. Those choices lead to the handler:
to determineSearchTerm given userIntervention:shouldAsk
set {startChar, nextChar} to selectionRange without extendingFront and extendingEnd
if startChar is equal to nextChar then
-- empty selection
if shouldAsk then
try
display dialog "Enter search term:" default answer "" with title "Find Definition"
on error number -128
error "User canceled" number 901
end try
text returned of result
else
-- try the whole line
set selectionContents to extendedSelectionText with extendingFront without extendingEnd
get shellTransform of the selectionContents for "" thru "sed -E -e 's/.*([[:<:]][[:alnum:]_]+)$/\\1/'" without alteringLineEndings
-- sed returns lines that are terminated with linefeeds, so get text before the final linefeed
paragraph -2 of the result
end if
else
-- just use the selection; there is too much variation in what could be a tag to guess
selectionText()
end if
end determineSearchTerm


The handlers presented in this post are enough to get the path to the tag file and a (partial) tag to search for. Next time, I'll connect these values from SubEthaEdit to the shell scripts handling the lookup.

Friday, April 10, 2009

Experimenting with Implementing Automator Actions

I've recently experimented with implementing actions for Automator using AppleScript. Overall, it was an interesting experience, and one I'd definitely recommend trying yourself.

Creating an action is generally quite simple, consisting of three main steps:

  1. Laying out controls in Interface Builder and establishing bindings to an NSObjectController,

  2. Defining the properties of the action by filling out some values in plists,

  3. Writing an AppleScript to actually implement the behavior.


I found Apple's documentation and an article by Matt Neuburg from the now-defunct O'Reilly Mac developer center. The latter article is a nice tutorial, but a little dated in spots; some caution is warranted.

As a specific example, I decided to take some of the AppleScript handlers for SubEthaEdit I already had on hand and convert them into actions. After reviewing the possibilities, I decided that there were just two actions that looked sensible: getting text from the front SubEthaEdit document and setting text in the front SubEthaEdit document. These are quite similar to existing actions for TextEdit. Additionally, I decided to expand them a little bit to also allow getting or setting the selection or selected lines in the document, not just the full contents of the document.

The first step was pretty simple. With well-defined, compact actions like the ones I considered, the interface requires little thought and little effort. In my case, it was just a matter of populating the menu entries for a pop-up button and establishing a binding.

To my surprise, I spent the most time and had the most difficulty with the second step. I'd expected this to be easy, but found that there were quite a few details that weren't made especially clear in the description of the keys for the property lists. The analogous actions for TextEdit proved very helpful, here: in several cases, I determined the correct choices by examining the plists from the bundles for the TextEdit actions. I'd expect this step to get easier with practice.

Actually implementing the behavior was especially easy, in this case. As I already had working handlers, I was able to just focus on connecting the action to the handlers. The structure for this is quite pleasant: a record of parameters is injected into the run handler, containing values obtained from the bindings to the NSObjectController. The resulting structure for my actions was then just using if-then statements to interpret the parameters, calling out to different handlers.

Although implementation of the behavior was easy, I did find testing to be quite tedious. It was helpful to save a workflow for testing, as suggested by Neuburg. More importantly than that, I'd strongly recommend that you have working handlers before trying to make them part of your action—test out the handlers in, e.g., Script Editor, and test that your Automator action dispatches to the correct handler.

The actions I created are available, as are the sources.

Wednesday, February 18, 2009

Setting up SkyBlueCanvas for Local Use on Mac OS X Leopard

SkyBlueCanvas (SBC) is a content management system (CMS) for web sites. It can be set up to run on a Mac OS X system using Web Sharing, but some steps need to be taken first. The result is a subfolder of your Sites folder, which shows off pretty much the full features of SBC. Unfortunately, you won't get to this point just by naively following the installation instructions for SBC, because it depends on features not enabled by default in Mac OS X.

Note that I'm not making any recommendations as to whether or not you should use SBC, but am instead just sharing what I had to do to get SBC working. This was driven by my own interest in trying out SBC. I have insufficient experience with CMSs in general or SBC in particular to make an informed judgement.

Several of the steps are multi-step themselves. I've just linked to the websites I found that provided a useful description. Total time to set things up should be under half an hour. After the initial set-up, starting a new site can be done by starting with step four, taking under five minutes.

  1. Enable PHP.

  2. Make sure that your Sites folder is reachable.

  3. Enable .htaccess files.

  4. Copy the SkyBlueCanvas folder to your Sites folder, and give it a reasonable name (I used skyblue, and will assume you do the same).

  5. Change the file permissions of the skyblue folder to 755. You can do this in the Terminal with a command like chmod -R 755 ~username/Site/skyblue—replace username with your actual user name, of course. (I didn't actually find this to be necessary, although it is in the SBC installation instructions.)

  6. Edit htaccess.txt in the skyblue folder, adding a second line of RewriteBase /~username/skyblue/. Rename htaccess.txt to .htaccess.

  7. Change the group and owner for the SBC files. In Terminal, change to the skyblue directory, and run sudo chown -R www:www *. If you don't do this, PHP won't be able to make needed changes to the files.

  8. Start Apache. Open up the Sharing panel in the System Preferences, and check Web Sharing.

  9. Point your web browser at http://localhost/~username/skyblue/ and follow the instructions. When asked for the site URL, give http://localhost/~username/skyblue/.


Friday, August 1, 2008

An Anniversary, of Sorts

It has been one year since the O'Reilly MacDevCenter site has published an article on Cocoa. The articles since then are a mix of various Mac OS X tips and tricks, with a heavy emphasis on Aperture—so no actual Mac development articles.

It's been quite a while since O'Reilly has published a book on Mac development, too. There is the Missing Manual series, but it is really something distinct. Has O'Reilly given up on Mac developers?

Saturday, June 7, 2008

Plaxo Synchronization Oddity

I've used Plaxo for a while to synchronize my address book across a couple of Macs and a web mail account or two. This was the original intent for Plaxo, and it has served me reasonably well as a replacement for the .Mac synchronization (which didn't serve me well, and was overpriced to boot). Today, I discovered a rather strange bit of behavior in the syncronization.

Since I started using it, Plaxo has added a feature they call Pulse. Pulse seems like an interesting variant on the social networking websites, focusing on allowing you to connect up feeds from the sites you actually use and centralize the social networking aspects (e.g., from Blogger, Flickr, and del.icio.us). I set up Pulse today.

Without Pulse, I never had much reason to take a look at the online version of my address book. With Pulse, I could see my own address plainly while setting up my profile. It was wrong.

That was quite strange. The addresses at Plaxo came from synchronizing with the Address Book data on my Mac. Those were right! However, there was a chunk of my previous address showing up on Plaxo, mixed in with my current address.

Here's what happened. I now live in Vienna. The address format for my Address Book card is thus set to Austrian. Before coming to Vienna, I lived in Portugal. The Portuguese format has a field for territorial subdivision, while the Austrian one doesn't use that. The extra stuff showing up in my current address is just the old territorial subdivision. Switching the display format for my Address Book card back to Portuguese shows the territorial subdivision field again. The old data is still there, just hidden with the Austrian formatting. It seems that Plaxo doesn't handle this situation correctly, treating all the data as still relevant (to be fair, I don't know if it is even possible for Plaxo to handle it right).

Admittedly, the described case is a bit unusual. Regardless, it may affect others. The solution, fortunately, is simple: switch the Address Book card format back to the earlier country setting, delete the hidden, outdated fields, switch back to the correct country setting, and sync with Plaxo.

Update: Or maybe Pulse doesn't let you connect a feed from Blogger. Mine doesn't seem to be working, anyway.

Sunday, March 23, 2008

SEEing LaTeX 28: Some Critical Notes on the 'see' Command Line Tool

I've used the see command line tool to relate a LaTeX previewer to SubEthaEdit using pdfsync. Unfortunately, I've become aware that the natural usage of see has a definite problem. Plugging in see and -g %line "%file" for the "PDFSync support" preferences in Skim doesn't work quite the way one would hope.

With that usage, which I'd consider to be the most natural, you wind up producing a new see process each time you command-click in Skim to switch over to SubEthaEdit. That process, due to shortcomings in the design of see, will hang around until you close the document in SubEthaEdit. Of course, if you're using SubEthaEdit and Skim together like that, you're in the middle of editing a LaTeX document, so you're not likely to be closing the document very promptly. Do that enough, and you could consume all the user processes allowed by Mac OS X. If you've not experience running out of user processes, let's just describe it as Not Fun.

I don't think it is likely to be a big problem, especially with the more friendly process limits in Mac OS X Leopard, but it is definitely a real problem. The problem gets compounded with each new usage of see for integrating external applications or reporting from a mode script. End result is that some caution is warranted when using see, and it should be omitted in favor of another approach, if necessary. Personally, I'm still using it.

I've requested an enhancement to see consisting of a 'quiet' mode, like seen in, e.g., grep. That would eliminate the issue, and, as a side benefit, considerably simplify using see from AppleScripts for SubEthaEdit modes. Let's hope the Coding Monkeys act on it.

Friday, January 4, 2008

What Role Should the Script Editor Play in Writing AppleScript?

I think it is safe to say that Script Editor is the most widely used application for writing AppleScripts. But, should it be? I'm coming to the conclusion that it brings so many limitations along with it that it can only play a limited role.

Before going any further with this line of thought, I'd like to make clear that I'm not making any recommendation for or against the various commercial AppleScript development environments. Looking at the SEEing LaTeX series, you might get the impression that I use AppleScript a fair amount. I don't. I use it as little as possible, which is why in that series I do most of the real work not in AppleScript, but in shell scripts called from AppleScript. Paying for a commercial environment may make sense if you want to or have to write a lot of AppleScript, but would be a waste of money for me.

However, our choices are not limited to Script Editor and the commercial development environments. We have an excellent development environment that comes for free with Mac OS X: it's called Unix. We can use make to manage a project, of which AppleScript can play a useful part. Unfortunately, Script Editor is tied too strongly to AppleScript, which means we're more or less stuck with AppleScript's mechanisms for modularity. Since modularity goes hand in hand with reuse, this is a key point.

Within AppleScript, the usual approach is to put common functionality in a script library and use load script from the StandardAdditions. This has a number of problems. First, there isn't a standard search path from which to load the script library, so you need to introduce a new convention. Second, if a library is changed, there is no automated mechanism to update scripts that depend on it. You can load the script library, store it in a property, and just check to see if the library has been updated whenever the script is called; this roughly emulates how, e.g., import works in Python. There are enough steps to this process that you're looking at a lot of copy and paste programming. Also, you depend on whatever convention you've established to load the script library.

Consider now using make. You can use osacompile to compile your text source into a compiled script. You could also use various shell methods to manipulate your script to define the search path for the script libraries, but how is the Script Editor to work with that? You can also just forget about reloading the script libraries when they change, since make will take care of that for us. We just load the script library once, and forget about it. Since we're just loading the script library once, why not just forget about the search path while we're at it? Just compile the library source into your working directory, and have the script load it from that same directory.

At this point, we still can use Script Editor to edit the script sources. However, we still have a lot of boilerplate to write for little gain. We need to set up the scripts correctly so that make can run them once to load the library. We need to add extra steps to make in order to load the libraries. We need to refer to the script libraries by using the property in which we stored them, whenever we want to use a handler in the library.

Alternatively, instead of emulating how Python loads modules, let's take a look at how something like C would do things. In C, you just use the preprocessor to include the various files. We can do that for AppleScript using, e.g., m4. Assuming you're working in a script called demo.applescript, and want to use some handlers you've written for manipulating text. Just include the relevant library source with something like
include(`StringTools.applescript')
and compile with
m4 demo.applescript | osacompile -o demo.scpt


Just like in C, this approach includes everything into the namespace of the script. And, just like in C, this could lead to namespace conflicts. So far, I've only used this in a few scripts, all of which are relatively small. I've not encountered any difficulties. But, should trouble arise, there is a solution. Instead of including the text at the top level of the script, include it inside of a script object:
script StringTools
    include(`StringTools.applescript')
end script
This could be further automated by writing an m4 macro, such as:
define(`import', `script `$2'
include(`$1')
end script
')dnl


I'm not at all sure that this approach would scale to larger scripts well. You'd really like to be able to have scripts import what they need, and not worry about whether they will themselves be imported into others. Since I'm not planning to write any large AppleScripts, I'm not going to worry about that until it becomes a problem.

Where does this leave the Script Editor? It's no longer useful for scripts of any length, since it can't handle the include statements. We do want it in order to look at scripting dictionaries, that much is clear, but is it needed for any editing at all? It is a reasonable environment in which to try out little code snippets, but we can even dispose of that, since Script Editor offers that functionality through the services menu.

Tuesday, December 4, 2007

Sales, Sales, Sales!

There sure seem to be a lot of sales on Mac software this month. I don't recall quite so many last year. I've already mentioned MacSanta. There are also Give Good Food to Your Mac and a MacUpdate promotion. Are there others?

Of the three, Give Good Food to Your Mac is definitely the most appealing for me. It allows you to decide on your own bundle of applications, with a discount based on the number of applications. Their approach does lead to some oddities, though. For example, you should never buy between 7 and 9 applications, because it is always cheaper to add inexpensive applications and get 10. Even if you'd never use them, you still pay less, and, hey, I would use, e.g., CoverScout, even if I'd never buy it on its own merits.

MacSanta is more traditional in its structure. Unfortunately. New applications are announced every day, with a 20% discount possible for that day. Later in the month, you can still get the applications, but with only a 10% discount. It's a very annoying set-up. I really can't picture buying anything from it at the 10% level, but the 20% level seems only relevant for something that I'd decided to get anyway. Of course, if I've decided to get an application, I've probably already bought it! That said, I have already bought ShutterBug and may well get MarsEdit, too, so they are definitely doing something right. I guess it works to push me off the fence…

The MacUpdate promotion looks to be a good value, if it is offering applications you want. Which it is not, for me.

Update: There is one other point about Give Good Food to Your Mac that I'd wanted to mention. In their FAQ, there is a clear and unambiguous statement that all licenses purchased are normal licenses, with no restrictions. This is the way it should be done, in my opinion.

In contrast, MacUpdate has "full licenses with normal upgrade paths" except for "Swift Publisher, which will be a paid upgrade." Seriously, why?

MacSanta is structured differently, so the issue doesn't really come up there, as far as I can see. You just get a discount code that you use directly on the developers' sites, rather than a bundle through a third party.

Sunday, August 19, 2007

On TextExtras

I'm back from a lengthy workshop. There has been quite a delay since my last post, due to the workshop and all the associated tasks needed both before and after. Almost a month, in fact. Let's rectify that now with a quick, easy post to get things started again.

In an earlier post, I wrote that I was unable to download TextExtras. Not too long after that, I was able to reach the site again, and have been using TextExtras since.

TextExtras is, in short, a wonderful extension to every text field in Cocoa applications. There are an almost overwhelming number of options you can set by enabling key bindings. However, I've enabled just one key binding so far, since I don't really want to relearn a big list of keyboard shortcuts and don't really need lots of editing power in every application. All that I did was to bind esc to TextExtras' completion option. This calls the normal Cocoa autocompletion, including whatever extensions are provided by an application (like in SubEthaEdit), but with a different interface. The Cocoa autocompletion system completes the word for a single match and shows a list of possibilities with multiple possibilities. TextExtras' completions insert the first match and cycles through additional matches with additional strokes of the appropriate key. I find this is often more convenient when programming, because you often get the match on the first try with variable names. In this case, the Cocoa approach selects the newly inserted text, requiring you to use the arrow key or some other method to move on. In constrast, TextExtras just puts the cursor at the end of the word, and you can just keep typing. If Cocoa completions always showed the list of possibilities, even in the case of a single match, it would be a lot nicer to use, since you could, e.g., just press the space bar to choose the only match and keep typing. Of course, I still have the usual Cocoa completions using option-esc.

More significant is the "Execute Pipe..." action. This pipes text through a Unix shell command. SubEthaEdit has an AppleScript to do that, but it's pretty limited. TextExtras gives you something more like the pipe action in TextMate. This is great to have when editing, and comes in handy for all sorts of situations. Shell pipelines everywhere!

There are many, many other possibilities with TextExtras. Try it!

Sunday, July 22, 2007

SEEing LaTeX 8: Dealing with Auxiliary Files

As noted in part 5 of this series, there were three main features to add to the LaTeX mode for SubEthaEdit (SEE). I've already taken care of the first, typesetting, by taking advantage of latexmk. In this installment, I'll address the second, cleaning up the mess of auxiliary files.

LaTeX produces several sorts of auxiliary file to handle cross-references, tables of contents, bibliographies, and more. The auxiliary files are essential to how LaTeX works, but of little use when, e.g., sending a paper to a co-author or submitting it to a journal. With a bit of bad luck, it is also possible to have a corrupted auxiliary file that prevents you from creating a PDF from correct LaTeX sources; deleting some or all of the auxiliary files solves this problem.

Auxiliary file deletion is thus an infrequent, but necessary task. It would be possible to just ignore the issue and do the needed clean-up in either the Terminal or the Finder, but doing it by hand can be error prone. Also, it is simple to add to SEE. Overall, it seems worth doing.

In an earlier post, I used latexmk to trigger typesetting from SEE. With the right command line options, latexmk can also be used to clean up the auxiliary files! Adding a clean-up feature to SEE then just requires some relatively minor modifications to the scripts used for typesetting.

First, let's look at the shell script. It becomes:
#!/bin/sh

PATH=/usr/texbin:/usr/local/bin:$PATH
export PATH

cd "`dirname "$1"`"
latexmk -c "`basename "$1"`"
The only change is that latexmk is called with a -c option, so that it will clean up the auxiliary files, leaving the output PDF. I saved the script as cleanupaux.sh.

Second, we'll look at the AppleScript. It becomes:
tell application "SubEthaEdit"
    if exists path of front document then
        set latexFilePath to path of front document
        set modeResources to resource path of mode of front document
    else
        --Unsaved document, so LaTeX not run on it and can just return
        return
    end if
end tell

set cleanupScript to quote & modeResources & "/Scripts/shell/cleanupaux.sh" & quote & space & quote & latexFilePath & quote

do shell script cleanupScript

on seescriptsettings()
    return {displayName:"Clean Up Auxiliary Files"}
end seescriptsettings
Again, the changes are minimal. For typesetting, it mattered whether the front document was saved; here, it is only necessary to check whether the document has ever been saved. If it has, we run the shell script to clean up the auxiliary files, but if it hasn't, then LaTeX must not have been run on it so we just quietly do nothing.

The seescriptsettings handler is simplified, as well. All that remains is a displayName, which appears in the mode menu. There seems to be little reason to have either a keyboard shortcut or a toolbar item, since the task is infrequent.

The clean-up done by latexmk is fairly conservative. It only eliminates the auxiliary files from the specifically named document (in our case, the front document in SEE), and not from other files that are included. Further, it doesn't clean up absolutely everything, since different LaTeX packages can create files of their own which latexmk doesn't recognize. My feeling is that being conservative here is appropriate, as we don't want to accidentally delete something important while getting rid of the usual suspects.

Thursday, July 5, 2007

SEEing LaTeX 6: Typesetting

Up to this point, I've written about composing LaTeX in SubEthaEdit, but not said much about invoking LaTeX to typeset the document. What I'd been doing was to run latexmk in a Terminal. Latexmk has the nice feature that it will run latex repeatedly until the document is ready, instead of having to do it repeatedly yourself. It also handles bibtex. Maybe best of all, you can set it to a continuous preview mode, which sets latexmk to watch your LaTeX file, recompiling it whenever you save changes you make; if you use a PDF previewer, like PDFView, that can reload the document whenever it changes, you can just set latexmk going and pretty much always have an up-to-date PDF ready to examine.

More typical is to have a command in your editor that typesets the LaTeX document and sends the resulting PDF (or, if you must, dvi) file to a previewer. While it may lack the sheer coolness of the continuous preview that latexmk provides, it's a lot more natural and probably just as effective. Let's add that to SubEthaEdit now. Let's also start saving some typing by referring to it as SEE.

The strategy will be to write a shell script to handle the typesetting, and an AppleScript to get needed information from SEE and feed it to the shell script. The shell script is easy, but does something that would be pretty awkward in AppleScript. The combination of shell and AppleScript is a useful way to take advantage of the power of Unix with data from an application.

The shell script takes a single argument that is the path to the LaTeX file. First, we set a PATH that includes the directories for TeX (TeXlive, in my case, so /usr/texbin) and latexmk (/usr/local/bin/). The rest of the script then just changes to the directory for the LaTeX file, and calls latexmk to do the typesetting. The script is simple:
#!/bin/sh

PATH=/usr/texbin:/usr/local/bin:$PATH
export PATH

cd "`dirname "$1"`"
latexmk -pv -pdf -quiet "`basename "$1"`"

The trickiest part was getting the quoting right for the arguments to cd and latexmk. They may look excessive, but all those quotes are necessary!

A copy of the script goes into the LaTeX mode bundle. I put it at ${RESOURCES}/Scripts/shell/buildlatex.sh, where RESOURCES refers to the Resources folder in the bundle. The executable bit of the script is set.

Now, let's look at the AppleScript. It's first going to save the document, if possible and necessary, since we'll normally want to see the effect of changes to a LaTeX document. We then extract the path to the file from the document, and the path to the shell script from the mode. These we glue together into a string that invokes the shell script on the document, and then we use do shell script to run it. Here's the script:
tell application "SubEthaEdit"
    if exists path of front document then
        if modified of front document then
            try
                save front document
            end try
        end if
        set latexFilePath to path of front document
        set modeResources to resource path of mode of front document
    else
        error "You have to save the document first"
    end if
end tell

set buildLatexScript to quote & modeResources & "/Scripts/shell/buildlatex.sh" & quote & space & quote & latexFilePath & quote

do shell script buildLatexScript

on seescriptsettings()
    return {displayName:"Typeset and View PDF", shortDisplayName:"Typeset", keyboardShortcut:"@b", toolbarIcon:"ToolbarIconBuildAndRun", inDefaultToolbar:"yes", toolbarTooltip:"Typeset and view the current document", inContextMenu:"no"}
end seescriptsettings

The structure of the tell block is based on the discussion at the end of the SEE scripting page. The string we construct to invoke the shell script once again has a lot of quoting; once again, it's necessary!

The boilerplate in the seescriptsettings handler sets up a keyboard shortcut and a toolbar icon. I just used ToolbarIconBuildAndRun for the toolbar icon image; I don't know that it's a great choice, but I didn't have anything more appropriate. For the keyboard shortcut, I went with command-B. It seemed appropriate, since the same shortcut is used in the Objective-C mode for compilation. The Java mode uses command-B for syntax checking, but it's inconsistent with several others that use command-control-B for syntax checking. I'd really liked to have used command-R to override the HTML preview, which is useless for the LaTeX mode, but it doesn't work; SEE just refuses to assign command-R to the script. Consistency for different modes is important, so command-B it is.

The combination works well, but isn't flawless. I have several items set in my .latexmkrc, which aren't, at the moment, handled in the LaTeX mode bundle. Also, latexmk seems to give an error due to bibtex if you have a bibliography file called without any citations; I haven't established the exact conditions, but they match a new LaTeX document with the templates I use! Also, the AppleScript is a little slow if the document needs to be saved. In fact, there is no need for testing whether the document has been modified; I only check because the try block for saving makes the script conspicuously slower.

Those are relatively minor problems. However, there is a conceptual problem that has become apparent: SubEthaEdit provides no means to define a mode-dependent environment. Amongst other problems, it makes it quite hard to write scripts like the ones shown in this post that won't break on someone else's computer. It would be quite appropriate to simply include latexmk in the bundle, but that obviously won't work for TeX! So, what to do? Put in paths for every TeX distribution I can think of? It's simply bad design, and I don't see any easy way for users to adjust the environment to suit their own systems (and, no, I don't think an environment.plist file is an acceptable solution).

Wednesday, July 4, 2007

SEEing LaTeX 5: Taking Stock

Today, I opened SubEthaEdit and found that I have 23 days left on the trial period. Put another way, I've been using it to write LaTeX for a week. It seems like an appropriate time to assess where I'm at.

Citations have not been satisfactorily resolved. I'd really like to have a keyboard shortcut that completes a partially typed citation. At the moment, I have the system services available in SubEthaEdit (and pretty much everywhere else, of course!), and copying and pasting from the reference list in BibDesk. It looks feasible to write an AppleScript to get a list of completions from BibDesk, which would do what I want. However, it would be necessary to select out the right bit of text preceding the insertion point, with correct handling of the cases where text is selected or not selected. It sounds very fiddly and time consuming, but not actually difficult. Since I am trying to test out as much as possible during the trial period, it seems best to leave this for now. On the bright side, it's not like I've actually lost anything that I had with TextMate, because the citation completion wasn't working right, anyway.

Pdfsync has been successful. It was surprisingly easy, although there was a need to dig into UI Scripting. The scripts could use a little refinement to be more robust, but work nicely enough as they are.

As a whole, the scripting dictionary for SubEthaEdit seems adequate. As is common for scripting dictionaries, there are oddities and omissions, but nothing show-stopping as of yet. The degree to which scripts can be made to look like toolbar items and menu items is rather impressive, actually.

So, what's left to do? The main things are to have means for typesetting from directly in SubEthaEdit, cleaning up all the auxiliary files, and comments. Cross references within the document could be more directly supported, although this is low priority: I use some personal LaTeX macros that reduce the need for such support, SubEthaEdit completes words from within the document, and showkeys makes the keys visible.

After that, I can't think of much that isn't just typing shortcuts, such as inserting environments, sectioning, and formatting. That's clearly possible with AppleScript, but time consuming to enter it all in. It doesn't seem that you can arrange the scripts into submenus, so it's undesirable to overdo this, anyway.

Monday, July 2, 2007

SEEing LaTeX 4: More on Completions

Earlier, I took a look at using BibDesk to get SubEthaEdit to complete citations in LaTeX files. After working with it a few days, I've learned a few more things about BibDesk. Unfortunately, not all good.

There seems to be a rather unpleasant interaction between BibDesk's completions and SubEthaEdit's completions. Normally, SubEthaEdit injects some mode-dependent terms into the completion dictionary. For some modes, like Python, this is no big deal; most of the keywords in Python are normal English words, so they would be completed by the standard completion dictionary. For other modes, e.g., the Objective-C mode, it can make a more significant contribution; the Objective-C mode defines a large number of Cocoa class names, which you can see by typing NS and pressing F5. Also, and maybe more importantly, SubEthaEdit will search through the current file to find matches, so, e.g., variables you've defined will be completed. For LaTeX, that provides a way to have the labels for equations, figures, and the like to be completed; for user-defined macros to be easily referenced; and for names and technical terms that don't appear in the standard dictionaries to be completed.

Using BibDesk's Autocompletion input manager disables SubEthaEdit's Autocompletion features. Apparently, any input manager that extends completions will have this effect, as the SubEthaEdit FAQ notes that TextExtras will stop SubEthaEdit's Autocompletions from working. With TextExtras, that wouldn't be so bad, since it provides completions of its own, but the tradeoff with BibDesk is more extreme. You can have citations and, interestingly, the \ref and \pageref commands for cross-references completed, but lose completion of the terms in the document, whether it be LaTeX or something else. Or, you can have autocompletion of the terms in the document, but lose autocompletion of citations (cross-reference completion is handled just fine by completion of terms from the document).

An early idea on how to resolve this was that I could install TextExtras, and use its completions. I used to use it, and liked it a lot, but gave up on it some time ago. I'm not sure exactly why, now, but I think it was because an earlier version of TextExtras conflicted with the then-new autocompletions in Mac OS X 10.3 Panther. Unfortunately, I haven't been able to connect to the TextExtras page to download it since coming up with the idea!

In the end, I disabled BibDesk's Autocompletion input manager, it simply comes at too high of a cost. BibDesk has a number of system services, which allow citations to be inserted that way. It will have to do for now. A solution using AppleScript to get completions from BibDesk looks possible, but it seems best to make the issue clear now and solve it later.

This is definitely a strike against using SubEthaEdit for LaTeX.

Update: The same thing happens when BibDesk's input manager is loaded into other editors, too. In Smultron, you lose completion of words from the document; I'm not so familiar with Smultron, so I don't know if there are other features that get lost. It is truly a shame that one of the coolest features of BibDesk is also one that I just don't want to use.

Sunday, July 1, 2007

SEEing LaTeX 3: From PDFView to SubEthaEdit

In my last post, I presented an AppleScript that took us from a LaTeX document in SubEthaEdit to an appropriate portion of the corresponding PDF document in PDFView. This useful action is possible thanks to the magic of pdfsync.

What's more, pdfsync can be used to work both ways. Let's take a look at how to go from a PDF document open in PDFView to the appropriate line in the LaTeX document. In PDFView, you can command-click in the PDF, which invokes a shell command to presumably take you to the right spot in your editor. This works very nicely in TextMate, for example.

PDFView has a "command" text field and an "arguments" text field in its preferences. I don't really see why it's broken into two fields, but that's how it is, so that's what we'll work with. The arguments can include "%file" and "%line" tokens to indicate the file and line number, respectively. PDFView substitutes those tokens appropriately, and runs the script.

SubEthaEdit has the see command line tool that lets us open a file. That will be our starting point. Shockingly, it doesn't have a switch to go to a particular line! We'll work around that with AppleScript. This will be needlessly ugly, I'm afraid.

The see tool opens a file, and makes it the front document. That is the behavior we want for the document, so we'll start there and build a shell script to extend the behavior. We won't try to expose all the options of see in our script, instead just opening a file and going to a given line. We'll call the script seeline, and take its first argument to be the filename.

The next step in the script is to set the selection in the front document to a line number given as the second script argument. This is easy enough with AppleScript, and is a fairly direct transcription of the English-language description. Unfortunately, that turns out not to be enough. The selection is set as desired, but the selection may not be visible! What's worse, there is nothing in SubEthaEdit's AppleScript dictionary that lets us scroll the view as needed.

SubEthaEdit does have a "Jump to Selection" item in its "Find" menu. We could just hit command-J after PDFView transfers us to the LaTeX document in SubEthaEdit, I guess, but it is inelegant, at best. Instead, let's try something else. AppleScript can be used to script the user interface. You may need to first open the AppleScript Utility (why isn't this a preference pane, anyway?) and check the "Enable GUI Scripting" box. We can then use System Events to directly work with the menus of SubEthaEdit, choosing the "Jump to Selection" item.

Taken all together, we get a script:
#!/bin/sh
#
# Brings document to front in SubEthaEdit, opening if necessary, and
# selects given line.
#

#$Id: seeline.sh,v 1.4 2007/07/01 18:28:31 mjb Exp $


/usr/bin/see "$1"
/usr/bin/osascript > /dev/null <<ASCPT

tell application "SubEthaEdit"
    tell front document to set selection to paragraph $2
end tell

tell application "System Events"
    tell process "SubEthaEdit"
        tell menu bar 1
            tell menu bar item "Find"
                tell menu 1
                    click menu item "Jump to Selection"
                end tell
            end tell
        end tell
    end tell
end tell
ASCPT

All the AppleScript is included as a here document, which prevents lots of trouble with how the shell processes quotes and other characters.

I'm not pleased with using UI scripting. It seems fragile and likely to break with updated version of SubEthaEdit. Clearly, this should be an option for the see command line tool, which pretty much means it needs to be handled by the developers.

Whatever my feelings on the aesthetics of UI scripting, the important question is something else: does it work? Yes, it does, and pretty well at that. I have saved the script as ~/Library/bin/seeline, and set the "Command" and "Arguments" in the PDFView preferences to '$HOME/Library/bin/seeline' and '"%file" %line', respectively. Command-clicking in the PDF takes me to the LaTeX file.

Edit: Fixed formatting of included script.

Update: As of version 3.0 of SubEthaEdit, the see command line tool has an option to go to a desired line.

SEEing LaTeX 2: Opening the PDF in a Viewer

Getting completions for citations in SubEthaEdit is easy. Of course, someone else did all the work for us, so it should be easy! Let's try something a little more ambitious.

I generally use pdflatex. Thanks to pdfsync, a number of editors can integrate nicely with PDF viewers, albeit the viewers themselves must incorporate support for pdfsync. So, let's try to set up pdfsync support for SubEthaEdit. I'll focus on integrating SubEthaEdit with my current viewer of choice, PDFView. Sadly, PDFView appears not to be supported any more, but it's still useful and the basic approach should be the same for any viewer. For now, I'll just focus on going from SubEthaEdit to PDFView.

The mechanism for linking SubEthaEdit to PDFVIew will be AppleScript. You can have PDFView display a particular LaTeX line using AppleScript, which is just what we need. The strategy then is to get the line number for the selection in SubEthaEdit, figure out the file name for the PDF, and then tell PDFView to show the line number we obtained from SubEthaEdit. According to the SubEthaEdit scripting guide, we also need to include a seescriptsettings handler. Here, I present something simple, without working too hard to catch errors:
tell application "SubEthaEdit"
    tellfront document
        
set lineNumber to startLineNumber of selection
        set texFile to path
    end tell
end tell

set pdfFile to (text 1 through ((length of texFile) - 3) of texFile) & "pdf"

tell application "PDFView"
    activate
    display tex line lineNumber of file pdfFile
end tell

-- SubEthaEdit settings

on seescriptsettings()
    {displayName:"Show in PDFView", shortDisplayName:"PDFView", keyboardShortcut:"^~@o", toolbarIcon:"ToolbarPDFView.png", inDefaultToolbar:"yes", toolbarTooltip:"Opens PDF for document in PDFView", inContextMenu:"no"}
end seescriptsettings

I saved the above script as a compiled script into a copy of the LaTeX mode. Now, for a LaTeX document, the Mode menu in SubEthaEdit shows a script called "View in PDFView" which can be activated by a keyboard shortcut. It works well.

I also added an image to the LaTeX mode bundle to provide a toolbar item. I encountered some difficulties with this. The description in the scripting guide isn't very specific, it really just says to put images in the bundle. I got an appropriate image by copying the icon from the PDFView application bundle, and converting it to PNG (since that's what all the other images in SubEthaEdit mode bundles were). This didn't work. It took me some time to discover that the toolbar icon cannot be any size; the 128 by 128 pixel image I had simply didn't show up. Downsizing it to 32 by 32 using ImageWell solved the problem, providing a nice toolbar item. Of course, I normally leave the toolbar hidden, so I doubt I'll be using it much, but it is good to know how that works.

The script has a couple of oddities and shortcomings. The tell block for SubEthaEdit needs to get the selection and path for the front document. However, the scripting dictionary for SubEthaEdit has both documents containing windows and windows containing documents. Is tell front document the right thing to do? Should it be tell document of front window? Or maybe something else?

Also, I don't check to see if the document has been saved. I'm not really sure what the right solution is: unsaved changes won't be reflected in the PDF, anyway, so should I care? I'll revisit that later, assuming that I decide SubEthaEdit is suitable for my LaTeX needs.

Nor do I check to see if there is a document open at all. As far as I can tell, this is not an error. The script is mode dependent. Since the LaTeX mode is only active if there is a document open, there is thus no need to check.

The transformation of the LaTeX file path to a PDF file path is not done well. It should really be improved by properly splitting off the file extension, instead of just chopping off the last three characters and assuming we've gotten the file extension. While it is probably OK for LaTeX, I'm not sure if it could cause any problems. Again, this is something to fix later, once a more complete picture of editing LaTeX in SubEthaEdit takes shape.

The tell block for PDFView is weird. There aren't any changes I'd make, but it is just strange. If you open the script in Script Editor and compile it, the display tex line lineNumber of file pdfFile becomes display tex line lineNumber file pdfFile, which is syntactically invalid. However, you can run the script without first compiling, and it works fine. Which is really weird, because the script has to be compiled before running it, so it works once but fails the second time you run it. My solution was to do the editing in SubEthaEdit, compile it in Terminal using osacompile, and not worry about the matter. I suspect that the scripting dictionary in PDFView should just be considered in error, even though it can be made to work.

Next, I'll look at going the other direction, from PDFView to SubEthaEdit. A quite pleasant environment already looks possible.

Edit: Fixed formatting of included script.

Friday, June 29, 2007

SEEing LaTeX 1: Citations

As a first step towards turning SubEthaEdit into a capable LaTeX editing machine, let's address citations. Citations are a challenge to keep consistent and accurate, so mechanized assistance is quite useful. For LaTeX, that means BibTeX. BibTeX separates content from presentation in much the way that TeX does. Considering the many different formatting styles for bibliographies and citations that are required by different journals, this is something that I won't give up on -- I'll type the citation keys by hand if needed.

Fortunately, getting citation support in SubEthaEdit is trivial! Just install BibDesk, if you're not already using it, and set up autocompletion for SubEthaEdit. Inside a \cite, hit F5 or command-escape, and you get a list of completions for a partial citation key. This works really well. I hadn't used BibDesk for that for a while, since TextMate had its own method (not that it has worked in recent versions), so I'd forgotten how slick this is.

Completions in general, not just for citations, can be made a little bit easier. In your user Library folder, there may be a KeyBindings folder; if not, create it. Create a file DefaultKeyBinding.dict in the KeyBindings folder. The file is an XML file with a list of key-action pairs; bind the escape key to a string "complete:" (with the colon, without the quotes). The Property List Editor works nicely for doing this. The end result is that you just press escape instead of command-escape to get a completion. It doesn't cause any problems for using escape to, say, select cancel in dialogs, so there's no down side, just completions that are easier to activate.

Thursday, June 28, 2007

Mac OS X editors for writing LaTeX

Well, it's taken a bit longer for me to get back to writing than I'd expected. I do intend to follow up on the one million answers post, but for now I'll take a look at something else.

In short, I am fed up with the LaTeX mode for TextMate, and maybe TextMate in general. Writing text, instead of programming, really seems to highlight the worst aspects of TextMate. Besides the well known failings of its undo, TextMate has two other serious annoyances that are relevant here.

First, you can only use monospace fonts. I accept it as the reality for a lot of programming, even if I don't like it. For writing LaTeX, it's terrible. Literally headache inducing.

The second flaw is how words are selected using the mouse. Standard behavior for Mac OS X is that when you double-click on a word, it is selected. Extending this, if you "click and a half," (double click, but keep the mouse button held down on the second click) you can then drag and select text by words instead of characters. In most applications, this works very well, and is often simpler than character-based selection. However, in TextMate, this works poorly, because you can't select a sentence including the period at the end. You can get to the last word, and then the selection jumps to include the first word of the next sentence.

All three of these problems make writing text a little bit harder, but aren't that big of a deal for programming. Taken all together, they make writing LaTeX into a stressful experience, and formal writing is hard enough. These troubles all come from the use of a custom text view instead of the standard Cocoa NSTextView. The author of TextMate, Allan Odgaard, has written that it would be easier to write a replacement for NSTextView than to get NSTextView to do what he wanted. I'm not sure I believe him; for me, at least, behaving like the rest of the Mac OS X text system is pretty important. In this sense, he hasn't written a replacement for NSTextView.

TextMate does have a LaTeX mode, though. It has a lot of nice features, like integrating with PDF viewers through pdfsync, text completion for citations and cross references, assorted functions for project management, and various snippets for simplifying typing. Unfortunately, not all of that works very well -- I've never got citation completion to work reliably, and not for lack of trying. Text completions have some funny behavior. Showing an outline of the document fails if it includes a file that started with the letter 'x' (no, I'm not kidding).

Because of all this, I've downloaded the current version of SubEthaEdit and will give that a try for the next month. So far, I'm finding the basic editing to be much more pleasant. I'm using a proportional font, undos work sanely, and I can select by words using the mouse. The basic task of writing seems a lot more pleasant. I haven't used SubEthaEdit much since getting TextMate, and the last version I'd used was version 2.2. It's still the same elegant, minimalist editor, but things have changed a bit. There are now language modes with mode-dependent scripting, not just syntax highlighting. There are now tabs, and a quite elegant implementation at that (no projects, yet, though).

That said, the LaTeX mode just has syntax highlighting. No pdfsync support. No keystroke to compile the file into a PDF and send it to a viewer. No completion of citations. However, SubEthaEdit is extendable by scripting and, thanks to its reliance on Mac OS X standards, the editing features can be extended by input managers. Over the thirty-day trial period, I'll see what I can do to extend SubEthaEdit's LaTeX mode into something a little more capable.