review 5.1.1 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby-tex.yml +5 -1
- data/.github/workflows/ruby-win.yml +5 -1
- data/.github/workflows/ruby.yml +5 -1
- data/.rubocop.yml +6 -2
- data/NEWS.ja.md +48 -0
- data/NEWS.md +48 -1
- data/bin/review-compile +8 -15
- data/bin/review-preproc +28 -34
- data/doc/config.yml.sample +2 -0
- data/doc/writing_vertical.ja.md +6 -0
- data/lib/review/builder.rb +34 -40
- data/lib/review/compiler.rb +59 -43
- data/lib/review/epubmaker.rb +24 -38
- data/lib/review/epubmaker/producer.rb +3 -4
- data/lib/review/exception.rb +7 -0
- data/lib/review/htmlbuilder.rb +51 -20
- data/lib/review/idgxmlbuilder.rb +19 -18
- data/lib/review/idgxmlmaker.rb +12 -13
- data/lib/review/img_math.rb +11 -18
- data/lib/review/index_builder.rb +6 -15
- data/lib/review/latexbuilder.rb +38 -43
- data/lib/review/loggable.rb +27 -0
- data/lib/review/logger.rb +48 -0
- data/lib/review/makerhelper.rb +5 -1
- data/lib/review/markdownbuilder.rb +5 -4
- data/lib/review/pdfmaker.rb +23 -21
- data/lib/review/plaintextbuilder.rb +8 -8
- data/lib/review/preprocessor.rb +94 -296
- data/lib/review/preprocessor/directive.rb +35 -0
- data/lib/review/preprocessor/line.rb +34 -0
- data/lib/review/preprocessor/repository.rb +177 -0
- data/lib/review/rstbuilder.rb +1 -1
- data/lib/review/template.rb +5 -1
- data/lib/review/textmaker.rb +12 -13
- data/lib/review/tocprinter.rb +1 -1
- data/lib/review/topbuilder.rb +7 -7
- data/lib/review/version.rb +1 -1
- data/lib/review/webmaker.rb +13 -14
- data/review.gemspec +1 -1
- data/samples/sample-book/src/lib/tasks/review.rake +3 -1
- data/samples/sample-book/src/lib/tasks/z01_copy_sty.rake +2 -1
- data/samples/syntax-book/lib/tasks/z01_copy_sty.rake +2 -1
- data/templates/latex/review-jlreq/review-base.sty +3 -5
- data/templates/latex/review-jlreq/review-jlreq.cls +4 -3
- data/templates/latex/review-jsbook/review-base.sty +3 -3
- data/templates/latex/review-jsbook/review-jsbook.cls +1 -1
- data/test/test_builder.rb +8 -8
- data/test/test_epubmaker.rb +13 -8
- data/test/test_epubmaker_cmd.rb +13 -2
- data/test/test_htmlbuilder.rb +68 -26
- data/test/test_idgxmlbuilder.rb +31 -23
- data/test/test_latexbuilder.rb +30 -22
- data/test/test_latexbuilder_v2.rb +18 -10
- data/test/test_plaintextbuilder.rb +30 -22
- data/test/test_preprocessor.rb +188 -1
- data/test/test_topbuilder.rb +26 -18
- metadata +12 -8
@@ -0,0 +1,35 @@
|
|
1
|
+
# Copyright (c) 2010-2021 Minero Aoki, Kenshi Muto
|
2
|
+
#
|
3
|
+
# This program is free software.
|
4
|
+
# You can distribute or modify this program under the terms of
|
5
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
6
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
7
|
+
#
|
8
|
+
|
9
|
+
module ReVIEW
|
10
|
+
class Preprocessor
|
11
|
+
class Directive
|
12
|
+
def initialize(op, args, opts)
|
13
|
+
@op = op
|
14
|
+
@args = args
|
15
|
+
@opts = opts
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :op
|
19
|
+
attr_reader :args
|
20
|
+
attr_reader :opts
|
21
|
+
|
22
|
+
def arg
|
23
|
+
@args.first
|
24
|
+
end
|
25
|
+
|
26
|
+
def opt
|
27
|
+
@opts.first
|
28
|
+
end
|
29
|
+
|
30
|
+
def [](key)
|
31
|
+
@opts[key]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Copyright (c) 2010-2021 Minero Aoki, Kenshi Muto
|
2
|
+
#
|
3
|
+
# This program is free software.
|
4
|
+
# You can distribute or modify this program under the terms of
|
5
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
6
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
7
|
+
#
|
8
|
+
|
9
|
+
module ReVIEW
|
10
|
+
class Preprocessor
|
11
|
+
class Line
|
12
|
+
def initialize(number, string)
|
13
|
+
@number = number
|
14
|
+
@string = string
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :number
|
18
|
+
attr_reader :string
|
19
|
+
alias_method :to_s, :string
|
20
|
+
|
21
|
+
def edit
|
22
|
+
self.class.new(@number, yield(@string))
|
23
|
+
end
|
24
|
+
|
25
|
+
def empty?
|
26
|
+
@string.strip.empty?
|
27
|
+
end
|
28
|
+
|
29
|
+
def num_indent
|
30
|
+
@string.slice(/\A\s*/).size
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
# Copyright (c) 2010-2021 Minero Aoki, Kenshi Muto
|
2
|
+
#
|
3
|
+
# This program is free software.
|
4
|
+
# You can distribute or modify this program under the terms of
|
5
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
6
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'review/textutils'
|
10
|
+
require 'review/loggable'
|
11
|
+
|
12
|
+
module ReVIEW
|
13
|
+
class Preprocessor
|
14
|
+
class Repository
|
15
|
+
include TextUtils
|
16
|
+
include Loggable
|
17
|
+
|
18
|
+
def initialize(param)
|
19
|
+
@repository = {}
|
20
|
+
@config = param
|
21
|
+
@leave_content = nil
|
22
|
+
@logger = ReVIEW.logger
|
23
|
+
end
|
24
|
+
|
25
|
+
def fetch_file(file)
|
26
|
+
file_descripter(file)['file']
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch_range(file, name)
|
30
|
+
fetch(file, 'range', name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def fetch(file, type, name)
|
34
|
+
table = file_descripter(file)[type] or return nil
|
35
|
+
table[name]
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def file_descripter(fname)
|
41
|
+
@leave_content = File.extname(fname) == '.re'
|
42
|
+
return @repository[fname] if @repository[fname]
|
43
|
+
|
44
|
+
@repository[fname] = git?(fname) ? parse_git_blob(fname) : parse_file(fname)
|
45
|
+
end
|
46
|
+
|
47
|
+
def git?(fname)
|
48
|
+
fname.start_with?('git|')
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse_git_blob(g_obj)
|
52
|
+
IO.popen('git show ' + g_obj.sub(/\Agit\|/, ''), 'r') do |f|
|
53
|
+
@inf = f
|
54
|
+
return _parse_file(f)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def parse_file(fname)
|
59
|
+
File.open(fname, 'rt:BOM|utf-8') do |f|
|
60
|
+
@inf = f
|
61
|
+
return _parse_file(f)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def _parse_file(f)
|
66
|
+
whole = []
|
67
|
+
repo = { 'file' => whole }
|
68
|
+
curr = { 'WHOLE' => whole }
|
69
|
+
lineno = 1
|
70
|
+
yacchack = false # remove ';'-only lines.
|
71
|
+
opened = [['(not opened)', '(not opened)']] * 3
|
72
|
+
|
73
|
+
f.each do |line|
|
74
|
+
begin
|
75
|
+
case line
|
76
|
+
when /(?:\A\#@|\#@@)([a-z]+)_(begin|end)\((.*)\)/
|
77
|
+
type = check_type($1)
|
78
|
+
direction = $2
|
79
|
+
spec = check_spec($3)
|
80
|
+
case direction
|
81
|
+
when 'begin'
|
82
|
+
key = "#{type}/#{spec}"
|
83
|
+
if curr[key]
|
84
|
+
app_error "begin x2: #{key}"
|
85
|
+
end
|
86
|
+
(repo[type] ||= {})[spec] = curr[key] = []
|
87
|
+
when 'end'
|
88
|
+
curr.delete("#{type}/#{spec}") or
|
89
|
+
app_error "end before begin: #{type}/#{spec}"
|
90
|
+
else
|
91
|
+
app_error 'must not happen'
|
92
|
+
end
|
93
|
+
|
94
|
+
when %r{(?:\A\#@|\#@@)([a-z]+)/(\w+)\{}
|
95
|
+
type = check_type($1)
|
96
|
+
spec = check_spec($2)
|
97
|
+
key = "#{type}/#{spec}"
|
98
|
+
if curr[key]
|
99
|
+
app_error "begin x2: #{key}"
|
100
|
+
end
|
101
|
+
(repo[type] ||= {})[spec] = curr[key] = []
|
102
|
+
opened.push([type, spec])
|
103
|
+
|
104
|
+
when %r{(?:\A\#@|\#@@)([a-z]+)/(\w+)\}}
|
105
|
+
type = check_type($1)
|
106
|
+
spec = check_spec($2)
|
107
|
+
curr.delete("#{type}/#{spec}") or
|
108
|
+
app_error "end before begin: #{type}/#{spec}"
|
109
|
+
opened.delete("#{type}/#{spec}")
|
110
|
+
|
111
|
+
when /(?:\A\#@|\#@@)\}/
|
112
|
+
type, spec = opened.last
|
113
|
+
curr.delete("#{type}/#{spec}") or
|
114
|
+
app_error "closed before open: #{type}/#{spec}"
|
115
|
+
opened.pop
|
116
|
+
|
117
|
+
when /(?:\A\#@|\#@@)yacchack/
|
118
|
+
yacchack = true
|
119
|
+
|
120
|
+
when /\A\#@-/ # does not increment line number.
|
121
|
+
line = canonical($')
|
122
|
+
curr.each_value { |list| list.push(Line.new(nil, line)) }
|
123
|
+
|
124
|
+
else
|
125
|
+
next if yacchack && (line.strip == ';')
|
126
|
+
|
127
|
+
line = canonical(line)
|
128
|
+
curr.each_value { |list| list.push(Line.new(lineno, line)) }
|
129
|
+
lineno += 1
|
130
|
+
end
|
131
|
+
rescue ApplicationError => e
|
132
|
+
@has_errors = true
|
133
|
+
error e.message, location: location
|
134
|
+
end
|
135
|
+
end
|
136
|
+
if curr.size > 1
|
137
|
+
curr.delete('WHOLE')
|
138
|
+
curr.each { |range, lines| warn "#{@inf.path}: unclosed range: #{range} (begin @#{lines.first.number})" }
|
139
|
+
@has_errors = true
|
140
|
+
end
|
141
|
+
|
142
|
+
if @has_errors
|
143
|
+
error! 'repository failed.'
|
144
|
+
end
|
145
|
+
|
146
|
+
repo
|
147
|
+
end
|
148
|
+
|
149
|
+
def canonical(line)
|
150
|
+
if @leave_content
|
151
|
+
return line
|
152
|
+
end
|
153
|
+
|
154
|
+
tabwidth = @config['tabwidth'] || 8
|
155
|
+
if tabwidth > 0
|
156
|
+
detab(line, tabwidth).rstrip + "\n"
|
157
|
+
else
|
158
|
+
line
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def check_type(type)
|
163
|
+
app_error "wrong type: #{type.inspect}" unless Preprocessor::TYPES.index(type)
|
164
|
+
type
|
165
|
+
end
|
166
|
+
|
167
|
+
def check_spec(spec)
|
168
|
+
app_error "wrong spec: #{spec.inspect}" unless /\A\w+\z/ =~ spec
|
169
|
+
spec
|
170
|
+
end
|
171
|
+
|
172
|
+
def location
|
173
|
+
"#{@inf.path}:#{@inf.lineno}"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
data/lib/review/rstbuilder.rb
CHANGED
@@ -54,12 +54,12 @@ module ReVIEW
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def builder_init_file
|
57
|
+
super
|
57
58
|
@section = 0
|
58
59
|
@subsection = 0
|
59
60
|
@subsubsection = 0
|
60
61
|
@subsubsubsection = 0
|
61
62
|
@blank_seen = true
|
62
|
-
@sec_counter = SecCounter.new(5, @chapter)
|
63
63
|
@ul_indent = 0
|
64
64
|
@ol_indent = 0
|
65
65
|
@in_role = false
|
data/lib/review/template.rb
CHANGED
@@ -19,7 +19,11 @@ module ReVIEW
|
|
19
19
|
return unless filename
|
20
20
|
|
21
21
|
content = File.read(filename)
|
22
|
-
@erb =
|
22
|
+
@erb = if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.6')
|
23
|
+
ERB.new(content, trim_mode: mode)
|
24
|
+
else
|
25
|
+
ERB.new(content, nil, mode)
|
26
|
+
end
|
23
27
|
end
|
24
28
|
|
25
29
|
def result(bind_data = nil)
|
data/lib/review/textmaker.rb
CHANGED
@@ -17,10 +17,12 @@ require 'review/topbuilder'
|
|
17
17
|
require 'review/version'
|
18
18
|
require 'review/makerhelper'
|
19
19
|
require 'review/img_math'
|
20
|
+
require 'review/loggable'
|
20
21
|
|
21
22
|
module ReVIEW
|
22
23
|
class TEXTMaker
|
23
24
|
include MakerHelper
|
25
|
+
include Loggable
|
24
26
|
|
25
27
|
attr_accessor :config, :basedir
|
26
28
|
|
@@ -29,15 +31,7 @@ module ReVIEW
|
|
29
31
|
@logger = ReVIEW.logger
|
30
32
|
@plaintext = nil
|
31
33
|
@img_math = nil
|
32
|
-
|
33
|
-
|
34
|
-
def error(msg)
|
35
|
-
@logger.error msg
|
36
|
-
exit 1
|
37
|
-
end
|
38
|
-
|
39
|
-
def warn(msg)
|
40
|
-
@logger.warn msg
|
34
|
+
@compile_errors = nil
|
41
35
|
end
|
42
36
|
|
43
37
|
def self.execute(*args)
|
@@ -78,7 +72,7 @@ module ReVIEW
|
|
78
72
|
|
79
73
|
def execute(*args)
|
80
74
|
cmd_config, yamlfile = parse_opts(args)
|
81
|
-
error "#{yamlfile} not found." unless File.exist?(yamlfile)
|
75
|
+
error! "#{yamlfile} not found." unless File.exist?(yamlfile)
|
82
76
|
|
83
77
|
@config = ReVIEW::Configure.create(maker: 'textmaker',
|
84
78
|
yamlfile: yamlfile,
|
@@ -92,7 +86,7 @@ module ReVIEW
|
|
92
86
|
rescue ApplicationError => e
|
93
87
|
raise if @config['debug']
|
94
88
|
|
95
|
-
error
|
89
|
+
error! e.message
|
96
90
|
end
|
97
91
|
|
98
92
|
if @config['math_format'] == 'imgmath'
|
@@ -109,6 +103,10 @@ module ReVIEW
|
|
109
103
|
@book = ReVIEW::Book::Base.new(@basedir, config: @config)
|
110
104
|
|
111
105
|
build_body(@path, yamlfile)
|
106
|
+
|
107
|
+
if @compile_errors
|
108
|
+
app_error 'compile error, No TEXT file output.'
|
109
|
+
end
|
112
110
|
end
|
113
111
|
|
114
112
|
def build_body(basetmpdir, _yamlfile)
|
@@ -162,8 +160,9 @@ module ReVIEW
|
|
162
160
|
begin
|
163
161
|
@converter.convert(filename, File.join(basetmpdir, textfile))
|
164
162
|
rescue => e
|
165
|
-
|
166
|
-
|
163
|
+
@compile_errors = true
|
164
|
+
error "compile error in #{filename} (#{e.class})"
|
165
|
+
error e.message
|
167
166
|
end
|
168
167
|
end
|
169
168
|
end
|
data/lib/review/tocprinter.rb
CHANGED
data/lib/review/topbuilder.rb
CHANGED
@@ -105,7 +105,7 @@ module ReVIEW
|
|
105
105
|
list_header(id, caption, lang)
|
106
106
|
end
|
107
107
|
rescue KeyError
|
108
|
-
|
108
|
+
app_error "no such list: #{id}"
|
109
109
|
end
|
110
110
|
puts "◆→終了:#{@titles['list']}←◆"
|
111
111
|
blank
|
@@ -178,7 +178,7 @@ module ReVIEW
|
|
178
178
|
list_header(id, caption, lang)
|
179
179
|
end
|
180
180
|
rescue KeyError
|
181
|
-
|
181
|
+
app_error "no such list: #{id}"
|
182
182
|
end
|
183
183
|
puts "◆→終了:#{@titles['list']}←◆"
|
184
184
|
blank
|
@@ -202,7 +202,7 @@ module ReVIEW
|
|
202
202
|
if @chapter.image_bound?(id)
|
203
203
|
puts "◆→#{@chapter.image(id).path}#{metrics}←◆"
|
204
204
|
else
|
205
|
-
warn "image not bound: #{id}"
|
205
|
+
warn "image not bound: #{id}", location: location
|
206
206
|
lines.each do |line|
|
207
207
|
puts line
|
208
208
|
end
|
@@ -287,7 +287,7 @@ module ReVIEW
|
|
287
287
|
def inline_fn(id)
|
288
288
|
"【注#{@chapter.footnote(id).number}】"
|
289
289
|
rescue KeyError
|
290
|
-
|
290
|
+
app_error "unknown footnote: #{id}"
|
291
291
|
end
|
292
292
|
|
293
293
|
def compile_ruby(base, ruby)
|
@@ -381,7 +381,7 @@ module ReVIEW
|
|
381
381
|
begin
|
382
382
|
"◆→画像 #{@chapter.image(id).path.sub(%r{\A\./}, '')}←◆"
|
383
383
|
rescue
|
384
|
-
warn "image not bound: #{id}"
|
384
|
+
warn "image not bound: #{id}", location: location
|
385
385
|
"◆→画像 #{id}←◆"
|
386
386
|
end
|
387
387
|
end
|
@@ -425,7 +425,7 @@ module ReVIEW
|
|
425
425
|
def inline_bib(id)
|
426
426
|
%Q([#{@chapter.bibpaper(id).number}])
|
427
427
|
rescue KeyError
|
428
|
-
|
428
|
+
app_error "unknown bib: #{id}"
|
429
429
|
end
|
430
430
|
|
431
431
|
def noindent
|
@@ -487,7 +487,7 @@ module ReVIEW
|
|
487
487
|
begin
|
488
488
|
puts "◆→画像 #{@chapter.image(id).path.sub(%r{\A\./}, '')}#{metrics}←◆"
|
489
489
|
rescue
|
490
|
-
warn "image not bound: #{id}"
|
490
|
+
warn "image not bound: #{id}", location: location
|
491
491
|
puts "◆→画像 #{id}←◆"
|
492
492
|
end
|
493
493
|
if !caption_top?('image') && caption.present?
|
data/lib/review/version.rb
CHANGED
data/lib/review/webmaker.rb
CHANGED
@@ -21,11 +21,13 @@ require 'review/tocprinter'
|
|
21
21
|
require 'review/version'
|
22
22
|
require 'review/makerhelper'
|
23
23
|
require 'review/img_math'
|
24
|
+
require 'review/loggable'
|
24
25
|
|
25
26
|
module ReVIEW
|
26
27
|
class WEBMaker
|
27
28
|
include ERB::Util
|
28
29
|
include MakerHelper
|
30
|
+
include Loggable
|
29
31
|
|
30
32
|
attr_accessor :config, :basedir
|
31
33
|
|
@@ -33,15 +35,7 @@ module ReVIEW
|
|
33
35
|
@basedir = nil
|
34
36
|
@logger = ReVIEW.logger
|
35
37
|
@img_math = nil
|
36
|
-
|
37
|
-
|
38
|
-
def error(msg)
|
39
|
-
@logger.error msg
|
40
|
-
exit 1
|
41
|
-
end
|
42
|
-
|
43
|
-
def warn(msg)
|
44
|
-
@logger.warn msg
|
38
|
+
@compile_errors = nil
|
45
39
|
end
|
46
40
|
|
47
41
|
def self.execute(*args)
|
@@ -82,7 +76,7 @@ module ReVIEW
|
|
82
76
|
|
83
77
|
def execute(*args)
|
84
78
|
cmd_config, yamlfile = parse_opts(args)
|
85
|
-
error "#{yamlfile} not found." unless File.exist?(yamlfile)
|
79
|
+
error! "#{yamlfile} not found." unless File.exist?(yamlfile)
|
86
80
|
|
87
81
|
@config = ReVIEW::Configure.create(maker: 'webmaker',
|
88
82
|
yamlfile: yamlfile,
|
@@ -98,7 +92,7 @@ module ReVIEW
|
|
98
92
|
rescue ApplicationError => e
|
99
93
|
raise if @config['debug']
|
100
94
|
|
101
|
-
error
|
95
|
+
error! e.message
|
102
96
|
end
|
103
97
|
end
|
104
98
|
|
@@ -129,6 +123,7 @@ module ReVIEW
|
|
129
123
|
|
130
124
|
def build_body(basetmpdir, _yamlfile)
|
131
125
|
base_path = Pathname.new(@basedir)
|
126
|
+
@compile_errors = nil
|
132
127
|
@book.parts.each do |part|
|
133
128
|
if part.name.present?
|
134
129
|
if part.file?
|
@@ -143,6 +138,9 @@ module ReVIEW
|
|
143
138
|
|
144
139
|
part.chapters.each { |chap| build_chap(chap, base_path, basetmpdir, false) }
|
145
140
|
end
|
141
|
+
if @compile_errors
|
142
|
+
app_error 'compile error, No web files output.'
|
143
|
+
end
|
146
144
|
end
|
147
145
|
|
148
146
|
def build_part(part, basetmpdir, htmlfile)
|
@@ -191,9 +189,10 @@ module ReVIEW
|
|
191
189
|
|
192
190
|
begin
|
193
191
|
@converter.convert(filename, File.join(basetmpdir, htmlfile))
|
194
|
-
rescue => e
|
195
|
-
|
196
|
-
|
192
|
+
rescue ApplicationError => e
|
193
|
+
@compile_errors = true
|
194
|
+
error "compile error in #{filename} (#{e.class})"
|
195
|
+
error e.message
|
197
196
|
end
|
198
197
|
end
|
199
198
|
|