Scheme line "values"

Years ago, when I first saw TextMate demonstrated, one of the ways it was used as a teaching tool, when teaching Ruby, was to have the current line executed, and the value it returned appended to the current line:

(2 + 3) * 4 / 5 # => 4

That is, pressing Cmd-Shift-Ctrl-E would execute the line, and update the marker.

Today, while playing around with Scheme, I came up with a neat way to do the same type of thing.

Initially, I made it so that it executed the current line, and added/updated the marker. Then, I realised I could load the file, and then execute the current line.

You can create a new bundle command, and bind it to whatever key you want, with a scope selector of source.scheme, Input of Line, Output of Replace Input.

#!/usr/bin/env bash

[[ -f "${TM_SUPPORT_PATH}/lib/bash_init.sh" ]] && . "${TM_SUPPORT_PATH}/lib/bash_init.sh"

# Evaluate the current line in our Scheme interpreter
#
# The interpreter you use should be set in the environment
# variable TM_SCHEME

# The whole file will be loaded, and the current line's value executed,
# and added to the line as a comment.

INTERPRET=${TM_SCHEME}
CMD=$(basename "$INTERPRET")

LINE=`cat /dev/stdin | sed 's/; =>.*//'`
VALUE=`echo $LINE | $INTERPRET --load $TM_FILEPATH | grep ';Value: ' | sed 's/;Value: //'`

echo -n $LINE "; =>" $VALUE

Unfortunately, trailing comments are handled as a seperate line, so getting the ruby-like behaviour of updating all of the ; => comments will have to wait for another day.

Highlight 'highlight' blocks in Markdown/Textmate

The other day, I mentioned that I had Marked.app nicely handling my {% highlight %} blocks, and syntax highlighting them. In passing at the end, I mentioned that TextMate was still formatting them as if they were Markdown.

Now, one way around this is to indent them, but then within the code block they are indented further, and that offends my sensibilities.

Now, within TextMate, syntax highlighting is based on scopes, so to do what I want (which is the same as how HTML may have CSS or JS embedded in it), we just need a language grammar pattern that matches, and applies the relevant scope.

TextMate 2 has even nicer features, where you can set the scope (but not, as it turns out, include rules) dynamically based on a match in the pattern.

Anyway, on to the rules.

Rather than edit the Markdown rules, I wanted to just inject the language grammars in from a bundle of my own, but had no luck with this. Instead, I decided to extend the Jekyll bundle.

This is what I wanted to put in the patterns (simplified a little):

1 {
2   begin = ' "%\}\n';
3   end = ' "%\}\n';
4   name = 'source.$1.embedded.html.markdown';
5   patterns = ( { include = 'source.$1'; } );
6 }

However, as I mentioned above, the expression on line 5 does not actually include source.js patterns in this case.

Instead, I needed to have a seperate pattern for each language I wanted to include patterns from. Since mostly I work in python, html and javascript, for now those ones will do.

Oh, and the last thing is that html needs to include text.html.basic.

You can see my fork at jekyll-tmbundle. The current code is:

Open in Textmate Service

Listening to the new podcast by the ever-present Dan Benjamin and the effervescent Merlin Mann today reminded me of the one Mac OS X service I wrote, that I use almost daily.

It allows me to right-click on the filename line in a python traceback, and have the file opened at that line in Textmate. If the file is part of an already open project, it will open in the project window (unless it is open in another window, in which case that may pop to front).

Fairly simple stuff, and should be easy to extend for other traceback/output types.

https://bitbucket.org/schinckel/open-in-textmate-service

Django Management.tmbundle

Did some work on my Django Management.tmbundle last night.

It now handles running tests when (a) The apps are not directly in the project root, but inside another folder, for instance; and (b) the app/tests.py file has been split into seperate files.

The main reason I made this was so that I could run tests and have clickable links in the results window for the location of failing tests.

There is still much to do on this. I am considering re-writing it in python rather than ruby, so I can programmatically find the app name, rather than guess it. I also want to refactor the hell out of it and make it much nicer.

Anyway, if you are interested, you can find the most recent version at http://github.com/schinckel/Django-Management.tmbundle - and I think it also appears in TextMate’s getBundles bundle.

TextMate return codes

From the TextMate manual:

These functions only work when the initial output option is not set as “Show as HTML”. The list of functions is as follows:

  • exit_discard
  • exit_replace_text
  • exit_replace_document
  • exit_insert_text
  • exit_insert_snippet
  • exit_show_html
  • exit_show_tool_tip
  • exit_create_new_document¬†¬†

This is all well and good, but what about when you are in another language?

Simple. Just ensure your exit code matches. The values start at 200, for exit_discard, and 205 is exit_show_html.

This is probably not the best way to do it, as these may change in the future. But, I couldn’t think of another way, at least not offline.

Patch for Mercurial.tmbundle

 1 diff -r 5e13047a2284 Support/hg_commit.rb
 2     --- a/Support/hg_commit.rb	Mon Apr 27 11:38:15 2009 +0930
 3     +++ b/Support/hg_commit.rb	Mon Apr 27 11:39:00 2009 +0930
 4     @@ -79,7 +79,7 @@
 5        commit_paths_array = matches_to_paths(commit_matches)
 6        commit_status = matches_to_status(commit_matches).join(":")
 7        commit_path_text = commit_paths_array.collect{|path| path.quote_filename_for_shell }.join(" ")
 8     -  commit_args = %x{"#{commit_tool}" --status #{commit_status} #{commit_path_text} }
 9     +  commit_args = %x{"#{commit_tool}" --diff-cmd hg,diff --status #{commit_status} #{commit_path_text} }
10      
11        status = $CHILD_STATUS
12        if status != 0
13     

SWI-Prolog Bundle

I’ve cloned and updated the included Prolog bundle to use my Run script.

This can be found at GitHub: SWI-Prolog.tmbundle.

Run Prolog Program

In the continuing effort to do ‘productive’ tasks, but not actually the project I am supposed to be working on, I present a TextMate command to run the current prolog file, with nice HTML output, and input via a dialog box.

 1     #! /usr/bin/env ruby
 2     
 3     require ENV["TM_SUPPORT_PATH"] + "/lib/tm/executor"
 4     
 5     command = [ENV["TM_PROLOG"] || "swipl", "-s", ENV["TM_FILEPATH"]]
 6     two_line = false
 7     
 8     welcome = /^(Welcome to SWI-Prolog)|(Copyright )|(SWI-Prolog comes with)|(and you are welcome)|(Please visit)|(For help, use)/
 9     
10     TextMate::Executor.run(command) do |str, type|
11       if type == :err
12         if two_line
13           two_line = false
14           # this line is part of the previous message
15           "#{str}</div>"
16         # Is this a warning line?
17         elsif str =~ /(Warning):\s(.*):(\d+):/
18           warn, file, line = $1, $2, $3
19           filename = file.split('/')[-1]
20           two_line = true
21           file_link = "<a class=\"near\" href=\"txmt://open?line=#{line}&url=file://#{file}\">#{filename}</a>"
22           "<div class=\"#{warn}\">#{warn}: #{file_link}, line #{line}:"
23         elsif str =~ /(ERROR):\s(.*):(\d+):(\d+):\s(.*)/
24           file, line, char, message = $2, $3, $4, $5
25           filename = file.split('/')[-1]
26           file_link = "<a class=\"near\" href=\"txmt://open?line=#{line}&column=#{char}&url=file://#{file}\">#{filename}</a>"
27           "<div class=\"err\">ERROR: #{file_link}, line #{line}, col #{char}: #{message}</div>"
28         elsif str =~ /ERROR:\s(.*)/
29           message = $1
30           "<div class=\"test fail\">ERROR: #{message}</div>"
31         elsif str =~ /%\s(.*)\scompiled\s(.*)\ssec,\s(.*)\sbytes/
32           file, time, length = $1, $2, $3
33           filename = file.split('/')[-1]
34           file_link = "<a class=\"near\" href=\"txmt://open?url=file://#{file}\">#{filename}</a>"
35           "<div class=\"test ok\"> #{file_link} (#{length} bytes) compiled in #{time} sec.</div>"
36         elsif str =~ welcome
37           "<span class=\"copyright\" style=\"font-size:xx-small;\">#{str}</span> "
38         else
39           "<div class=\"output\">#{str}</div>"
40         end
41       else
42         "<div class=\"output\">#{str}</div>"
43       end
44     end

Run Django Tests from TextMate

It would be cool to be able to run my Django tests from within TextMate.

Update: this version will run just the tests from the active file, if there are any. Otherwise, it runs all of the tests in the whole project.

Here is a Command to do just that:

 1     #! /usr/bin/env ruby
 2     
 3     command = [ENV["TM_PYTHON"] || "python", "-u", "#{ENV['TM_PROJECT_DIRECTORY']}/manage.py", "test", "--noinput"]
 4     
 5     File.open(ENV['TM_FILEPATH']) do |f|
 6       f.readlines.each do |line|
 7         if line =~ /class (.*)\(.*TestCase\):/
 8           test_case = $1
 9           app_name = ENV['TM_FILEPATH'].split(ENV['TM_PROJECT_DIRECTORY'])[1].split('/')[1]
10           test_name = "#{app_name}.#{test_case}"
11           command << test_name
12         end
13       end
14     end
15     
16     require ENV["TM_SUPPORT_PATH"] + "/lib/tm/executor"
17     
18     ENV["PYTHONPATH"] = ENV["TM_BUNDLE_SUPPORT"] + (ENV.has_key?("PYTHONPATH") ? ":" + ENV["PYTHONPATH"] : "")
19     
20     TextMate::Executor.run(command) do |str, type|
21       if type == :err
22         if str =~ /\A[\.EF]*\Z/
23           str.gsub!(/(\.)/, "<span class=\"test ok\">\1</span>")
24           str.gsub!(/(E|F)/, "<span class=\"test fail\">\1</span>")
25           str + "<br/>\n"
26         elsif str =~ /\A(FAILED.*)\Z/
27           "<div class=\"test fail\">#{htmlize $1}</div>\n"
28         elsif str =~ /\A(OK.*)\Z/
29           "<div class=\"test ok\">#{htmlize $1}</div>\n"
30         elsif str =~ /^(\s+)File "(.+)", line (\d+), in (.*)/
31           indent = $1
32           file   = $2
33           line   = $3
34           method = $4
35           indent += " " if file.sub!(/^\"(.*)\"/,"\1")
36           url = "&url=file://" + e_url(file)
37           display_name = file.split('/').last 
38           "#{htmlize(indent)}<a class=\"near\" href=\"txmt://open?line=#{line + url}\">" +
39             (method ? "method #{method}" : "<em>at top level</em>") +
40             "</a> in <strong>#{display_name}</strong> at line #{line}<br/>\n"
41         end
42       end
43     end

Python Line Continuations

Using TextMate, it is very easy to get snippets and commands to do things that you often do. However, the python bundle is a bit lacking, and this is a great opportunity to improve that.

I’ve created a Command that will enter a newline, and if not inside a list, function call, dictionary or multi-line string, automatically add a trailing .

I’ve hooked it in to the Enter key, and the other settings can be seen in the screenshot below:

The actual code follows:

 1 #!/usr/bin/env ruby
 2 
 3 scope = ENV['TM_SCOPE'].split
 4 
 5 no_trail = ['punctuation.definition.arguments.end.python',
 6             'meta.structure.list.python',
 7             'meta.structure.dictionary.python',
 8             'string.quoted.single.block.python',
 9             'string.quoted.double.block.python']
10 
11 print (scope & no_trail) == [] ? "\\n" : "\n"