Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flatten namespaces when outputting RBI files #137

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions lib/parlour/conflict_resolver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def resolve_conflicts(namespace, &resolver)
# The types aren't the same, so ask the resolver what to do, and
# insert that (if not nil)
choice = resolver.call("Different kinds of definition for the same name", children)
namespace.children << choice if choice
namespace.add_child(choice) if choice
next
end

Expand All @@ -149,7 +149,7 @@ def resolve_conflicts(namespace, &resolver)
end

non_namespaces.each do |x|
namespace.children << x
namespace.add_child(x)
end

# For certain namespace types the order matters. For example, if there's
Expand All @@ -173,14 +173,14 @@ def resolve_conflicts(namespace, &resolver)
if T.must(first).mergeable?(T.must(rest))
Debugging.debug_puts(self, @debugging_tree.end("Children are all mergeable; resolving automatically"))
first.merge_into_self(rest)
namespace.children << first
namespace.add_child(first)
next
end

# I give up! Let it be resolved manually somehow
Debugging.debug_puts(self, @debugging_tree.end("Unable to resolve automatically; requesting manual resolution"))
choice = resolver.call("Can't automatically resolve", children)
namespace.children << choice if choice
namespace.add_child(choice) if choice
else
Debugging.debug_puts(self, @debugging_tree.end("No conflicts"))
end
Expand Down
17 changes: 11 additions & 6 deletions lib/parlour/rbi_generator/class_namespace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class ClassNamespace < Namespace
sealed: T::Boolean,
superclass: T.nilable(String),
abstract: T::Boolean,
path: String,
block: T.nilable(T.proc.params(x: ClassNamespace).void)
).void
end
Expand All @@ -30,8 +31,8 @@ class ClassNamespace < Namespace
# @param abstract [Boolean] A boolean indicating whether this class is abstract.
# @param block A block which the new instance yields itself to.
# @return [void]
def initialize(generator, name, final, sealed, superclass, abstract, &block)
super(generator, name, final, sealed, &block)
def initialize(generator, name, final, sealed, superclass, abstract, path: '', &block)
super(generator, name, final, sealed, path: path, &block)
@superclass = superclass
@abstract = abstract
end
Expand All @@ -43,20 +44,24 @@ def initialize(generator, name, final, sealed, superclass, abstract, &block)
).returns(T::Array[String])
end
# Generates the RBI lines for this class.
#
#
# @param indent_level [Integer] The indentation level to generate the lines at.
# @param options [Options] The formatting options to use.
# @return [Array<String>] The RBI lines, formatted as specified.
def generate_rbi(indent_level, options)
class_definition = superclass.nil? \
? "class #{name}"
: "class #{name} < #{superclass}"
? "class #{full_path}"
: "class #{full_path} < #{superclass}"

lines = generate_comments(indent_level, options)
lines << options.indented(indent_level, class_definition)
lines += [options.indented(indent_level + 1, "abstract!"), ""] if abstract
lines += generate_body(indent_level + 1, options)
lines << options.indented(indent_level, "end")

namespace_children = self.children.select { |c| c.is_a?(Namespace) }
lines + namespace_children.
map { |c| [''] + c.generate_rbi(indent_level, options)}
end

sig { returns(T.nilable(String)) }
Expand Down
8 changes: 5 additions & 3 deletions lib/parlour/rbi_generator/enum_class_namespace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class EnumClassNamespace < ClassNamespace
sealed: T::Boolean,
enums: T::Array[T.any([String, String], String)],
abstract: T::Boolean,
path: String,
block: T.nilable(T.proc.params(x: EnumClassNamespace).void)
).void
end
Expand All @@ -27,10 +28,11 @@ class EnumClassNamespace < ClassNamespace
# @param sealed [Boolean] Whether this namespace is sealed.
# @param enums [Array<(String, String), String>] The values of the enumeration.
# @param abstract [Boolean] A boolean indicating whether this class is abstract.
# @param path [String] the fully resolved path to this constant.
# @param block A block which the new instance yields itself to.
# @return [void]
def initialize(generator, name, final, sealed, enums, abstract, &block)
super(generator, name, final, sealed, 'T::Enum', abstract, &block)
def initialize(generator, name, final, sealed, enums, abstract, path: '', &block)
super(generator, name, final, sealed, 'T::Enum', abstract, path: path, &block)
@enums = enums
end

Expand Down Expand Up @@ -123,4 +125,4 @@ def describe_attrs
end
end
end
end
end
12 changes: 9 additions & 3 deletions lib/parlour/rbi_generator/module_namespace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class ModuleNamespace < Namespace
sealed: T::Boolean,
interface: T::Boolean,
abstract: T::Boolean,
path: String,
block: T.nilable(T.proc.params(x: ClassNamespace).void)
).void
end
Expand All @@ -28,10 +29,11 @@ class ModuleNamespace < Namespace
# @param interface [Boolean] A boolean indicating whether this module is an
# interface.
# @param abstract [Boolean] A boolean indicating whether this module is abstract.
# @param path [String] the fully resolved path to this constant.
# @param block A block which the new instance yields itself to.
# @return [void]
def initialize(generator, name, final, sealed, interface, abstract, &block)
super(generator, name, final, sealed, &block)
def initialize(generator, name, final, sealed, interface, abstract, path: '', &block)
super(generator, name, final, sealed, path: path, &block)
@name = name
@interface = interface
@abstract = abstract
Expand All @@ -50,11 +52,15 @@ def initialize(generator, name, final, sealed, interface, abstract, &block)
# @return [Array<String>] The RBI lines, formatted as specified.
def generate_rbi(indent_level, options)
lines = generate_comments(indent_level, options)
lines << options.indented(indent_level, "module #{name}")
lines << options.indented(indent_level, "module #{full_path}")
lines += [options.indented(indent_level + 1, "interface!"), ""] if interface
lines += [options.indented(indent_level + 1, "abstract!"), ""] if abstract
lines += generate_body(indent_level + 1, options)
lines << options.indented(indent_level, "end")

namespace_children = self.children.select { |c| c.is_a?(Namespace) }
lines + namespace_children.
map { |c| [''] + c.generate_rbi(indent_level, options)}
end

sig { returns(T::Boolean) }
Expand Down
65 changes: 53 additions & 12 deletions lib/parlour/rbi_generator/namespace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ class Namespace < RbiObject
# @param options [Options] The formatting options to use.
# @return [Array<String>] The RBI lines, formatted as specified.
def generate_rbi(indent_level, options)
namespace_children = self.children.select { |c| c.is_a?(Namespace) }
child_lines = namespace_children.map { |c| [''] + c.generate_rbi(indent_level, options)}.flatten
child_lines = child_lines[1..] if child_lines.any? # remove leading empty line

generate_comments(indent_level, options) +
generate_body(indent_level, options)
generate_body(indent_level, options) +
child_lines
end

sig do
Expand All @@ -29,6 +34,7 @@ def generate_rbi(indent_level, options)
name: T.nilable(String),
final: T::Boolean,
sealed: T::Boolean,
path: String,
block: T.nilable(T.proc.params(x: Namespace).void)
).void
end
Expand All @@ -39,15 +45,18 @@ def generate_rbi(indent_level, options)
# @param generator [RbiGenerator] The current RbiGenerator.
# @param name [String, nil] The name of this module.
# @param final [Boolean] Whether this namespace is final.
# @param final [Boolean] Whether this namespace is sealed.
# @param sealed [Boolean] Whether this namespace is sealed.
# @param path [String] the fully resolved path to this constant.
# @param block A block which the new instance yields itself to.
# @return [void]
def initialize(generator, name = nil, final = false, sealed = false, &block)
def initialize(generator, name = nil, final = false, sealed = false, path: '', &block)
@is_root = !name
super(generator, name || '<anonymous namespace>')
@children = []
@next_comments = []
@final = final
@sealed = sealed
@parent_path = path
yield_self(&block) if block
end

Expand All @@ -66,6 +75,10 @@ def initialize(generator, name = nil, final = false, sealed = false, &block)
# @return [Array<RbiObject>]
attr_reader :children

# The namespace (parent) path for this namespace.
sig { returns(String) }
attr_accessor :parent_path

include Mixin::Searchable
Child = type_member {{ fixed: RbiObject }}

Expand Down Expand Up @@ -111,9 +124,8 @@ def constants
end

sig { params(constant: Module, block: T.proc.params(x: Namespace).void).void }
# Given a constant (i.e. a Module instance), generates all classes
# and modules in the path to that object, then executes the given
# block on the last {Namespace}. This should only be executed on
# Given a constant (i.e. a Module instance), executes the given
# block on the {Namespace}. This should only be executed on
# the root namespace.
# @param [Module] constant
# @param block A block which the new {Namespace} yields itself to.
Expand Down Expand Up @@ -141,6 +153,19 @@ def path(constant, &block)
block.call(current_part)
end

sig { params(node: RbiObject).void }
# @param node [RbiObject]
def add_child(node)
node.parent_path = self.full_constant_path if node.respond_to?(:parent_path=)
self.children << node
end

sig { params(nodes: T::Array[RbiObject]).void }
# @param nodes [Array<RbiObject>]
def add_children(nodes)
nodes.each { |node| add_child(node) }
end

sig { params(comment: T.any(String, T::Array[String])).void }
# Adds one or more comments to the next child RBI object to be created.
#
Expand All @@ -162,6 +187,15 @@ def add_comment_to_next_child(comment)
end
end

sig { returns(String) }
# @return [String]
def full_constant_path
return '' if @is_root
return self.name if parent_path == ''

[parent_path, self.name].join('::')
end

sig do
params(
name: String,
Expand Down Expand Up @@ -191,7 +225,7 @@ def add_comment_to_next_child(comment)
# @param block A block which the new instance yields itself to.
# @return [ClassNamespace]
def create_class(name, final: false, sealed: false, superclass: nil, abstract: false, &block)
new_class = ClassNamespace.new(generator, name, final, sealed, superclass, abstract, &block)
new_class = ClassNamespace.new(generator, name, final, sealed, superclass, abstract, path: full_constant_path, &block)
move_next_comments(new_class)
children << new_class
new_class
Expand Down Expand Up @@ -220,7 +254,7 @@ def create_class(name, final: false, sealed: false, superclass: nil, abstract: f
# @param block A block which the new instance yields itself to.
# @return [EnumClassNamespace]
def create_enum_class(name, final: false, sealed: false, enums: nil, abstract: false, &block)
new_enum_class = EnumClassNamespace.new(generator, name, final, sealed, enums || [], abstract, &block)
new_enum_class = EnumClassNamespace.new(generator, name, final, sealed, enums || [], abstract, path: full_constant_path, &block)
move_next_comments(new_enum_class)
children << new_enum_class
new_enum_class
Expand Down Expand Up @@ -251,7 +285,7 @@ def create_enum_class(name, final: false, sealed: false, enums: nil, abstract: f
# @param block A block which the new instance yields itself to.
# @return [EnumClassNamespace]
def create_struct_class(name, final: false, sealed: false, props: nil, abstract: false, &block)
new_struct_class = StructClassNamespace.new(generator, name, final, sealed, props || [], abstract, &block)
new_struct_class = StructClassNamespace.new(generator, name, final, sealed, props || [], abstract, path: full_constant_path, &block)
move_next_comments(new_struct_class)
children << new_struct_class
new_struct_class
Expand Down Expand Up @@ -287,7 +321,7 @@ def create_struct_class(name, final: false, sealed: false, props: nil, abstract:
# @param block A block which the new instance yields itself to.
# @return [ModuleNamespace]
def create_module(name, final: false, sealed: false, interface: false, abstract: false, &block)
new_module = ModuleNamespace.new(generator, name, final, sealed, interface, abstract, &block)
new_module = ModuleNamespace.new(generator, name, final, sealed, interface, abstract, path: full_constant_path, &block)
move_next_comments(new_module)
children << new_module
new_module
Expand Down Expand Up @@ -647,7 +681,7 @@ def merge_into_self(others)
next if other.is_a?(RbiGenerator::Method)
other = T.cast(other, Namespace)

other.children.each { |c| children << c }
other.children.each { |c| add_child(c) }
end
end

Expand All @@ -663,6 +697,12 @@ def describe_attrs

private

sig { returns(String) }
# @return [String]
def full_path
parent_path == '' ? name : "#{parent_path}::#{name}"
end

sig do
overridable.params(
indent_level: Integer,
Expand Down Expand Up @@ -745,7 +785,8 @@ def generate_body(indent_level, options)

first, *rest = remaining_children.reject do |child|
# We already processed these kinds of children
child.is_a?(Include) || child.is_a?(Extend) || child.is_a?(Constant) || child.is_a?(TypeAlias)
child.is_a?(Include) || child.is_a?(Extend) || child.is_a?(Constant) || child.is_a?(TypeAlias) ||
child.is_a?(Namespace) # namespaces are handled separately
end
unless first
# Remove any trailing whitespace due to includes or class attributes
Expand Down
8 changes: 5 additions & 3 deletions lib/parlour/rbi_generator/struct_class_namespace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class StructClassNamespace < ClassNamespace
sealed: T::Boolean,
props: T::Array[StructProp],
abstract: T::Boolean,
path: String,
block: T.nilable(T.proc.params(x: StructClassNamespace).void)
).void
end
Expand All @@ -28,10 +29,11 @@ class StructClassNamespace < ClassNamespace
# @param sealed [Boolean] Whether this namespace is sealed.
# @param props [Array<StructProp>] The props of the struct.
# @param abstract [Boolean] A boolean indicating whether this class is abstract.
# @param path [String] the fully resolved path to this constant.
# @param block A block which the new instance yields itself to.
# @return [void]
def initialize(generator, name, final, sealed, props, abstract, &block)
super(generator, name, final, sealed, 'T::Struct', abstract, &block)
def initialize(generator, name, final, sealed, props, abstract, path: '', &block)
super(generator, name, final, sealed, 'T::Struct', abstract, path: path, &block)
@props = props
end

Expand Down Expand Up @@ -116,4 +118,4 @@ def describe_attrs
end
end
end
end
end
Loading