tex_log_parser 1.0.0.pre.10 → 1.0.0.pre.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c466836b3e482413ab935462f0bc513bc7bd43a5
4
- data.tar.gz: 8cf420477f87eb3e5cca65db7c5b5dedc1b1129b
2
+ SHA256:
3
+ metadata.gz: 9452fd78396c5094be2717ac70979ac88892678f73433966ee1ca79ba21fa73d
4
+ data.tar.gz: 0e72ee4dc90ca406c2ba557eb017bca1c1a6ae682f9b2847d7f391ac4ddf0483
5
5
  SHA512:
6
- metadata.gz: 7e10d29ce1cfee50a9ee0c6ff16fa57bf76afe91f88b71b81966e7ee12bd469c4995147f76821b8fbba60f7b8ef07bd264fc32500a6dcba25b257a5d5c947ed3
7
- data.tar.gz: 93884717b008a2b80f5b7d8aa4e8d9df4e7e4e563beed9e11333bd8bc28a8341b20fdd61b9d61b8c910537c8b5a0bc9cee1e7c207445e3e185348c6a51277ec1
6
+ metadata.gz: 52cf6f343c49d26fcc9e880301ea082e89f279a65a59ef1bd3c02c25d5810bf05de410224ec28008d55d64b295ea3168b683422b1a85e530a9e886507963da33
7
+ data.tar.gz: cda1bbf061fb89616784def652834b4020061d020be9c3874099a5bb7b7ed3b165f6343bf00398f3cbdbddae42cf8ab12b41d7dfc87929619c419f2fd56c32e5
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![Yard docs](http://img.shields.io/badge/yard-docs-green.svg)](http://www.rubydoc.info/gems/tex_log_parser/1.0.0.pre.2) **˙**
5
5
  [![Maintainability](https://api.codeclimate.com/v1/badges/748992a2c5f6570797d4/maintainability)](https://codeclimate.com/github/reitzig/texlogparser/maintainability)
6
6
  [![Test Coverage](https://api.codeclimate.com/v1/badges/748992a2c5f6570797d4/test_coverage)](https://codeclimate.com/github/reitzig/texlogparser/test_coverage) **˙**
7
- [![Travis build status](https://api.travis-ci.org/reitzig/texlogparser.svg?branch=master)](https://travis-ci.org/reitzig/texlogparser)
7
+ [![Circle CI](https://circleci.com/gh/reitzig/texlogparser.svg?style=svg)](https://circleci.com/gh/reitzig/texlogparser)
8
8
  [![Inline docs](http://inch-ci.org/github/reitzig/texlogparser.svg?branch=master)](http://inch-ci.org/github/reitzig/texlogparser)
9
9
 
10
10
  Eases the many pains around logs from (La)TeX engines.
data/bin/texlogparser CHANGED
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'tex_log_parser'
5
- require 'tex_log_parser/version'
5
+ require 'version'
6
6
  require 'optparse'
7
7
  require 'json'
8
8
 
@@ -17,7 +17,7 @@ formats = %i[file_line json]
17
17
  OptionParser.new do |opts|
18
18
  opts.banner = 'Usage: texlogparser [options]'
19
19
  opts.on('-d', '--debug', 'Output debug information') do
20
- Logger.debugging = true
20
+ LogParser::Logger.debug = true
21
21
  end
22
22
  opts.on('-f ENUM', '--format ENUM', formats,
23
23
  'Output format', "One of: #{formats.map(&:to_s).join(', ')}") do |format|
@@ -39,7 +39,7 @@ OptionParser.new do |opts|
39
39
  exit
40
40
  end
41
41
  opts.on_tail('-v', '--version', 'Show version') do
42
- puts TexLogParser::VERSION
42
+ puts "#{File.basename(__FILE__)} #{TexLogParser::VERSION}"
43
43
  exit
44
44
  end
45
45
 
@@ -58,7 +58,7 @@ end
58
58
 
59
59
  input = STDIN
60
60
  input = File.open(options[:input], 'r') if options[:input].is_a?(String)
61
- parser = TexLogParser.new(input, options)
61
+ parser = TexLogParser.new(input)
62
62
  messages = parser.parse
63
63
 
64
64
  output = STDOUT
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LogParser
4
+ # Log buffers provide line after line from a given source,
5
+ # and only store as many lines as necessary:
6
+ #
7
+ # * Read lines from `log` lazily, i.e. only if {find_index} or {[]} are called.
8
+ # * Drop lines that are no longer needed, i.e. when {forward} is called.
9
+ class Buffer
10
+ # Creates a new buffer that reads lines from the given source.
11
+ #
12
+ # @param [Array<String>,IO,StringIO] log
13
+ # Where to read log lines from. Can be either an array of `String`,
14
+ # an `IO` (e.g. `STDIN`), or a `StringIO`.
15
+ def initialize(log)
16
+ @buffer = []
17
+ @stream = nil
18
+
19
+ @buffer = log if log.is_a?(Array)
20
+ @stream = log if log.is_a?(IO) || log.is_a?(StringIO)
21
+ end
22
+
23
+ # Determines whether there are more lines in this log (buffer).
24
+ #
25
+ # @return [true,false]
26
+ def empty?
27
+ @buffer.empty? && stream_is_done?
28
+ end
29
+
30
+ # The current size of this buffer, that is the number of lines currently stored.
31
+ #
32
+ # @return [Integer]
33
+ def buffer_size
34
+ @buffer.size
35
+ end
36
+
37
+ # The first available line, if any.
38
+ #
39
+ # _Note:_ Will read from the source if necessary.
40
+ #
41
+ # @return [String,nil]
42
+ def first
43
+ self[0]
44
+ end
45
+
46
+ # Finds the first index of an element that fulfills a predicate.
47
+ #
48
+ # @param [Integer] starting_from
49
+ # The first index to check. That is, indices `(0..starting_from - 1)` are skipped.
50
+ # *Note:* Indices are relative to the start of the buffer, _not_ the start of the log!
51
+ #
52
+ # @yield Invokes a block as predicate over lines.
53
+ # @yieldparam [String] line
54
+ # A line of the log.
55
+ # @yieldreturn [true,false]
56
+ # `true` if (and only if) `line` fulfills the predicate.
57
+ #
58
+ # @return [Integer, nil]
59
+ # The first index of an element that fulfills the predicate, if any;
60
+ # otherwise, `nil`.
61
+ def find_index(starting_from = 0)
62
+ index = starting_from
63
+ element = self[index]
64
+
65
+ until element.nil?
66
+ return index if yield(element)
67
+ index += 1
68
+ element = self[index]
69
+ end
70
+
71
+ nil
72
+ end
73
+
74
+ # Retrieves the next `length` many log lines starting from index `offset`.
75
+ #
76
+ # @param [Integer] offset
77
+ # The first index to retrieve.
78
+ # *Note:* Indices are relative to the start of the buffer, _not_ the start of the log!
79
+ # @param [Integer] length
80
+ # The number of elements to retrieve.
81
+ # @return [String,Array<String>]
82
+ # If `length` is set, returns the array of lines with indices in `(offset..offset+length-1)`.
83
+ # Otherwise, returns the line with index `offset`.
84
+ def [](offset, length = nil)
85
+ base_length = length || 1
86
+ while offset + base_length > @buffer.size
87
+ return (length.nil? ? nil : @buffer[offset, @buffer.size]) if stream_is_done?
88
+ @buffer.push(@stream.readline.rstrip)
89
+ end
90
+
91
+ length.nil? ? @buffer[offset] : @buffer[offset, length]
92
+ end
93
+
94
+ # Moves the front of this buffer forwards by `offset` elements.
95
+ #
96
+ # That is, the following code returns `true`:
97
+ # ```ruby
98
+ # before = buffer[offset]
99
+ # buffer.forward[offset]
100
+ # after = buffer.first
101
+ # before == after
102
+ # ```
103
+ #
104
+ # @param [Integer] offset
105
+ # The number of lines to drop.
106
+ # @return [void]
107
+ def forward(offset = 1)
108
+ self[offset]
109
+ @buffer.slice!(0, offset)
110
+ end
111
+
112
+ # Closes the `IO` this buffer reads from, if any.
113
+ #
114
+ # @return [void]
115
+ def close
116
+ @stream.close unless @stream.nil? || @stream.closed?
117
+ end
118
+
119
+ private
120
+
121
+ # False if (and only if) this buffer reads from an `IO`,
122
+ # that `IO` is not closed, and has not yet reached the end.
123
+ #
124
+ # @return [true,false]
125
+ def stream_is_done?
126
+ @stream.nil? || @stream.closed? || @stream.eof?
127
+ end
128
+ end
129
+ end
@@ -1,29 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'log_parser/logger'
4
+ require 'log_parser/buffer'
5
+ require 'log_parser/message'
6
+ require 'log_parser/pattern'
7
+
3
8
  # TODO: document
4
9
  module LogParser
5
10
  attr_reader :messages
6
11
  attr_reader :scope_changes_by_line if Logger.debug?
7
12
 
8
13
  # TODO: document
14
+ # @return [Array<Message>]
15
+ def parse
16
+ skip_empty_lines
17
+ until empty?
18
+ parse_next_lines
19
+ skip_empty_lines
20
+ end
21
+
22
+ # TODO: Remove duplicates?
23
+ @messages
24
+ end
25
+
26
+ protected
27
+
28
+ # Creates a new instance.
29
+ #
30
+ # This parser will read lines one by one from the given `log`.
31
+ # If it is an `IO` or `StringIO`, only those lines currently under investigation will be kept in memory.
32
+ #
9
33
  # @param [Array<String>,IO,StringIO] log
10
- # @param [Hash] _options
11
- def initialize(log, _options = {})
34
+ # A set of log lines that will be parsed.
35
+ def initialize(log)
12
36
  @files = []
13
37
 
14
38
  @messages = []
15
39
  @log_line_number = 1
16
- @lines = LogBuffer.new(log)
40
+ @lines = LogParser::Buffer.new(log)
17
41
 
18
42
  Logger.debug "Parsing from '#{log}'"
19
43
  @scope_changes_by_line = {} if Logger.debug?
20
44
  end
21
45
 
22
- # @return [Array<LogPattern>]
46
+ # @abstract
47
+ # @return [Array<Pattern>]
23
48
  def patterns
24
49
  raise NotImplementedError
25
50
  end
26
51
 
52
+ # @abstract
27
53
  # @param [String] _line
28
54
  # @return [Array<String,:pop>] A list of new scopes this line enters (strings)
29
55
  # and leaves (`:pop`).
@@ -36,19 +62,6 @@ module LogParser
36
62
  @lines.empty?
37
63
  end
38
64
 
39
- # TODO: document
40
- # @return [Array<LogMessage>]
41
- def parse
42
- skip_empty_lines
43
- until empty?
44
- parse_next_lines
45
- skip_empty_lines
46
- end
47
-
48
- # TODO: Remove duplicates?
49
- @messages
50
- end
51
-
52
65
  private
53
66
 
54
67
  def skip_empty_lines
@@ -59,7 +72,7 @@ module LogParser
59
72
  end
60
73
 
61
74
  # TODO: document
62
- # @return [LogMessage,nil]
75
+ # @return [Message,nil]
63
76
  def parse_next_lines
64
77
  raise 'Parse already done!' if @lines.empty?
65
78
 
@@ -91,16 +104,16 @@ module LogParser
91
104
 
92
105
  def remove_consumed_lines(i)
93
106
  @lines.forward(i)
94
- @log_line_number += i
107
+ @log_line_number += i
95
108
 
96
- @scope_changes_by_line[@log_line_number] = [] if Logger.debug? && i > 0
109
+ @scope_changes_by_line[@log_line_number] = [] if Logger.debug? && i.positive?
97
110
  end
98
111
 
99
- # @return [LogMessage,nil]
112
+ # @return [Message,nil]
100
113
  def consume_pattern(pattern)
101
114
  # Apply the pattern, i.e. read the next message!
102
115
 
103
- # @type [LogMessage] message
116
+ # @type [Message] message
104
117
  message, consumed_lines = pattern.read(@lines)
105
118
  message.log_lines = { from: @log_line_number,
106
119
  to: @log_line_number + consumed_lines - 1 }
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LogParser
4
+ # A simple helper, probably to be replaced by a proper logging library
5
+ # at some point.
6
+ class Logger
7
+ class << self
8
+ # Switches debugging mode on and off.
9
+ #
10
+ # @param [true,false] flag
11
+ # @return [void]
12
+ def debug=(flag)
13
+ @debugging = flag
14
+ end
15
+
16
+ # Indicates whether we are debugging.
17
+ #
18
+ # @return [true,false]
19
+ # `true` if we are in debugging mode, `false` otherwise.
20
+ def debug?
21
+ @debugging || !ENV['DEBUG'].nil?
22
+ end
23
+
24
+ # Logs the given message to STDOUT if `debug?` is true.
25
+ #
26
+ # @param [String] message
27
+ # @return [void]
28
+ def debug(message)
29
+ puts message if debug?
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module LogParser
6
+ # A single log message.
7
+ #
8
+ # @attr [String] message
9
+ # The actual message body from the log.
10
+ # @attr [String,nil] source_file
11
+ # The name of the source file this message originated from.
12
+ # `nil` if it could not be determined.
13
+ # @attr [Hash<Symbol, Int>,nil] source_lines
14
+ # A hash with keys `:from` and `:to` mapping to the first and last line index in `source_file` this message pertains to.
15
+ # `nil` if they could not be determined.
16
+ # @attr [Hash<Symbol, Int>,nil] log_lines
17
+ # A hash with keys `:from` and `:to` mapping to the first and last line index in the log file that this message covers.
18
+ # `nil` if they could not be determined.
19
+ # @attr [true,false] preformatted
20
+ # If `true`, `message` should be printed as-is.
21
+ # Otherwise, whitespace can be eliminated.
22
+ # @attr [:error,:warning,:info,:debug] level
23
+ # The severity of this message.
24
+ # @attr [Class] pattern
25
+ # The {LogParser::Pattern} this message was matched by.
26
+ class Message
27
+ def initialize(message:, source_file: nil, source_lines: nil, log_lines: nil,
28
+ preformatted: false, level: :info, pattern: nil)
29
+ @message = message
30
+ @source_file = source_file
31
+ @source_lines = source_lines
32
+ @log_lines = log_lines
33
+ @preformatted = preformatted
34
+ @level = level
35
+ @pattern = pattern
36
+ end
37
+
38
+ attr_accessor :message, :source_file, :source_lines, :log_lines,
39
+ :preformatted, :level
40
+
41
+ # Convert this message to a file-line string representation.
42
+ #
43
+ # @return [String]
44
+ def to_s
45
+ lines = if @source_lines.nil?
46
+ ''
47
+ else
48
+ # @type [Hash<Symbol, Int>] @source_lines
49
+ ":#{@source_lines.values.uniq.join('-')}"
50
+ end
51
+
52
+ message = @message
53
+ message = message.split("\n").map(&:strip).join(' ') unless @preformatted
54
+ message += "\nLog pattern: '#{@pattern}'" if Logger.debug?
55
+
56
+ <<~MSG
57
+ #{@source_file}#{lines}: #{@level.to_s.upcase}
58
+ #{message}
59
+ MSG
60
+ end
61
+
62
+ # Convert this message to JSON.
63
+ #
64
+ # @return [String]
65
+ # The JSON string representing this message.
66
+ def to_json(_options = {})
67
+ hash = {
68
+ level: @level,
69
+ source_file: @source_file,
70
+ source_lines: @source_lines,
71
+ message: @message,
72
+ log_lines: @log_lines,
73
+ preformatted: @preformatted
74
+ }
75
+ hash[:pattern] = @pattern if Logger.debug?
76
+ JSON.pretty_generate hash
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LogParser
4
+ # Represents a certain pattern log message. The model we use is that
5
+ # * a pattern tells you if a message starts in a given line, and
6
+ # * reads lines from there on until it ends.
7
+ # The result will be a {Message}.
8
+ module Pattern
9
+ # Checks if this message pattern matches the given line.
10
+ #
11
+ # @abstract
12
+ # @param [String] _line
13
+ # The log line currently under investigation.
14
+ # @return [true,false]
15
+ # `true` if (and only if) this pattern can parse a single message from the given line onwards.
16
+ def begins_at?(_line)
17
+ raise NotImplementedError
18
+ end
19
+
20
+ # Reads a message from the given lines.
21
+ #
22
+ # @abstract
23
+ # @param [LogParser::Buffer] _lines
24
+ # A source of log lines to read from.
25
+ # @return [Array<(Message, Int)>]
26
+ # An array of the message that was read, and the number of lines that it spans.
27
+ # @raise
28
+ # If no message end could be found among the given lines.
29
+ def read(_lines)
30
+ raise NotImplementedError
31
+ end
32
+ end
33
+
34
+ # (see Pattern)
35
+ #
36
+ # This type of pattern is characterized mainly by two regular expressions:
37
+ # * One for matching the first line of a message, and
38
+ # * one for matching the last line (+- 1) of a message, which may depend on the first.
39
+ #
40
+ # @attr [Regexp] start
41
+ # Used to determine where a message starts.
42
+ # @see begins_at?
43
+ # @see initialize
44
+ # @attr [MatchData] start_match
45
+ # While a message is being processed, this attribute holds the match from the first line.
46
+ # @attr [Regexp] ending
47
+ # Used to determine where a message ends.
48
+ # @see ends_at?
49
+ # @see initialize
50
+ module RegExpPattern
51
+ include Pattern
52
+
53
+ # Creates a new instance.
54
+ #
55
+ # @param [Regexp] start
56
+ # Used to determine where a message starts.
57
+ # See also {begins_at?}.
58
+ # @param [Hash] ending
59
+ # Used to determine where a message ends.
60
+ # See also {ends_at?}.
61
+ # @option ending [Regexp] :pattern
62
+ # Describes either what all lines of a message look like, or matches the first line not part of the same message.
63
+ # @option ending [:match,:mismatch] :until
64
+ # Determines what is matched against `:pattern`.
65
+ # * If `:match`, messages end at the first line that _matches_ `:pattern`.
66
+ # * If `:mismatch`, messages end at the first line that does _not_ match `:pattern`.
67
+ # @option ending [true,false] :inclusive
68
+ # Determines whether the first line that (mis)matched `:pattern` should be part of the message.
69
+ def initialize(start, ending = { pattern: ->(_) { /^\s*$/ },
70
+ until: :match,
71
+ inclusive: false })
72
+ @start = start
73
+ @ending = ending
74
+ end
75
+
76
+ # Checks if this message pattern matches the given line,
77
+ # that is whether the `start` regexp (see {initialize}) matches it.
78
+ #
79
+ # @param [String] line
80
+ # The log line currently under investigation.
81
+ # @return [true,false]
82
+ # `true` if (and only if) this pattern can parse a single message from the given line onwards.
83
+ def begins_at?(line)
84
+ match = @start.match(line)
85
+ @start_match = match unless match.nil?
86
+ !match.nil?
87
+ end
88
+
89
+ # Reads a message from the given lines.
90
+ #
91
+ # @param [LogParser::Buffer] lines
92
+ # A source of log lines to read from.
93
+ # @return [Array<(Message, Int)>]
94
+ # An array of the message that was read, and the number of lines that it spans.
95
+ # @raise
96
+ # If no message end could be found among the given lines.
97
+ def read(lines)
98
+ ending = lines.find_index(1) { |l| ends_at?(l) }
99
+ raise "Did not find end of message (pattern '#{self.class}')." if ending.nil?
100
+ ending -= 1 unless @ending[:inclusive]
101
+
102
+ # Use ending+1 since ending is the index when we drop the first line!
103
+ msg = LogParser::Message.new(message: lines[0, ending + 1].join("\n"),
104
+ preformatted: true, level: nil,
105
+ pattern: self.class)
106
+ [msg, ending + 1]
107
+ end
108
+
109
+ protected
110
+
111
+ # Checks if the currently read message ends at the given line,
112
+ # that is whether the `ending[:pattern]` regexp (see {initialize}) matches it.
113
+ #
114
+ # @param [String] line
115
+ # The log line currently under investigation.
116
+ # @return [true,false]
117
+ # `true` if (and only if) the currently read message ends at the given line.
118
+ def ends_at?(line)
119
+ match = @ending[:pattern][@start_match].match(line)
120
+ @end_match = match unless match.nil?
121
+ !match.nil? == (@ending[:until] == :match)
122
+ end
123
+ end
124
+ end
@@ -1,24 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # TODO: document
4
- class BadHboxWarning
5
- include RegExpPattern
3
+ class TexLogParser
4
+ # TODO: document
5
+ class BadHboxWarning
6
+ include LogParser::RegExpPattern
6
7
 
7
- def initialize
8
- super(/^(Over|Under)full \\hbox.*at lines (\d+)--(\d+)/,
9
- { pattern: ->(_) { /^\s*\[\]\s*$/ }, until: :match, inclusive: false }
10
- )
11
- end
8
+ # Creates a new instance.
9
+ def initialize
10
+ super(/^(Over|Under)full \\hbox.*at lines (\d+)--(\d+)/,
11
+ { pattern: ->(_) { /^\s*\[\]\s*$/ }, until: :match, inclusive: false }
12
+ )
13
+ end
12
14
 
13
- def read(lines)
14
- # @type [LogMessage] msg
15
- msg, consumed = super(lines)
15
+ # (see LogParser::RegExpPattern#read)
16
+ def read(lines)
17
+ # @type [Message] msg
18
+ msg, consumed = super(lines)
16
19
 
17
- msg.source_lines = { from: @start_match[2].to_i,
18
- to: @start_match[3].to_i }
19
- msg.preformatted = true
20
- msg.level = :warning
20
+ msg.source_lines = { from: @start_match[2].to_i,
21
+ to: @start_match[3].to_i }
22
+ msg.preformatted = true
23
+ msg.level = :warning
21
24
 
22
- [msg, consumed]
25
+ [msg, consumed]
26
+ end
23
27
  end
24
28
  end
@@ -1,25 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Matches messages of this form:
4
- #
5
- # ! ==> Fatal error occurred, no output PDF file produced!
6
- # Transcript written on plain.log.
7
- class FatalErrorOccurred
8
- include RegExpPattern
3
+ class TexLogParser
4
+ # Matches messages of this form:
5
+ #
6
+ # ! ==> Fatal error occurred, no output PDF file produced!
7
+ # Transcript written on plain.log.
8
+ class FatalErrorOccurred
9
+ include LogParser::RegExpPattern
9
10
 
10
- def initialize
11
- super(/^\!\s+==>/,
12
- { pattern: ->(_) { /Transcript written/ }, until: :match, inclusive: true }
13
- )
14
- end
11
+ # Creates a new instance.
12
+ def initialize
13
+ super(/^!\s+==>/,
14
+ { pattern: ->(_) { /Transcript written/ }, until: :match, inclusive: true }
15
+ )
16
+ end
15
17
 
16
- def read(lines)
17
- # @type [LogMessage] msg
18
- msg, consumed = super(lines)
18
+ # (see LogParser::RegExpPattern#read)
19
+ def read(lines)
20
+ # @type [Message] msg
21
+ msg, consumed = super(lines)
19
22
 
20
- msg.level = :error
21
- msg.preformatted = false
23
+ msg.level = :error
24
+ msg.preformatted = false
22
25
 
23
- [msg, consumed]
26
+ [msg, consumed]
27
+ end
24
28
  end
25
29
  end
@@ -1,29 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Matches messages of this form:
4
- #
5
- # ./plain.tex:31: Undefined control sequence.
6
- # l.31 ...t contains some \ref{warnings} and \errors
7
- # for testing.
8
- class FileLineError
9
- include RegExpPattern
3
+ class TexLogParser
4
+ # Matches messages of this form:
5
+ #
6
+ # ./plain.tex:31: Undefined control sequence.
7
+ # l.31 ...t contains some \ref{warnings} and \errors
8
+ # for testing.
9
+ class FileLineError
10
+ include LogParser::RegExpPattern
10
11
 
11
- def initialize
12
- super(%r{^(/?(?:.*?/)*[^/]+):(\d+):})
13
- end
12
+ # Creates a new instance.
13
+ def initialize
14
+ super(%r{^(/?(?:.*?/)*[^/]+):(\d+):})
15
+ end
14
16
 
15
- def read(lines)
16
- # @type [LogMessage] msg
17
- msg, consumed = super(lines)
17
+ # (see LogParser::RegExpPattern#read)
18
+ def read(lines)
19
+ # @type [Message] msg
20
+ msg, consumed = super(lines)
18
21
 
19
- msg.source_file = @start_match[1]
20
- line = @start_match[2].to_i
21
- msg.source_lines = { from: line, to: line }
22
- msg.preformatted = true
23
- msg.level = :error
22
+ msg.source_file = @start_match[1]
23
+ line = @start_match[2].to_i
24
+ msg.source_lines = { from: line, to: line }
25
+ msg.preformatted = true
26
+ msg.level = :error
24
27
 
25
- msg.message.gsub!(@start, '')
28
+ msg.message.gsub!(@start, '')
26
29
 
27
- [msg, consumed]
30
+ [msg, consumed]
31
+ end
28
32
  end
29
33
  end