class Rufo::Formatter
def initialize(code, **options)
def initialize(code, **options) @code = code @tokens = Ripper.lex(code).reverse! @sexp = Ripper.sexp(code) unless @sexp raise ::Rufo::SyntaxError.new end @indent = 0 @line = 0 @column = 0 @last_was_newline = false @output = "" # The column of a `obj.method` call, so we can align # calls to that dot @dot_column = nil # The column of a `obj.method` call, but only the name part, # so we can also align arguments accordingly @name_dot_column = nil # Heredocs list, associated with calls ([heredoc, tilde]) @heredocs = [] # Current node, to be able to associate it to heredocs @current_node = nil # The current heredoc being printed @current_heredoc = nil # The current hash or call or method that has hash-like parameters @current_hash = nil # Map lines to commands that start at the begining of a line with the following info: # - line indent # - first param indent # - first line ends with '(', '[' or '{'? # - line of matching pair of the previous item # - last line of that call # # This is needed to dedent some calls that look like this: # # foo bar( # 2, # ) # # Without the dedent it would normally look like this: # # foo bar( # 2, # ) # # Because the formatter aligns this to the first parameter in the call. # However, for these cases it's better to not align it like that. @line_to_call_info = {} # Each line that belongs to a heredoc content is put here @heredoc_lines = {} # Position of comments that occur at the end of a line @comments_positions = [] # Associate lines to alignments # Associate a line to an index inside @comments_position # becuase when aligning something to the left of a comment # we need to adjust the relative comment @line_to_alignments_positions = Hash.new { |h, k| h[k] = [] } # Position of assignments @assignments_positions = [] # Range of assignment (line => end_line) # # We need this because when we have to format: # # ``` # abc = 1 # a = foo bar: 2 # baz: # # ``` # # Because we'll insert two spaces after `a`, this will # result in a mis-alignment for baz (and possibly other lines # below it). So, we remember the line ranges of an assignment, # and once we align the first one we fix the other ones. @assignments_ranges = {} # Hash keys positions @hash_keys_positions = [] # Case when positions @case_when_positions = [] # Settings indent_size options.fetch(:indent_size, 2) space_after_hash_brace options.fetch(:space_after_hash_brace, :dynamic) space_after_array_bracket options.fetch(:space_after_array_bracket, :never) align_comments options.fetch(:align_comments, true) align_assignments options.fetch(:align_assignments, false) align_hash_keys options.fetch(:align_hash_keys, true) align_case_when options.fetch(:align_case_when, true) align_chained_calls options.fetch(:align_chained_calls, true) preserve_whitespace options.fetch(:preserve_whitespace, true) trailing_commas options.fetch(:trailing_commas, :always) end