Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
You can’t perform that action at this time.