# 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) 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 Helper then 20 when TypeMember then 30 when MixesInClassMethods then 40 when Send then 50 when TStructField then 60 when TEnumBlock then 70 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::Helpers then 1 when Group::Kind::TypeMembers then 2 when Group::Kind::MixesInClassMethods then 3 when Group::Kind::Sends then 5 when Group::Kind::TStructFields then 6 when Group::Kind::TEnums then 7 when Group::Kind::Inits then 8 when Group::Kind::Methods then 9 when Group::Kind::SingletonClasses then 10 when Group::Kind::Consts then 11 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 node.name end end end end class Tree extend T::Sig sig { void } def sort_nodes! visitor = Rewriters::SortNodes.new visitor.visit(self) end end end