epuber 0.5.5 → 0.5.6

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
2
  SHA256:
3
- metadata.gz: fcab657cbef790188e95b1b238d68438e7f523e78be9c712209803c3808c3ca1
4
- data.tar.gz: d36fe7e3f63eb459e9036a587adee857a5f9552524c35e8ef63ef0a867302eac
3
+ metadata.gz: b03d446f6c71a6105554b794179ec35e719bc3a3c630ece5bd2607edbf379681
4
+ data.tar.gz: b8c27f00396dc720d1e5dfc13d92fa5d97061f58947217d873a173c2ce939f7d
5
5
  SHA512:
6
- metadata.gz: 640ffc22f2584d5ce642a7c7e923b13734e66e9db7fed5c2094b874b69ff56e0feabd75951be7886ac05ff94aa661b0a44e047b7cea4358d6f9efce7747b8ae2
7
- data.tar.gz: eff25321a3950ccfd005fcdc3fa12b2becb8d8f2a6e2f429afdb36569f940a0461ea0165a2436a963da5b7ee88aafb895a0a7d07e7c4d4c5aec56a78be1ef81d
6
+ metadata.gz: 632a864249f1e89cdf915e28bcfdd8e83dbae9f3b8b4c84d933ccb873eed6d4b5ef3ab74d6775af229e9b49b6e21f9d199fe8990a542ef5b12f50fb8eda3e30d
7
+ data.tar.gz: 9105843aac2528104ff2febf949ccd89e9a3efe9724e8a422c5a98de003563b3bd58643f5894964c5a787071b30325c64ace3e13414c2960058c28c2417e1291
@@ -4,85 +4,25 @@ require 'active_support/core_ext/string/access'
4
4
 
5
5
  require_relative '../ruby_extensions/match_data'
6
6
  require_relative '../checker'
7
-
7
+ require_relative '../compiler/problem'
8
8
 
9
9
  module Epuber
10
10
  class Checker
11
11
  class TextChecker < Checker
12
- class MatchProblem
12
+ class MatchProblem < Compiler::Problem
13
13
  # @param message [String]
14
14
  # @param file_path [String]
15
15
  # @param match [MatchData]
16
16
  #
17
17
  def initialize(match, message, file_path)
18
- @match = match
19
- @message = message
20
- @file_path = file_path
21
- end
22
-
23
- # Formats caret symbol with space indent
24
- #
25
- # @param [Fixnum] indent
26
- #
27
- # @return [String]
28
- #
29
- def caret_symbol(indent)
30
- ' ' * indent + '^'
31
- end
32
-
33
- # Formats caret symbols for indent and length
34
- #
35
- # @param [Fixnum] length
36
- # @param [Fixnum] indent
37
- #
38
- # @return [String]
39
- #
40
- def caret_symbols(indent, length)
41
- start_sign = caret_symbol(indent)
42
- end_sign = if length > 1
43
- caret_symbol(length-2)
44
- else
45
- ''
46
- end
47
-
48
- "#{start_sign}#{end_sign}"
49
- end
50
-
51
- def formatted_match_line
52
- match_line = @match.matched_string
53
- pre_line = @match.pre_match_lines.last || ''
54
-
55
- pre = match_pre_line = pre_line
56
- if remove_tabs(match_pre_line).length > 100
57
- pre = "#{match_pre_line.first(20)}...#{match_pre_line.last(30)}"
58
- end
59
-
60
- pre = remove_tabs(pre)
61
-
62
- post_line = @match.post_match_lines.first || ''
63
-
64
- post = if post_line.length > 50
65
- "#{post_line.first(50)}..."
66
- else
67
- post_line
68
- end
69
-
70
- [pre, match_line, post]
71
- end
72
-
73
- def remove_tabs(text)
74
- text.gsub("\t", ' ' * 4)
75
- end
76
-
77
- def to_s
78
- pre_original = @match.pre_match_lines.last || ''
79
- pre, match_text, post = formatted_match_line
18
+ whole_text = match.pre_match + match.matched_string + match.post_match
80
19
 
81
- pointers = caret_symbols(pre.length, match_text.length)
20
+ line = match.pre_match_lines.count
21
+ column = (match.pre_match_lines.last || '').length + 1
22
+ length = match.matched_string.length
23
+ location = Epuber::Compiler::Problem::Location.new(line, column, length)
82
24
 
83
- %{#{@file_path}:#{@match.line_number} column: #{pre_original.length} --- #{@message}
84
- #{pre + match_text.ansi.red + post}
85
- #{pointers}}
25
+ super(:warn, message, whole_text, location: location, file_path: file_path)
86
26
  end
87
27
  end
88
28
 
@@ -68,11 +68,11 @@ module Epuber
68
68
  xhtml_content
69
69
  end
70
70
 
71
- # @param [Array] errors
71
+ # @param [Array<Epuber::Compiler::Problem>] errors
72
72
  #
73
73
  def process_nokogiri_errors(errors)
74
- errors.each do |e|
75
- UI.warning(e)
74
+ errors.each do |problem|
75
+ UI.warning(problem, location: self)
76
76
  end
77
77
  end
78
78
 
@@ -86,12 +86,12 @@ module Epuber
86
86
  book = compilation_context.book
87
87
  file_resolver = compilation_context.file_resolver
88
88
 
89
- xhtml_doc = UI.print_step_processing_time('parsing XHTML file') do
90
- XHTMLProcessor.xml_document_from_string(content, source_path)
89
+ xhtml_doc, errors = UI.print_step_processing_time('parsing XHTML file') do
90
+ XHTMLProcessor.xml_doc_from_str_with_errors(content, source_path)
91
91
  end
92
92
 
93
93
  if compilation_context.release_build && xhtml_doc.errors.count > 0
94
- process_nokogiri_errors(xhtml_doc.errors)
94
+ process_nokogiri_errors(errors)
95
95
  end
96
96
 
97
97
  UI.print_step_processing_time('adding missing elements') do
@@ -0,0 +1,124 @@
1
+ # encoding: utf-8
2
+
3
+
4
+ module Epuber
5
+ class Compiler
6
+ class Problem
7
+ class Location
8
+ attr_reader :line
9
+ attr_reader :column
10
+ attr_reader :length
11
+
12
+ def initialize(line, column, length = nil)
13
+ @line = line
14
+ @column = column
15
+ @length = length || 1
16
+ end
17
+ end
18
+
19
+ attr_reader :level
20
+ attr_reader :message
21
+ attr_reader :source
22
+ attr_reader :location
23
+ attr_reader :file_path
24
+
25
+ def initialize(level, message, source, location: nil, line: nil, column: nil, length: nil, file_path: nil)
26
+ @level = level
27
+ @message = message
28
+ @source = source
29
+ @location = location
30
+ if @location.nil? && line && column
31
+ @location = Location.new(line, column, length)
32
+ end
33
+
34
+ @file_path = file_path
35
+ end
36
+
37
+ # Formats caret symbol with space indent
38
+ #
39
+ # @param [Fixnum] indent
40
+ #
41
+ # @return [String]
42
+ #
43
+ def self.caret_symbol(indent)
44
+ ' ' * indent + '^'
45
+ end
46
+
47
+ # Formats caret symbols for indent and length
48
+ #
49
+ # @param [Fixnum] length
50
+ # @param [Fixnum] indent
51
+ #
52
+ # @return [String]
53
+ #
54
+ def self.caret_symbols(indent, length)
55
+ start_sign = caret_symbol(indent)
56
+ end_sign = if length > 1
57
+ caret_symbol(length-2)
58
+ else
59
+ ''
60
+ end
61
+
62
+ "#{start_sign}#{end_sign}"
63
+ end
64
+
65
+ def self.remove_tabs(text)
66
+ text.gsub("\t", ' ' * 4)
67
+ end
68
+
69
+ # @param [Location] location
70
+ #
71
+ def self.text_at(text, location)
72
+ line_index = location.line - 1
73
+ column_index = location.column - 1
74
+
75
+ lines = text.split("\n")
76
+
77
+ line = lines[line_index] || ''
78
+ matched_text = line[column_index ... column_index + location.length] || ''
79
+
80
+ pre = (lines[0 ... line_index] + [line[0 ... column_index]]).join("\n")
81
+ post = ([line[column_index + location.length .. line.length]] + (lines[location.line .. lines.count] || [])).join("\n")
82
+
83
+ [pre, matched_text, post]
84
+ end
85
+
86
+ def self.formatted_match_line(text, location)
87
+ pre, matched, post = text_at(text, location)
88
+
89
+ pre_line = pre.split("\n").last || ''
90
+ post_line = post.split("\n").first || ''
91
+
92
+ pre = match_pre_line = pre_line
93
+ if remove_tabs(match_pre_line).length > 100
94
+ pre = "#{match_pre_line.first(20)}...#{match_pre_line.last(30)}"
95
+ end
96
+
97
+ pre = remove_tabs(pre)
98
+
99
+ post = if post_line.length > 50
100
+ "#{post_line.first(50)}..."
101
+ else
102
+ post_line
103
+ end
104
+
105
+ [pre, matched, post]
106
+ end
107
+
108
+ def to_s
109
+ pre, match_text, post = self.class.formatted_match_line(@source, @location)
110
+
111
+ pointers = self.class.caret_symbols(pre.length, @location.length)
112
+ colored_match_text = match_text.empty? ? match_text : match_text.ansi.red
113
+ column = @location.column
114
+ line = [@location.line, 1].max
115
+
116
+ [
117
+ "#{@file_path}:#{line} column: #{column} --- #{@message}",
118
+ ' ' + pre + colored_match_text + post,
119
+ ' ' + pointers,
120
+ ].join("\n")
121
+ end
122
+ end
123
+ end
124
+ end
@@ -19,14 +19,14 @@ module Epuber
19
19
  #
20
20
  # @return [Nokogiri::XML::Document] parsed document
21
21
  #
22
- def self.xml_document_from_string(text, file_path = nil)
22
+ def self.xml_doc_from_str_with_errors(text, file_path = nil)
23
23
  if /\A[\n\r ]+(<\?xml)/ =~ text
24
24
  UI.warning('XML header must be at the beginning of document', location: UI::Location.new(file_path, 1))
25
25
 
26
26
  text = text.lstrip
27
27
  end
28
28
 
29
- xml_header = ''
29
+ xml_header = nil
30
30
  if /\A\s*(<\?xml[^>]*\?>)/ =~ text
31
31
  match = Regexp.last_match
32
32
  xml_header = text[match.begin(1)...match.end(1)]
@@ -50,20 +50,38 @@ module Epuber
50
50
  Nokogiri::XML::ParseOptions::NOERROR | # to silence any errors or warnings printing into console
51
51
  Nokogiri::XML::ParseOptions::NOWARNING
52
52
 
53
- doc = Nokogiri::XML("#{before}<root>#{text}</root>", nil, nil, parse_options)
53
+ doc = Nokogiri::XML("#{before}<root>#{text}</root>", file_path, nil, parse_options)
54
+ text_for_errors = before + text
54
55
  doc.encoding = 'UTF-8'
55
56
  doc.file_path = file_path
56
57
 
58
+ if doc.errors.empty?
59
+ errors = []
60
+ else
61
+ errors = doc.errors.map do |e|
62
+ Problem.new(:error, e.message, text_for_errors, line: e.line, column: e.column, file_path: file_path)
63
+ end
64
+ end
65
+
57
66
  root = root_node = doc.root
58
67
  root_elements = root.children.select { |a| a.element? || a.comment? }
59
68
 
60
69
  if root_elements.count == 1
61
70
  doc.root = root_elements.first
71
+ elsif root_node.at_css('html')
72
+ doc.root = root_node.at_css('html')
62
73
  elsif root_node.at_css('body').nil?
63
74
  root_node.node_name = 'body'
75
+ else
76
+ root_node.node_name = 'html'
64
77
  end
65
78
 
66
- doc
79
+ [doc, errors]
80
+ end
81
+
82
+ def self.xml_document_from_string(text, file_path = nil)
83
+ xml, errros = self.xml_doc_from_str_with_errors(text, file_path)
84
+ xml
67
85
  end
68
86
 
69
87
 
@@ -83,25 +101,37 @@ module Epuber
83
101
  def self.add_missing_root_elements(xhtml_doc, title, epub_version)
84
102
  # add missing body element
85
103
  if xhtml_doc.at_css('body').nil?
86
- xhtml_doc.root.surround_with_element('body')
104
+ if xhtml_doc.root.node_name == 'html'
105
+ xhtml_doc.root << xhtml_doc.create_element('body')
106
+ else
107
+ xhtml_doc.root.surround_with_element('body')
108
+ end
87
109
  end
88
110
 
111
+ html = xhtml_doc.at_css('html')
112
+
89
113
  # add missing root html element
90
- if xhtml_doc.at_css('html').nil?
114
+ if html.nil?
91
115
  attrs = {}
92
116
  attrs['xmlns'] = 'http://www.w3.org/1999/xhtml'
93
117
  attrs['xmlns:epub'] = 'http://www.idpf.org/2007/ops' if epub_version >= 3
94
- xhtml_doc.root.surround_with_element('html', attrs)
118
+ html = xhtml_doc.root.surround_with_element('html', attrs)
119
+ elsif html.namespaces.empty?
120
+ html['xmlns'] = 'http://www.w3.org/1999/xhtml'
121
+ html['xmlns:epub'] = 'http://www.idpf.org/2007/ops' if epub_version >= 3
95
122
  end
96
123
 
97
124
  # add missing head in html
98
125
  if xhtml_doc.at_css('html > head').nil?
99
- html = xhtml_doc.css('html').first
100
126
  head = xhtml_doc.create_element('head')
101
127
  head << xhtml_doc.create_element('title', title)
102
128
  head << xhtml_doc.create_element('meta', charset: 'utf-8') if epub_version >= 3.0
103
129
 
104
- html.children.first.before(head)
130
+ if (first = html.children.first)
131
+ first.before(head)
132
+ else
133
+ html << head
134
+ end
105
135
  end
106
136
 
107
137
  # https://github.com/IDPF/epubcheck/issues/631
@@ -191,7 +191,13 @@ module Epuber
191
191
 
192
192
  comps = []
193
193
  comps << message.to_s
194
- comps << " (in file #{location.path} line #{location.lineno}" unless location.nil?
194
+ if !location.nil? && !(message.is_a?(Epuber::Compiler::Problem) || message.is_a?(Epuber::Checker::TextChecker::MatchProblem))
195
+ if location.lineno
196
+ comps << " (in file #{location.path} line #{location.lineno})"
197
+ else
198
+ comps << " (in file #{location.path})"
199
+ end
200
+ end
195
201
 
196
202
  comps.join("\n").ansi.send(_color_from_level(level))
197
203
  end
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Epuber
3
- VERSION = '0.5.5'
3
+ VERSION = '0.5.6'
4
4
 
5
5
  HOME_URL = 'https://github.com/epuber-io/epuber'
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: epuber
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.5
4
+ version: 0.5.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman Kříž
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-09-15 00:00:00.000000000 Z
11
+ date: 2018-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -369,6 +369,7 @@ files:
369
369
  - lib/epuber/compiler/meta_inf_generator.rb
370
370
  - lib/epuber/compiler/nav_generator.rb
371
371
  - lib/epuber/compiler/opf_generator.rb
372
+ - lib/epuber/compiler/problem.rb
372
373
  - lib/epuber/compiler/xhtml_processor.rb
373
374
  - lib/epuber/config.rb
374
375
  - lib/epuber/dsl/attribute.rb