class Rufo::Formatter
def consume_end_of_line(at_prefix: false, want_semicolon: false, want_multiline: true, needs_two_lines_on_comment: false)
- want_semicolon: do we want do print a semicolon to separate expressions?
- at_prefix: are we at a point before an expression? (if so, we don't need a space before the first comment)
Consume and print an end of line, handling semicolons and comments
def consume_end_of_line(at_prefix: false, want_semicolon: false, want_multiline: true, needs_two_lines_on_comment: false) found_newline = false # Did we find any newline during this method? found_comment_after_newline = false # Did we find a comment after some newline? last = nil # Last token kind found multilple_lines = false # Did we pass through more than one newline? last_comment_has_newline = false # Does the last comment has a newline? newline_count = 0 # Number of newlines we passed loop do case current_token_kind when :on_sp # Ignore spaces next_token when :on_nl, :on_ignored_nl # I don't know why but sometimes a on_ignored_nl # can appear with nil as the "text", and that's wrong if current_token[2].nil? next_token next end if last == :newline # If we pass through consecutive newlines, don't print them # yet, but remember this fact multilple_lines = true unless last_comment_has_newline else # If we just printed a comment that had a newline, # we must print two newlines because we remove newlines from comments (rstrip call) write_line if last == :comment && last_comment_has_newline multilple_lines = true else multilple_lines = false end end found_newline = true next_token last = :newline newline_count += 1 when :on_semicolon next_token # If we want to print semicolons and we didn't find a newline yet, # print it, but only if it's not followed by a newline if !found_newline && want_semicolon && last != :semicolon skip_space case current_token_kind when :on_ignored_nl, :on_eof else write "; " last = :semicolon end end multilple_lines = false when :on_comment if last == :comment # Since we remove newlines from comments, we must add the last # one if it was a comment write_line # If the last comment is in the previous line and it was already # aligned to this comment, keep it aligned. This is useful for # this: # # ``` # a = 1 # some comment # # that continues here # ``` # # We want to preserve it like that and not change it to: # # ``` # a = 1 # some comment # # that continues here # ``` if current_comment_aligned_to_previous_one? write_indent(@last_comment_column) track_comment(match_previous_id: true) else write_indent end else if found_newline if newline_count == 1 && needs_two_lines_on_comment if multilple_lines write_line multilple_lines = false else multilple_lines = true end needs_two_lines_on_comment = false end # Write line or second line if needed write_line if last != :newline || multilple_lines write_indent track_comment(id: @last_was_newline ? true : nil) else # If we didn't find any newline yet, this is the first comment, # so append a space if needed (for example after an expression) write_space unless at_prefix # First we check if the comment was aligned to the previous comment # in the previous line, in order to keep them like that. if current_comment_aligned_to_previous_one? track_comment(match_previous_id: true) else # We want to distinguish comments that appear at the beginning # of a line (which means the line has only a comment) and comments # that appear after some expression. We don't want to align these # and consider them separate entities. So, we use `@last_was_newline` # as an id to distinguish that. # # For example, this: # # # comment 1 # # comment 2 # call # comment 3 # # Should format to: # # # comment 1 # # comment 2 # call # comment 3 # # Instead of: # # # comment 1 # # comment 2 # call # comment 3 # # We still want to track the first two comments to align to the # beginning of the line according to indentation in case they # are not already there. track_comment(id: @last_was_newline ? true : nil) end end end @last_comment = current_token @last_comment_column = @column last_comment_has_newline = current_token_value.end_with?("\n") last = :comment found_comment_after_newline = found_newline multilple_lines = false write current_token_value.rstrip next_token when :on_embdoc_beg write_line if multilple_lines consume_embedded_comment last = :comment last_comment_has_newline = true else break end end # Output a newline if we didn't do so yet: # either we didn't find a newline and we are at the end of a line (and we didn't just pass a semicolon), # or the last thing was a comment (from which we removed the newline) # or we just passed multiple lines (but printed only one) if (!found_newline && !at_prefix && !(want_semicolon && last == :semicolon)) || last == :comment || (multilple_lines && (want_multiline || found_comment_after_newline)) write_line end end