Class: SimpleCov::Directive
| Relationships & Source Files | |
| Inherits: | Object |
| Defined in: | lib/simplecov/directive.rb |
Overview
Parses # simplecov:disable / # simplecov:enable directive comments.
Two forms are supported:
Block form (the directive is the entire comment on its own line) opens a
region that runs until the matching # simplecov:enable:
# simplecov:disable line
#...
# simplecov:enable line
Inline form (the directive trails real code on the same line) only affects that single line and does not need to be re-enabled:
raise "absurd" # simplecov:disable
Categories are :line, :branch, and :method. They may be combined
with commas. Omitting categories targets all three.
Any text after the directive (and the optional category list) is treated as a free-form reason and discarded:
# simplecov:disable line not worth testing this glue
As a consequence, an unrecognised category name silently falls into the
reason bucket. # simplecov:disable cyclomatic is parsed as the bare
form (disable everything) with reason "cyclomatic" — a deliberate
over-disable so the typo is visible in the report rather than silently
disabling nothing.
Comment extraction goes through Ripper.lex so directive markers inside
string literals or heredocs are correctly ignored.
Constant Summary
-
CATEGORIES =
# File 'lib/simplecov/directive.rb', line 39%i[line branch method].freeze
-
CATEGORIES_PATTERN =
# File 'lib/simplecov/directive.rb', line 42"(?:#{CATEGORY_PATTERN}(?:\\s*,\\s*#{CATEGORY_PATTERN})*)".freeze
-
CATEGORY_PATTERN =
# File 'lib/simplecov/directive.rb', line 41"(?:#{CATEGORIES.join('|')})".freeze
-
PATTERN =
# File 'lib/simplecov/directive.rb', line 43/ \#\s*simplecov\s*:\s* (?<mode>disable|enable)\b (?:\s+(?<categories>#{CATEGORIES_PATTERN}))? .*? \s*\z /x
Class Method Summary
-
.disabled_ranges(src_lines)
Walk an array of source lines and return the disabled line ranges per category as
{ line: [Range, ...], branch: [...], method: [...] }. - .new(line_number:, mode:, categories:, inline:) ⇒ Directive constructor
- .comments_in(lines) private
-
.directives_in(lines)
private
Extract every directive in the file, in source order.
-
.inline?(lines, line_number, column) ⇒ Boolean
private
Whether the directive sits after non-whitespace content on its line.
- .parse_categories(text) private
- .parse_comment(lines, line_number, column, text) private
-
.source_might_contain_directive?(lines) ⇒ Boolean
private
Cheap pre-check so we don't tokenize files that obviously can't contain a directive.
Instance Attribute Summary
- #categories readonly
- #disabled? ⇒ Boolean readonly
- #inline? ⇒ Boolean readonly
- #line_number readonly
- #mode readonly
Instance Method Summary
-
#apply(ranges, open_starts)
Apply this directive's effect to the in-flight per-category state.
Constructor Details
.new(line_number:, mode:, categories:, inline:) ⇒ Directive
# File 'lib/simplecov/directive.rb', line 132
def initialize(line_number:, mode:, categories:, inline:) @line_number = line_number @mode = mode @categories = categories @inline = inline end
Class Method Details
.comments_in(lines) (private)
[ GitHub ]# File 'lib/simplecov/directive.rb', line 120
def self.comments_in(lines) source = lines.map { |line| line.end_with?("\n") ? line : "#{line}\n" }.join Ripper.lex(source).filter_map do |(line_number, column), type, text| [line_number, column, text] if type == :on_comment end rescue ArgumentError, EncodingError [] # simplecov:disable — Ripper.lex can raise on invalid byte sequences end
.directives_in(lines) (private)
Extract every directive in the file, in source order. Comments inside
string literals or heredocs are skipped because Ripper.lex doesn't tag
them as :on_comment tokens.
# File 'lib/simplecov/directive.rb', line 70
def self.directives_in(lines) return [] unless source_might_contain_directive?(lines) comments_in(lines).filter_map do |line_number, column, text| parse_comment(lines, line_number, column, text) end end
.disabled_ranges(src_lines)
Walk an array of source lines and return the disabled line ranges per
category as { line: [Range, ...], branch: [...], method: [...] }.
An unclosed disable block extends to the end of the file.
# File 'lib/simplecov/directive.rb', line 56
def self.disabled_ranges(src_lines) lines = src_lines.to_a ranges = CATEGORIES.to_h { |category| [category, []] } open_starts = {} directives_in(lines).each { |directive| directive.apply(ranges, open_starts) } open_starts.each { |category, start| ranges[category] << (start..lines.size) } ranges end
.inline?(lines, line_number, column) ⇒ Boolean (private)
Whether the directive sits after non-whitespace content on its line.
column is the byte column of the directive's # in the source line,
adjusted for any prefix that may precede it within the comment token
(e.g., # prefix # simplecov:disable line).
# File 'lib/simplecov/directive.rb', line 113
def self.inline?(lines, line_number, column) line = lines[line_number - 1].to_s !line.byteslice(0, column).to_s.strip.empty? rescue ArgumentError, EncodingError false # simplecov:disable — defensive guard for invalid byte sequences end
.parse_categories(text) (private)
[ GitHub ]# File 'lib/simplecov/directive.rb', line 103
def self.parse_categories(text) return CATEGORIES.dup if text.nil? text.split(/\s*,\s*/).map(&:to_sym) end
.parse_comment(lines, line_number, column, text) (private)
[ GitHub ]# File 'lib/simplecov/directive.rb', line 88
def self.parse_comment(lines, line_number, column, text) match = PATTERN.match(text) return nil unless match new( line_number: line_number, mode: match[:mode].to_sym, categories: parse_categories(match[:categories]), inline: inline?(lines, line_number, column + match.begin(0)) ) rescue ArgumentError, EncodingError # E.g., comment text contains an invalid byte sequence in UTF-8. nil end
.source_might_contain_directive?(lines) ⇒ Boolean (private)
Cheap pre-check so we don't tokenize files that obviously can't contain a directive.
# File 'lib/simplecov/directive.rb', line 80
def self.source_might_contain_directive?(lines) lines.any? do |line| line.include?("simplecov") rescue ArgumentError, EncodingError false # simplecov:disable — defensive guard for invalid byte sequences in source end end
Instance Attribute Details
#categories (readonly)
[ GitHub ]# File 'lib/simplecov/directive.rb', line 51
attr_reader :line_number, :mode, :categories
#disabled? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/simplecov/directive.rb', line 139
def disabled? mode == :disable end
#inline? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/simplecov/directive.rb', line 143
def inline? @inline end
#line_number (readonly)
[ GitHub ]# File 'lib/simplecov/directive.rb', line 51
attr_reader :line_number, :mode, :categories
#mode (readonly)
[ GitHub ]# File 'lib/simplecov/directive.rb', line 51
attr_reader :line_number, :mode, :categories
Instance Method Details
#apply(ranges, open_starts)
Apply this directive's effect to the in-flight per-category state. Inline directives mark just their line; block disables open a region; block enables close one. Re-opening an already-open block is a no-op.
# File 'lib/simplecov/directive.rb', line 150
def apply(ranges, open_starts) categories.each do |category| if inline? ranges[category] << (line_number..line_number) if disabled? elsif disabled? open_starts[category] ||= line_number elsif (start = open_starts.delete(category)) ranges[category] << (start..line_number) end end end