ronn-ng 0.7.4 → 0.8.0.SNAPSHOT
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/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)
|