review 2.4.0 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +20 -5
- data/.travis.yml +2 -1
- data/NEWS.ja.md +93 -0
- data/NEWS.md +77 -0
- data/README.md +1 -1
- data/bin/review +38 -12
- data/bin/review-compile +106 -88
- data/bin/review-epubmaker +6 -1
- data/bin/review-init +21 -1
- data/bin/review-textmaker +16 -0
- data/doc/config.yml.sample +6 -1
- data/doc/format.ja.md +23 -0
- data/doc/format.md +20 -2
- data/doc/quickstart.ja.md +8 -4
- data/doc/quickstart.md +11 -8
- data/lib/review/book/base.rb +29 -18
- data/lib/review/book/index.rb +10 -5
- data/lib/review/builder.rb +58 -33
- data/lib/review/catalog.rb +30 -0
- data/lib/review/compiler.rb +53 -19
- data/lib/review/configure.rb +15 -14
- data/lib/review/epubmaker.rb +15 -4
- data/lib/review/htmlbuilder.rb +56 -24
- data/lib/review/idgxmlbuilder.rb +17 -7
- data/lib/review/latexbuilder.rb +113 -38
- data/lib/review/markdownbuilder.rb +12 -5
- data/lib/review/md2inaobuilder.rb +3 -1
- data/lib/review/pdfmaker.rb +23 -9
- data/lib/review/plaintextbuilder.rb +683 -0
- data/lib/review/rstbuilder.rb +30 -10
- data/lib/review/textmaker.rb +158 -0
- data/lib/review/textutils.rb +10 -1
- data/lib/review/topbuilder.rb +32 -417
- data/lib/review/version.rb +1 -1
- data/lib/review/webmaker.rb +29 -8
- data/review.gemspec +3 -4
- data/templates/html/layout-xhtml1.html.erb +0 -2
- data/templates/latex/layout.tex.erb +6 -4
- data/templates/web/html/layout-xhtml1.html.erb +0 -2
- data/test/book_test_helper.rb +1 -0
- data/test/run_test.rb +1 -1
- data/test/sample-book/src/Rakefile +19 -3
- data/test/syntax-book/Rakefile +19 -3
- data/test/test_catalog.rb +45 -0
- data/test/test_compiler.rb +8 -2
- data/test/test_htmlbuilder.rb +22 -0
- data/test/test_idgxmlbuilder.rb +22 -0
- data/test/test_index.rb +31 -0
- data/test/test_latexbuilder.rb +48 -16
- data/test/test_plaintextbuilder.rb +390 -0
- data/test/test_textutils.rb +2 -0
- data/test/test_topbuilder.rb +23 -1
- metadata +13 -7
data/bin/review-epubmaker
CHANGED
@@ -33,11 +33,16 @@ rescue OptionParser::ParseError => err
|
|
33
33
|
exit 1
|
34
34
|
end
|
35
35
|
|
36
|
-
if ARGV.size < 1
|
36
|
+
if ARGV.size < 1
|
37
37
|
puts opts.help
|
38
38
|
exit 1
|
39
39
|
end
|
40
40
|
|
41
|
+
unless File.exist?(ARGV[0])
|
42
|
+
@logger.error "#{File.basename($PROGRAM_NAME, '.*')}: #{ARGV[0]} not found."
|
43
|
+
exit 1
|
44
|
+
end
|
45
|
+
|
41
46
|
yaml_file = ARGV[0]
|
42
47
|
bookname = ARGV[1]
|
43
48
|
rv.produce(yaml_file, bookname)
|
data/bin/review-init
CHANGED
@@ -29,6 +29,7 @@ def main
|
|
29
29
|
opts.on('-f', '--force', 'generate files (except *.re) if directory has already existed.') { @force = true }
|
30
30
|
opts.on('-l', '--locale', 'generate locale.yml file.') { @locale = true }
|
31
31
|
opts.on('', '--epub-version VERSION', 'define EPUB version') { |version| @epub_version = version }
|
32
|
+
opts.on('', '--without-doc', "don't generate doc files") { @without_doc = true }
|
32
33
|
begin
|
33
34
|
opts.parse!
|
34
35
|
rescue OptionParser::ParseError => err
|
@@ -57,6 +58,7 @@ def main
|
|
57
58
|
generate_locale(dir) if @locale
|
58
59
|
generate_rakefile(dir)
|
59
60
|
generate_gemfile(dir)
|
61
|
+
generate_doc(dir) unless @without_doc
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
@@ -130,7 +132,18 @@ def generate_texmacro(dir)
|
|
130
132
|
end
|
131
133
|
|
132
134
|
def generate_rakefile(dir)
|
133
|
-
FileUtils.
|
135
|
+
FileUtils.mkdir_p dir + '/lib/tasks'
|
136
|
+
|
137
|
+
File.open(dir + '/Rakefile', 'w') do |file|
|
138
|
+
file.write <<-EOS
|
139
|
+
Dir.glob('lib/tasks/*.rake').each do |file|
|
140
|
+
load(file)
|
141
|
+
end
|
142
|
+
EOS
|
143
|
+
end
|
144
|
+
|
145
|
+
FileUtils.cp @review_dir + '/test/sample-book/src/Rakefile',
|
146
|
+
dir + '/lib/tasks/review.rake'
|
134
147
|
end
|
135
148
|
|
136
149
|
def generate_locale(dir)
|
@@ -148,4 +161,11 @@ EOS
|
|
148
161
|
end
|
149
162
|
end
|
150
163
|
|
164
|
+
def generate_doc(dir)
|
165
|
+
docdir = dir + '/doc'
|
166
|
+
FileUtils.mkdir_p docdir
|
167
|
+
md_files = Dir.glob(@review_dir + '/doc/*.md').map.to_a
|
168
|
+
FileUtils.cp md_files, docdir
|
169
|
+
end
|
170
|
+
|
151
171
|
main if File.basename($PROGRAM_NAME) == File.basename(__FILE__)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright (c) 2018 Kenshi Muto
|
3
|
+
#
|
4
|
+
# This program is free software.
|
5
|
+
# You can distribute or modify this program under the terms of
|
6
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
7
|
+
# For details of the GNU LGPL, see the file "COPYING".
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'pathname'
|
11
|
+
bindir = Pathname.new(__FILE__).realpath.dirname
|
12
|
+
$LOAD_PATH.unshift((bindir + '../lib').realpath)
|
13
|
+
|
14
|
+
require 'review/textmaker'
|
15
|
+
|
16
|
+
ReVIEW::TEXTMaker.execute(*ARGV)
|
data/doc/config.yml.sample
CHANGED
@@ -314,9 +314,14 @@ pdfmaker:
|
|
314
314
|
# 渡される引数1=作業用展開ディレクトリ、引数2=呼び出しを実行したディレクトリ
|
315
315
|
# hook_afterdvipdf: null
|
316
316
|
#
|
317
|
-
# 画像のscale=X.X
|
317
|
+
# 画像のscale=X.Xという指定を画像拡大縮小率からページ最大幅の相対倍率に変換する
|
318
318
|
# image_scale2width: true
|
319
319
|
#
|
320
|
+
# PDFやIllustratorファイル(.ai)の画像のBoudingBoxの抽出に指定のボックスを採用する
|
321
|
+
# cropbox(デフォルト), mediabox, artbox, trimbox, bleedboxから選択する。
|
322
|
+
# Illustrator CC以降のIllustratorファイルに対してはmediaboxを指定する必要がある
|
323
|
+
# bbox: mediabox
|
324
|
+
#
|
320
325
|
# 奥付を作成するか。trueを指定するとデフォルトの奥付、ファイル名を指定するとそれがcolophon.htmlとしてコピーされる
|
321
326
|
colophon: true
|
322
327
|
# pdfmaker:階層を使うものはここまで
|
data/doc/format.ja.md
CHANGED
@@ -337,6 +337,12 @@ V1 --> V6 --|
|
|
337
337
|
* `<id>` は //image[〜] の最初に入れた「〜」のことです(つまり、ID に日本語や空白交じりの文字を使ってしまうと、後で画像ファイル名の名前付けに苦労することになります!)。
|
338
338
|
* `<ext>` は Re:VIEW が自動で判別する拡張子です。ビルダによってサポートおよび優先する拡張子は異なります。
|
339
339
|
|
340
|
+
各ビルダでは、以下の拡張子から最初に発見した画像ファイルが使われます。
|
341
|
+
|
342
|
+
* HTMLBuilder (EPUBMaker、WEBMaker)、MARKDOWNBuilder: .png、.jpg、.jpeg、.gif、.svg
|
343
|
+
* LATEXBuilder (PDFMaker): .ai、.eps、.pdf、.tif、.tiff、.png、.bmp、.jpg、.jpeg、.gif
|
344
|
+
* それ以外のビルダ: .ai、.psd、.eps、.pdf、.tif、.tiff、.png、.bmp、.jpg、.jpeg、.gif、.svg
|
345
|
+
|
340
346
|
### インラインの画像挿入
|
341
347
|
|
342
348
|
段落途中などに画像を貼り込むには、インライン命令の `@<icon>{識別子}` を使います。ファイルの探索ルールは同じです。
|
@@ -569,6 +575,23 @@ LaTeX の数式が正常に整形されるかどうかは処理系に依存し
|
|
569
575
|
|
570
576
|
段落の行頭字下げを制御するタグとして、`//noindent` があります。HTML では `noindent` が `class` 属性に設定されます。
|
571
577
|
|
578
|
+
## 空行
|
579
|
+
|
580
|
+
1行ぶんの空行を明示して入れるには、`//blankline` を使います。
|
581
|
+
|
582
|
+
例:
|
583
|
+
|
584
|
+
```
|
585
|
+
この下に1行の空行が入る
|
586
|
+
|
587
|
+
//blankline
|
588
|
+
|
589
|
+
この下に2行の空行が入る
|
590
|
+
|
591
|
+
//blankline
|
592
|
+
//blankline
|
593
|
+
```
|
594
|
+
|
572
595
|
## 見出し参照
|
573
596
|
章に対する参照は、次の3つのインライン命令を利用できます。章 ID は、各章のファイル名から拡張子を除いたものです。たとえば `advanced.re` であれば `advanced` が章 ID です。
|
574
597
|
|
data/doc/format.md
CHANGED
@@ -380,6 +380,12 @@ The order of finding image is as follows. The first matched one is used.
|
|
380
380
|
* ``<id>`` is the ID of the first argument of `//image`. You should use only printable ASCII characters as ID.
|
381
381
|
* ``<ext>`` is file extensions of Re:VIEW. They are different by the builder you use.
|
382
382
|
|
383
|
+
For each builder, image files are searched in order of the following extensions, and the first hit file is adopted.
|
384
|
+
|
385
|
+
* HTMLBuilder (EPUBMaker, WEBMaker), MARKDOWNBuilder: .png, .jpg, .jpeg, .gif, .svg
|
386
|
+
* LATEXBuilder (PDFMaker): .ai, .eps, .pdf, .tif, .tiff, .png, .bmp, .jpg, .jpeg, .gif
|
387
|
+
* Other builders: .ai, .psd, .eps, .pdf, .tif, .tiff, .png, .bmp, .jpg, .jpeg, .gif, .svg
|
388
|
+
|
383
389
|
### Inline Images
|
384
390
|
|
385
391
|
When you want to use images in paragraph, you can use the inline command `@<icon>{ID}`. The order of finding images are same as `//image`.
|
@@ -610,12 +616,24 @@ Usage:
|
|
610
616
|
|
611
617
|
`//noindent` is a tag for spacing.
|
612
618
|
|
613
|
-
|
614
619
|
* `//noindent` : ingore indentation immediately following line. (in HTML, add `noindent` class)
|
615
620
|
|
621
|
+
## Blank line
|
622
|
+
|
623
|
+
`//blankline` put an empty line.
|
624
|
+
|
625
|
+
Usage:
|
626
|
+
|
627
|
+
```
|
628
|
+
Insert one blank line below.
|
616
629
|
|
617
|
-
|
630
|
+
//blankline
|
618
631
|
|
632
|
+
Insert two blank line below.
|
633
|
+
|
634
|
+
//blankline
|
635
|
+
//blankline
|
636
|
+
```
|
619
637
|
|
620
638
|
## Referring headings
|
621
639
|
|
data/doc/quickstart.ja.md
CHANGED
@@ -113,17 +113,19 @@ review-init コマンドによらず、独自に作業フォルダを作成し
|
|
113
113
|
|
114
114
|
テキストファイルの文字エンコーディングには、UTF-8 を使用してください。
|
115
115
|
|
116
|
-
### PDF 化と EPUB
|
116
|
+
### PDF 化と EPUB 化、プレインテキスト化
|
117
117
|
|
118
|
-
review-pdfmaker コマンドで PDF ブックの作成、review-epubmaker コマンドで EPUB
|
118
|
+
review-pdfmaker コマンドで PDF ブックの作成、review-epubmaker コマンドで EPUB ファイルの作成、review-textmaker コマンドでプレインテキストの作成ができます。
|
119
119
|
|
120
120
|
PDF を作成するには、TeXLive2012 以上の環境が必要です。EPUB を作成するには、rubyzip gem あるいは zip コマンドが必要です(MathML も使いたいときには、 [MathML ライブラリ](http://www.hinet.mydns.jp/?mathml.rb)も必要です)。
|
121
121
|
|
122
122
|
いずれのコマンドも、必要な設定情報を記した YAML 形式ファイルを引数に指定して実行します。review-init コマンドで作成した環境には、デフォルトで config.yml として用意されているので、これを利用します。
|
123
123
|
|
124
124
|
```bash
|
125
|
-
$ review-pdfmaker config.yml
|
126
|
-
$ review-epubmaker config.yml
|
125
|
+
$ review-pdfmaker config.yml ←PDFの作成
|
126
|
+
$ review-epubmaker config.yml ←EPUBの作成
|
127
|
+
$ review-textmaker config.yml ←テキストの作成(装飾情報あり)
|
128
|
+
$ review-textmaker -n config.yml ←テキストの作成(装飾情報なし)
|
127
129
|
```
|
128
130
|
|
129
131
|
rake コマンドを利用できるなら、次のように実行することもできます。
|
@@ -131,6 +133,8 @@ rake コマンドを利用できるなら、次のように実行することも
|
|
131
133
|
```bash
|
132
134
|
$ rake pdf ←PDFの作成
|
133
135
|
$ rake epub ←EPUBの作成
|
136
|
+
$ rake text ←テキストの作成(装飾情報あり)
|
137
|
+
$ rake plaintext ←テキストの作成(装飾情報なし)
|
134
138
|
```
|
135
139
|
|
136
140
|
config.yml のサンプルについては以下を参照してください。
|
data/doc/quickstart.md
CHANGED
@@ -115,28 +115,31 @@ The web site of Re:VIEW is @<tt>{https://reviewml.org/}.
|
|
115
115
|
|
116
116
|
You should use UTF-8 as encodings in text files.
|
117
117
|
|
118
|
-
### generating PDF and
|
118
|
+
### generating PDF, EPUB, and plain-text
|
119
119
|
|
120
|
-
You can generate a PDF file with `review-pdfmaker` command. Also you can generate an EPUB file with `review-epubmaker` command.
|
120
|
+
You can generate a PDF file with `review-pdfmaker` command. Also you can generate an EPUB file with `review-epubmaker` command, and can generate an plain-text file with 'review-textmaker' command.
|
121
121
|
|
122
122
|
To generate PDF, you should install TeXLive 2012 or later. To generate EPUB, you should install zip command.
|
123
123
|
When you want to use MathML, you should install [MathML library](http://www.hinet.mydns.jp/?mathml.rb)
|
124
124
|
|
125
|
-
|
125
|
+
Each maker need `config.yml`, configuration YAML files. `review-init` command generates `config.yml` in default.
|
126
126
|
|
127
127
|
```bash
|
128
|
-
$ review-pdfmaker config.yml
|
129
|
-
$ review-epubmaker config.yml
|
128
|
+
$ review-pdfmaker config.yml ## generate PDF
|
129
|
+
$ review-epubmaker config.yml ## generate EPUB
|
130
|
+
$ review-textmaker config.yml ## generate text with decoration
|
131
|
+
$ review-textmaker -n config.yml ## generate text without decoration
|
130
132
|
```
|
131
133
|
|
132
134
|
You also can generate them with Rake.
|
133
135
|
|
134
136
|
```bash
|
135
|
-
$ rake pdf
|
136
|
-
$ rake epub
|
137
|
+
$ rake pdf ## generate PDF
|
138
|
+
$ rake epub ## generate EPUB
|
139
|
+
$ rake text ## generate text with decoration
|
140
|
+
$ rake plaintext ## generate text without decoration
|
137
141
|
```
|
138
142
|
|
139
|
-
|
140
143
|
There is a sample YAML file [config.yml.sample](https://github.com/kmuto/review/blob/master/doc/config.yml.sample) in the same directory of this document.
|
141
144
|
|
142
145
|
### add chapters and modify them
|
data/lib/review/book/base.rb
CHANGED
@@ -32,11 +32,11 @@ module ReVIEW
|
|
32
32
|
|
33
33
|
def self.update_rubyenv(dir)
|
34
34
|
return if @basedir_seen.key?(dir)
|
35
|
-
if File.file?(
|
35
|
+
if File.file?(File.join(dir, 'review-ext.rb'))
|
36
36
|
if ENV['REVIEW_SAFE_MODE'].to_i & 2 > 0
|
37
37
|
ReVIEW.logger.warn 'review-ext.rb is prohibited in safe mode. ignored.'
|
38
38
|
else
|
39
|
-
Kernel.load File.expand_path(
|
39
|
+
Kernel.load File.expand_path(File.join(dir, 'review-ext.rb'))
|
40
40
|
end
|
41
41
|
end
|
42
42
|
@basedir_seen[dir] = true
|
@@ -183,8 +183,11 @@ module ReVIEW
|
|
183
183
|
def catalog
|
184
184
|
return @catalog if @catalog.present?
|
185
185
|
|
186
|
-
catalogfile_path =
|
187
|
-
@catalog = File.open(catalogfile_path) { |f| Catalog.new(f) } if File.file? catalogfile_path
|
186
|
+
catalogfile_path = filename_join(@basedir, config['catalogfile'])
|
187
|
+
@catalog = File.open(catalogfile_path, 'r:BOM|utf-8') { |f| Catalog.new(f) } if File.file? catalogfile_path
|
188
|
+
if @catalog
|
189
|
+
@catalog.validate!(basedir)
|
190
|
+
end
|
188
191
|
@catalog
|
189
192
|
end
|
190
193
|
|
@@ -226,7 +229,7 @@ module ReVIEW
|
|
226
229
|
if catalog
|
227
230
|
@read_part = catalog.parts
|
228
231
|
else
|
229
|
-
@read_part = File.read(
|
232
|
+
@read_part = File.read(File.join(@basedir, config['part_file']))
|
230
233
|
end
|
231
234
|
end
|
232
235
|
|
@@ -234,23 +237,26 @@ module ReVIEW
|
|
234
237
|
if catalog
|
235
238
|
catalog.parts.present?
|
236
239
|
else
|
237
|
-
File.exist?(
|
240
|
+
File.exist?(File.join(@basedir, config['part_file']))
|
238
241
|
end
|
239
242
|
end
|
240
243
|
|
241
244
|
def read_bib
|
242
|
-
File.read(
|
245
|
+
File.read(File.join(@basedir, bib_file))
|
243
246
|
end
|
244
247
|
|
245
248
|
def bib_exist?
|
246
|
-
File.exist?(
|
249
|
+
File.exist?(File.join(@basedir, bib_file))
|
247
250
|
end
|
248
251
|
|
249
252
|
def prefaces
|
250
|
-
|
253
|
+
if catalog
|
254
|
+
return mkpart_from_namelist(catalog.predef.split("\n"))
|
255
|
+
end
|
251
256
|
|
252
257
|
begin
|
253
|
-
|
258
|
+
predef_file = filename_join(@basedir, config['predef_file'])
|
259
|
+
mkpart_from_namelistfile(predef_file) if File.file?(predef_file)
|
254
260
|
rescue FileNotFound => err
|
255
261
|
raise FileNotFound, "preface #{err.message}"
|
256
262
|
end
|
@@ -264,7 +270,8 @@ module ReVIEW
|
|
264
270
|
end
|
265
271
|
|
266
272
|
begin
|
267
|
-
|
273
|
+
postdef_file = filename_join(@basedir, config['postdef_file'])
|
274
|
+
mkpart_from_namelistfile(postdef_file) if File.file?(postdef_file)
|
268
275
|
rescue FileNotFound => err
|
269
276
|
raise FileNotFound, "postscript #{err.message}"
|
270
277
|
end
|
@@ -301,12 +308,12 @@ module ReVIEW
|
|
301
308
|
return catalog.parts_with_chaps.map do |entry|
|
302
309
|
if entry.is_a?(Hash)
|
303
310
|
chaps = entry.values.first.map do |chap|
|
304
|
-
chap = Chapter.new(self, num += 1, chap,
|
311
|
+
chap = Chapter.new(self, num += 1, chap, File.join(@basedir, chap))
|
305
312
|
chap
|
306
313
|
end
|
307
314
|
Part.new(self, part += 1, chaps, read_part.split("\n")[part - 1])
|
308
315
|
else
|
309
|
-
chap = Chapter.new(self, num += 1, entry,
|
316
|
+
chap = Chapter.new(self, num += 1, entry, File.join(@basedir, entry))
|
310
317
|
if chap.number
|
311
318
|
num = chap.number
|
312
319
|
else
|
@@ -320,7 +327,7 @@ module ReVIEW
|
|
320
327
|
chap = read_chaps.
|
321
328
|
strip.lines.map(&:strip).join("\n").split(/\n{2,}/).
|
322
329
|
map do |part_chunk|
|
323
|
-
chaps = part_chunk.split.map { |chapid| Chapter.new(self, num += 1, chapid,
|
330
|
+
chaps = part_chunk.split.map { |chapid| Chapter.new(self, num += 1, chapid, File.join(@basedir, chapid)) }
|
324
331
|
if part_exist? && read_part.split("\n").size > part
|
325
332
|
Part.new(self, part += 1, chaps, read_part.split("\n")[part - 1])
|
326
333
|
else
|
@@ -352,14 +359,14 @@ module ReVIEW
|
|
352
359
|
|
353
360
|
def mkchap(name, number = nil)
|
354
361
|
name += ext if File.extname(name).empty?
|
355
|
-
path =
|
362
|
+
path = File.join(@basedir, name)
|
356
363
|
raise FileNotFound, "file not exist: #{path}" unless File.file?(path)
|
357
364
|
Chapter.new(self, number, name, path)
|
358
365
|
end
|
359
366
|
|
360
367
|
def mkchap_ifexist(name, idx = nil)
|
361
368
|
name += ext if File.extname(name).empty?
|
362
|
-
path =
|
369
|
+
path = File.join(@basedir, name)
|
363
370
|
if File.file?(path)
|
364
371
|
idx += 1 if idx
|
365
372
|
Chapter.new(self, idx, name, path)
|
@@ -369,10 +376,10 @@ module ReVIEW
|
|
369
376
|
def read_file(filename)
|
370
377
|
unless @warn_old_files[filename]
|
371
378
|
@warn_old_files[filename] = true
|
372
|
-
warn "!!! #{filename} is obsoleted. please use catalog.yml." if caller.none? { |item| item =~ %r{/review/test/test_} }
|
379
|
+
ReVIEW.logger.warn "!!! #{filename} is obsoleted. please use catalog.yml." if caller.none? { |item| item =~ %r{/review/test/test_} }
|
373
380
|
end
|
374
381
|
res = ''
|
375
|
-
File.open(
|
382
|
+
File.open(filename_join(@basedir, filename), 'r:BOM|utf-8') do |f|
|
376
383
|
f.each_line do |line|
|
377
384
|
next if /\A#/ =~ line
|
378
385
|
line.gsub!(/#.*\Z/, '')
|
@@ -385,6 +392,10 @@ module ReVIEW
|
|
385
392
|
rescue Errno::EISDIR
|
386
393
|
''
|
387
394
|
end
|
395
|
+
|
396
|
+
def filename_join(*args)
|
397
|
+
File.join(args.reject(&:nil?))
|
398
|
+
end
|
388
399
|
end
|
389
400
|
end
|
390
401
|
end
|
data/lib/review/book/index.rb
CHANGED
@@ -88,9 +88,11 @@ module ReVIEW
|
|
88
88
|
|
89
89
|
def number(id)
|
90
90
|
chapter = @index.fetch(id)
|
91
|
-
|
92
|
-
|
93
|
-
|
91
|
+
begin
|
92
|
+
chapter.format_number
|
93
|
+
rescue # part
|
94
|
+
I18n.t('part', chapter.number)
|
95
|
+
end
|
94
96
|
end
|
95
97
|
|
96
98
|
def title(id)
|
@@ -280,6 +282,7 @@ module ReVIEW
|
|
280
282
|
headlines = []
|
281
283
|
inside_column = false
|
282
284
|
inside_block = nil
|
285
|
+
column_level = -1
|
283
286
|
src.each do |line|
|
284
287
|
if line =~ %r{\A//[a-z]+.*\{\Z}
|
285
288
|
inside_block = true
|
@@ -293,19 +296,20 @@ module ReVIEW
|
|
293
296
|
|
294
297
|
m = HEADLINE_PATTERN.match(line)
|
295
298
|
next if m.nil? || m[1].size > 10 # Ignore too deep index
|
296
|
-
next if m[4].strip.empty? # no title
|
297
299
|
index = m[1].size - 2
|
298
300
|
|
299
301
|
# column
|
300
302
|
if m[2] == 'column'
|
301
303
|
inside_column = true
|
304
|
+
column_level = index
|
302
305
|
next
|
303
306
|
elsif m[2] == '/column'
|
304
307
|
inside_column = false
|
305
308
|
next
|
306
309
|
end
|
307
|
-
inside_column = false if indexs.blank? || index <=
|
310
|
+
inside_column = false if indexs.blank? || index <= column_level
|
308
311
|
next if inside_column
|
312
|
+
next if m[4].strip.empty? # no title
|
309
313
|
|
310
314
|
next unless index >= 0
|
311
315
|
if indexs.size > (index + 1)
|
@@ -324,6 +328,7 @@ module ReVIEW
|
|
324
328
|
@items = items
|
325
329
|
@chap = chap
|
326
330
|
@index = {}
|
331
|
+
@logger = ReVIEW.logger
|
327
332
|
items.each do |i|
|
328
333
|
@logger.warn "warning: duplicate ID: #{i.id}" if @index[i.id]
|
329
334
|
@index[i.id] = i
|
data/lib/review/builder.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2002-
|
1
|
+
# Copyright (c) 2002-2018 Minero Aoki, Kenshi Muto
|
2
2
|
#
|
3
3
|
# This program is free software.
|
4
4
|
# You can distribute or modify this program under the terms of
|
@@ -12,6 +12,7 @@ require 'review/compiler'
|
|
12
12
|
require 'review/sec_counter'
|
13
13
|
require 'stringio'
|
14
14
|
require 'cgi'
|
15
|
+
require 'fileutils'
|
15
16
|
|
16
17
|
module ReVIEW
|
17
18
|
class Builder
|
@@ -27,10 +28,13 @@ module ReVIEW
|
|
27
28
|
nil
|
28
29
|
end
|
29
30
|
|
31
|
+
attr_accessor :doc_status
|
32
|
+
|
30
33
|
def initialize(strict = false, *args)
|
31
34
|
@strict = strict
|
32
35
|
@output = nil
|
33
36
|
@logger = ReVIEW.logger
|
37
|
+
@doc_status = {}
|
34
38
|
builder_init(*args)
|
35
39
|
end
|
36
40
|
|
@@ -148,8 +152,12 @@ module ReVIEW
|
|
148
152
|
return if rows.empty?
|
149
153
|
table_begin rows.first.size
|
150
154
|
if sepidx
|
151
|
-
sepidx.times
|
152
|
-
|
155
|
+
sepidx.times do
|
156
|
+
tr(rows.shift.map { |s| th(s) })
|
157
|
+
end
|
158
|
+
rows.each do |cols|
|
159
|
+
tr(cols.map { |s| td(s) })
|
160
|
+
end
|
153
161
|
else
|
154
162
|
rows.each do |cols|
|
155
163
|
h, *cs = *cols
|
@@ -160,9 +168,15 @@ module ReVIEW
|
|
160
168
|
end
|
161
169
|
|
162
170
|
def adjust_n_cols(rows)
|
163
|
-
rows.each
|
171
|
+
rows.each do |cols|
|
172
|
+
while cols.last and cols.last.strip.empty?
|
173
|
+
cols.pop
|
174
|
+
end
|
175
|
+
end
|
164
176
|
n_maxcols = rows.map(&:size).max
|
165
|
-
rows.each
|
177
|
+
rows.each do |cols|
|
178
|
+
cols.concat [''] * (n_maxcols - cols.size)
|
179
|
+
end
|
166
180
|
rows
|
167
181
|
end
|
168
182
|
private :adjust_n_cols
|
@@ -183,6 +197,10 @@ module ReVIEW
|
|
183
197
|
# footnote_end
|
184
198
|
# end
|
185
199
|
|
200
|
+
def blankline
|
201
|
+
puts ''
|
202
|
+
end
|
203
|
+
|
186
204
|
def compile_inline(s)
|
187
205
|
@compiler.text(s)
|
188
206
|
end
|
@@ -191,35 +209,30 @@ module ReVIEW
|
|
191
209
|
compile_inline @book.chapter_index.display_string(id)
|
192
210
|
rescue KeyError
|
193
211
|
error "unknown chapter: #{id}"
|
194
|
-
nofunc_text("[UnknownChapter:#{id}]")
|
195
212
|
end
|
196
213
|
|
197
214
|
def inline_chap(id)
|
198
215
|
@book.chapter_index.number(id)
|
199
216
|
rescue KeyError
|
200
217
|
error "unknown chapter: #{id}"
|
201
|
-
nofunc_text("[UnknownChapter:#{id}]")
|
202
218
|
end
|
203
219
|
|
204
220
|
def inline_title(id)
|
205
221
|
compile_inline @book.chapter_index.title(id)
|
206
222
|
rescue KeyError
|
207
223
|
error "unknown chapter: #{id}"
|
208
|
-
nofunc_text("[UnknownChapter:#{id}]")
|
209
224
|
end
|
210
225
|
|
211
226
|
def inline_list(id)
|
212
227
|
"#{I18n.t('list')}#{@chapter.list(id).number}"
|
213
228
|
rescue KeyError
|
214
229
|
error "unknown list: #{id}"
|
215
|
-
nofunc_text("[UnknownList:#{id}]")
|
216
230
|
end
|
217
231
|
|
218
232
|
def inline_img(id)
|
219
233
|
"#{I18n.t('image')}#{@chapter.image(id).number}"
|
220
234
|
rescue KeyError
|
221
235
|
error "unknown image: #{id}"
|
222
|
-
nofunc_text("[UnknownImage:#{id}]")
|
223
236
|
end
|
224
237
|
|
225
238
|
def inline_imgref(id)
|
@@ -236,14 +249,12 @@ module ReVIEW
|
|
236
249
|
"#{I18n.t('table')}#{@chapter.table(id).number}"
|
237
250
|
rescue KeyError
|
238
251
|
error "unknown table: #{id}"
|
239
|
-
nofunc_text("[UnknownTable:#{id}]")
|
240
252
|
end
|
241
253
|
|
242
254
|
def inline_fn(id)
|
243
255
|
@chapter.footnote(id).content
|
244
256
|
rescue KeyError
|
245
257
|
error "unknown footnote: #{id}"
|
246
|
-
nofunc_text("[UnknownFootnote:#{id}]")
|
247
258
|
end
|
248
259
|
|
249
260
|
def inline_bou(str)
|
@@ -252,8 +263,12 @@ module ReVIEW
|
|
252
263
|
|
253
264
|
def inline_ruby(arg)
|
254
265
|
base, *ruby = *arg.scan(/(?:(?:(?:\\\\)*\\,)|[^,\\]+)+/)
|
255
|
-
|
256
|
-
|
266
|
+
if base
|
267
|
+
base = base.gsub(/\\,/, ',')
|
268
|
+
end
|
269
|
+
if ruby
|
270
|
+
ruby = ruby.join(',').gsub(/\\,/, ',')
|
271
|
+
end
|
257
272
|
compile_ruby(base, ruby)
|
258
273
|
end
|
259
274
|
|
@@ -265,7 +280,9 @@ module ReVIEW
|
|
265
280
|
def inline_href(arg)
|
266
281
|
url, label = *arg.scan(/(?:(?:(?:\\\\)*\\,)|[^,\\]+)+/).map(&:lstrip)
|
267
282
|
url = url.gsub(/\\,/, ',').strip
|
268
|
-
|
283
|
+
if label
|
284
|
+
label = label.gsub(/\\,/, ',').strip
|
285
|
+
end
|
269
286
|
compile_href(url, label)
|
270
287
|
end
|
271
288
|
|
@@ -284,20 +301,23 @@ module ReVIEW
|
|
284
301
|
|
285
302
|
def inline_hd(id)
|
286
303
|
m = /\A([^|]+)\|(.+)/.match(id)
|
287
|
-
|
304
|
+
if m && m[1]
|
305
|
+
chapter = @book.contents.detect { |chap| chap.id == m[1] }
|
306
|
+
end
|
288
307
|
if chapter
|
289
308
|
inline_hd_chap(chapter, m[2])
|
290
309
|
else
|
291
310
|
inline_hd_chap(@chapter, id)
|
292
311
|
end
|
293
312
|
rescue KeyError
|
294
|
-
error "unknown
|
295
|
-
nofunc_text("[UnknownHeader:#{id}]")
|
313
|
+
error "unknown headline: #{id}"
|
296
314
|
end
|
297
315
|
|
298
316
|
def inline_column(id)
|
299
317
|
m = /\A([^|]+)\|(.+)/.match(id)
|
300
|
-
|
318
|
+
if m && m[1]
|
319
|
+
chapter = @book.chapters.detect { |chap| chap.id == m[1] }
|
320
|
+
end
|
301
321
|
if chapter
|
302
322
|
inline_column_chap(chapter, m[2])
|
303
323
|
else
|
@@ -305,7 +325,6 @@ module ReVIEW
|
|
305
325
|
end
|
306
326
|
rescue KeyError
|
307
327
|
error "unknown column: #{id}"
|
308
|
-
nofunc_text("[UnknownColumn:#{id}]")
|
309
328
|
end
|
310
329
|
|
311
330
|
def inline_column_chap(chapter, id)
|
@@ -324,7 +343,9 @@ module ReVIEW
|
|
324
343
|
if matched = str.match(/\|(.*?)\|(.*)/)
|
325
344
|
builders = matched[1].split(',').map { |i| i.gsub(/\s/, '') }
|
326
345
|
c = target_name
|
327
|
-
|
346
|
+
if builders.include?(c)
|
347
|
+
print matched[2].gsub('\\n', "\n")
|
348
|
+
end
|
328
349
|
else
|
329
350
|
print str.gsub('\\n', "\n")
|
330
351
|
end
|
@@ -345,8 +366,11 @@ module ReVIEW
|
|
345
366
|
end
|
346
367
|
|
347
368
|
def error(msg)
|
348
|
-
|
349
|
-
|
369
|
+
if msg =~ /:\d+: error: /
|
370
|
+
raise ApplicationError, msg
|
371
|
+
else
|
372
|
+
raise ApplicationError, "#{@location}: error: #{msg}"
|
373
|
+
end
|
350
374
|
end
|
351
375
|
|
352
376
|
def handle_metric(str)
|
@@ -374,15 +398,20 @@ module ReVIEW
|
|
374
398
|
|
375
399
|
def get_chap(chapter = @chapter)
|
376
400
|
if @book.config['secnolevel'] > 0 && !chapter.number.nil? && !chapter.number.to_s.empty?
|
377
|
-
|
378
|
-
|
401
|
+
if chapter.is_a?(ReVIEW::Book::Part)
|
402
|
+
return I18n.t('part_short', chapter.number)
|
403
|
+
else
|
404
|
+
return chapter.format_number(nil)
|
405
|
+
end
|
379
406
|
end
|
380
407
|
nil
|
381
408
|
end
|
382
409
|
|
383
410
|
def extract_chapter_id(chap_ref)
|
384
411
|
m = /\A([\w+-]+)\|(.+)/.match(chap_ref)
|
385
|
-
|
412
|
+
if m
|
413
|
+
return [@book.contents.detect { |chap| chap.id == m[1] }, m[2]]
|
414
|
+
end
|
386
415
|
[@chapter, chap_ref]
|
387
416
|
end
|
388
417
|
|
@@ -401,7 +430,7 @@ module ReVIEW
|
|
401
430
|
def graph(lines, id, command, caption = nil)
|
402
431
|
c = target_name
|
403
432
|
dir = File.join(@book.basedir, @book.image_dir, c)
|
404
|
-
|
433
|
+
FileUtils.mkdir_p(dir)
|
405
434
|
file = "#{id}.#{image_ext}"
|
406
435
|
file_path = File.join(dir, file)
|
407
436
|
|
@@ -410,7 +439,7 @@ module ReVIEW
|
|
410
439
|
graphviz: "echo '#{line}' | dot -T#{image_ext} -o#{file_path}",
|
411
440
|
gnuplot: %Q(echo 'set terminal ) +
|
412
441
|
"#{image_ext == 'eps' ? 'postscript eps' : image_ext}\n" +
|
413
|
-
%Q(
|
442
|
+
%Q( set output "#{file_path}"\n#{line}' | gnuplot),
|
414
443
|
blockdiag: "echo '#{line}' " +
|
415
444
|
"| blockdiag -a -T #{image_ext} -o #{file_path} /dev/stdin",
|
416
445
|
aafigure: "echo '#{line}' | aafigure -t#{image_ext} -o#{file_path}"
|
@@ -428,11 +457,7 @@ module ReVIEW
|
|
428
457
|
end
|
429
458
|
|
430
459
|
def inline_include(file_name)
|
431
|
-
compile_inline File.read(file_name)
|
432
|
-
end
|
433
|
-
|
434
|
-
def include(file_name)
|
435
|
-
File.foreach(file_name) { |line| paragraph([line]) }
|
460
|
+
compile_inline File.read(file_name, mode: 'rt:BOM|utf-8').chomp
|
436
461
|
end
|
437
462
|
|
438
463
|
def ul_item_begin(lines)
|