mirror of
https://gitea.invidious.io/iv-org/shard-ameba.git
synced 2024-08-15 00:53:29 +00:00
Merge pull request #390 from crystal-ameba/refactor-rules-cli-switch
Refactor `--rules` CLI switch output + add `--describe <rule-name>` CLI switch
This commit is contained in:
commit
ddb6e3c38f
10 changed files with 191 additions and 16 deletions
32
spec/ameba/presenter/rule_collection_presenter_spec.cr
Normal file
32
spec/ameba/presenter/rule_collection_presenter_spec.cr
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
require "../../spec_helper"
|
||||||
|
|
||||||
|
module Ameba
|
||||||
|
private def with_rule_collection_presenter(&)
|
||||||
|
with_presenter(Presenter::RuleCollectionPresenter) do |presenter, io|
|
||||||
|
rules = Config.load.rules
|
||||||
|
presenter.run(rules)
|
||||||
|
|
||||||
|
output = io.to_s
|
||||||
|
output = Formatter::Util.deansify(output).to_s
|
||||||
|
|
||||||
|
yield rules, output, presenter
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe Presenter::RuleCollectionPresenter do
|
||||||
|
it "outputs rule collection details" do
|
||||||
|
with_rule_collection_presenter do |rules, output|
|
||||||
|
rules.each do |rule|
|
||||||
|
output.should contain rule.name
|
||||||
|
output.should contain rule.severity.symbol
|
||||||
|
|
||||||
|
if description = rule.description
|
||||||
|
output.should contain description
|
||||||
|
end
|
||||||
|
end
|
||||||
|
output.should contain "Total rules: #{rules.size}"
|
||||||
|
output.should match /\d+ enabled/
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
30
spec/ameba/presenter/rule_presenter_spec.cr
Normal file
30
spec/ameba/presenter/rule_presenter_spec.cr
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
require "../../spec_helper"
|
||||||
|
|
||||||
|
module Ameba
|
||||||
|
private def rule_presenter_each_rule(&)
|
||||||
|
with_presenter(Presenter::RulePresenter) do |presenter, io|
|
||||||
|
rules = Config.load.rules
|
||||||
|
rules.each do |rule|
|
||||||
|
presenter.run(rule)
|
||||||
|
|
||||||
|
output = io.to_s
|
||||||
|
output = Formatter::Util.deansify(output).to_s
|
||||||
|
|
||||||
|
yield rule, output, presenter
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe Presenter::RulePresenter do
|
||||||
|
it "outputs rule details" do
|
||||||
|
rule_presenter_each_rule do |rule, output|
|
||||||
|
output.should contain rule.name
|
||||||
|
output.should contain rule.severity.to_s
|
||||||
|
|
||||||
|
if description = rule.description
|
||||||
|
output.should contain description
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -282,6 +282,13 @@ module Ameba
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def with_presenter(klass, &)
|
||||||
|
io = IO::Memory.new
|
||||||
|
presenter = klass.new(io)
|
||||||
|
|
||||||
|
yield presenter, io
|
||||||
|
end
|
||||||
|
|
||||||
def as_node(source)
|
def as_node(source)
|
||||||
Crystal::Parser.new(source).parse
|
Crystal::Parser.new(source).parse
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@ require "./ameba/ast/**"
|
||||||
require "./ameba/ext/**"
|
require "./ameba/ext/**"
|
||||||
require "./ameba/rule/**"
|
require "./ameba/rule/**"
|
||||||
require "./ameba/formatter/*"
|
require "./ameba/formatter/*"
|
||||||
|
require "./ameba/presenter/*"
|
||||||
require "./ameba/source/**"
|
require "./ameba/source/**"
|
||||||
|
|
||||||
# Ameba's entry module.
|
# Ameba's entry module.
|
||||||
|
|
|
@ -28,7 +28,14 @@ module Ameba::Cli
|
||||||
configure_rules(config, opts)
|
configure_rules(config, opts)
|
||||||
|
|
||||||
if opts.rules?
|
if opts.rules?
|
||||||
print_rules(config)
|
print_rules(config.rules)
|
||||||
|
end
|
||||||
|
|
||||||
|
if describe_rule_name = opts.describe_rule
|
||||||
|
unless rule = config.rules.find(&.name.== describe_rule_name)
|
||||||
|
raise "Unknown rule"
|
||||||
|
end
|
||||||
|
describe_rule(rule)
|
||||||
end
|
end
|
||||||
|
|
||||||
runner = Ameba.run(config)
|
runner = Ameba.run(config)
|
||||||
|
@ -49,6 +56,7 @@ module Ameba::Cli
|
||||||
property globs : Array(String)?
|
property globs : Array(String)?
|
||||||
property only : Array(String)?
|
property only : Array(String)?
|
||||||
property except : Array(String)?
|
property except : Array(String)?
|
||||||
|
property describe_rule : String?
|
||||||
property location_to_explain : NamedTuple(file: String, line: Int32, column: Int32)?
|
property location_to_explain : NamedTuple(file: String, line: Int32, column: Int32)?
|
||||||
property fail_level : Severity?
|
property fail_level : Severity?
|
||||||
property? skip_reading_config = false
|
property? skip_reading_config = false
|
||||||
|
@ -119,6 +127,11 @@ module Ameba::Cli
|
||||||
configure_explain_opts(loc, opts)
|
configure_explain_opts(loc, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
parser.on("-d", "--describe Category/Rule",
|
||||||
|
"Describe a rule with specified name") do |rule_name|
|
||||||
|
configure_describe_opts(rule_name, opts)
|
||||||
|
end
|
||||||
|
|
||||||
parser.on("--without-affected-code",
|
parser.on("--without-affected-code",
|
||||||
"Stop showing affected code while using a default formatter") do
|
"Stop showing affected code while using a default formatter") do
|
||||||
opts.without_affected_code = true
|
opts.without_affected_code = true
|
||||||
|
@ -152,6 +165,11 @@ module Ameba::Cli
|
||||||
opts.without_affected_code?
|
opts.without_affected_code?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private def configure_describe_opts(rule_name, opts)
|
||||||
|
opts.describe_rule = rule_name.presence
|
||||||
|
opts.formatter = :silent
|
||||||
|
end
|
||||||
|
|
||||||
private def configure_explain_opts(loc, opts)
|
private def configure_explain_opts(loc, opts)
|
||||||
location_to_explain = parse_explain_location(loc)
|
location_to_explain = parse_explain_location(loc)
|
||||||
opts.location_to_explain = location_to_explain
|
opts.location_to_explain = location_to_explain
|
||||||
|
@ -183,14 +201,13 @@ module Ameba::Cli
|
||||||
exit 0
|
exit 0
|
||||||
end
|
end
|
||||||
|
|
||||||
private def print_rules(config)
|
private def describe_rule(rule)
|
||||||
config.rules.each do |rule|
|
Presenter::RulePresenter.new.run(rule)
|
||||||
puts "%s [%s] - %s" % {
|
exit 0
|
||||||
rule.name.colorize(:white),
|
end
|
||||||
rule.severity.symbol.to_s.colorize(:green),
|
|
||||||
rule.description.colorize(:dark_gray),
|
private def print_rules(rules)
|
||||||
}
|
Presenter::RuleCollectionPresenter.new.run(rules)
|
||||||
end
|
|
||||||
exit 0
|
exit 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,8 +4,6 @@ module Ameba::Formatter
|
||||||
# A formatter that shows the detailed explanation of the issue at
|
# A formatter that shows the detailed explanation of the issue at
|
||||||
# a specific location.
|
# a specific location.
|
||||||
class ExplainFormatter
|
class ExplainFormatter
|
||||||
HEADING_MARKER = "## "
|
|
||||||
|
|
||||||
include Util
|
include Util
|
||||||
|
|
||||||
getter output : IO::FileDescriptor | IO::Memory
|
getter output : IO::FileDescriptor | IO::Memory
|
||||||
|
@ -64,9 +62,8 @@ module Ameba::Formatter
|
||||||
rule.name.colorize(:magenta),
|
rule.name.colorize(:magenta),
|
||||||
rule.severity.to_s.colorize(rule.severity.color),
|
rule.severity.to_s.colorize(rule.severity.color),
|
||||||
}
|
}
|
||||||
|
if rule_description = colorize_code_fences(rule.description)
|
||||||
if rule.responds_to?(:description)
|
output_paragraph rule_description
|
||||||
output_paragraph rule.description
|
|
||||||
end
|
end
|
||||||
|
|
||||||
rule_doc = colorize_code_fences(rule.class.parsed_doc)
|
rule_doc = colorize_code_fences(rule.class.parsed_doc)
|
||||||
|
@ -84,7 +81,7 @@ module Ameba::Formatter
|
||||||
end
|
end
|
||||||
|
|
||||||
private def output_title(title)
|
private def output_title(title)
|
||||||
output << HEADING_MARKER.colorize(:yellow)
|
output << "### ".colorize(:yellow)
|
||||||
output << title.upcase.colorize(:yellow)
|
output << title.upcase.colorize(:yellow)
|
||||||
output << "\n\n"
|
output << "\n\n"
|
||||||
end
|
end
|
||||||
|
@ -95,7 +92,7 @@ module Ameba::Formatter
|
||||||
|
|
||||||
private def output_paragraph(paragraph : Array)
|
private def output_paragraph(paragraph : Array)
|
||||||
paragraph.each do |line|
|
paragraph.each do |line|
|
||||||
output << ' ' << line << '\n'
|
output << " " << line << '\n'
|
||||||
end
|
end
|
||||||
output << '\n'
|
output << '\n'
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
module Ameba::Formatter
|
module Ameba::Formatter
|
||||||
module Util
|
module Util
|
||||||
|
extend self
|
||||||
|
|
||||||
def deansify(message : String?) : String?
|
def deansify(message : String?) : String?
|
||||||
message.try &.gsub(/\x1b[^m]*m/, "").presence
|
message.try &.gsub(/\x1b[^m]*m/, "").presence
|
||||||
end
|
end
|
||||||
|
|
12
src/ameba/presenter/base_presenter.cr
Normal file
12
src/ameba/presenter/base_presenter.cr
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module Ameba::Presenter
|
||||||
|
private ENABLED_MARK = "✓".colorize(:green)
|
||||||
|
private DISABLED_MARK = "x".colorize(:red)
|
||||||
|
|
||||||
|
class BasePresenter
|
||||||
|
# TODO: allow other IOs
|
||||||
|
getter output : IO::FileDescriptor | IO::Memory
|
||||||
|
|
||||||
|
def initialize(@output = STDOUT)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
34
src/ameba/presenter/rule_collection_presenter.cr
Normal file
34
src/ameba/presenter/rule_collection_presenter.cr
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
module Ameba::Presenter
|
||||||
|
class RuleCollectionPresenter < BasePresenter
|
||||||
|
def run(rules)
|
||||||
|
rules = rules.to_h do |rule|
|
||||||
|
name = rule.name.split('/')
|
||||||
|
name = "%s/%s" % {
|
||||||
|
name[0...-1].join('/').colorize(:light_gray),
|
||||||
|
name.last.colorize(:white),
|
||||||
|
}
|
||||||
|
{name, rule}
|
||||||
|
end
|
||||||
|
longest_name = rules.max_of(&.first.size)
|
||||||
|
|
||||||
|
rules.group_by(&.last.group).each do |group, group_rules|
|
||||||
|
output.puts "— %s" % group.colorize(:light_blue).underline
|
||||||
|
output.puts
|
||||||
|
group_rules.each do |name, rule|
|
||||||
|
output.puts " %s [%s] %s %s" % {
|
||||||
|
rule.enabled? ? ENABLED_MARK : DISABLED_MARK,
|
||||||
|
rule.severity.symbol.to_s.colorize(:green),
|
||||||
|
name.ljust(longest_name),
|
||||||
|
rule.description.colorize(:dark_gray),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
output.puts
|
||||||
|
end
|
||||||
|
|
||||||
|
output.puts "Total rules: %s / %s enabled" % {
|
||||||
|
rules.size.to_s.colorize(:light_blue),
|
||||||
|
rules.count(&.last.enabled?).to_s.colorize(:light_blue),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
43
src/ameba/presenter/rule_presenter.cr
Normal file
43
src/ameba/presenter/rule_presenter.cr
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
module Ameba::Presenter
|
||||||
|
class RulePresenter < BasePresenter
|
||||||
|
def run(rule)
|
||||||
|
output.puts
|
||||||
|
output_title "Rule info"
|
||||||
|
output_paragraph "%s of a %s severity [enabled: %s]" % {
|
||||||
|
rule.name.colorize(:magenta),
|
||||||
|
rule.severity.to_s.colorize(rule.severity.color),
|
||||||
|
rule.enabled? ? ENABLED_MARK : DISABLED_MARK,
|
||||||
|
}
|
||||||
|
if rule_description = colorize_code_fences(rule.description)
|
||||||
|
output_paragraph rule_description
|
||||||
|
end
|
||||||
|
|
||||||
|
if rule_doc = colorize_code_fences(rule.class.parsed_doc)
|
||||||
|
output_title "Detailed description"
|
||||||
|
output_paragraph rule_doc
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private def output_title(title)
|
||||||
|
output.print "### %s\n\n" % title.upcase.colorize(:yellow)
|
||||||
|
end
|
||||||
|
|
||||||
|
private def output_paragraph(paragraph : String)
|
||||||
|
output_paragraph(paragraph.lines)
|
||||||
|
end
|
||||||
|
|
||||||
|
private def output_paragraph(paragraph : Array)
|
||||||
|
paragraph.each do |line|
|
||||||
|
output.puts " #{line}"
|
||||||
|
end
|
||||||
|
output.puts
|
||||||
|
end
|
||||||
|
|
||||||
|
private def colorize_code_fences(string)
|
||||||
|
return unless string
|
||||||
|
string
|
||||||
|
.gsub(/```(.+?)```/m, &.colorize(:dark_gray))
|
||||||
|
.gsub(/`(?!`)(.+?)`/, &.colorize(:dark_gray))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue