class Rufo::Formatter

def visit(node)

def visit(node)
  @node_level += 1
  unless node.is_a?(Array)
    bug "unexpected node: #{node} at #{current_token}"
  end
  case node.first
  when :program
    # Topmost node
    #
    # [:program, exps]
    visit_exps node[1], with_indent: true
  when :void_stmt
    # Empty statement
    #
    # [:void_stmt]
    skip_space_or_newline
  when :@int
    # Integer literal
    #
    # [:@int, "123", [1, 0]]
    consume_token :on_int
  when :@float
    # Float literal
    #
    # [:@int, "123.45", [1, 0]]
    consume_token :on_float
  when :@rational
    # Rational literal
    #
    # [:@rational, "123r", [1, 0]]
    consume_token :on_rational
  when :@imaginary
    # Imaginary literal
    #
    # [:@imaginary, "123i", [1, 0]]
    consume_token :on_imaginary
  when :@CHAR
    # [:@CHAR, "?a", [1, 0]]
    consume_token :on_CHAR
  when :@gvar
    # [:@gvar, "$abc", [1, 0]]
    write node[1]
    next_token
  when :@backref
    # [:@backref, "$1", [1, 0]]
    write node[1]
    next_token
  when :@backtick
    # [:@backtick, "`", [1, 4]]
    consume_token :on_backtick
  when :string_literal, :xstring_literal
    visit_string_literal node
  when :string_concat
    visit_string_concat node
  when :@tstring_content
    # [:@tstring_content, "hello ", [1, 1]]
    heredoc, tilde = @current_heredoc
    if heredoc && tilde && broken_ripper_version?
      @squiggly_flag = true
    end
    # For heredocs with tilde we sometimes need to align the contents
    if heredoc && tilde && @last_was_newline
      unless (current_token_value == "\n" ||
              current_token_kind == :on_heredoc_end)
        write_indent(next_indent)
      end
      skip_ignored_space
      if current_token_kind == :on_tstring_content
        check :on_tstring_content
        consume_token_value(current_token_value)
        next_token
      end
    else
      while (current_token_kind == :on_ignored_sp) ||
            (current_token_kind == :on_tstring_content) ||
            (current_token_kind == :on_embexpr_beg)
        check current_token_kind
        break if current_token_kind == :on_embexpr_beg
        consume_token current_token_kind
      end
    end
  when :string_content
    # [:string_content, exp]
    visit_exps node[1..-1], with_lines: false
  when :string_embexpr
    # String interpolation piece ( #{exp} )
    visit_string_interpolation node
  when :string_dvar
    visit_string_dvar(node)
  when :symbol_literal
    visit_symbol_literal(node)
  when :symbol
    visit_symbol(node)
  when :dyna_symbol
    visit_quoted_symbol_literal(node)
  when :@ident
    consume_token :on_ident
  when :var_ref
    # [:var_ref, exp]
    visit node[1]
  when :var_field
    # [:var_field, exp]
    visit node[1]
  when :@kw
    # [:@kw, "nil", [1, 0]]
    consume_token :on_kw
  when :@ivar
    # [:@ivar, "@foo", [1, 0]]
    consume_token :on_ivar
  when :@cvar
    # [:@cvar, "@@foo", [1, 0]]
    consume_token :on_cvar
  when :@const
    # [:@const, "FOO", [1, 0]]
    consume_token :on_const
  when :const_ref
    # [:const_ref, [:@const, "Foo", [1, 8]]]
    visit node[1]
  when :top_const_ref
    # [:top_const_ref, [:@const, "Foo", [1, 2]]]
    consume_op "::"
    skip_space_or_newline
    visit node[1]
  when :top_const_field
    # [:top_const_field, [:@const, "Foo", [1, 2]]]
    consume_op "::"
    visit node[1]
  when :const_path_ref
    visit_path(node)
  when :const_path_field
    visit_path(node)
  when :assign
    visit_assign(node)
  when :opassign
    visit_op_assign(node)
  when :massign
    visit_multiple_assign(node)
  when :ifop
    visit_ternary_if(node)
  when :if_mod
    visit_suffix(node, "if")
  when :unless_mod
    visit_suffix(node, "unless")
  when :while_mod
    visit_suffix(node, "while")
  when :until_mod
    visit_suffix(node, "until")
  when :rescue_mod
    visit_suffix(node, "rescue")
  when :vcall
    # [:vcall, exp]
    token_column = current_token_column
    visit node[1]
  when :fcall
    # [:fcall, [:@ident, "foo", [1, 0]]]
    visit node[1]
  when :command
    visit_command(node)
  when :command_call
    visit_command_call(node)
  when :args_add_block
    visit_call_args(node)
  when :args_add_star
    visit_args_add_star(node)
  when :bare_assoc_hash
    # [:bare_assoc_hash, exps]
    # Align hash elements to the first key
    indent(@column) do
      visit_comma_separated_list node[1]
    end
  when :method_add_arg
    visit_call_without_receiver(node)
  when :method_add_block
    visit_call_with_block(node)
  when :call
    visit_call_with_receiver(node)
  when :brace_block
    visit_brace_block(node)
  when :do_block
    visit_do_block(node)
  when :block_var
    visit_block_arguments(node)
  when :begin
    visit_begin(node)
  when :bodystmt
    visit_bodystmt(node)
  when :if
    visit_if(node)
  when :unless
    visit_unless(node)
  when :while
    visit_while(node)
  when :until
    visit_until(node)
  when :case
    visit_case(node)
  when :when
    visit_when(node)
  when :unary
    visit_unary(node)
  when :binary
    visit_binary(node)
  when :class
    visit_class(node)
  when :module
    visit_module(node)
  when :mrhs_new_from_args
    visit_mrhs_new_from_args(node)
  when :mlhs_paren
    visit_mlhs_paren(node)
  when :mlhs
    visit_mlhs(node)
  when :mrhs_add_star
    visit_mrhs_add_star(node)
  when :def
    visit_def(node)
  when :defs
    visit_def_with_receiver(node)
  when :paren
    visit_paren(node)
  when :params
    visit_params(node)
  when :array
    visit_array(node)
  when :hash
    visit_hash(node)
  when :assoc_new
    visit_hash_key_value(node)
  when :assoc_splat
    visit_splat_inside_hash(node)
  when :@label
    # [:@label, "foo:", [1, 3]]
    write node[1]
    next_token
  when :dot2
    visit_range(node, true)
  when :dot3
    visit_range(node, false)
  when :regexp_literal
    visit_regexp_literal(node)
  when :aref
    visit_array_access(node)
  when :aref_field
    visit_array_setter(node)
  when :sclass
    visit_sclass(node)
  when :field
    visit_setter(node)
  when :return0
    consume_keyword "return"
  when :return
    visit_return(node)
  when :break
    visit_break(node)
  when :next
    visit_next(node)
  when :yield0
    consume_keyword "yield"
  when :yield
    visit_yield(node)
  when :@op
    # [:@op, "*", [1, 1]]
    write node[1]
    next_token
  when :lambda
    visit_lambda(node)
  when :zsuper
    # [:zsuper]
    consume_keyword "super"
  when :super
    visit_super(node)
  when :defined
    visit_defined(node)
  when :alias, :var_alias
    visit_alias(node)
  when :undef
    visit_undef(node)
  when :mlhs_add_star
    visit_mlhs_add_star(node)
  when :rest_param
    visit_rest_param(node)
  when :kwrest_param
    visit_kwrest_param(node)
  when :retry
    # [:retry]
    consume_keyword "retry"
  when :redo
    # [:redo]
    consume_keyword "redo"
  when :for
    visit_for(node)
  when :BEGIN
    visit_BEGIN(node)
  when :END
    visit_END(node)
  else
    bug "Unhandled node: #{node.first}"
  end
ensure
  @node_level -= 1
end