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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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