rdoc 6.3.0 → 6.3.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rdoc might be problematic. Click here for more details.

data/lib/rdoc/markup.rb CHANGED
@@ -843,6 +843,7 @@ https://github.com/ruby/rdoc/issues
843
843
  autoload :List, 'rdoc/markup/list'
844
844
  autoload :ListItem, 'rdoc/markup/list_item'
845
845
  autoload :Paragraph, 'rdoc/markup/paragraph'
846
+ autoload :Table, 'rdoc/markup/table'
846
847
  autoload :Raw, 'rdoc/markup/raw'
847
848
  autoload :Rule, 'rdoc/markup/rule'
848
849
  autoload :Verbatim, 'rdoc/markup/verbatim'
@@ -7,16 +7,22 @@ class RDoc::Markup::AttrSpan
7
7
  ##
8
8
  # Creates a new AttrSpan for +length+ characters
9
9
 
10
- def initialize(length)
10
+ def initialize(length, exclusive)
11
11
  @attrs = Array.new(length, 0)
12
+ @exclusive = exclusive
12
13
  end
13
14
 
14
15
  ##
15
16
  # Toggles +bits+ from +start+ to +length+
16
17
  def set_attrs(start, length, bits)
18
+ updated = false
17
19
  for i in start ... (start+length)
18
- @attrs[i] |= bits
20
+ if (@exclusive & @attrs[i]) == 0 || (@exclusive & bits) != 0
21
+ @attrs[i] |= bits
22
+ updated = true
23
+ end
19
24
  end
25
+ updated
20
26
  end
21
27
 
22
28
  ##
@@ -58,6 +58,10 @@ class RDoc::Markup::AttributeManager
58
58
 
59
59
  attr_reader :regexp_handlings
60
60
 
61
+ ##
62
+ # A bits of exclusive maps
63
+ attr_reader :exclusive_bitmap
64
+
61
65
  ##
62
66
  # Creates a new attribute manager that understands bold, emphasized and
63
67
  # teletype text.
@@ -68,17 +72,18 @@ class RDoc::Markup::AttributeManager
68
72
  @protectable = %w[<]
69
73
  @regexp_handlings = []
70
74
  @word_pair_map = {}
75
+ @exclusive_bitmap = 0
71
76
  @attributes = RDoc::Markup::Attributes.new
72
77
 
73
- add_word_pair "*", "*", :BOLD
74
- add_word_pair "_", "_", :EM
75
- add_word_pair "+", "+", :TT
78
+ add_word_pair "*", "*", :BOLD, true
79
+ add_word_pair "_", "_", :EM, true
80
+ add_word_pair "+", "+", :TT, true
76
81
 
77
- add_html "em", :EM
78
- add_html "i", :EM
79
- add_html "b", :BOLD
80
- add_html "tt", :TT
81
- add_html "code", :TT
82
+ add_html "em", :EM, true
83
+ add_html "i", :EM, true
84
+ add_html "b", :BOLD, true
85
+ add_html "tt", :TT, true
86
+ add_html "code", :TT, true
82
87
  end
83
88
 
84
89
  ##
@@ -122,29 +127,67 @@ class RDoc::Markup::AttributeManager
122
127
  res
123
128
  end
124
129
 
130
+ def exclusive?(attr)
131
+ (attr & @exclusive_bitmap) != 0
132
+ end
133
+
134
+ NON_PRINTING_START = "\1" # :nodoc:
135
+ NON_PRINTING_END = "\2" # :nodoc:
136
+
125
137
  ##
126
138
  # Map attributes like <b>text</b>to the sequence
127
139
  # \001\002<char>\001\003<char>, where <char> is a per-attribute specific
128
140
  # character
129
141
 
130
- def convert_attrs(str, attrs)
142
+ def convert_attrs(str, attrs, exclusive = false)
143
+ convert_attrs_matching_word_pairs(str, attrs, exclusive)
144
+ convert_attrs_word_pair_map(str, attrs, exclusive)
145
+ end
146
+
147
+ def convert_attrs_matching_word_pairs(str, attrs, exclusive)
131
148
  # first do matching ones
132
- tags = @matching_word_pairs.keys.join("")
149
+ tags = @matching_word_pairs.select { |start, bitmap|
150
+ if exclusive && exclusive?(bitmap)
151
+ true
152
+ elsif !exclusive && !exclusive?(bitmap)
153
+ true
154
+ else
155
+ false
156
+ end
157
+ }.keys
158
+ return if tags.empty?
159
+ all_tags = @matching_word_pairs.keys
133
160
 
134
- re = /(^|\W)([#{tags}])([#\\]?[\w:.\/-]+?\S?)\2(\W|$)/
161
+ re = /(^|\W|[#{all_tags.join("")}])([#{tags.join("")}])(\2*[#\\]?[\w:.\/\[\]-]+?\S?)\2(?!\2)([#{all_tags.join("")}]|\W|$)/
135
162
 
136
- 1 while str.gsub!(re) do
163
+ 1 while str.gsub!(re) { |orig|
137
164
  attr = @matching_word_pairs[$2]
138
- attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
139
- $1 + NULL * $2.length + $3 + NULL * $2.length + $4
140
- end
165
+ attr_updated = attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
166
+ if attr_updated
167
+ $1 + NULL * $2.length + $3 + NULL * $2.length + $4
168
+ else
169
+ $1 + NON_PRINTING_START + $2 + NON_PRINTING_END + $3 + NON_PRINTING_START + $2 + NON_PRINTING_END + $4
170
+ end
171
+ }
172
+ str.delete!(NON_PRINTING_START + NON_PRINTING_END)
173
+ end
141
174
 
175
+ def convert_attrs_word_pair_map(str, attrs, exclusive)
142
176
  # then non-matching
143
177
  unless @word_pair_map.empty? then
144
178
  @word_pair_map.each do |regexp, attr|
145
- str.gsub!(regexp) {
146
- attrs.set_attrs($`.length + $1.length, $2.length, attr)
147
- NULL * $1.length + $2 + NULL * $3.length
179
+ if !exclusive
180
+ next if exclusive?(attr)
181
+ else
182
+ next if !exclusive?(attr)
183
+ end
184
+ 1 while str.gsub!(regexp) { |orig|
185
+ updated = attrs.set_attrs($`.length + $1.length, $2.length, attr)
186
+ if updated
187
+ NULL * $1.length + $2 + NULL * $3.length
188
+ else
189
+ orig
190
+ end
148
191
  }
149
192
  end
150
193
  end
@@ -153,10 +196,18 @@ class RDoc::Markup::AttributeManager
153
196
  ##
154
197
  # Converts HTML tags to RDoc attributes
155
198
 
156
- def convert_html(str, attrs)
157
- tags = @html_tags.keys.join '|'
199
+ def convert_html(str, attrs, exclusive = false)
200
+ tags = @html_tags.select { |start, bitmap|
201
+ if exclusive && exclusive?(bitmap)
202
+ true
203
+ elsif !exclusive && !exclusive?(bitmap)
204
+ true
205
+ else
206
+ false
207
+ end
208
+ }.keys.join '|'
158
209
 
159
- 1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) {
210
+ 1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) { |orig|
160
211
  attr = @html_tags[$1.downcase]
161
212
  html_length = $1.length + 2
162
213
  seq = NULL * html_length
@@ -168,8 +219,13 @@ class RDoc::Markup::AttributeManager
168
219
  ##
169
220
  # Converts regexp handling sequences to RDoc attributes
170
221
 
171
- def convert_regexp_handlings str, attrs
222
+ def convert_regexp_handlings str, attrs, exclusive = false
172
223
  @regexp_handlings.each do |regexp, attribute|
224
+ if exclusive
225
+ next if !exclusive?(attribute)
226
+ else
227
+ next if exclusive?(attribute)
228
+ end
173
229
  str.scan(regexp) do
174
230
  capture = $~.size == 1 ? 0 : 1
175
231
 
@@ -205,7 +261,7 @@ class RDoc::Markup::AttributeManager
205
261
  #
206
262
  # am.add_word_pair '*', '*', :BOLD
207
263
 
208
- def add_word_pair(start, stop, name)
264
+ def add_word_pair(start, stop, name, exclusive = false)
209
265
  raise ArgumentError, "Word flags may not start with '<'" if
210
266
  start[0,1] == '<'
211
267
 
@@ -220,6 +276,8 @@ class RDoc::Markup::AttributeManager
220
276
 
221
277
  @protectable << start[0,1]
222
278
  @protectable.uniq!
279
+
280
+ @exclusive_bitmap |= bitmap if exclusive
223
281
  end
224
282
 
225
283
  ##
@@ -228,8 +286,10 @@ class RDoc::Markup::AttributeManager
228
286
  #
229
287
  # am.add_html 'em', :EM
230
288
 
231
- def add_html(tag, name)
232
- @html_tags[tag.downcase] = @attributes.bitmap_for name
289
+ def add_html(tag, name, exclusive = false)
290
+ bitmap = @attributes.bitmap_for name
291
+ @html_tags[tag.downcase] = bitmap
292
+ @exclusive_bitmap |= bitmap if exclusive
233
293
  end
234
294
 
235
295
  ##
@@ -238,8 +298,10 @@ class RDoc::Markup::AttributeManager
238
298
  #
239
299
  # @am.add_regexp_handling(/((https?:)\S+\w)/, :HYPERLINK)
240
300
 
241
- def add_regexp_handling pattern, name
242
- @regexp_handlings << [pattern, @attributes.bitmap_for(name)]
301
+ def add_regexp_handling pattern, name, exclusive = false
302
+ bitmap = @attributes.bitmap_for(name)
303
+ @regexp_handlings << [pattern, bitmap]
304
+ @exclusive_bitmap |= bitmap if exclusive
243
305
  end
244
306
 
245
307
  ##
@@ -250,8 +312,11 @@ class RDoc::Markup::AttributeManager
250
312
 
251
313
  mask_protected_sequences
252
314
 
253
- @attrs = RDoc::Markup::AttrSpan.new @str.length
315
+ @attrs = RDoc::Markup::AttrSpan.new @str.length, @exclusive_bitmap
254
316
 
317
+ convert_attrs @str, @attrs, true
318
+ convert_html @str, @attrs, true
319
+ convert_regexp_handlings @str, @attrs, true
255
320
  convert_attrs @str, @attrs
256
321
  convert_html @str, @attrs
257
322
  convert_regexp_handlings @str, @attrs
@@ -314,6 +314,29 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
314
314
  @res << raw.parts.join("\n")
315
315
  end
316
316
 
317
+ ##
318
+ # Adds +table+ to the output
319
+
320
+ def accept_table header, body, aligns
321
+ @res << "\n<table role=\"table\">\n<thead>\n<tr>\n"
322
+ header.zip(aligns) do |text, align|
323
+ @res << '<th'
324
+ @res << ' align="' << align << '"' if align
325
+ @res << '>' << CGI.escapeHTML(text) << "</th>\n"
326
+ end
327
+ @res << "</tr>\n</thead>\n<tbody>\n"
328
+ body.each do |row|
329
+ @res << "<tr>\n"
330
+ row.zip(aligns) do |text, align|
331
+ @res << '<td'
332
+ @res << ' align="' << align << '"' if align
333
+ @res << '>' << CGI.escapeHTML(text) << "</td>\n"
334
+ end
335
+ @res << "</tr>\n"
336
+ end
337
+ @res << "</tbody>\n</table>\n"
338
+ end
339
+
317
340
  # :section: Utilities
318
341
 
319
342
  ##
@@ -334,6 +357,10 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
334
357
  url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then
335
358
  "<img src=\"#{url}\" />"
336
359
  else
360
+ if scheme != 'link' and /\.(?:rb|rdoc|md)\z/i =~ url
361
+ url = url.sub(%r%\A([./]*)(.*)\z%) { "#$1#{$2.tr('.', '_')}.html" }
362
+ end
363
+
337
364
  text = text.sub %r%^#{scheme}:/*%i, ''
338
365
  text = text.sub %r%^[*\^](\d+)$%, '\1'
339
366
 
@@ -41,6 +41,7 @@ class RDoc::Markup::ToJoinedParagraph < RDoc::Markup::Formatter
41
41
  alias accept_raw ignore
42
42
  alias accept_rule ignore
43
43
  alias accept_verbatim ignore
44
+ alias accept_table ignore
44
45
 
45
46
  end
46
47
 
@@ -237,6 +237,34 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter
237
237
  @res << "\n"
238
238
  end
239
239
 
240
+ ##
241
+ # Adds +table+ to the output
242
+
243
+ def accept_table header, body, aligns
244
+ widths = header.zip(body) do |h, b|
245
+ [h.size, b.size].max
246
+ end
247
+ aligns = aligns.map do |a|
248
+ case a
249
+ when nil
250
+ :center
251
+ when :left
252
+ :ljust
253
+ when :right
254
+ :rjust
255
+ end
256
+ end
257
+ @res << header.zip(widths, aligns) do |h, w, a|
258
+ h.__send__(a, w)
259
+ end.join("|").rstrip << "\n"
260
+ @res << widths.map {|w| "-" * w }.join("|") << "\n"
261
+ body.each do |row|
262
+ @res << row.zip(widths, aligns) do |t, w, a|
263
+ t.__send__(a, w)
264
+ end.join("|").rstrip << "\n"
265
+ end
266
+ end
267
+
240
268
  ##
241
269
  # Applies attribute-specific markup to +text+ using RDoc::AttributeManager
242
270
 
@@ -82,6 +82,7 @@ class RDoc::Markup::ToTableOfContents < RDoc::Markup::Formatter
82
82
  alias accept_list_item_end ignore
83
83
  alias accept_list_end_bullet ignore
84
84
  alias accept_list_start ignore
85
+ alias accept_table ignore
85
86
  # :startdoc:
86
87
 
87
88
  end
data/lib/rdoc/options.rb CHANGED
@@ -338,8 +338,9 @@ class RDoc::Options
338
338
 
339
339
  attr_reader :visibility
340
340
 
341
- def initialize # :nodoc:
341
+ def initialize loaded_options = nil # :nodoc:
342
342
  init_ivars
343
+ override loaded_options if loaded_options
343
344
  end
344
345
 
345
346
  def init_ivars # :nodoc:
@@ -417,6 +418,37 @@ class RDoc::Options
417
418
  init_with map
418
419
  end
419
420
 
421
+ def override map # :nodoc:
422
+ if map.has_key?('encoding')
423
+ encoding = map['encoding']
424
+ @encoding = encoding ? Encoding.find(encoding) : encoding
425
+ end
426
+
427
+ @charset = map['charset'] if map.has_key?('charset')
428
+ @exclude = map['exclude'] if map.has_key?('exclude')
429
+ @generator_name = map['generator_name'] if map.has_key?('generator_name')
430
+ @hyperlink_all = map['hyperlink_all'] if map.has_key?('hyperlink_all')
431
+ @line_numbers = map['line_numbers'] if map.has_key?('line_numbers')
432
+ @locale_name = map['locale_name'] if map.has_key?('locale_name')
433
+ @locale_dir = map['locale_dir'] if map.has_key?('locale_dir')
434
+ @main_page = map['main_page'] if map.has_key?('main_page')
435
+ @markup = map['markup'] if map.has_key?('markup')
436
+ @op_dir = map['op_dir'] if map.has_key?('op_dir')
437
+ @show_hash = map['show_hash'] if map.has_key?('show_hash')
438
+ @tab_width = map['tab_width'] if map.has_key?('tab_width')
439
+ @template_dir = map['template_dir'] if map.has_key?('template_dir')
440
+ @title = map['title'] if map.has_key?('title')
441
+ @visibility = map['visibility'] if map.has_key?('visibility')
442
+ @webcvs = map['webcvs'] if map.has_key?('webcvs')
443
+
444
+ if map.has_key?('rdoc_include')
445
+ @rdoc_include = sanitize_path map['rdoc_include']
446
+ end
447
+ if map.has_key?('static_path')
448
+ @static_path = sanitize_path map['static_path']
449
+ end
450
+ end
451
+
420
452
  def == other # :nodoc:
421
453
  self.class === other and
422
454
  @encoding == other.encoding and
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- require 'time'
3
2
 
4
3
  ##
5
4
  # A ChangeLog file parser.
@@ -106,14 +105,32 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
106
105
  entries.group_by do |title, _|
107
106
  begin
108
107
  time = @time_cache[title]
109
- (time || Time.parse(title)).strftime '%Y-%m-%d'
108
+ (time || parse_date(title)).strftime '%Y-%m-%d'
110
109
  rescue NoMethodError, ArgumentError
111
110
  time, = title.split ' ', 2
112
- Time.parse(time).strftime '%Y-%m-%d'
111
+ parse_date(time).strftime '%Y-%m-%d'
113
112
  end
114
113
  end
115
114
  end
116
115
 
116
+ ##
117
+ # Parse date in ISO-8601, RFC-2822, or default of Git
118
+
119
+ def parse_date(date)
120
+ case date
121
+ when /\A\s*(\d+)-(\d+)-(\d+)(?:[ T](\d+):(\d+):(\d+) *([-+]\d\d):?(\d\d))?\b/
122
+ Time.new($1, $2, $3, $4, $5, $6, ("#{$7}:#{$8}" if $7))
123
+ when /\A\s*\w{3}, +(\d+) (\w{3}) (\d+) (\d+):(\d+):(\d+) *(?:([-+]\d\d):?(\d\d))\b/
124
+ Time.new($3, $2, $1, $4, $5, $6, ("#{$7}:#{$8}" if $7))
125
+ when /\A\s*\w{3} (\w{3}) +(\d+) (\d+) (\d+):(\d+):(\d+) *(?:([-+]\d\d):?(\d\d))\b/
126
+ Time.new($3, $1, $2, $4, $5, $6, ("#{$7}:#{$8}" if $7))
127
+ when /\A\s*\w{3} (\w{3}) +(\d+) (\d+):(\d+):(\d+) (\d+)\b/
128
+ Time.new($6, $1, $2, $3, $4, $5)
129
+ else
130
+ raise ArgumentError, "bad date: #{date}"
131
+ end
132
+ end
133
+
117
134
  ##
118
135
  # Parses the entries in the ChangeLog.
119
136
  #
@@ -131,6 +148,13 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
131
148
 
132
149
  def parse_entries
133
150
  @time_cache ||= {}
151
+
152
+ if /\A((?:.*\n){,3})commit\s/ =~ @content
153
+ class << self; prepend Git; end
154
+ parse_info($1)
155
+ return parse_entries
156
+ end
157
+
134
158
  entries = []
135
159
  entry_name = nil
136
160
  entry_body = []
@@ -145,19 +169,10 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
145
169
  entry_name = $&
146
170
 
147
171
  begin
148
- time = Time.parse entry_name
172
+ time = parse_date entry_name
149
173
  @time_cache[entry_name] = time
150
- # HACK Ruby 1.8 does not raise ArgumentError for Time.parse "Other"
151
- entry_name = nil unless entry_name =~ /#{time.year}/
152
- rescue NoMethodError
153
- # HACK Ruby 2.1.2 and earlier raises NoMethodError if time part is absent
154
- entry_name.split ' ', 2
155
174
  rescue ArgumentError
156
- if /out of range/ =~ $!.message
157
- Time.parse(entry_name.split(' ', 2)[0]) rescue entry_name = nil
158
- else
159
- entry_name = nil
160
- end
175
+ entry_name = nil
161
176
  end
162
177
 
163
178
  entry_body = []
@@ -190,6 +205,7 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
190
205
 
191
206
  def scan
192
207
  @time_cache = {}
208
+
193
209
  entries = parse_entries
194
210
  grouped_entries = group_entries entries
195
211
 
@@ -200,5 +216,120 @@ class RDoc::Parser::ChangeLog < RDoc::Parser
200
216
  @top_level
201
217
  end
202
218
 
219
+ module Git
220
+ def parse_info(info)
221
+ /^\s*base-url\s*=\s*(.*\S)/ =~ info
222
+ @base_url = $1
223
+ end
224
+
225
+ def parse_entries
226
+ entries = []
227
+
228
+ @content.scan(/^commit\s+(\h{20})\h*\n((?:.+\n)*)\n((?: {4}.*\n+)*)/) do
229
+ entry_name, header, entry_body = $1, $2, $3.gsub(/^ {4}/, '')
230
+ # header = header.scan(/^ *(\S+?): +(.*)/).to_h
231
+ # date = header["CommitDate"] || header["Date"]
232
+ date = header[/^ *(?:Author)?Date: +(.*)/, 1]
233
+ author = header[/^ *Author: +(.*)/, 1]
234
+ begin
235
+ time = parse_date(header[/^ *CommitDate: +(.*)/, 1] || date)
236
+ @time_cache[entry_name] = time
237
+ author.sub!(/\s*<(.*)>/, '')
238
+ email = $1
239
+ entries << [entry_name, [author, email, date, entry_body]]
240
+ rescue ArgumentError
241
+ end
242
+ end
243
+
244
+ entries
245
+ end
246
+
247
+ def create_entries entries
248
+ # git log entries have no strictly itemized style like the old
249
+ # style, just assume Markdown.
250
+ entries.map do |commit, entry|
251
+ LogEntry.new(@base_url, commit, *entry)
252
+ end
253
+ end
254
+
255
+ LogEntry = Struct.new(:base, :commit, :author, :email, :date, :contents) do
256
+ HEADING_LEVEL = 3
257
+
258
+ def initialize(base, commit, author, email, date, contents)
259
+ case contents
260
+ when String
261
+ contents = RDoc::Markdown.parse(contents).parts.each do |body|
262
+ case body
263
+ when RDoc::Markup::Heading
264
+ body.level += HEADING_LEVEL + 1
265
+ end
266
+ end
267
+ case first = contents[0]
268
+ when RDoc::Markup::Paragraph
269
+ contents[0] = RDoc::Markup::Heading.new(HEADING_LEVEL + 1, first.text)
270
+ end
271
+ end
272
+ super
273
+ end
274
+
275
+ def level
276
+ HEADING_LEVEL
277
+ end
278
+
279
+ def aref
280
+ "label-#{commit}"
281
+ end
282
+
283
+ def label context = nil
284
+ aref
285
+ end
286
+
287
+ def text
288
+ case base
289
+ when nil
290
+ "#{date}"
291
+ when /%s/
292
+ "{#{date}}[#{base % commit}]"
293
+ else
294
+ "{#{date}}[#{base}#{commit}]"
295
+ end + " {#{author}}[mailto:#{email}]"
296
+ end
297
+
298
+ def accept visitor
299
+ visitor.accept_heading self
300
+ begin
301
+ if visitor.respond_to?(:code_object=)
302
+ code_object = visitor.code_object
303
+ visitor.code_object = self
304
+ end
305
+ contents.each do |body|
306
+ body.accept visitor
307
+ end
308
+ ensure
309
+ if visitor.respond_to?(:code_object)
310
+ visitor.code_object = code_object
311
+ end
312
+ end
313
+ end
314
+
315
+ def pretty_print q # :nodoc:
316
+ q.group(2, '[log_entry: ', ']') do
317
+ q.text commit
318
+ q.text ','
319
+ q.breakable
320
+ q.group(2, '[date: ', ']') { q.text date }
321
+ q.text ','
322
+ q.breakable
323
+ q.group(2, '[author: ', ']') { q.text author }
324
+ q.text ','
325
+ q.breakable
326
+ q.group(2, '[email: ', ']') { q.text email }
327
+ q.text ','
328
+ q.breakable
329
+ q.pp contents
330
+ end
331
+ end
332
+ end
333
+ end
203
334
  end
204
335