epuber 0.5.5 → 0.5.6

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
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