Created
August 8, 2023 03:19
-
-
Save tomoasleep/cb79b44e412f83aa99b60ee1df0743eb to your computer and use it in GitHub Desktop.
SigDivider divides a single RBI file into multiple files by each class or module.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
require 'fileutils' | |
require 'rbi' | |
require 'active_support/all' | |
# SigDivider divides a single RBI file into multiple files by each class or module. | |
class SigDivider < RBI::Visitor | |
TreeWithName = Struct.new(:name, :tree, keyword_init: true) | |
# @return [String] | |
attr_reader :output_dir | |
# @param output_dir [String] | |
def initialize(output_dir:) | |
@output_dir = output_dir | |
end | |
# @param rbi_source [String] | |
def process(rbi_source:) | |
rbi_tree = RBI::Parser.parse_string(rbi_source) | |
trees_with_names = visit(rbi_tree) | |
if rbi_tree.nodes.present? | |
trees_with_names << TreeWithName.new(name: "Root", tree: rbi_tree) | |
end | |
FileUtils.rmtree(output_dir) | |
trees_with_names.each do |tree_with_name| | |
filename = File.join(output_dir, "#{tree_with_name.name.underscore}.rbi") | |
RBI::Formatter.new( | |
group_nodes: true, | |
sort_nodes: true, | |
).format_tree(tree_with_name.tree) | |
puts "Writing #{tree_with_name.name} -> #{filename}" | |
FileUtils.mkdir_p(File.dirname(filename)) | |
tree_string = <<~RBI | |
# typed: true | |
# DO NOT EDIT MANUALLY. | |
# THIS FILE IS AUTOGENERATED. | |
#{tree_with_name.tree.string.chomp} | |
RBI | |
File.write(filename, tree_string) | |
end | |
end | |
# @param node [RBI::Node, nil] | |
# @return [Array<TreeWithName>] | |
def visit(node) | |
case node | |
when RBI::Module | |
# puts "Module: #{node.fully_qualified_name}" | |
tree = build_detached_tree(node) | |
[tree, *node.nodes.dup.flat_map { |n| visit(n) }] | |
when RBI::Class | |
# puts "Class: #{node.fully_qualified_name}" | |
tree = build_detached_tree(node) | |
[tree, *node.nodes.dup.flat_map { |n| visit(n) }] | |
when RBI::Tree | |
# puts "Tree: #{node.try(:index_ids)}" | |
node.nodes.dup.flat_map { |n| visit(n) } | |
else | |
# puts "Other: #{node.try(:index_ids)}" | |
[] | |
end | |
end | |
# @param node [RBI::Scope, nil] | |
# @return [TrewWithName] | |
def build_detached_tree(node) | |
name = node.fully_qualified_name | |
ancestors_rev = node_ancestors(node).reverse | |
node.detach | |
tree = ancestors_rev.reduce(node) do |child_node, parent_node_origin| | |
parent_node = | |
case parent_node_origin | |
when RBI::Module | |
RBI::Module.new(parent_node_origin.name) | |
when RBI::Class | |
RBI::Class.new(parent_node_origin.name) | |
when RBI::SingletonClass | |
RBI::SingletonClass.new | |
when RBI::Struct | |
RBI::Struct.new(parent_node_origin.name, members: parent_node_origin.members, keyword_init: parent_node_origin.keyword_init) | |
end | |
parent_node << child_node | |
parent_node | |
end | |
TreeWithName.new(name: name, tree: tree) | |
end | |
# @param node [RBI::Scope, nil] | |
# @return [Array<RBI::Scope>] | |
def node_ancestors(node) | |
ancestors = [] | |
iter = node | |
while iter = iter.parent_scope | |
ancestors.unshift(iter) | |
end | |
ancestors | |
end | |
end | |
SigDivider.new(output_dir: ARGV[0]).process(rbi_source: File.read(ARGV[1])) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment