forked from crystal-lang/crystal
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
3 changed files
with
330 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
require "../../../spec_helper" | ||
|
||
private def assert_text_namespaces(source, filter, expected, *, file = __FILE__, line = __LINE__) | ||
program = semantic(source).program | ||
output = String.build { |io| Crystal.print_namespaces(program, io, filter, "text") } | ||
output.should eq(expected), file: file, line: line | ||
end | ||
|
||
private def assert_json_namespaces(source, filter, expected, *, file = __FILE__, line = __LINE__) | ||
program = semantic(source).program | ||
output = String.build { |io| Crystal.print_namespaces(program, io, filter, "json") } | ||
JSON.parse(output).should eq(JSON.parse(expected)), file: file, line: line | ||
end | ||
|
||
describe Crystal::TextNamespacesPrinter do | ||
it "works" do | ||
assert_text_namespaces <<-CRYSTAL, "Foo", <<-EOS | ||
class Foo | ||
annotation Baz | ||
end | ||
module Fizz | ||
abstract struct Buzz | ||
end | ||
end | ||
end | ||
class Foo::Bar < Foo | ||
end | ||
struct Foo::Fizz::Buzz | ||
end | ||
CRYSTAL | ||
- class Foo | ||
@ :1:1 | ||
- class Foo::Bar | ||
@ :11:1 | ||
- annotation Foo::Baz | ||
@ :2:3 | ||
- module Foo::Fizz | ||
@ :5:3 | ||
- struct Foo::Fizz::Buzz | ||
@ :6:5 | ||
@ :14:1\n | ||
EOS | ||
end | ||
end | ||
|
||
describe Crystal::JSONNamespacesPrinter do | ||
it "works" do | ||
assert_json_namespaces <<-CRYSTAL, "Foo", <<-JSON | ||
class Foo | ||
annotation Baz | ||
end | ||
module Fizz | ||
abstract struct Buzz | ||
end | ||
end | ||
end | ||
class Foo::Bar < Foo | ||
end | ||
struct Foo::Fizz::Buzz | ||
end | ||
CRYSTAL | ||
[ | ||
{"name": "Foo", "kind": "class", "locations": [":1:1"]}, | ||
{"name": "Foo::Bar", "kind": "class", "locations": [":11:1"]}, | ||
{"name": "Foo::Baz", "kind": "annotation", "locations": [":2:3"]}, | ||
{"name": "Foo::Fizz", "kind": "module", "locations": [":5:3"]}, | ||
{ | ||
"name": "Foo::Fizz::Buzz", | ||
"kind": "struct", | ||
"locations": [":6:5", ":14:1"] | ||
} | ||
] | ||
JSON | ||
end | ||
|
||
it "supports macros" do | ||
assert_json_namespaces <<-CRYSTAL, "Foo", <<-JSON | ||
macro my_macro | ||
class Foo | ||
annotation Baz | ||
end | ||
module Fizz | ||
abstract struct Buzz | ||
end | ||
end | ||
end | ||
end | ||
my_macro | ||
class Foo::Bar < Foo | ||
end | ||
struct Foo::Fizz::Buzz | ||
end | ||
CRYSTAL | ||
[ | ||
{"name": "Foo", "kind": "class", "locations": [":2:3"]}, | ||
{"name": "Foo::Bar", "kind": "class", "locations": [":15:1"]}, | ||
{"name": "Foo::Baz", "kind": "annotation", "locations": [":3:5"]}, | ||
{"name": "Foo::Fizz", "kind": "module", "locations": [":6:5"]}, | ||
{ | ||
"name": "Foo::Fizz::Buzz", | ||
"kind": "struct", | ||
"locations": [":7:7", ":18:1"] | ||
} | ||
] | ||
JSON | ||
end | ||
|
||
it "supports enum values" do | ||
assert_json_namespaces <<-CRYSTAL, "Foo", <<-JSON | ||
class Foo | ||
enum Baz | ||
VALUE_1 | ||
VALUE_2 | ||
VALUE_3 | ||
VALUE_4 | ||
end | ||
end | ||
CRYSTAL | ||
[ | ||
{"name": "Foo", "kind": "class", "locations": [":1:1"]}, | ||
{"name": "Foo::Baz", "kind": "enum", "locations": [":2:3"]}, | ||
{"name": "Foo::Baz::VALUE_1", "kind": "const", "locations": [":3:5"]}, | ||
{"name": "Foo::Baz::VALUE_2", "kind": "const", "locations": [":4:5"]}, | ||
{"name": "Foo::Baz::VALUE_3", "kind": "const", "locations": [":5:5"]}, | ||
{"name": "Foo::Baz::VALUE_4", "kind": "const", "locations": [":6:5"]} | ||
] | ||
JSON | ||
end | ||
end |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
require "set" | ||
require "colorize" | ||
require "../syntax/ast" | ||
|
||
module Crystal | ||
def self.print_namespaces(program, io, exp, format) | ||
case format | ||
when "text" | ||
TextNamespacesPrinter.new(program, io, exp).print_all | ||
when "json" | ||
JSONNamespacesPrinter.new(program, io, exp).print_all | ||
else | ||
raise "Unknown namespaces format: #{format}" | ||
end | ||
end | ||
|
||
abstract class NamespacesPrinter | ||
abstract def print_all | ||
|
||
def initialize(@program : Program, exp : String?) | ||
@exp = exp ? Regex.new(exp) : nil | ||
end | ||
|
||
private def collect_types(types : Array(Type)) | ||
collected = Set(NamedType).new | ||
|
||
types.each do |type| | ||
next unless type.is_a?(NamedType) | ||
|
||
collected.add(type) | ||
|
||
if type.struct? || type.class? || type.metaclass? | ||
collected.concat collect_types(type.subclasses) | ||
end | ||
|
||
if sub_types = type.types? | ||
collected.concat collect_types(sub_types.values) | ||
end | ||
end | ||
|
||
collected | ||
end | ||
|
||
protected def location_to_s(loc : Location) | ||
f = loc.filename | ||
case f | ||
when String | ||
line = loc.line_number | ||
column = loc.column_number | ||
filename = f | ||
when VirtualFile | ||
macro_location = f.macro.location.not_nil! | ||
filename = macro_location.filename.to_s | ||
line = macro_location.line_number + loc.line_number | ||
column = loc.column_number | ||
else | ||
raise "not implemented" | ||
end | ||
|
||
"#{filename}:#{line}:#{column}" | ||
end | ||
end | ||
|
||
class TextNamespacesPrinter < NamespacesPrinter | ||
def initialize(program : Program, @io : IO, exp : String?) | ||
super(program, exp) | ||
end | ||
|
||
def print_all | ||
types = collect_types(@program.types.values) | ||
types = types.to_a.sort_by(&.full_name) | ||
|
||
types.each do |type| | ||
print_type(type) | ||
end | ||
end | ||
|
||
def print_type(type : NamedType) | ||
return if (exp = @exp) && (exp !~ type.full_name) | ||
return if type.is_a?(MetaclassType) | ||
|
||
@io << "- " | ||
@io << case type | ||
in Const | ||
"const" | ||
in .struct? | ||
"struct" | ||
in .class? | ||
"class" | ||
in .module? | ||
"module" | ||
in AliasType | ||
"alias" | ||
in EnumType | ||
"enum" | ||
in NoReturnType, VoidType | ||
"struct" | ||
in AnnotationType | ||
"annotation" | ||
in LibType | ||
"module" | ||
in TypeDefType | ||
"typedef" | ||
in MetaclassType, NamedType | ||
"" | ||
end | ||
|
||
@io << " " << type.full_name | ||
|
||
if locations = type.locations | ||
@io << "\n" | ||
locations.each do |loc| | ||
@io << " @ " << location_to_s(loc) << "\n" | ||
end | ||
end | ||
end | ||
end | ||
|
||
class JSONNamespacesPrinter < NamespacesPrinter | ||
def initialize(program : Program, io : IO, exp : String?) | ||
super(program, exp) | ||
@json = JSON::Builder.new(io) | ||
end | ||
|
||
def print_all | ||
types = collect_types(@program.types.values) | ||
types = types.to_a.sort_by(&.full_name) | ||
|
||
@json.document do | ||
@json.array do | ||
types.each do |type| | ||
print_type(type) | ||
end | ||
end | ||
end | ||
end | ||
|
||
def print_type(type : NamedType) | ||
return if (exp = @exp) && (exp !~ type.full_name) | ||
return if type.is_a?(MetaclassType) | ||
|
||
@json.object do | ||
@json.field("name", type.full_name) | ||
|
||
case type | ||
in Const | ||
@json.field("kind", "const") | ||
in .struct? | ||
@json.field("kind", "struct") | ||
in .class? | ||
@json.field("kind", "class") | ||
in .module? | ||
@json.field("kind", "module") | ||
in AliasType | ||
@json.field("kind", "alias") | ||
in EnumType | ||
@json.field("kind", "enum") | ||
in NoReturnType, VoidType | ||
@json.field("kind", "struct") | ||
in AnnotationType | ||
@json.field("kind", "annotation") | ||
in LibType | ||
@json.field("kind", "module") | ||
in TypeDefType | ||
@json.field("kind", "typedef") | ||
in MetaclassType, NamedType | ||
end | ||
|
||
if locations = type.locations | ||
@json.string("locations") | ||
@json.array do | ||
locations.each do |loc| | ||
@json.string(location_to_s(loc)) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end |