ronn-ng 0.7.4 → 0.8.0.SNAPSHOT
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES +12 -3
- data/Gemfile +2 -0
- data/INSTALLING.md +100 -0
- data/README.md +19 -1
- data/Rakefile +39 -49
- data/bin/ronn +93 -75
- data/completion/bash/ronn +32 -0
- data/completion/zsh/_ronn +24 -0
- data/config.ru +3 -3
- data/lib/ronn/document.rb +127 -106
- data/lib/ronn/index.rb +8 -9
- data/lib/ronn/roff.rb +153 -76
- data/lib/ronn/server.rb +19 -22
- data/lib/ronn/template.rb +27 -26
- data/lib/ronn/utils.rb +9 -7
- data/lib/ronn.rb +5 -3
- data/man/ronn-format.7 +6 -62
- data/man/ronn.1 +21 -123
- data/man/ronn.1.ronn +8 -0
- data/ronn-ng.gemspec +38 -13
- data/test/angle_bracket_syntax.html +4 -5
- data/test/backticks.html +14 -0
- data/test/backticks.ronn +10 -0
- data/test/basic_document.html +3 -4
- data/test/basic_document.ronn +2 -2
- data/test/circumflexes.ronn +1 -0
- data/test/code_blocks.7.ronn +41 -0
- data/test/contest.rb +56 -54
- data/test/custom_title_document.html +2 -2
- data/test/definition_list_syntax.html +13 -9
- data/test/definition_list_syntax.roff +2 -9
- data/test/definition_list_syntax.ronn +2 -2
- data/test/dots_at_line_start_test.roff +12 -3
- data/test/dots_at_line_start_test.ronn +8 -0
- data/test/ellipses.roff +7 -0
- data/test/ellipses.ronn +7 -0
- data/test/entity_encoding_test.html +13 -14
- data/test/entity_encoding_test.roff +1 -22
- data/test/entity_encoding_test.ronn +1 -1
- data/test/markdown_syntax.html +4 -5
- data/test/markdown_syntax.roff +1 -561
- data/test/middle_paragraph.html +2 -3
- data/test/middle_paragraph.roff +1 -5
- data/test/middle_paragraph.ronn +1 -1
- data/test/missing_spaces.roff +0 -2
- data/test/nested_list.ronn +19 -0
- data/test/nested_list_with_code.html +15 -0
- data/test/nested_list_with_code.roff +11 -0
- data/test/nested_list_with_code.ronn +6 -0
- data/test/page.with.periods.in.name.5.ronn +4 -0
- data/test/pre_block_with_quotes.roff +0 -5
- data/test/section_reference_links.html +2 -3
- data/test/section_reference_links.roff +1 -4
- data/test/section_reference_links.ronn +1 -1
- data/test/tables.ronn +24 -0
- data/test/test_ronn.rb +49 -35
- data/test/test_ronn_document.rb +81 -81
- data/test/test_ronn_index.rb +11 -11
- data/test/titleless_document.html +0 -1
- data/test/underline_spacing_test.roff +0 -8
- metadata +140 -22
- data/INSTALLING +0 -20
@@ -0,0 +1,24 @@
|
|
1
|
+
#compdef ronn
|
2
|
+
|
3
|
+
# Zsh completion definitions for ronn, from Ronn-NG
|
4
|
+
|
5
|
+
_arguments -C -s -S \
|
6
|
+
'(--pipe)--pipe[write to standard output instead of generating files]' \
|
7
|
+
'(-m --man)'{-m,--man}'[show manual like with man]' \
|
8
|
+
'(-S --server)'{-S,--server}'[serve <file>s at http://localhost:1207/]' \
|
9
|
+
'(--port)--port[run server at specified port instead of 1207]:Port Number' \
|
10
|
+
'(-o --output-dir)'{-o,--output-dir}'[write generated files to specified directory]:Output Directory:_dirs' \
|
11
|
+
'(-r --roff)'{-r,--roff}'[generate roff output]' \
|
12
|
+
'(-5 --html)'{-5,--html}'[generate entire HTML page with layout]' \
|
13
|
+
'(-f --fragment)'{-f,--fragment}'[generate HTML fragment]' \
|
14
|
+
'(--markdown)--markdown[generate post-processed markdown output]' \
|
15
|
+
'(--date)--date[published date in YYYY-MM-DD format]:Date' \
|
16
|
+
'(--manual)--manual[name of the manual]:Manual Name' \
|
17
|
+
'(--organization)--organization[publishing group or individual]:Organization Name' \
|
18
|
+
'(-w --warnings)'{-w,--warnings}'[show troff warnings on stderr]' \
|
19
|
+
'(-W)-W[disable previously enabled troff warnings]' \
|
20
|
+
'(--version)--version[show ronn version and exit]' \
|
21
|
+
'(--help)--help[show help message and exit]' \
|
22
|
+
'*:input files:_files' \
|
23
|
+
&& ret=0
|
24
|
+
|
data/config.ru
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#\ -p 1207
|
2
|
-
|
2
|
+
$LOAD_PATH << File.expand_path('lib', __dir__)
|
3
3
|
|
4
4
|
require 'ronn'
|
5
5
|
require 'ronn/server'
|
@@ -7,8 +7,8 @@ require 'ronn/server'
|
|
7
7
|
# use Rack::Lint
|
8
8
|
|
9
9
|
options = {
|
10
|
-
:
|
11
|
-
:
|
10
|
+
styles: %w[man toc],
|
11
|
+
organization: "Ronn v#{Ronn::VERSION}"
|
12
12
|
}
|
13
13
|
files = Dir['man/*.ronn'] + Dir['test/*.ronn']
|
14
14
|
|
data/lib/ronn/document.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'time'
|
2
2
|
require 'cgi'
|
3
|
-
require '
|
3
|
+
require 'nokogiri'
|
4
4
|
require 'rdiscount'
|
5
5
|
require 'ronn/index'
|
6
6
|
require 'ronn/roff'
|
@@ -33,11 +33,11 @@ module Ronn
|
|
33
33
|
# a program or filename; displayed along with the section in
|
34
34
|
# the left and right portions of the header as well as the bottom
|
35
35
|
# right section of the footer.
|
36
|
-
|
36
|
+
attr_writer :name
|
37
37
|
|
38
38
|
# The man page's section: a string whose first character
|
39
39
|
# is numeric; displayed in parenthesis along with the name.
|
40
|
-
|
40
|
+
attr_writer :section
|
41
41
|
|
42
42
|
# Single sentence description of the thing being described
|
43
43
|
# by this man page; displayed in the NAME section.
|
@@ -53,26 +53,29 @@ module Ronn
|
|
53
53
|
|
54
54
|
# The date the document was published; center displayed in
|
55
55
|
# the document footer.
|
56
|
-
|
56
|
+
attr_writer :date
|
57
57
|
|
58
58
|
# Array of style modules to apply to the document.
|
59
|
-
|
59
|
+
attr_reader :styles
|
60
|
+
|
61
|
+
# Output directory to write files to
|
62
|
+
attr_accessor :outdir
|
60
63
|
|
61
64
|
# Create a Ronn::Document given a path or with the data returned by
|
62
65
|
# calling the block. The document is loaded and preprocessed before
|
63
66
|
# the intialize method returns. The attributes hash may contain values
|
64
67
|
# for any writeable attributes defined on this class.
|
65
|
-
def initialize(path=nil, attributes={}, &block)
|
68
|
+
def initialize(path = nil, attributes = {}, &block)
|
66
69
|
@path = path
|
67
70
|
@basename = path.to_s =~ /^-?$/ ? nil : File.basename(path)
|
68
71
|
@reader = block ||
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
lambda do |f|
|
73
|
+
if ['-', nil].include?(f)
|
74
|
+
STDIN.read
|
75
|
+
else
|
76
|
+
File.read(f)
|
77
|
+
end
|
78
|
+
end
|
76
79
|
@data = @reader.call(path)
|
77
80
|
@name, @section, @tagline = sniff
|
78
81
|
|
@@ -82,24 +85,26 @@ module Ronn
|
|
82
85
|
@index = Ronn::Index[path || '.']
|
83
86
|
@index.add_manual(self) if path && name
|
84
87
|
|
85
|
-
attributes.each { |attr_name,value| send("#{attr_name}=", value) }
|
88
|
+
attributes.each { |attr_name, value| send("#{attr_name}=", value) }
|
86
89
|
end
|
87
90
|
|
88
91
|
# Generate a file basename of the form "<name>.<section>.<type>"
|
89
92
|
# for the given file extension. Uses the name and section from
|
90
93
|
# the source file path but falls back on the name and section
|
91
94
|
# defined in the document.
|
92
|
-
def basename(type=nil)
|
95
|
+
def basename(type = nil)
|
93
96
|
type = nil if ['', 'roff'].include?(type.to_s)
|
94
|
-
[path_name || @name, path_section || @section, type]
|
95
|
-
|
97
|
+
[path_name || @name, path_section || @section, type]
|
98
|
+
.compact.join('.')
|
96
99
|
end
|
97
100
|
|
98
101
|
# Construct a path for a file near the source file. Uses the
|
99
102
|
# Document#basename method to generate the basename part and
|
100
103
|
# appends it to the dirname of the source document.
|
101
|
-
def path_for(type=nil)
|
102
|
-
if @
|
104
|
+
def path_for(type = nil)
|
105
|
+
if @outdir
|
106
|
+
File.join(@outdir, basename(type))
|
107
|
+
elsif @basename
|
103
108
|
File.join(File.dirname(path), basename(type))
|
104
109
|
else
|
105
110
|
basename(type)
|
@@ -110,7 +115,12 @@ module Ronn
|
|
110
115
|
# available. This is used as the manual page name when the
|
111
116
|
# file contents do not include a name section.
|
112
117
|
def path_name
|
113
|
-
|
118
|
+
return unless @basename
|
119
|
+
|
120
|
+
parts = @basename.split('.')
|
121
|
+
parts.pop if parts.last.casecmp('ronn').zero?
|
122
|
+
parts.pop if parts.last =~ /^\d+$/
|
123
|
+
parts.join('.')
|
114
124
|
end
|
115
125
|
|
116
126
|
# Returns the <section> part of the path, or nil when
|
@@ -120,7 +130,10 @@ module Ronn
|
|
120
130
|
end
|
121
131
|
|
122
132
|
# Returns the manual page name based first on the document's
|
123
|
-
# contents and then on the path name.
|
133
|
+
# contents and then on the path name. Usually a single word name of
|
134
|
+
# a program or filename; displayed along with the section in
|
135
|
+
# the left and right portions of the header as well as the bottom
|
136
|
+
# right section of the footer.
|
124
137
|
def name
|
125
138
|
@name || path_name
|
126
139
|
end
|
@@ -132,7 +145,8 @@ module Ronn
|
|
132
145
|
end
|
133
146
|
|
134
147
|
# Returns the manual page section based first on the document's
|
135
|
-
# contents and then on the path name.
|
148
|
+
# contents and then on the path name. A string whose first character
|
149
|
+
# is numeric; displayed in parenthesis along with the name.
|
136
150
|
def section
|
137
151
|
@section || path_section
|
138
152
|
end
|
@@ -158,15 +172,17 @@ module Ronn
|
|
158
172
|
# The document's title when no name section was defined. When a name section
|
159
173
|
# exists, this value is nil.
|
160
174
|
def title
|
161
|
-
@tagline
|
175
|
+
@tagline unless name?
|
162
176
|
end
|
163
177
|
|
164
178
|
# The date the man page was published. If not set explicitly,
|
165
179
|
# this is the file's modified time or, if no file is given,
|
166
|
-
# the current time.
|
180
|
+
# the current time. Center displayed in the document footer.
|
167
181
|
def date
|
168
182
|
return @date if @date
|
183
|
+
|
169
184
|
return File.mtime(path) if File.exist?(path)
|
185
|
+
|
170
186
|
Time.now
|
171
187
|
end
|
172
188
|
|
@@ -175,7 +191,7 @@ module Ronn
|
|
175
191
|
# id and +text+ is the inner text of the heading element.
|
176
192
|
def toc
|
177
193
|
@toc ||=
|
178
|
-
html.search('h2[@id]').map { |h2| [h2.attributes['id'], h2.inner_text] }
|
194
|
+
html.search('h2[@id]').map { |h2| [h2.attributes['id'].content.upcase, h2.inner_text] }
|
179
195
|
end
|
180
196
|
alias section_heads toc
|
181
197
|
|
@@ -189,7 +205,7 @@ module Ronn
|
|
189
205
|
# tuple of the form: [name, section, description], where missing information
|
190
206
|
# is represented by nil and any element may be missing.
|
191
207
|
def sniff
|
192
|
-
html = Markdown.new(data[0, 512]).to_html
|
208
|
+
html = Markdown.new(data[0, 512], :no_superscript).to_html
|
193
209
|
heading, html = html.split("</h1>\n", 2)
|
194
210
|
return [nil, nil, nil] if html.nil?
|
195
211
|
|
@@ -211,7 +227,7 @@ module Ronn
|
|
211
227
|
@markdown ||= process_markdown!
|
212
228
|
end
|
213
229
|
|
214
|
-
# A
|
230
|
+
# A Nokogiri DocumentFragment for the manual content fragment.
|
215
231
|
def html
|
216
232
|
@html ||= process_html!
|
217
233
|
end
|
@@ -225,36 +241,38 @@ module Ronn
|
|
225
241
|
# Convert the document to roff and return the result as a string.
|
226
242
|
def to_roff
|
227
243
|
RoffFilter.new(
|
228
|
-
to_html_fragment(
|
244
|
+
to_html_fragment(nil),
|
229
245
|
name, section, tagline,
|
230
246
|
manual, organization, date
|
231
247
|
).to_s
|
232
248
|
end
|
233
249
|
|
234
250
|
# Convert the document to HTML and return the result as a string.
|
251
|
+
# The returned string is a complete HTML document.
|
235
252
|
def to_html
|
236
|
-
|
237
|
-
|
253
|
+
layout = ENV['RONN_LAYOUT']
|
254
|
+
layout_path = nil
|
255
|
+
if layout
|
256
|
+
layout_path = File.expand_path(layout)
|
257
|
+
unless File.exist?(layout_path)
|
238
258
|
warn "warn: can't find #{layout}, using default layout."
|
239
259
|
layout_path = nil
|
240
260
|
end
|
241
261
|
end
|
242
262
|
|
243
263
|
template = Ronn::Template.new(self)
|
244
|
-
template.context.push :
|
264
|
+
template.context.push html: to_html_fragment(nil)
|
245
265
|
template.render(layout_path || 'default')
|
246
266
|
end
|
247
267
|
|
248
268
|
# Convert the document to HTML and return the result
|
249
269
|
# as a string. The HTML does not include <html>, <head>,
|
250
270
|
# or <style> tags.
|
251
|
-
def to_html_fragment(wrap_class='mp')
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
"</div>"
|
257
|
-
].join("\n")
|
271
|
+
def to_html_fragment(wrap_class = 'mp')
|
272
|
+
frag_nodes = html.at('body').children
|
273
|
+
out = frag_nodes.to_s.rstrip
|
274
|
+
out = "<div class='#{wrap_class}'>#{out}\n</div>" unless wrap_class.nil?
|
275
|
+
out
|
258
276
|
end
|
259
277
|
|
260
278
|
def to_markdown
|
@@ -262,8 +280,8 @@ module Ronn
|
|
262
280
|
end
|
263
281
|
|
264
282
|
def to_h
|
265
|
-
%w[name section tagline manual organization date styles toc]
|
266
|
-
|
283
|
+
%w[name section tagline manual organization date styles toc]
|
284
|
+
.each_with_object({}) { |name, hash| hash[name] = send(name) }
|
267
285
|
end
|
268
286
|
|
269
287
|
def to_yaml
|
@@ -276,7 +294,8 @@ module Ronn
|
|
276
294
|
to_h.merge('date' => date.iso8601).to_json
|
277
295
|
end
|
278
296
|
|
279
|
-
|
297
|
+
protected
|
298
|
+
|
280
299
|
##
|
281
300
|
# Document Processing
|
282
301
|
|
@@ -288,7 +307,7 @@ module Ronn
|
|
288
307
|
end
|
289
308
|
|
290
309
|
def input_html
|
291
|
-
@input_html ||= strip_heading(Markdown.new(markdown).to_html)
|
310
|
+
@input_html ||= strip_heading(Markdown.new(markdown, :no_superscript).to_html)
|
292
311
|
end
|
293
312
|
|
294
313
|
def strip_heading(html)
|
@@ -297,13 +316,14 @@ module Ronn
|
|
297
316
|
end
|
298
317
|
|
299
318
|
def process_markdown!
|
300
|
-
|
301
|
-
markdown_filter_link_index(
|
302
|
-
markdown_filter_angle_quotes(
|
319
|
+
md = markdown_filter_heading_anchors(data)
|
320
|
+
md = markdown_filter_link_index(md)
|
321
|
+
markdown_filter_angle_quotes(md)
|
303
322
|
end
|
304
323
|
|
305
324
|
def process_html!
|
306
|
-
|
325
|
+
wrapped_html = "<html>\n <body>\n#{input_html}\n </body>\n</html>"
|
326
|
+
@html = Nokogiri::HTML.parse(wrapped_html)
|
307
327
|
html_filter_angle_quotes
|
308
328
|
html_filter_definition_lists
|
309
329
|
html_filter_inject_name_section
|
@@ -320,8 +340,10 @@ module Ronn
|
|
320
340
|
# links. This lets us use [foo(3)][] syntax to link to index entries.
|
321
341
|
def markdown_filter_link_index(markdown)
|
322
342
|
return markdown if index.nil? || index.empty?
|
343
|
+
|
323
344
|
markdown << "\n\n"
|
324
345
|
index.each { |ref| markdown << "[#{ref.name}]: #{ref.url}\n" }
|
346
|
+
markdown
|
325
347
|
end
|
326
348
|
|
327
349
|
# Add [id]: #ANCHOR elements to the markdown source text for all sections.
|
@@ -340,11 +362,11 @@ module Ronn
|
|
340
362
|
|
341
363
|
# Convert <WORD> to <var>WORD</var> but only if WORD isn't an HTML tag.
|
342
364
|
def markdown_filter_angle_quotes(markdown)
|
343
|
-
markdown.gsub(
|
365
|
+
markdown.gsub(/<([^:.\/]+?)>/) do |match|
|
344
366
|
contents = $1
|
345
367
|
tag, attrs = contents.split(' ', 2)
|
346
368
|
if attrs =~ /\/=/ || html_element?(tag.sub(/^\//, '')) ||
|
347
|
-
data.include?("</#{tag}>")
|
369
|
+
data.include?("</#{tag}>") || contents =~ /^!/
|
348
370
|
match.to_s
|
349
371
|
else
|
350
372
|
"<var>#{contents}</var>"
|
@@ -356,12 +378,14 @@ module Ronn
|
|
356
378
|
def html_filter_angle_quotes
|
357
379
|
# convert all angle quote vars nested in code blocks
|
358
380
|
# back to the original text
|
359
|
-
@html.search('code')
|
381
|
+
code_nodes = @html.search('code')
|
382
|
+
code_nodes.search('.//text() | text()').each do |node|
|
360
383
|
next unless node.to_html.include?('var>')
|
384
|
+
|
361
385
|
new =
|
362
|
-
node.to_html
|
363
|
-
|
364
|
-
|
386
|
+
node.to_html
|
387
|
+
.gsub('<var>', '<')
|
388
|
+
.gsub('</var>', '>')
|
365
389
|
node.swap(new)
|
366
390
|
end
|
367
391
|
end
|
@@ -369,27 +393,28 @@ module Ronn
|
|
369
393
|
# Convert special format unordered lists to definition lists.
|
370
394
|
def html_filter_definition_lists
|
371
395
|
# process all unordered lists depth-first
|
372
|
-
@html.search('ul').to_a.
|
396
|
+
@html.search('ul').to_a.reverse_each do |ul|
|
373
397
|
items = ul.search('li')
|
374
398
|
next if items.any? { |item| item.inner_text.split("\n", 2).first !~ /:$/ }
|
375
399
|
|
376
|
-
|
400
|
+
dl = Nokogiri::XML::Node.new 'dl', html
|
377
401
|
items.each do |item|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
end
|
385
|
-
term, definition = container.inner_html.split(":\n", 2)
|
386
|
-
|
387
|
-
dt = item.before("<dt>#{term}</dt>").first
|
402
|
+
# This processing is specific to how Markdown generates definition lists
|
403
|
+
term, definition = item.inner_html.split(":\n", 2)
|
404
|
+
term = term.sub(/^<p>/, '')
|
405
|
+
|
406
|
+
dt = Nokogiri::XML::Node.new 'dt', html
|
407
|
+
dt.children = Nokogiri::HTML.fragment(term)
|
388
408
|
dt.attributes['class'] = 'flush' if dt.inner_text.length <= 7
|
389
409
|
|
390
|
-
|
391
|
-
|
410
|
+
dd = Nokogiri::XML::Node.new 'dd', html
|
411
|
+
dd_contents = Nokogiri::HTML.fragment(definition)
|
412
|
+
dd.children = dd_contents
|
413
|
+
|
414
|
+
dl.add_child(dt)
|
415
|
+
dl.add_child(dd)
|
392
416
|
end
|
417
|
+
ul.replace(dl)
|
393
418
|
end
|
394
419
|
end
|
395
420
|
|
@@ -398,23 +423,24 @@ module Ronn
|
|
398
423
|
if title?
|
399
424
|
"<h1>#{title}</h1>"
|
400
425
|
elsif name
|
401
|
-
"<h2>NAME</h2>\n"
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
end
|
406
|
-
if markup
|
407
|
-
if @html.children
|
408
|
-
@html.at("*").before(markup)
|
409
|
-
else
|
410
|
-
@html = Hpricot(markup)
|
426
|
+
"<h2>NAME</h2>\n" \
|
427
|
+
"<p class='man-name'>\n <code>#{name}</code>" +
|
428
|
+
(tagline ? " - <span class='man-whatis'>#{tagline}</span>\n" : "\n") +
|
429
|
+
"</p>\n"
|
411
430
|
end
|
431
|
+
return unless markup
|
432
|
+
|
433
|
+
if html.at('body').first_element_child
|
434
|
+
html.at('body').first_element_child.before(Nokogiri::HTML.fragment(markup))
|
435
|
+
else
|
436
|
+
html.at('body').add_child(Nokogiri::HTML.fragment(markup))
|
412
437
|
end
|
413
438
|
end
|
414
439
|
|
415
440
|
# Add URL anchors to all HTML heading elements.
|
416
441
|
def html_filter_heading_anchors
|
417
|
-
@html.search('h2
|
442
|
+
h_nodes = @html.search('//*[self::h1 or self::h2 or self::h3 or self::h4 or self::h5 and not(@id)]')
|
443
|
+
h_nodes.each do |heading|
|
418
444
|
heading.set_attribute('id', heading.inner_text.gsub(/\W+/, '-'))
|
419
445
|
end
|
420
446
|
end
|
@@ -423,15 +449,13 @@ module Ronn
|
|
423
449
|
# whose text labels are the same as their href URLs.
|
424
450
|
def html_filter_annotate_bare_links
|
425
451
|
@html.search('a[@href]').each do |node|
|
426
|
-
href = node.attributes['href']
|
452
|
+
href = node.attributes['href'].content
|
427
453
|
text = node.inner_text
|
428
454
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
node.set_attribute('data-bare-link', 'true')
|
434
|
-
end
|
455
|
+
next unless href == text || href[0] == '#' ||
|
456
|
+
CGI.unescapeHTML(href) == "mailto:#{CGI.unescapeHTML(text)}"
|
457
|
+
|
458
|
+
node.set_attribute('data-bare-link', 'true')
|
435
459
|
end
|
436
460
|
end
|
437
461
|
|
@@ -439,17 +463,18 @@ module Ronn
|
|
439
463
|
# to a hyperlink. The URL is obtained from the index.
|
440
464
|
def html_filter_manual_reference_links
|
441
465
|
return if index.nil?
|
442
|
-
|
466
|
+
|
467
|
+
name_pattern = '[0-9A-Za-z_:.+=@~-]+'
|
443
468
|
|
444
469
|
# Convert "name(section)" by traversing text nodes searching for
|
445
470
|
# text that fits the pattern. This is the original implementation.
|
446
|
-
@html.search('text()').each do |node|
|
447
|
-
next
|
471
|
+
@html.search('.//text() | text()').each do |node|
|
472
|
+
next unless node.content.include?(')')
|
448
473
|
next if %w[pre code h1 h2 h3].include?(node.parent.name)
|
449
474
|
next if child_of?(node, 'a')
|
450
|
-
node.swap(node.content.gsub(/(#{name_pattern})(\(\d+\w*\))/)
|
475
|
+
node.swap(node.content.gsub(/(#{name_pattern})(\(\d+\w*\))/) do
|
451
476
|
html_build_manual_reference_link($1, $2)
|
452
|
-
|
477
|
+
end)
|
453
478
|
end
|
454
479
|
|
455
480
|
# Convert "<code>name</code>(section)" by traversing <code> nodes.
|
@@ -461,14 +486,12 @@ module Ronn
|
|
461
486
|
next if child_of?(node, 'a')
|
462
487
|
next unless node.inner_text =~ /^#{name_pattern}$/
|
463
488
|
sibling = node.next
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
end
|
489
|
+
next unless sibling
|
490
|
+
next unless sibling.text?
|
491
|
+
next unless sibling.content =~ /^\((\d+\w*)\)/
|
492
|
+
node.swap(html_build_manual_reference_link(node, "(#{$1})"))
|
493
|
+
sibling.content = sibling.content.gsub(/^\(\d+\w*\)/, '')
|
470
494
|
end
|
471
|
-
|
472
495
|
end
|
473
496
|
|
474
497
|
# HTMLize the manual page reference. The result is an <a> if the
|
@@ -477,19 +500,17 @@ module Ronn
|
|
477
500
|
# be a string of the form "(#{section})".
|
478
501
|
def html_build_manual_reference_link(name_or_node, section)
|
479
502
|
name = if name_or_node.respond_to?(:inner_text)
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
503
|
+
name_or_node.inner_text
|
504
|
+
else
|
505
|
+
name_or_node
|
506
|
+
end
|
507
|
+
ref = index["#{name}#{section}"]
|
508
|
+
if ref
|
509
|
+
"<a class='man-ref' href='#{ref.url}'>#{name_or_node}<span class='s'>#{section}</span></a>"
|
487
510
|
else
|
488
511
|
# warn "warn: manual reference not defined: '#{name}#{section}'"
|
489
|
-
"<span class='man-ref'>#{name_or_node}<span class='s'>#{section
|
490
|
-
}</span></span>"
|
512
|
+
"<span class='man-ref'>#{name_or_node}<span class='s'>#{section}</span></span>"
|
491
513
|
end
|
492
514
|
end
|
493
|
-
|
494
515
|
end
|
495
516
|
end
|
data/lib/ronn/index.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'ronn'
|
2
2
|
|
3
3
|
module Ronn
|
4
|
-
|
5
4
|
# Maintains a list of links / references to manuals and other resources.
|
6
5
|
class Index
|
7
6
|
include Enumerable
|
@@ -27,7 +26,7 @@ module Ronn
|
|
27
26
|
)
|
28
27
|
end
|
29
28
|
|
30
|
-
def initialize(path
|
29
|
+
def initialize(path)
|
31
30
|
@path = path
|
32
31
|
@references = []
|
33
32
|
@manuals = {}
|
@@ -48,7 +47,7 @@ module Ronn
|
|
48
47
|
def read!(data)
|
49
48
|
data.each_line do |line|
|
50
49
|
line = line.strip.gsub(/\s*#.*$/, '')
|
51
|
-
|
50
|
+
unless line.empty?
|
52
51
|
name, url = line.split(/\s+/, 2)
|
53
52
|
@references << reference(name, url)
|
54
53
|
end
|
@@ -58,8 +57,8 @@ module Ronn
|
|
58
57
|
##
|
59
58
|
# Enumerable and friends
|
60
59
|
|
61
|
-
def each(&
|
62
|
-
references.each(&
|
60
|
+
def each(&block)
|
61
|
+
references.each(&block)
|
63
62
|
end
|
64
63
|
|
65
64
|
def size
|
@@ -87,7 +86,7 @@ module Ronn
|
|
87
86
|
end
|
88
87
|
|
89
88
|
def <<(path)
|
90
|
-
raise ArgumentError,
|
89
|
+
raise ArgumentError, 'local paths only' if path =~ /(https?|mailto):/
|
91
90
|
return self if any? { |ref| ref.path == File.expand_path(path) }
|
92
91
|
relative_path = relative_to_index(path)
|
93
92
|
@references << \
|
@@ -109,8 +108,8 @@ module Ronn
|
|
109
108
|
end
|
110
109
|
|
111
110
|
def manuals
|
112
|
-
select { |ref| ref.relative? && ref.ronn? }
|
113
|
-
|
111
|
+
select { |ref| ref.relative? && ref.ronn? }
|
112
|
+
.map { |ref| manual(ref.path) }
|
114
113
|
end
|
115
114
|
|
116
115
|
##
|
@@ -125,7 +124,7 @@ module Ronn
|
|
125
124
|
end
|
126
125
|
|
127
126
|
def to_h
|
128
|
-
to_a.map
|
127
|
+
to_a.map(&:to_hash)
|
129
128
|
end
|
130
129
|
|
131
130
|
def relative_to_index(path)
|