Class: RemLint::Document
- Inherits:
-
Object
- Object
- RemLint::Document
- Defined in:
- lib/remlint/document.rb
Overview
Everything a rule may look at, built once per source and shared by all of them.
The four views answer four different questions and rules should take the narrowest one that works:
raw_lines the bytes, per physical line -- for whitespace rules
logical_lines continuations joined -- for anything spanning lines
commands classified -- for anything keyed on which command it is
tokens_for lexed -- for anything looking inside a command
trigger_for clauses located -- for anything keyed on AT, UNTIL, TZ ...
Building all of it eagerly except the token streams keeps rules cheap without lexing files no rule looks inside; the token streams are memoised per logical line, so several rules asking cost one lex.
Instance Attribute Summary collapse
-
#commands ⇒ Object
readonly
Returns the value of attribute commands.
-
#logical_lines ⇒ Object
readonly
Returns the value of attribute logical_lines.
-
#source ⇒ Object
readonly
Returns the value of attribute source.
Instance Method Summary collapse
- #code_commands ⇒ Object
- #each_raw_line ⇒ Object
-
#initialize(source) ⇒ Document
constructor
A new instance of Document.
-
#invocation ⇒ Object
How the file says it is meant to be run, from a
# remlint:invocationcomment. - #label ⇒ Object
-
#line_number_at(index) ⇒ Object
The file line number of
raw_lines[index]. - #line_offset ⇒ Object
- #path ⇒ Object
-
#raw_lines ⇒ Object
The physical lines, exactly as they are in the file, newline and all.
-
#tokens_for(logical_line) ⇒ Object
Significant tokens of one logical line, lexed at most once.
-
#trigger_for(command) ⇒ Object
The clauses of one command's trigger, parsed at most once.
Constructor Details
#initialize(source) ⇒ Document
Returns a new instance of Document.
29 30 31 32 33 34 35 |
# File 'lib/remlint/document.rb', line 29 def initialize(source) @source = source @logical_lines = Joiner.call(source) @commands = Classifier.all(@logical_lines) @token_cache = {} @trigger_cache = {} end |
Instance Attribute Details
#commands ⇒ Object (readonly)
Returns the value of attribute commands.
27 28 29 |
# File 'lib/remlint/document.rb', line 27 def commands @commands end |
#logical_lines ⇒ Object (readonly)
Returns the value of attribute logical_lines.
27 28 29 |
# File 'lib/remlint/document.rb', line 27 def logical_lines @logical_lines end |
#source ⇒ Object (readonly)
Returns the value of attribute source.
27 28 29 |
# File 'lib/remlint/document.rb', line 27 def source @source end |
Instance Method Details
#code_commands ⇒ Object
87 88 89 |
# File 'lib/remlint/document.rb', line 87 def code_commands @code_commands ||= commands.select(&:code?) end |
#each_raw_line ⇒ Object
59 60 61 62 63 |
# File 'lib/remlint/document.rb', line 59 def each_raw_line raw_lines.each_with_index do |raw, index| yield raw, line_number_at(index) end end |
#invocation ⇒ Object
How the file says it is meant to be run, from a
# remlint:invocation comment. Undeclared for a file that says nothing,
which is the honest default.
73 74 75 |
# File 'lib/remlint/document.rb', line 73 def invocation @invocation ||= Invocation.of(self) end |
#label ⇒ Object
45 46 47 |
# File 'lib/remlint/document.rb', line 45 def label source.label end |
#line_number_at(index) ⇒ Object
The file line number of raw_lines[index].
55 56 57 |
# File 'lib/remlint/document.rb', line 55 def line_number_at(index) index + 1 + line_offset end |
#line_offset ⇒ Object
41 42 43 |
# File 'lib/remlint/document.rb', line 41 def line_offset source.line_offset end |
#path ⇒ Object
37 38 39 |
# File 'lib/remlint/document.rb', line 37 def path source.path end |
#raw_lines ⇒ Object
The physical lines, exactly as they are in the file, newline and all.
50 51 52 |
# File 'lib/remlint/document.rb', line 50 def raw_lines @raw_lines ||= source.lines end |
#tokens_for(logical_line) ⇒ Object
Significant tokens of one logical line, lexed at most once.
66 67 68 |
# File 'lib/remlint/document.rb', line 66 def tokens_for(logical_line) @token_cache[logical_line.line] ||= ExprLexer.significant(logical_line.text) end |
#trigger_for(command) ⇒ Object
The clauses of one command's trigger, parsed at most once. Rules ask this rather than scanning tokens themselves, because the trigger/body boundary is easy to get wrong and expensive to get wrong twice.
80 81 82 83 84 85 |
# File 'lib/remlint/document.rb', line 80 def trigger_for(command) @trigger_cache[command.line] ||= Trigger.of( tokens_for(command.logical_line), triggered: Trigger.triggered?(command), ) end |