Class: RemLint::Document

Inherits:
Object
  • Object
show all
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

Instance Method Summary collapse

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

#commandsObject (readonly)

Returns the value of attribute commands.



27
28
29
# File 'lib/remlint/document.rb', line 27

def commands
  @commands
end

#logical_linesObject (readonly)

Returns the value of attribute logical_lines.



27
28
29
# File 'lib/remlint/document.rb', line 27

def logical_lines
  @logical_lines
end

#sourceObject (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_commandsObject



87
88
89
# File 'lib/remlint/document.rb', line 87

def code_commands
  @code_commands ||= commands.select(&:code?)
end

#each_raw_lineObject



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

#invocationObject

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

#labelObject



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_offsetObject



41
42
43
# File 'lib/remlint/document.rb', line 41

def line_offset
  source.line_offset
end

#pathObject



37
38
39
# File 'lib/remlint/document.rb', line 37

def path
  source.path
end

#raw_linesObject

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