# typed: strict # frozen_string_literal: true module RBI module Rewriters class SortNodes < Visitor extend T::Sig sig { override.params(node: T.nilable(Node)).void } def visit(node) sort_node_names!(node) if node return unless node.is_a?(Tree) visit_all(node.nodes) original_order = node.nodes.map.with_index.to_h node.nodes.sort! do |a, b| # First we try to compare the nodes by their node rank (based on the node type) res = node_rank(a) <=> node_rank(b) next res if res != 0 # we can sort the nodes by their rank, let's stop here # Then, if the nodes ranks are the same (res == 0), we try to compare the nodes by their name res = node_name(a) <=> node_name(b) next res if res && res != 0 # we can sort the nodes by their name, let's stop here # Finally, if the two nodes have the same rank and the same name or at least one node is anonymous then, T.must(original_order[a]) <=> T.must(original_order[b]) # we keep the original order end end private sig { params(node: Node).returns(Integer) } def node_rank(node) case node when Group then group_rank(node.kind) when Include, Extend then 10 when RequiresAncestor then 15 when Helper then 20 when TypeMember then 30 when MixesInClassMethods then 40 when Send then 50 when TStructField then 60 when TEnumBlock then 70 when Attr then 75 when Method if node.name == "initialize" 81 elsif !node.is_singleton 82 else 83 end when SingletonClass then 90 when Scope, Const then 100 else 110 end end sig { params(kind: Group::Kind).returns(Integer) } def group_rank(kind) case kind when Group::Kind::Mixins then 0 when Group::Kind::RequiredAncestors then 1 when Group::Kind::Helpers then 2 when Group::Kind::TypeMembers then 3 when Group::Kind::MixesInClassMethods then 4 when Group::Kind::Sends then 5 when Group::Kind::TStructFields then 6 when Group::Kind::TEnums then 7 when Group::Kind::Attrs then 8 when Group::Kind::Inits then 9 when Group::Kind::Methods then 10 when Group::Kind::SingletonClasses then 11 when Group::Kind::Consts then 12 else T.absurd(kind) end end sig { params(node: Node).returns(T.nilable(String)) } def node_name(node) case node when Module, Class, Struct, Const, Method, Helper, TStructField, RequiresAncestor node.name when Attr node.names.first.to_s end end sig { params(node: Node).void } def sort_node_names!(node) case node when Attr node.names.sort! end end end end class Tree extend T::Sig sig { void } def sort_nodes! visitor = Rewriters::SortNodes.new visitor.visit(self) end end end