Compare commits

...

1 Commits

Author SHA1 Message Date
Sijawusz Pur Rahnama 09368f24c2 Add `Lint/DocumentationAdmonition` rule 2023-06-15 03:14:47 +02:00
1 changed files with 84 additions and 0 deletions

View File

@ -0,0 +1,84 @@
module Ameba::Rule::Lint
# A rule that ...
#
# `TODO` comments are used to remind yourself of source code related things.
#
# The premise here is that `TODO` should be dealt with in the near future
# and are therefore reported by Ameba.
#
# `FIXME` comments are used to indicate places where source code needs fixing.
#
# The premise here is that `FIXME` should indeed be fixed as soon as possible
# and are therefore reported by Ameba.
#
# YAML configuration example:
#
# ```
# Lint/DocumentationAdmonition:
# Enabled: true
# Admonitions: [TODO, FIXME, BUG]
# Timezone: UTC
# ```
#
# ameba:disable Lint/DocumentationAdmonition
class DocumentationAdmonition < Base
properties do
description "..."
admonitions %w[TODO FIXME BUG]
timezone "UTC"
end
MSG = "Found a %s admonition in a comment"
MSG_LATE = "Found a %s admonition in a comment (%s)"
MSG_ERR = "%s admonition error: %s"
@[YAML::Field(ignore: true)]
private getter pattern : Regex {
/(?:\W+|^)(?<admonition>#{Regex.union(admonitions)})(?:\((?<context>.+?)\))?(?:\W+|$)/
}
@[YAML::Field(ignore: true)]
private getter location : Time::Location {
Time::Location.load(self.timezone)
}
def test(source, node)
return unless doc = node.doc.presence
matches = doc.scan(pattern)
matches.each do |match|
admonition = match["admonition"]
begin
case expr = match["context"]?.presence
when /\A\d{4}-\d{2}-\d{2}\Z/ # date
# ameba:disable Lint/NotNil
date = Time.parse(expr.not_nil!, "%F", location)
issue_for_date source, node, admonition, date
when /\A\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2})?\Z/ # date + time
# ameba:disable Lint/NotNil
date = Time.parse(expr.not_nil!, "%F #{$1?.presence ? "%T" : "%R"}", location)
issue_for_date source, node, admonition, date
else
issue_for node, MSG % admonition
end
rescue ex
issue_for node, MSG_ERR % {admonition, "#{ex}: #{expr.inspect}"}
end
end
end
private def issue_for_date(source, node, admonition, date)
diff = Time.utc - date.to_utc
return if diff.negative?
past = case days = diff.total_hours.round.to_i
when .< 24 then "today is the day!"
when .< 48 then "1 day past"
else "#{days} days past"
end
issue_for node, MSG_LATE % {admonition, past}
end
end
end