From 3d6125473907062567ae33ac00e1ebd60f9a478a Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 29 Oct 2022 01:23:26 +0200 Subject: [PATCH 1/3] Populate `Rule::Base+.parsed_doc` on compile time --- src/ameba/rule/base.cr | 72 +++++++++++++----------------------------- src/read_rule_doc.cr | 32 +++++++++++++++++++ 2 files changed, 54 insertions(+), 50 deletions(-) create mode 100644 src/read_rule_doc.cr diff --git a/src/ameba/rule/base.cr b/src/ameba/rule/base.cr index 0ae7016e..5d8ac2cb 100644 --- a/src/ameba/rule/base.cr +++ b/src/ameba/rule/base.cr @@ -138,57 +138,29 @@ module Ameba::Rule end end + private macro read_rule_doc(filepath = __FILE__) + {{ run("../../read_rule_doc", + @type.name.split("::").last, + filepath + ).chomp.stringify }}.presence + end + macro inherited - protected def self.path_to_source_file - __FILE__ - end - end - - # Returns documentation for this rule if any. - # - # ``` - # module Ameba - # # This is a test rule. - # # Does nothing. - # class MyRule < Ameba::Rule::Base - # def test(source) - # end - # end - # end - # - # MyRule.parsed_doc # => "This is a test rule.\nDoes nothing." - # ``` - def self.parsed_doc - source = File.read(path_to_source_file) - nodes = Crystal::Parser.new(source) - .tap(&.wants_doc = true) - .parse - type_name = rule_name.split('/').last? - - DocFinder.new(nodes, type_name).doc - end - - # :nodoc: - private class DocFinder < Crystal::Visitor - getter doc : String? - getter type_name : String? - - def initialize(nodes, @type_name) - self.accept(nodes) - end - - def visit(node : Crystal::ASTNode) - return false if @doc - - if node.responds_to?(:name) && - (name = node.name) && - name.is_a?(Crystal::Path) && - name.names.last? == @type_name - @doc = node.doc - end - - true - end + # Returns documentation for this rule, if there is any. + # + # ``` + # module Ameba + # # This is a test rule. + # # Does nothing. + # class MyRule < Ameba::Rule::Base + # def test(source) + # end + # end + # end + # + # MyRule.parsed_doc # => "This is a test rule.\nDoes nothing." + # ``` + class_getter parsed_doc : String? = read_rule_doc end end diff --git a/src/read_rule_doc.cr b/src/read_rule_doc.cr new file mode 100644 index 00000000..d02b12e8 --- /dev/null +++ b/src/read_rule_doc.cr @@ -0,0 +1,32 @@ +require "compiler/crystal/syntax/*" + +private class DocFinder < Crystal::Visitor + getter type_name : String? + getter doc : String? + + def initialize(nodes, @type_name) + self.accept(nodes) + end + + def visit(node : Crystal::ASTNode) + return false if @doc + + if node.responds_to?(:name) && + (name = node.name) && + name.is_a?(Crystal::Path) && + name.names.last? == @type_name + @doc = node.doc + end + + true + end +end + +type_name, path_to_source_file = ARGV + +source = File.read(path_to_source_file) +nodes = Crystal::Parser.new(source) + .tap(&.wants_doc = true) + .parse + +puts DocFinder.new(nodes, type_name).doc From d55b93c866d7ef1fbebda52ec5d11dc0f870a2c0 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 29 Oct 2022 18:03:30 +0200 Subject: [PATCH 2/3] Rename `read_rule_doc` -> `read_type_doc` Also, move the helper script into the `contrib` directory --- src/ameba/rule/base.cr | 6 +++--- src/{read_rule_doc.cr => contrib/read_type_doc.cr} | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename src/{read_rule_doc.cr => contrib/read_type_doc.cr} (100%) diff --git a/src/ameba/rule/base.cr b/src/ameba/rule/base.cr index 5d8ac2cb..70ae53d8 100644 --- a/src/ameba/rule/base.cr +++ b/src/ameba/rule/base.cr @@ -138,8 +138,8 @@ module Ameba::Rule end end - private macro read_rule_doc(filepath = __FILE__) - {{ run("../../read_rule_doc", + private macro read_type_doc(filepath = __FILE__) + {{ run("../../contrib/read_type_doc", @type.name.split("::").last, filepath ).chomp.stringify }}.presence @@ -160,7 +160,7 @@ module Ameba::Rule # # MyRule.parsed_doc # => "This is a test rule.\nDoes nothing." # ``` - class_getter parsed_doc : String? = read_rule_doc + class_getter parsed_doc : String? = read_type_doc end end diff --git a/src/read_rule_doc.cr b/src/contrib/read_type_doc.cr similarity index 100% rename from src/read_rule_doc.cr rename to src/contrib/read_type_doc.cr From ca2c5a727329e984d1122700bf74fb529e091024 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Sat, 29 Oct 2022 18:07:52 +0200 Subject: [PATCH 3/3] Small refactor to `DocFinder#visit` --- src/contrib/read_type_doc.cr | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/contrib/read_type_doc.cr b/src/contrib/read_type_doc.cr index d02b12e8..53ec0e2d 100644 --- a/src/contrib/read_type_doc.cr +++ b/src/contrib/read_type_doc.cr @@ -1,7 +1,7 @@ require "compiler/crystal/syntax/*" private class DocFinder < Crystal::Visitor - getter type_name : String? + getter type_name : String getter doc : String? def initialize(nodes, @type_name) @@ -11,11 +11,8 @@ private class DocFinder < Crystal::Visitor def visit(node : Crystal::ASTNode) return false if @doc - if node.responds_to?(:name) && - (name = node.name) && - name.is_a?(Crystal::Path) && - name.names.last? == @type_name - @doc = node.doc + if node.responds_to?(:name) && (name = node.name).is_a?(Crystal::Path) + @doc = node.doc if name.names.last? == @type_name end true