class Tailor::LexedLine
This class provides methods for finding info about the current line. It works off the format that results from {Tailor::Lexer}.
Public Class Methods
# File lib/tailor/lexed_line.rb, line 13 def initialize(lexed_file, lineno) @lineno = lineno super(current_line_lex(lexed_file, lineno)) end
Public Instance Methods
@return [Boolean]
# File lib/tailor/lexed_line.rb, line 35 def comment_line? first_non_space_element[1] == :on_comment end
@return [Boolean] true
if the line contains an keyword and it is in
+KEYWORDS_TO_INDENT.
# File lib/tailor/lexed_line.rb, line 147 def contains_keyword_to_indent? self.any? do |e| e[1] == :on_kw && KEYWORDS_TO_INDENT.include?(e[2]) end end
@param [Array] lexed_output The lexed output for the whole file. @return [Array]
# File lib/tailor/lexed_line.rb, line 20 def current_line_lex(lexed_output, lineno) lexed_output.find_all { |token| token.first.first == lineno }.uniq end
@return [Boolean]
# File lib/tailor/lexed_line.rb, line 76 def does_line_end_with(event, exclude_newlines=true) if exclude_newlines if last_non_line_feed_event.first.empty? false else last_non_line_feed_event[1] == event end else self.last[1] == :on_ignored_nl || self.last[1] == :on_nl end end
Determines if the current lexed line is just the end of a tstring.
@return [Boolean] true
if the line contains a :on_tstring_end
and
not a +:on_tstring_beg+.
# File lib/tailor/lexed_line.rb, line 239 def end_of_multi_line_string? self.any? { |e| e[1] == :on_tstring_end } && self.none? { |e| e[1] == :on_tstring_beg } end
Checks to see if the line ends with a keyword, and that the keyword is used as a modifier.
@return [Boolean]
# File lib/tailor/lexed_line.rb, line 66 def ends_with_modifier_kw? return false unless ends_with_kw? token = Tailor::Lexer::Token.new(last.last, { full_line_of_text: to_s }) token.modifier_keyword? end
Checks to see if the current line ends with an operator (not counting the newline that might come after it).
@return [Boolean] true if the line ends with an operator; false if not.
# File lib/tailor/lexed_line.rb, line 43 def ends_with_op? lexed_line = self.dup tokens_in_line = lexed_line.map { |e| e[1] } until tokens_in_line.last != (:on_ignored_nl || :on_nl) tokens_in_line.pop lexed_line.pop end return false if lexed_line.empty? if MULTILINE_OPERATORS.include?(lexed_line.last.last) && tokens_in_line.last == :on_op true else false end end
@param [Fixnum] column Number of the column to get the event for. @return [Array] The event at the given column.
# File lib/tailor/lexed_line.rb, line 177 def event_at(column) self.find { |e| e.first.last == column } end
Useful for inspecting events relevant to this one.
@example
i = lexed_line.event_index(11) previous_event = lexed_line.at(i - 1)
@param [Fixnum] column Number of the column of which event to get the
index for.
@return [Fixnum] The index within self
that the event is at.
# File lib/tailor/lexed_line.rb, line 189 def event_index(column) column_event = self.event_at column self.index(column_event) end
Gets the first non-space element from a line of lexed output.
@return [Array] The element; nil
if none is found.
# File lib/tailor/lexed_line.rb, line 126 def first_non_space_element self.find do |e| e[1] != :on_sp && e[1] != :on_nl && e[1] != :on_ignored_nl end end
Checks to see if the line contains only event
(where it may or may not be preceded by spaces, and is proceeded by a newline).
@param [Symbol] event The type of event to check for. @return [Boolean]
# File lib/tailor/lexed_line.rb, line 93 def is_line_only_a(event) last_event = last_non_line_feed_event return false if last_event[1] != event index = event_index(last_event.first.last) previous_event = self.at(index - 1) previous_event.first.last.zero? || previous_event.first.last.nil? end
When Ripper lexes a Symbol, it generates one event for :on_symbeg, which is the ':' token, and one for the name of the Symbol. Since your Symbol name can be anything, the second event could be something like “class”, in which case :on_kw will get called and probably result in unexpected behavior.
This assumes the keyword in question is the last event in the line.
@return [Boolean]
# File lib/tailor/lexed_line.rb, line 253 def keyword_is_symbol? current_index = self.index(self.last) previous_event = self.at(current_index - 1) return false if previous_event.nil? return false unless self.last[1] == :on_kw return false unless previous_event[1] == :on_symbeg true end
@return [Array] The lexed event that represents the last event in the
line that's not a line-feed. Line-feed events are signified by +:on_nl+ and +on_ignored_nl+ events, and by +:on_sp+ events when they equal +"\\\n" (which occurs when a line is broken by a backslash).
# File lib/tailor/lexed_line.rb, line 157 def last_non_line_feed_event events = self.find_all do |e| e[1] != :on_nl && e[1] != :on_ignored_nl && e.last != "\\\n" end events.last || [[]] end
@return [Fixnum] The length of the line minus the \n
.
# File lib/tailor/lexed_line.rb, line 168 def line_length event = last_non_line_feed_event return 0 if event.first.empty? event.first.last + event.last.size end
Checks to see if the current line is a keyword loop (for, while, until) that uses the optional 'do' at the end of the statement.
@return [Boolean]
# File lib/tailor/lexed_line.rb, line 136 def loop_with_do? keyword_elements = self.find_all { |e| e[1] == :on_kw } keyword_tokens = keyword_elements.map { |e| e.last } loop_start = keyword_tokens.any? { |t| LOOP_KEYWORDS.include? t } with_do = keyword_tokens.any? { |t| t == 'do' } loop_start && with_do end
Allows for calling a couple styles of methods:
-
ends_with_(.+)? - Allows for checking if the line ends with (.+)
-
only_(.+)? - Allows for checking if the line is only spaces and (.+)
# File lib/tailor/lexed_line.rb, line 106 def method_missing(meth, *args, &blk) if meth.to_s =~ /^ends_with_(.+)\?$/ event = "on_#{$1}".to_sym if event == :on_ignored_nl || event == :on_nl does_line_end_with(event, false) else does_line_end_with event end elsif meth.to_s =~ /^only_(.+)\?$/ event = "on_#{$1}".to_sym is_line_only_a(event) else super(meth, *args, &blk) end end
Looks at self and determines if it' s a line of just space characters: spaces, newlines.
@return [Boolean]
# File lib/tailor/lexed_line.rb, line 28 def only_spaces? element = first_non_space_element log "first non-space element '#{element}'" element.nil? || element.empty? end
If a trailing comment exists in the line, remove it and the spaces that come before it. This is necessary, as Ripper
doesn't trigger an event for the end of the line when the line ends with a comment. Without this observers that key off ending the line will never get triggered, and thus style won't get checked for that line.
@param [String] file_text The whole file's worth of text. Required in
order to be able to reconstruct the context in which the line exists.
@return [LexedLine] The current lexed line, but with the trailing comment
removed.
# File lib/tailor/lexed_line.rb, line 209 def remove_trailing_comment(file_text) file_lines = file_text.split("\n") lineno = self.last.first.first column = self.last.first.last log "Removing comment event at #{lineno}:#{column}." comment_index = event_index(column) self.delete_at(comment_index) self.insert(comment_index, [[lineno, column], :on_nl, "\n"]) log "Inserted newline for comma; self is now #{self.inspect}" if self.at(comment_index - 1)[1] == :on_sp self.delete_at(comment_index - 1) end new_text = self.to_s log "New line as text: '#{new_text}'" file_lines.delete_at(lineno - 1) file_lines.insert(lineno - 1, new_text) file_lines = file_lines.join("\n") ripped_output = ::Ripper.lex(file_lines) LexedLine.new(ripped_output, lineno) end
@return [String] The string reassembled from self's tokens.
# File lib/tailor/lexed_line.rb, line 195 def to_s self.inject('') { |new_string, e| new_string << e.last } end
Private Instance Methods
# File lib/tailor/lexed_line.rb, line 269 def log(*args) l = begin; lineno; rescue; '<EOF>'; end c = begin; column; rescue; '<EOF>'; end subclass_name = self.class.to_s.sub(/^Tailor::/, '') args.first.insert(0, "<#{subclass_name}> #{l}[#{c}]: ") Tailor::Logger.log(*args) end