Matlab (not Octave) Bundle
This has a couple of small advantages over the stock MATLAB bundle:
- Send commands to a MATLAB process running in Terminal (currently only works if MATLAB is running in command-line mode in Terminal; does not work with iTerm, TotalTerminal, etc.)
- Cell editing - skip to next/previous cell, execute cell in MATLAB
- Better comment wrapping using the standard "Reformat Comment" command from the Source bundle (bound to ⌃Q by default)
- Better function and for-loop snippets - includes a for-loop snippet to iterate over cell arrays of strings
- Better documentation look-up (using Google's I'm Feeling Lucky, which is a faster and more accurate way to get to a function's documentation than searching MathWorks' web site) and quick access to built-in documentation (by typing 'help ...' to a running MATLAB process)
- Many things that exist in the stock MATLAB bundle which cause irritation do not exist in this bundle (all the Enter in ... commands that insert ellipses in strange places, many essentially useless snippets)
This bundle is targeted specifically at MATLAB. Octave users will probably not find it very helpful.
How to use it
To send commands to a running MATLAB process, either start MATLAB manually in Terminal by typing
matlab -nosplash -nodesktop or use the bundle's "Spawn MATLAB" command. Once MATLAB is running, you can execute the current line or selection with ⌘R or the current cell with ⇧⌘R.
Move to the next/previous cell with ⌥⌘↓ and ⌥⌘↑ respectively.
Ask MATLAB for help (if it's running) on the current word with ⌃H, and ask Google for help with ⌃⇧H.
Rationale and implementation notes
Cells should not be scoped in the language grammar.
Pragmatically, doing so makes the language grammar complicated because you still need to scope every other language element inside each cell, and the gains from doing this are small. The reasons for scoping are to apply coloring and to easily select parts of a document as input for an automation. The benefit of applying some sort of coloring to an entire cell seems dubious, and it's possible to highlight the cell divisions without scoping the entire cell (using the
comment.double.percentage.matlabscope). Sending a cell to an automation is certainly more useful, but here I don't see anything beyond the obvious 'Execute this cell' command, and this is easily handled with a macro.
Philosophically, a cell is like any other block-level element of the MATLAB language, such as a
forloop. These are also not scoped, in MATLAB or any language, because language grammars are for discovering syntax elements and not higher-level semantic elements.
You can find cell divisions using the regular expression
'^[ \t]*%% '(note the two spaces). The dividing line between cells is the first character of that expression (at the beginning of the first line of the cell). A cell may start on the first line of a file, so you can't count on a newline preceding the cell marker. The next and previous cell macros work by searching for this expression and placing the cursor at the first character.
Getting that to work right in practice was a little bit tricky for me. When you just paste that expression into TextMate's Find dialog box and use the Next/Previous buttons, you cycle through the cells as expected. I think this works because the expression you search for gets highlighted, and then when you click on Next, it means "find the next occurrence after the highlighted region," and Previous means "find the prior occurrence before the highlighted region." The Next/Previous Cell macros should put the cursor at the dividing line between cells, which is the beginning of the line on which a new cell starts. So the Next Cell macro says "find that regular expression, then move left," which puts the cursor at the left side of the highlighted region, in this case the beginning of the line. But now if we repeat that same sequence of commands, we find the cell division we're already at. To deal with this, the Next Cell macro actually says, "move right, then find the regex, then move left."
To select an entire cell, first move the cursor to the beginning of the line that starts the current cell. You can always do this by first using Next Cell, then Previous Cell. This will work even if the current cell is the last one in the file, because the Next/Previous macros wrap around. You cannot just do Previous Cell because the cursor might already be at the beginning of the current cell, in which case that will move you back one cell. Then search for this regex:
%%(?m:.*?)(?=^[ \t]*%% |\z)
This bit of nonsense says to match two percentage signs, which will be the first non-whitespace characters after the cursor, followed by a multi-line (dot matches newline) subexpression that can contain any characters, but is reluctant instead of the greedy default (if it were greedy, it would match all the way to the last cell division in the file). This subexpression matches all the way to the first point where the look-ahead expression is satisfied, and the look-ahead says that the next thing in the character stream should be a cell division or the end of the stream (so that we still match the last cell in the file).
The "Execute Cell" macro works by doing that, and then moving left to put the cursor at the beginning of the current cell. It would be nicer to have an automation that didn't move the cursor, but I haven't thought of a way to do this without writing an actual command. Macros are simpler.
Strategies for communication
The problem is to set up a line of communication with a MATLAB process. I take the view that MATLAB does not emit useful information under any circumstances, and therefore this communication need be only one-way. Aside from this, TextMate is a document editor. A MATLAB document is not the series of input-output pairs that appears at the terminal; it's a complete file that describes some useful series of actions, such as a function or script for generating a figure. The idea that interactive input-output belongs in the body proper of your document is taken to the extreme in Mathematica (and probably other programs like it), and one thing I've learned from using MM is that you do not gain anything from this approach. Instead, you spend lots of time going back through the notebook and deleting cells that were part of the process of discovery, but that aren't germane to the final result. Further evidence for the wisdom of separating interactive exploration from the body of a document is seen in Emacs, where having separate buffers for input-output and file editing has been preferred by Lisp hackers for nigh on these 1,000 years. Interaction with MATLAB, Mathematica, Lisp, Ruby, etc, is tremendously helpful while you're figuring out how to do something, but the document that you preserve should be a description and explanation of the finished procedure. Exploratory wanderings have no place there.
There is consequently no need for two-way communication with a running MATLAB process. It should be possible to send code from your document to MATLAB and examine the results, but the results should not be inserted into your document by default, and I don't think this process should be easy or automatic at all. So in the end, what we desire is to have MATLAB running (as a command-line utility, without the GUI) and to be able to interact with it in two ways: directly, just as if we had launched it from Terminal manually, and also from TextMate, by sending the current line, cell, or selected text to MATLAB.
I first tried to get this style of interaction working with traditional Unix solutions, specifically named pipes and screen. (Flip back through the repository's history to see the named pipes in action; I'm not sure I ever got too far with screen.) My big plan for named pipes was to create an input pipe and attach the Terminal's keyboard input directly to this pipe, then start a MATLAB process and attach the output end of this pipe to the input of the MATLAB process. Then, my theory went, I would have a Terminal window in which I could type to MATLAB as normal, and behind the scenes a named pipe that some other process could write to that also went to MATLAB. After spending an embarrassing amount of time on this, I discovered that either I don't understand Unix and all its baggage very well, or that that whole strategy is a bad idea. (One disadvantage is certainly that it's not clear what should happen if more than one process was writing to the named input pipe at the same time. I figured I would just be careful and never do that. Anyway, this never worked too well, and I ended up experimenting with ...
Screen, which has a built-in feature that kind of does what I was trying to accomplish. This was a non-starter for me because it meant running another layer of stuff on top of MATLAB that bought me nothing but unneeded complexity, and because I almost immediately ran in to issues with keystrokes like ⌃A not doing what I expected and some kind of problem with a buffer being too small to accommodate some commands. (Actually a frequent problem, since I was trying to send whole cell's worth of code at a time.)
The best solution I've found so far was also the most surprising: AppleScript. The Terminal application provides a
do script command which behaves exactly the right way (the one, single, incontrovertible right way), and the rest of it is stupidly simple and inefficient. When you use any of the bundle's commands to execute something in MATLAB, TextMate pipes the relevant part of your document to an AppleScript. This script searches through all the open Terminal windows and tabs looking for any process with "MATLAB" in its name. If it finds such a process, it uses Terminal's
do script to send the input to that tab. This has turned out to be easier and more robust than named pipes or screen, and it also doesn't require you to start up the MATLAB process in any special way. Some very helpful suggestions throughout this process came from stackoverflow if you're interested in reading more, and that is the end of this long story of a Unix noob trying to wire things together, failing, and falling back on AppleScript.