pseudohikiparser 0.0.3 → 0.0.4.develop
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 +4 -4
- data/README.ja.md +132 -8
- data/README.md +134 -10
- data/lib/htmlelement.rb +42 -42
- data/lib/pseudohiki/autolink.rb +60 -0
- data/lib/pseudohiki/blockparser.rb +233 -125
- data/lib/pseudohiki/converter.rb +140 -81
- data/lib/pseudohiki/htmlformat.rb +156 -82
- data/lib/pseudohiki/htmlplugin.rb +9 -28
- data/lib/pseudohiki/inlineparser.rb +21 -24
- data/lib/pseudohiki/markdownformat.rb +94 -98
- data/lib/pseudohiki/plaintextformat.rb +67 -57
- data/lib/pseudohiki/sinatra_helpers.rb +23 -0
- data/lib/pseudohiki/treestack.rb +4 -4
- data/lib/pseudohiki/utils.rb +1 -2
- data/lib/pseudohiki/version.rb +1 -1
- data/lib/pseudohikiparser.rb +49 -28
- data/test/test_autolink.rb +104 -0
- data/test/test_blockparser.rb +78 -11
- data/test/test_htmlformat.rb +436 -0
- data/test/test_htmlplugin.rb +43 -0
- data/test/test_markdownformat.rb +156 -3
- data/test/test_plaintextformat.rb +85 -0
- data/test/test_pseudohiki2html.rb +82 -0
- data/test/test_pseudohikiparser.rb +286 -0
- data/test/test_utils.rb +1 -1
- metadata +22 -4
data/lib/pseudohiki/converter.rb
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
require 'optparse'
|
5
5
|
require 'erb'
|
6
6
|
require 'pseudohiki/blockparser'
|
7
|
+
require 'pseudohiki/autolink'
|
7
8
|
require 'pseudohiki/htmlformat'
|
8
9
|
require 'pseudohiki/plaintextformat'
|
9
10
|
require 'pseudohiki/markdownformat'
|
@@ -19,9 +20,13 @@ module PseudoHiki
|
|
19
20
|
|
20
21
|
def initialize(options)
|
21
22
|
@options = options
|
22
|
-
@is_toc_item_pat =
|
23
|
+
@is_toc_item_pat = proc_for_is_toc_item_pat
|
24
|
+
end
|
25
|
+
|
26
|
+
def proc_for_is_toc_item_pat
|
27
|
+
proc do |node|
|
23
28
|
node.kind_of?(PseudoHiki::BlockParser::HeadingLeaf) and
|
24
|
-
(2..3).include? node.
|
29
|
+
(2..3).include? node.level and
|
25
30
|
node.node_id
|
26
31
|
end
|
27
32
|
end
|
@@ -40,18 +45,14 @@ module PseudoHiki
|
|
40
45
|
|
41
46
|
def create_plain_table_of_contents(tree)
|
42
47
|
toc_lines = collect_nodes_for_table_of_contents(tree).map do |toc_node|
|
43
|
-
('*' * toc_node.
|
48
|
+
('*' * toc_node.level) + to_plain(toc_node)
|
44
49
|
end
|
45
50
|
|
46
51
|
@options.formatter.format(BlockParser.parse(toc_lines))
|
47
52
|
end
|
48
53
|
|
49
54
|
def create_html_table_of_contents(tree)
|
50
|
-
|
51
|
-
"%s[[%s|#%s]]"%['*'*toc_node.nominal_level, to_plain(toc_node).lstrip, toc_node.node_id.upcase]
|
52
|
-
end
|
53
|
-
|
54
|
-
@options.formatter.format(BlockParser.parse(toc_lines)).tap do |toc|
|
55
|
+
@options.formatter.format(create_html_toc_tree(tree)).tap do |toc|
|
55
56
|
toc.traverse do |element|
|
56
57
|
if element.kind_of? HtmlElement and element.tagname == "a"
|
57
58
|
element["title"] = "toc_item: " + element.children.join.chomp
|
@@ -60,13 +61,26 @@ module PseudoHiki
|
|
60
61
|
end
|
61
62
|
end
|
62
63
|
|
64
|
+
def create_html_toc_tree(tree, newline=nil)
|
65
|
+
toc_lines = collect_nodes_for_table_of_contents(tree).map do |line|
|
66
|
+
format("%s[[%s|#%s]]#{newline}",
|
67
|
+
'*' * line.level,
|
68
|
+
to_plain(line).lstrip,
|
69
|
+
line.node_id.upcase)
|
70
|
+
end
|
71
|
+
BlockParser.parse(toc_lines)
|
72
|
+
end
|
73
|
+
|
63
74
|
def gfm_id(heading_node)
|
64
|
-
MarkDownFormat.
|
75
|
+
MarkDownFormat.convert_into_gfm_id_format(to_plain(heading_node).strip)
|
65
76
|
end
|
66
77
|
|
67
78
|
def create_gfm_table_of_contents(tree)
|
68
79
|
toc_lines = collect_nodes_for_table_of_contents(tree).map do |toc_node|
|
69
|
-
"%s[[%s|#%s]]#{$/}"
|
80
|
+
format("%s[[%s|#%s]]#{$/}",
|
81
|
+
'*' * toc_node.level,
|
82
|
+
to_plain(toc_node).strip,
|
83
|
+
gfm_id(toc_node))
|
70
84
|
end
|
71
85
|
|
72
86
|
@options.formatter.format(BlockParser.parse(toc_lines))
|
@@ -74,7 +88,8 @@ module PseudoHiki
|
|
74
88
|
|
75
89
|
def create_table_of_contents(tree)
|
76
90
|
return "" unless @options[:toc]
|
77
|
-
|
91
|
+
gfm_chosen = @options[:html_version].version == "gfm"
|
92
|
+
return create_gfm_table_of_contents(tree) if gfm_chosen
|
78
93
|
return create_plain_table_of_contents(tree) unless @options.html_template
|
79
94
|
create_html_table_of_contents(tree)
|
80
95
|
end
|
@@ -90,27 +105,37 @@ module PseudoHiki
|
|
90
105
|
def create_plain_main(toc, body, h1)
|
91
106
|
contents = [body]
|
92
107
|
contents.unshift toc unless toc.empty?
|
93
|
-
|
108
|
+
if title = @options[:toc]
|
109
|
+
toc_title = @options.formatter.format(BlockParser.parse("!!" + title))
|
110
|
+
contents.unshift toc_title
|
111
|
+
end
|
94
112
|
contents.unshift h1 unless h1.empty?
|
95
113
|
contents.join($/)
|
96
114
|
end
|
97
115
|
|
98
116
|
def create_html_main(toc, body, h1)
|
99
117
|
return nil unless @options[:toc]
|
100
|
-
toc_container = formatter.create_element("section").tap do |element|
|
101
|
-
element["id"] = "toc"
|
102
|
-
element.push formatter.create_element("h2", @options[:toc]) unless @options[:toc].empty?
|
103
|
-
element.push toc
|
104
|
-
end
|
105
|
-
contents_container = formatter.create_element("section").tap do |element|
|
106
|
-
element["id"] = "contents"
|
107
|
-
element.push body
|
108
|
-
end
|
109
118
|
main = formatter.create_element("section").tap do |element|
|
110
119
|
element["id"] = "main"
|
111
120
|
element.push h1 unless h1.empty?
|
112
|
-
element.push
|
113
|
-
element.push
|
121
|
+
element.push create_html_toc_container(toc)
|
122
|
+
element.push create_html_contents_container(body)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def create_html_toc_container(toc)
|
127
|
+
formatter.create_element("section").tap do |elm|
|
128
|
+
elm["id"] = "toc"
|
129
|
+
title = @options[:toc]
|
130
|
+
elm.push formatter.create_element("h2", title) unless title.empty?
|
131
|
+
elm.push toc
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def create_html_contents_container(body)
|
136
|
+
formatter.create_element("section").tap do |elm|
|
137
|
+
elm["id"] = "contents"
|
138
|
+
elm.push body
|
114
139
|
end
|
115
140
|
end
|
116
141
|
|
@@ -140,14 +165,17 @@ module PseudoHiki
|
|
140
165
|
body = compose_body(tree)
|
141
166
|
title = @options.title
|
142
167
|
main = create_main(toc, body, h1)
|
168
|
+
choose_template(main, body, binding)
|
169
|
+
end
|
143
170
|
|
171
|
+
def choose_template(main, body, current_binding)
|
144
172
|
if @options[:template]
|
145
|
-
|
146
|
-
html = erb.result(binding)
|
173
|
+
html = ERB.new(@options.read_template_file).result(current_binding)
|
147
174
|
else
|
148
175
|
html = @options.create_html_template_with_current_options
|
149
|
-
|
150
|
-
html.push
|
176
|
+
embed_css = @options[:embed_css]
|
177
|
+
html.head.push create_style(embed_css) if embed_css
|
178
|
+
html.push main || body
|
151
179
|
end
|
152
180
|
|
153
181
|
html
|
@@ -161,8 +189,7 @@ module PseudoHiki
|
|
161
189
|
MDFormat = MarkDownFormat.create
|
162
190
|
GFMFormat = MarkDownFormat.create(:gfm_style => true)
|
163
191
|
|
164
|
-
|
165
|
-
end
|
192
|
+
Formatter = Struct.new(:version, :formatter, :template, :ext, :opt_pat)
|
166
193
|
|
167
194
|
VERSIONS = [
|
168
195
|
["html4", HtmlFormat, HtmlTemplate, ".html", /^h/io],
|
@@ -192,6 +219,21 @@ module PseudoHiki
|
|
192
219
|
'latin1' => LATIN1
|
193
220
|
}
|
194
221
|
|
222
|
+
@default_options = {
|
223
|
+
:html_version => VERSIONS[0],
|
224
|
+
:lang => 'en',
|
225
|
+
:encoding => 'utf8',
|
226
|
+
:title => nil,
|
227
|
+
:css => "default.css",
|
228
|
+
:embed_css => nil,
|
229
|
+
:base => nil,
|
230
|
+
:template => nil,
|
231
|
+
:output => nil,
|
232
|
+
:force => false,
|
233
|
+
:toc => nil,
|
234
|
+
:split_main_heading => false
|
235
|
+
}
|
236
|
+
|
195
237
|
attr_accessor :need_output_file, :default_title
|
196
238
|
attr_reader :input_file_basename
|
197
239
|
|
@@ -200,23 +242,16 @@ module PseudoHiki
|
|
200
242
|
input.rewind unless BOM == bom
|
201
243
|
end
|
202
244
|
|
245
|
+
def self.default_options
|
246
|
+
@default_options.dup
|
247
|
+
end
|
248
|
+
|
203
249
|
def initialize(options=nil)
|
204
|
-
@options = options||
|
205
|
-
:html_version => VERSIONS[0],
|
206
|
-
:lang => 'en',
|
207
|
-
:encoding => 'utf8',
|
208
|
-
:title => nil,
|
209
|
-
:css => "default.css",
|
210
|
-
:embed_css => nil,
|
211
|
-
:base => nil,
|
212
|
-
:template => nil,
|
213
|
-
:output => nil,
|
214
|
-
:force => false,
|
215
|
-
:toc => nil,
|
216
|
-
:split_main_heading => false
|
217
|
-
}
|
250
|
+
@options = options || self.class.default_options
|
218
251
|
@written_option_pat = {}
|
219
|
-
@options.keys.each
|
252
|
+
@options.keys.each do |opt|
|
253
|
+
@written_option_pat[opt] = /^\/\/#{opt}:\s*(.*)$/
|
254
|
+
end
|
220
255
|
end
|
221
256
|
|
222
257
|
def [](key)
|
@@ -250,18 +285,18 @@ module PseudoHiki
|
|
250
285
|
def base
|
251
286
|
base_dir = self[:base]
|
252
287
|
if base_dir and base_dir !~ /[\/\\]\.*$/o
|
253
|
-
base_dir = File.join(base_dir,".")
|
254
|
-
base_dir = "file:///"+base_dir if base_dir !~ /^\./o and win32?
|
288
|
+
base_dir = File.join(base_dir, ".")
|
289
|
+
base_dir = "file:///" + base_dir if base_dir !~ /^\./o and win32?
|
255
290
|
end
|
256
291
|
base_dir
|
257
292
|
end
|
258
293
|
|
259
294
|
def title
|
260
|
-
self[:title]
|
295
|
+
self[:title] || @default_title || "-"
|
261
296
|
end
|
262
297
|
|
263
298
|
def read_template_file
|
264
|
-
File.read(File.expand_path(self[:template]), :encoding =>
|
299
|
+
File.read(File.expand_path(self[:template]), :encoding => charset)
|
265
300
|
end
|
266
301
|
|
267
302
|
def set_html_version(version)
|
@@ -272,7 +307,8 @@ module PseudoHiki
|
|
272
307
|
self[:html_version] = v if v.opt_pat =~ version
|
273
308
|
end
|
274
309
|
end
|
275
|
-
STDERR.puts "\"#{version}\" is an invalid option for --format-version. \
|
310
|
+
STDERR.puts "\"#{version}\" is an invalid option for --format-version. \
|
311
|
+
\"#{self[:html_version].version}\" is chosen instead."
|
276
312
|
end
|
277
313
|
|
278
314
|
def set_html_encoding(given_opt)
|
@@ -282,7 +318,8 @@ module PseudoHiki
|
|
282
318
|
ENCODING_REGEXP.each do |pat, encoding|
|
283
319
|
self[:encoding] = encoding if pat =~ given_opt
|
284
320
|
end
|
285
|
-
STDERR.puts "\"#{self[:encoding]}\" is chosen as an encoding system,
|
321
|
+
STDERR.puts "\"#{self[:encoding]}\" is chosen as an encoding system, \
|
322
|
+
instead of \"#{given_opt}\"."
|
286
323
|
end
|
287
324
|
end
|
288
325
|
|
@@ -294,67 +331,80 @@ module PseudoHiki
|
|
294
331
|
end
|
295
332
|
|
296
333
|
def parse_command_line_options
|
297
|
-
OptionParser.new("
|
298
|
-
|
334
|
+
OptionParser.new("USAGE: #{File.basename($0)} [OPTION]... [FILE]...
|
335
|
+
Convert texts written in a Hiki-like notation into another format.") do |opt|
|
299
336
|
opt.on("-f [html_version]", "--format-version [=format_version]",
|
300
|
-
"
|
301
|
-
|
337
|
+
"Choose a formart for the output. Available options: \
|
338
|
+
html4, xhtml1, html5, plain, plain_verbose, markdown or gfm \
|
339
|
+
(default: #{self[:html_version].version})") do |version|
|
340
|
+
set_html_version(version)
|
302
341
|
end
|
303
342
|
|
304
343
|
opt.on("-l [lang]", "--lang [=lang]",
|
305
|
-
"Set the value of charset attributes
|
344
|
+
"Set the value of charset attributes \
|
345
|
+
(default: #{self[:lang]})") do |lang|
|
306
346
|
self[:lang] = lang if value_given?(lang)
|
307
347
|
end
|
308
348
|
|
309
349
|
opt.on("-e [encoding]", "--format-encoding [=encoding]",
|
310
|
-
"Available options: utf8, euc-jp, sjis, latin1
|
311
|
-
|
350
|
+
"Available options: utf8, euc-jp, sjis, latin1 \
|
351
|
+
(default: #{self[:encoding]})") do |given_opt|
|
352
|
+
set_html_encoding(given_opt)
|
312
353
|
end
|
313
354
|
|
314
355
|
opt.on("-E [ex[:in]]", "--encoding [=ex[:in]]",
|
315
|
-
"Specify the default external and internal character encodings
|
316
|
-
|
356
|
+
"Specify the default external and internal character encodings \
|
357
|
+
(same as the option of MRI") do |given_opt|
|
358
|
+
set_encoding(given_opt)
|
317
359
|
end
|
318
360
|
|
319
|
-
#use '-w' to avoid the conflict with the short option for '[-t]emplate'
|
361
|
+
# use '-w' to avoid the conflict with the short option for '[-t]emplate'
|
320
362
|
opt.on("-w [(window) title]", "--title [=title]",
|
321
|
-
"Set the value of the <title> element
|
363
|
+
"Set the value of the <title> element \
|
364
|
+
(default: the basename of the input file)") do |title|
|
322
365
|
self[:title] = title if value_given?(title)
|
323
366
|
end
|
324
367
|
|
325
368
|
opt.on("-c [css]", "--css [=css]",
|
326
|
-
"Set the path to a css file to be used
|
369
|
+
"Set the path to a css file to be used \
|
370
|
+
(default: #{self[:css]})") do |css|
|
327
371
|
self[:css] = css
|
328
372
|
end
|
329
373
|
|
330
374
|
opt.on("-C [path_to_css_file]", "--embed-css [=path_to_css_file]",
|
331
|
-
"Set the path to a css file to embed
|
375
|
+
"Set the path to a css file to embed \
|
376
|
+
(default: not to embed)") do |path_to_css_file|
|
332
377
|
self[:embed_css] = path_to_css_file
|
333
378
|
end
|
334
379
|
|
335
380
|
opt.on("-b [base]", "--base [=base]",
|
336
|
-
"Specify the value of href attribute of the <base> element
|
381
|
+
"Specify the value of href attribute of the <base> element \
|
382
|
+
(default: not specified)") do |base_dir|
|
337
383
|
self[:base] = base_dir if value_given?(base_dir)
|
338
384
|
end
|
339
385
|
|
340
386
|
opt.on("-t [template]", "--template [=template]",
|
341
|
-
"Specify a template file
|
387
|
+
"Specify a template file in eruby format with \"<%= body %>\" \
|
388
|
+
inside (default: not specified)") do |template|
|
342
389
|
self[:template] = template if value_given?(template)
|
343
390
|
end
|
344
391
|
|
345
392
|
opt.on("-o [output]", "--output [=output]",
|
346
|
-
"Output to the specified file. If no file is given, \
|
393
|
+
"Output to the specified file. If no file is given, \
|
394
|
+
\"[input_file_basename].html\" will be used.(default: STDOUT)") do |output|
|
347
395
|
self[:output] = File.expand_path(output) if value_given?(output)
|
348
|
-
|
396
|
+
@need_output_file = true
|
349
397
|
end
|
350
398
|
|
351
399
|
opt.on("-F", "--force",
|
352
|
-
"Force to apply command line options.
|
400
|
+
"Force to apply command line options. \
|
401
|
+
(default: false)") do |force|
|
353
402
|
self[:force] = force
|
354
403
|
end
|
355
404
|
|
356
405
|
opt.on("-m [contents-title]", "--table-of-contents [=contents-title]",
|
357
|
-
"Include the list of h2 and/or h3 headings with ids.
|
406
|
+
"Include the list of h2 and/or h3 headings with ids. \
|
407
|
+
(default: nil)") do |toc_title|
|
358
408
|
self[:toc] = toc_title
|
359
409
|
end
|
360
410
|
|
@@ -363,6 +413,14 @@ USAGE: #{File.basename(__FILE__)} [options]") do |opt|
|
|
363
413
|
self[:split_main_heading] = should_be_split
|
364
414
|
end
|
365
415
|
|
416
|
+
opt.on("-W", "--with-wikiname",
|
417
|
+
"Use WikiNames") do |with_wikiname|
|
418
|
+
if with_wikiname
|
419
|
+
auto_linker = PseudoHiki::AutoLink::WikiName.new
|
420
|
+
PseudoHiki::BlockParser.auto_linker = auto_linker
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
366
424
|
opt.parse!
|
367
425
|
end
|
368
426
|
end
|
@@ -370,11 +428,11 @@ USAGE: #{File.basename(__FILE__)} [options]") do |opt|
|
|
370
428
|
def check_argv
|
371
429
|
case ARGV.length
|
372
430
|
when 0
|
373
|
-
if
|
431
|
+
if @need_output_file and not self[:output]
|
374
432
|
raise "You must specify a file name for output"
|
375
433
|
end
|
376
434
|
when 1
|
377
|
-
|
435
|
+
read_input_filename(ARGV[0])
|
378
436
|
end
|
379
437
|
end
|
380
438
|
|
@@ -396,33 +454,34 @@ USAGE: #{File.basename(__FILE__)} [options]") do |opt|
|
|
396
454
|
end
|
397
455
|
|
398
456
|
def create_html_template_with_current_options
|
399
|
-
return [] unless
|
400
|
-
html =
|
401
|
-
html.charset =
|
457
|
+
return [] unless html_template
|
458
|
+
html = html_template.new
|
459
|
+
html.charset = charset
|
402
460
|
html.language = self[:lang]
|
403
461
|
html.default_css = self[:css] if self[:css]
|
404
|
-
html.base =
|
405
|
-
html.title =
|
462
|
+
html.base = base if self[:base]
|
463
|
+
html.title = title
|
406
464
|
html
|
407
465
|
end
|
408
466
|
|
409
467
|
def read_input_filename(filename)
|
410
468
|
@input_file_dir, @input_file_name = File.split(File.expand_path(filename))
|
411
|
-
@input_file_basename = File.basename(@input_file_name,".*")
|
469
|
+
@input_file_basename = File.basename(@input_file_name, ".*")
|
412
470
|
end
|
413
471
|
|
414
472
|
def output_filename
|
415
|
-
return nil unless
|
473
|
+
return nil unless @need_output_file
|
416
474
|
if self[:output]
|
417
475
|
File.expand_path(self[:output])
|
418
476
|
else
|
419
|
-
|
477
|
+
ext = self[:html_version].ext
|
478
|
+
File.join(@input_file_dir, @input_file_basename + ext)
|
420
479
|
end
|
421
480
|
end
|
422
481
|
|
423
482
|
def open_output
|
424
|
-
if
|
425
|
-
open(
|
483
|
+
if output_filename
|
484
|
+
open(output_filename, "w") {|f| yield f }
|
426
485
|
else
|
427
486
|
yield STDOUT
|
428
487
|
end
|