Skip to content

Instantly share code, notes, and snippets.

@sellmerfud
Created November 9, 2013 14:26
Show Gist options
  • Save sellmerfud/7385988 to your computer and use it in GitHub Desktop.
Save sellmerfud/7385988 to your computer and use it in GitHub Desktop.
Textmate align command for scala Adapted from the align bundle. Handles alignment on =>, =, ->, <-
#!/usr/bin/env ruby
#
# Scala alignment command.
#
# This script will attempt to align lines of scala code around one of the following
# symbols:
# case symbol: =>
# assignment: =
# right arrow: ->
# left arrow: <-
#
# To determine which alignment symbol to use, the script will try matching
# the current line to see which symbol it contains. The symbols are
# tried in the following order (=>, =, ->, <-).
#
# If there is a selection, then the we start at the top of the selection and move
# down to the last line of the selection using the first line that contains one
# of the alignment symbols. And the entire operation will be limited
# to the selected text. In this case there may be lines that do not contain
# the alignment symbol and these will be left alone.
#
# If there is no selection, then only lines directly above and below the current
# line that contain the alignment symbol are affected. As soon as a line above
# or below the current line is found that does not contain the alignment symbol,
# that forms the boundary of the operation.
#
#
# This should be set up as a TextMate command as follows:
# input : 'Selected Text' or 'Document'
# output: 'Replace Selected Text
#
# `lines` will contain either selected text or entire document.
# If there is a selection, current_line will be the last line of the selection.
def match_symbol(line)
alignments = [
{ :symbol => "=>",
:line_pattern => /=>/,
:col_pattern => /[\t ]*=>/,
:split_pattern => /[\t ]*=>[\t ]*/
},
{ :symbol => "=",
:line_pattern => /[^=]*[^-+<>=!%\/|&*^]=(?!=|~|>)/,
:col_pattern => /[\t ]*=(?!=|~|>)/,
:split_pattern => /[\t ]*=(?!=|~|>)[\t ]*/
},
{ :symbol => "->",
:line_pattern => /->/,
:col_pattern => /[\t ]*->/,
:split_pattern => /[\t ]*->[\t ]*/
},
{ :symbol => "<-",
:line_pattern => /<-/,
:col_pattern => /[\t ]*<-/,
:split_pattern => /[\t ]*<-[\t ]*/
}
]
i = alignments.index { |entry| line =~ entry[:line_pattern] }
i ? alignments[i] : nil
end
lines = STDIN.readlines()
have_selection = ENV.member?("TM_SELECTED_TEXT")
block_top = 0
block_bottom = lines.length - 1
a = nil
if have_selection
# Starting at the top of the selection find the first line that contains
# one of our alignment symbols.
0.upto(lines.length - 1) do |number|
a = match_symbol(lines[number])
break unless a.nil?
end
else
#
# We start looking on the current line. However, if the
# current line doesn't match the line_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 - 1
block_top = lines.length
block_bottom = 0
a = match_symbol(lines[start_on])
#
# Now with the search boundaries set, start looking for
# the block top and bottom.
unless a.nil?
start_on.downto(0) do |number|
break unless lines[number] =~ a[:line_pattern]
block_top = number
end
start_on.upto(lines.length - 1) do |number|
break unless lines[number] =~ a[:line_pattern]
block_bottom = number
end
end
end
# If we found a line with an alignment symbol then we are in business
unless a.nil?
# First off, make sure that the indentation is the same for each line.
# The smallest indentation is used.
indentation = nil
block_top.upto(block_bottom) do |number|
line = lines[number]
if line =~ a[:line_pattern] then
leading = line.match(/^\s*/).to_s
indentation = leading if indentation.nil? || leading.size < indentation.size
end
end
#
# Indent all relevant lines with the calculated indention.
block_top.upto(block_bottom) do |number|
line = lines[number]
lines[number] = indentation + line.sub(/^\s*/,"") if line =~ a[:line_pattern]
end
#
# Now, iterate over the block and find the best column number
# for the `symbol`. The `column_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.
#
# First find the best column for the symbol.
best_column = 0
block_top.upto(block_bottom) do |number|
line = lines[number]
if line =~ a[:line_pattern] then
m = a[:col_pattern].match(line)
best_column = m.begin(0) if m.begin(0) > best_column
end
end
#
# Now reformat the relevant lines in the block.
block_top.upto(block_bottom) do |number|
line = lines[number]
if line =~ a[:line_pattern] then
before, after = line.split(a[:split_pattern], 2)
lines[number] = [before.ljust(best_column), after].join(" #{a[:symbol]} ")
end
end
end
#
# Output the replacement text
print lines
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment