beforeRunningCommand nop command #!/usr/bin/env ruby # # Based on (from Source.tmbundle): # # Assignment block tidier, version 0.1. # # Copyright Chris Poirier 2006. # Licensed under the Academic Free License version 3.0. # # This script can be used as a command for TextMate to align all # of the assignment signs within a block of text. When using it with # TextMate, set the command input to "Selected Text" or "Document", # and the output to "Replace Selected Text". Map it to a key # equivalent, and any time you want to tidy up a block, either # select it, or put your cursor somewhere within it; then hit the # key equivalent. Voila. # # Note that this is the first version of the script, and it hasn't # been heavily tested. You might encounter a bug or two. # # Note (by Dr Nic) - the "first version" seems to have worked for years. # I hope the CoffeeScript version is as successful. # # Per the license, use of this script is ENTIRELY at your own risk. # See the license for full details (they override anything I've # said here). lines = STDIN.readlines() selected_text = ENV.member?("TM_SELECTED_TEXT") relevant_line_pattern = /^[^:]+:/ column_search_pattern = /[\t ]*:/ comments = [] begin # # If called on a selection, every assignment statement # is in the block. If called on the document, we start on the # current line and look up and down for the start and end of the # block. if selected_text then block_top = 1 block_bottom = lines.length else # # We start looking on the current line. However, if the # current line doesn't match the pattern, we may be just # after or just before a block, and we should check. If # neither, we are done. start_on = ENV["TM_LINE_NUMBER"].to_i block_top = lines.length + 1 block_bottom = 0 search_top = 1 search_bottom = lines.length search_failed = false if lines[start_on - 1] !~ relevant_line_pattern then if lines[start_on - 2] =~ relevant_line_pattern then search_bottom = start_on = start_on - 1 elsif lines[start_on] =~ relevant_line_pattern then search_top = start_on = start_on else search_failed = true end end # # Now with the search boundaries set, start looking for # the block top and bottom. unless search_failed start_on.downto(search_top) do |number| if lines[number-1] =~ relevant_line_pattern then block_top = number else break end end start_on.upto(search_bottom) do |number| if lines[number-1] =~ relevant_line_pattern then block_bottom = number else break end end end end # # Now, iterate over the block and find the best column number # for the = sign. The pattern will tell us the position of the # first bit of whitespace before the equal sign. We put the # equals sign to the right of the furthest-right one. Note that # we cannot assume every line in the block is relevant. best_column = 0 block_top.upto(block_bottom) do |number| line = lines[number - 1] if line =~ relevant_line_pattern then m = column_search_pattern.match(line) best_column = m.begin(0) if m.begin(0) > best_column end end # # Reformat the block. Again, we cannot assume all lines in the # block are relevant. block_top.upto(block_bottom) do |number| if lines[number-1] =~ relevant_line_pattern then before, after = lines[number-1].split(/[\t ]*:[\t ]*/, 2) # lines[number-1] = [before.ljust(best_column), after].join(after[0,1] == '>' ? ":" : ": ") lines[number-1] = ["#{before}:".ljust(best_column + 2), after].join end end rescue => e comments << "Error: #{e.inspect}" comments << e.backtrace end # # Output the replacement text lines.each do |line| puts line end comments.flatten.each { |c| puts "# #{c}" } input selection keyEquivalent ~@] name Align Assignments output replaceSelectedText scope source.coffee uuid EE3293A5-3761-40BD-9CA8-DAAA176AA19E