diff options
-rw-r--r-- | lib/rdoc/generator/template/darkfish/css/rdoc.css | 20 | ||||
-rw-r--r-- | lib/rdoc/markdown.rb | 442 | ||||
-rw-r--r-- | lib/rdoc/markup.rb | 1 | ||||
-rw-r--r-- | lib/rdoc/markup/attr_span.rb | 10 | ||||
-rw-r--r-- | lib/rdoc/markup/attribute_manager.rb | 121 | ||||
-rw-r--r-- | lib/rdoc/markup/table.rb | 47 | ||||
-rw-r--r-- | lib/rdoc/markup/to_html.rb | 27 | ||||
-rw-r--r-- | lib/rdoc/markup/to_joined_paragraph.rb | 1 | ||||
-rw-r--r-- | lib/rdoc/markup/to_rdoc.rb | 28 | ||||
-rw-r--r-- | lib/rdoc/markup/to_table_of_contents.rb | 1 | ||||
-rw-r--r-- | lib/rdoc/options.rb | 34 | ||||
-rw-r--r-- | lib/rdoc/parser/changelog.rb | 159 | ||||
-rw-r--r-- | lib/rdoc/rdoc.rb | 11 | ||||
-rw-r--r-- | lib/rdoc/version.rb | 2 | ||||
-rw-r--r-- | test/rdoc/test_rdoc_markdown.rb | 59 | ||||
-rw-r--r-- | test/rdoc/test_rdoc_markup_attribute_manager.rb | 25 | ||||
-rw-r--r-- | test/rdoc/test_rdoc_markup_to_html.rb | 32 | ||||
-rw-r--r-- | test/rdoc/test_rdoc_parser_changelog.rb | 167 | ||||
-rw-r--r-- | test/rdoc/test_rdoc_rdoc.rb | 35 | ||||
-rw-r--r-- | test/rdoc/test_rdoc_top_level.rb | 3 | ||||
-rw-r--r-- | version.h | 6 |
21 files changed, 1151 insertions, 80 deletions
diff --git a/lib/rdoc/generator/template/darkfish/css/rdoc.css b/lib/rdoc/generator/template/darkfish/css/rdoc.css index 35aff77fcb..ebe2e93af6 100644 --- a/lib/rdoc/generator/template/darkfish/css/rdoc.css +++ b/lib/rdoc/generator/template/darkfish/css/rdoc.css @@ -79,6 +79,25 @@ pre { border-radius: 0.2em; } +table { + margin: 0; + border-spacing: 0; + border-collapse: collapse; +} + +table tr th, table tr td { + padding: 0.2em 0.4em; + border: 1px solid #ccc; +} + +table tr th { + background-color: #eceaed; +} + +table tr:nth-child(even) td { + background-color: #f5f4f6; +} + /* @group Generic Classes */ .initially-hidden { @@ -166,6 +185,7 @@ nav { width: 260px; font-family: Helvetica, sans-serif; font-size: 14px; + border-right: 1px solid #ccc; } main { diff --git a/lib/rdoc/markdown.rb b/lib/rdoc/markdown.rb index 903e744105..15beaefcca 100644 --- a/lib/rdoc/markdown.rb +++ b/lib/rdoc/markdown.rb @@ -897,7 +897,7 @@ class RDoc::Markdown return _tmp end - # Block = @BlankLine* (BlockQuote | Verbatim | CodeFence | Note | Reference | HorizontalRule | Heading | OrderedList | BulletList | DefinitionList | HtmlBlock | StyleBlock | Para | Plain) + # Block = @BlankLine* (BlockQuote | Verbatim | CodeFence | Table | Note | Reference | HorizontalRule | Heading | OrderedList | BulletList | DefinitionList | HtmlBlock | StyleBlock | Para | Plain) def _Block _save = self.pos @@ -923,6 +923,9 @@ class RDoc::Markdown _tmp = apply(:_CodeFence) break if _tmp self.pos = _save2 + _tmp = apply(:_Table) + break if _tmp + self.pos = _save2 _tmp = apply(:_Note) break if _tmp self.pos = _save2 @@ -9320,7 +9323,7 @@ class RDoc::Markdown return _tmp end - # Inlines = (!@Endline Inline:i { i } | @Endline:c &Inline { c })+:chunks @Endline? { chunks } + # Inlines = (!@Endline Inline:i { i } | @Endline:c !(&{ github? } Ticks3 /[^`\n]*$/) &Inline { c })+:chunks @Endline? { chunks } def _Inlines _save = self.pos @@ -9367,12 +9370,41 @@ class RDoc::Markdown break end _save6 = self.pos - _tmp = apply(:_Inline) + + _save7 = self.pos + while true # sequence + _save8 = self.pos + _tmp = begin; github? ; end + self.pos = _save8 + unless _tmp + self.pos = _save7 + break + end + _tmp = apply(:_Ticks3) + unless _tmp + self.pos = _save7 + break + end + _tmp = scan(/\A(?-mix:[^`\n]*$)/) + unless _tmp + self.pos = _save7 + end + break + end # end sequence + + _tmp = _tmp ? nil : true self.pos = _save6 unless _tmp self.pos = _save5 break end + _save9 = self.pos + _tmp = apply(:_Inline) + self.pos = _save9 + unless _tmp + self.pos = _save5 + break + end @result = begin; c ; end _tmp = true unless _tmp @@ -9390,61 +9422,90 @@ class RDoc::Markdown _ary << @result while true - _save7 = self.pos + _save10 = self.pos while true # choice - _save8 = self.pos + _save11 = self.pos while true # sequence - _save9 = self.pos + _save12 = self.pos _tmp = _Endline() _tmp = _tmp ? nil : true - self.pos = _save9 + self.pos = _save12 unless _tmp - self.pos = _save8 + self.pos = _save11 break end _tmp = apply(:_Inline) i = @result unless _tmp - self.pos = _save8 + self.pos = _save11 break end @result = begin; i ; end _tmp = true unless _tmp - self.pos = _save8 + self.pos = _save11 end break end # end sequence break if _tmp - self.pos = _save7 + self.pos = _save10 - _save10 = self.pos + _save13 = self.pos while true # sequence _tmp = _Endline() c = @result unless _tmp - self.pos = _save10 + self.pos = _save13 break end - _save11 = self.pos + _save14 = self.pos + + _save15 = self.pos + while true # sequence + _save16 = self.pos + _tmp = begin; github? ; end + self.pos = _save16 + unless _tmp + self.pos = _save15 + break + end + _tmp = apply(:_Ticks3) + unless _tmp + self.pos = _save15 + break + end + _tmp = scan(/\A(?-mix:[^`\n]*$)/) + unless _tmp + self.pos = _save15 + end + break + end # end sequence + + _tmp = _tmp ? nil : true + self.pos = _save14 + unless _tmp + self.pos = _save13 + break + end + _save17 = self.pos _tmp = apply(:_Inline) - self.pos = _save11 + self.pos = _save17 unless _tmp - self.pos = _save10 + self.pos = _save13 break end @result = begin; c ; end _tmp = true unless _tmp - self.pos = _save10 + self.pos = _save13 end break end # end sequence break if _tmp - self.pos = _save7 + self.pos = _save10 break end # end choice @@ -9461,11 +9522,11 @@ class RDoc::Markdown self.pos = _save break end - _save12 = self.pos + _save18 = self.pos _tmp = _Endline() unless _tmp _tmp = true - self.pos = _save12 + self.pos = _save18 end unless _tmp self.pos = _save @@ -15847,6 +15908,338 @@ class RDoc::Markdown return _tmp end + # Table = &{ github? } TableRow:header TableLine:line TableRow+:body { table = RDoc::Markup::Table.new(header, line, body) } + def _Table + + _save = self.pos + while true # sequence + _save1 = self.pos + _tmp = begin; github? ; end + self.pos = _save1 + unless _tmp + self.pos = _save + break + end + _tmp = apply(:_TableRow) + header = @result + unless _tmp + self.pos = _save + break + end + _tmp = apply(:_TableLine) + line = @result + unless _tmp + self.pos = _save + break + end + _save2 = self.pos + _ary = [] + _tmp = apply(:_TableRow) + if _tmp + _ary << @result + while true + _tmp = apply(:_TableRow) + _ary << @result if _tmp + break unless _tmp + end + _tmp = true + @result = _ary + else + self.pos = _save2 + end + body = @result + unless _tmp + self.pos = _save + break + end + @result = begin; table = RDoc::Markup::Table.new(header, line, body) ; end + _tmp = true + unless _tmp + self.pos = _save + end + break + end # end sequence + + set_failed_rule :_Table unless _tmp + return _tmp + end + + # TableRow = TableItem+:row "|" @Newline { row } + def _TableRow + + _save = self.pos + while true # sequence + _save1 = self.pos + _ary = [] + _tmp = apply(:_TableItem) + if _tmp + _ary << @result + while true + _tmp = apply(:_TableItem) + _ary << @result if _tmp + break unless _tmp + end + _tmp = true + @result = _ary + else + self.pos = _save1 + end + row = @result + unless _tmp + self.pos = _save + break + end + _tmp = match_string("|") + unless _tmp + self.pos = _save + break + end + _tmp = _Newline() + unless _tmp + self.pos = _save + break + end + @result = begin; row ; end + _tmp = true + unless _tmp + self.pos = _save + end + break + end # end sequence + + set_failed_rule :_TableRow unless _tmp + return _tmp + end + + # TableItem = "|" < (!"|" !@Newline .)+ > { text.strip } + def _TableItem + + _save = self.pos + while true # sequence + _tmp = match_string("|") + unless _tmp + self.pos = _save + break + end + _text_start = self.pos + _save1 = self.pos + + _save2 = self.pos + while true # sequence + _save3 = self.pos + _tmp = match_string("|") + _tmp = _tmp ? nil : true + self.pos = _save3 + unless _tmp + self.pos = _save2 + break + end + _save4 = self.pos + _tmp = _Newline() + _tmp = _tmp ? nil : true + self.pos = _save4 + unless _tmp + self.pos = _save2 + break + end + _tmp = get_byte + unless _tmp + self.pos = _save2 + end + break + end # end sequence + + if _tmp + while true + + _save5 = self.pos + while true # sequence + _save6 = self.pos + _tmp = match_string("|") + _tmp = _tmp ? nil : true + self.pos = _save6 + unless _tmp + self.pos = _save5 + break + end + _save7 = self.pos + _tmp = _Newline() + _tmp = _tmp ? nil : true + self.pos = _save7 + unless _tmp + self.pos = _save5 + break + end + _tmp = get_byte + unless _tmp + self.pos = _save5 + end + break + end # end sequence + + break unless _tmp + end + _tmp = true + else + self.pos = _save1 + end + if _tmp + text = get_text(_text_start) + end + unless _tmp + self.pos = _save + break + end + @result = begin; text.strip ; end + _tmp = true + unless _tmp + self.pos = _save + end + break + end # end sequence + + set_failed_rule :_TableItem unless _tmp + return _tmp + end + + # TableLine = TableColumn+:line "|" @Newline { line } + def _TableLine + + _save = self.pos + while true # sequence + _save1 = self.pos + _ary = [] + _tmp = apply(:_TableColumn) + if _tmp + _ary << @result + while true + _tmp = apply(:_TableColumn) + _ary << @result if _tmp + break unless _tmp + end + _tmp = true + @result = _ary + else + self.pos = _save1 + end + line = @result + unless _tmp + self.pos = _save + break + end + _tmp = match_string("|") + unless _tmp + self.pos = _save + break + end + _tmp = _Newline() + unless _tmp + self.pos = _save + break + end + @result = begin; line ; end + _tmp = true + unless _tmp + self.pos = _save + end + break + end # end sequence + + set_failed_rule :_TableLine unless _tmp + return _tmp + end + + # TableColumn = "|" < ("-"+ ":"? | ":" "-"*) > { text.start_with?(":") ? :left : text.end_with?(":") ? :right : nil } + def _TableColumn + + _save = self.pos + while true # sequence + _tmp = match_string("|") + unless _tmp + self.pos = _save + break + end + _text_start = self.pos + + _save1 = self.pos + while true # choice + + _save2 = self.pos + while true # sequence + _save3 = self.pos + _tmp = match_string("-") + if _tmp + while true + _tmp = match_string("-") + break unless _tmp + end + _tmp = true + else + self.pos = _save3 + end + unless _tmp + self.pos = _save2 + break + end + _save4 = self.pos + _tmp = match_string(":") + unless _tmp + _tmp = true + self.pos = _save4 + end + unless _tmp + self.pos = _save2 + end + break + end # end sequence + + break if _tmp + self.pos = _save1 + + _save5 = self.pos + while true # sequence + _tmp = match_string(":") + unless _tmp + self.pos = _save5 + break + end + while true + _tmp = match_string("-") + break unless _tmp + end + _tmp = true + unless _tmp + self.pos = _save5 + end + break + end # end sequence + + break if _tmp + self.pos = _save1 + break + end # end choice + + if _tmp + text = get_text(_text_start) + end + unless _tmp + self.pos = _save + break + end + @result = begin; text.start_with?(":") ? :left : + text.end_with?(":") ? :right : nil + ; end + _tmp = true + unless _tmp + self.pos = _save + end + break + end # end sequence + + set_failed_rule :_TableColumn unless _tmp + return _tmp + end + # DefinitionList = &{ definition_lists? } DefinitionListItem+:list { RDoc::Markup::List.new :NOTE, *list.flatten } def _DefinitionList @@ -16046,7 +16439,7 @@ class RDoc::Markdown Rules = {} Rules[:_root] = rule_info("root", "Doc") Rules[:_Doc] = rule_info("Doc", "BOM? Block*:a { RDoc::Markup::Document.new(*a.compact) }") - Rules[:_Block] = rule_info("Block", "@BlankLine* (BlockQuote | Verbatim | CodeFence | Note | Reference | HorizontalRule | Heading | OrderedList | BulletList | DefinitionList | HtmlBlock | StyleBlock | Para | Plain)") + Rules[:_Block] = rule_info("Block", "@BlankLine* (BlockQuote | Verbatim | CodeFence | Table | Note | Reference | HorizontalRule | Heading | OrderedList | BulletList | DefinitionList | HtmlBlock | StyleBlock | Para | Plain)") Rules[:_Para] = rule_info("Para", "@NonindentSpace Inlines:a @BlankLine+ { paragraph a }") Rules[:_Plain] = rule_info("Plain", "Inlines:a { paragraph a }") Rules[:_AtxInline] = rule_info("AtxInline", "!@Newline !(@Sp /\#*/ @Sp @Newline) Inline") @@ -16190,7 +16583,7 @@ class RDoc::Markdown Rules[:_StyleClose] = rule_info("StyleClose", "\"<\" Spnl \"/\" (\"style\" | \"STYLE\") Spnl \">\"") Rules[:_InStyleTags] = rule_info("InStyleTags", "StyleOpen (!StyleClose .)* StyleClose") Rules[:_StyleBlock] = rule_info("StyleBlock", "< InStyleTags > @BlankLine* { if css? then RDoc::Markup::Raw.new text end }") - Rules[:_Inlines] = rule_info("Inlines", "(!@Endline Inline:i { i } | @Endline:c &Inline { c })+:chunks @Endline? { chunks }") + Rules[:_Inlines] = rule_info("Inlines", "(!@Endline Inline:i { i } | @Endline:c !(&{ github? } Ticks3 /[^`\\n]*$/) &Inline { c })+:chunks @Endline? { chunks }") Rules[:_Inline] = rule_info("Inline", "(Str | @Endline | UlOrStarLine | @Space | Strong | Emph | Strike | Image | Link | NoteReference | InlineNote | Code | RawHtml | Entity | EscapedChar | Symbol)") Rules[:_Space] = rule_info("Space", "@Spacechar+ { \" \" }") Rules[:_Str] = rule_info("Str", "@StartList:a < @NormalChar+ > { a = text } (StrChunk:c { a << c })* { a }") @@ -16279,6 +16672,11 @@ class RDoc::Markdown Rules[:_Notes] = rule_info("Notes", "(Note | SkipBlock)*") Rules[:_RawNoteBlock] = rule_info("RawNoteBlock", "@StartList:a (!@BlankLine OptionallyIndentedLine:l { a << l })+ < @BlankLine* > { a << text } { a }") Rules[:_CodeFence] = rule_info("CodeFence", "&{ github? } Ticks3 (@Sp StrChunk:format)? Spnl < ((!\"`\" Nonspacechar)+ | !Ticks3 /`+/ | Spacechar | @Newline)+ > Ticks3 @Sp @Newline* { verbatim = RDoc::Markup::Verbatim.new text verbatim.format = format.intern if format.instance_of?(String) verbatim }") + Rules[:_Table] = rule_info("Table", "&{ github? } TableRow:header TableLine:line TableRow+:body { table = RDoc::Markup::Table.new(header, line, body) }") + Rules[:_TableRow] = rule_info("TableRow", "TableItem+:row \"|\" @Newline { row }") + Rules[:_TableItem] = rule_info("TableItem", "\"|\" < (!\"|\" !@Newline .)+ > { text.strip }") + Rules[:_TableLine] = rule_info("TableLine", "TableColumn+:line \"|\" @Newline { line }") + Rules[:_TableColumn] = rule_info("TableColumn", "\"|\" < (\"-\"+ \":\"? | \":\" \"-\"*) > { text.start_with?(\":\") ? :left : text.end_with?(\":\") ? :right : nil }") Rules[:_DefinitionList] = rule_info("DefinitionList", "&{ definition_lists? } DefinitionListItem+:list { RDoc::Markup::List.new :NOTE, *list.flatten }") Rules[:_DefinitionListItem] = rule_info("DefinitionListItem", "DefinitionListLabel+:label DefinitionListDefinition+:defns { list_items = [] list_items << RDoc::Markup::ListItem.new(label, defns.shift) list_items.concat defns.map { |defn| RDoc::Markup::ListItem.new nil, defn } unless list_items.empty? list_items }") Rules[:_DefinitionListLabel] = rule_info("DefinitionListLabel", "StrChunk:label @Sp @Newline { label }") diff --git a/lib/rdoc/markup.rb b/lib/rdoc/markup.rb index fd59fca314..92aed757cf 100644 --- a/lib/rdoc/markup.rb +++ b/lib/rdoc/markup.rb @@ -843,6 +843,7 @@ https://github.com/ruby/rdoc/issues autoload :List, 'rdoc/markup/list' autoload :ListItem, 'rdoc/markup/list_item' autoload :Paragraph, 'rdoc/markup/paragraph' + autoload :Table, 'rdoc/markup/table' autoload :Raw, 'rdoc/markup/raw' autoload :Rule, 'rdoc/markup/rule' autoload :Verbatim, 'rdoc/markup/verbatim' diff --git a/lib/rdoc/markup/attr_span.rb b/lib/rdoc/markup/attr_span.rb index 63aace60d2..20ef11cd6d 100644 --- a/lib/rdoc/markup/attr_span.rb +++ b/lib/rdoc/markup/attr_span.rb @@ -7,16 +7,22 @@ class RDoc::Markup::AttrSpan ## # Creates a new AttrSpan for +length+ characters - def initialize(length) + def initialize(length, exclusive) @attrs = Array.new(length, 0) + @exclusive = exclusive end ## # Toggles +bits+ from +start+ to +length+ def set_attrs(start, length, bits) + updated = false for i in start ... (start+length) - @attrs[i] |= bits + if (@exclusive & @attrs[i]) == 0 || (@exclusive & bits) != 0 + @attrs[i] |= bits + updated = true + end end + updated end ## diff --git a/lib/rdoc/markup/attribute_manager.rb b/lib/rdoc/markup/attribute_manager.rb index f052bc8b01..50764510f3 100644 --- a/lib/rdoc/markup/attribute_manager.rb +++ b/lib/rdoc/markup/attribute_manager.rb @@ -59,6 +59,10 @@ class RDoc::Markup::AttributeManager attr_reader :regexp_handlings ## + # A bits of exclusive maps + attr_reader :exclusive_bitmap + + ## # Creates a new attribute manager that understands bold, emphasized and # teletype text. @@ -68,17 +72,18 @@ class RDoc::Markup::AttributeManager @protectable = %w[<] @regexp_handlings = [] @word_pair_map = {} + @exclusive_bitmap = 0 @attributes = RDoc::Markup::Attributes.new - add_word_pair "*", "*", :BOLD - add_word_pair "_", "_", :EM - add_word_pair "+", "+", :TT + add_word_pair "*", "*", :BOLD, true + add_word_pair "_", "_", :EM, true + add_word_pair "+", "+", :TT, true - add_html "em", :EM - add_html "i", :EM - add_html "b", :BOLD - add_html "tt", :TT - add_html "code", :TT + add_html "em", :EM, true + add_html "i", :EM, true + add_html "b", :BOLD, true + add_html "tt", :TT, true + add_html "code", :TT, true end ## @@ -122,29 +127,67 @@ class RDoc::Markup::AttributeManager res end + def exclusive?(attr) + (attr & @exclusive_bitmap) != 0 + end + + NON_PRINTING_START = "\1" # :nodoc: + NON_PRINTING_END = "\2" # :nodoc: + ## # Map attributes like <b>text</b>to the sequence # \001\002<char>\001\003<char>, where <char> is a per-attribute specific # character - def convert_attrs(str, attrs) + def convert_attrs(str, attrs, exclusive = false) + convert_attrs_matching_word_pairs(str, attrs, exclusive) + convert_attrs_word_pair_map(str, attrs, exclusive) + end + + def convert_attrs_matching_word_pairs(str, attrs, exclusive) # first do matching ones - tags = @matching_word_pairs.keys.join("") + tags = @matching_word_pairs.select { |start, bitmap| + if exclusive && exclusive?(bitmap) + true + elsif !exclusive && !exclusive?(bitmap) + true + else + false + end + }.keys + return if tags.empty? + all_tags = @matching_word_pairs.keys - re = /(^|\W)([#{tags}])([#\\]?[\w:.\/-]+?\S?)\2(\W|$)/ + re = /(^|\W|[#{all_tags.join("")}])([#{tags.join("")}])(\2*[#\\]?[\w:.\/\[\]-]+?\S?)\2(?!\2)([#{all_tags.join("")}]|\W|$)/ - 1 while str.gsub!(re) do + 1 while str.gsub!(re) { |orig| attr = @matching_word_pairs[$2] - attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr) - $1 + NULL * $2.length + $3 + NULL * $2.length + $4 - end + attr_updated = attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr) + if attr_updated + $1 + NULL * $2.length + $3 + NULL * $2.length + $4 + else + $1 + NON_PRINTING_START + $2 + NON_PRINTING_END + $3 + NON_PRINTING_START + $2 + NON_PRINTING_END + $4 + end + } + str.delete!(NON_PRINTING_START + NON_PRINTING_END) + end + def convert_attrs_word_pair_map(str, attrs, exclusive) # then non-matching unless @word_pair_map.empty? then @word_pair_map.each do |regexp, attr| - str.gsub!(regexp) { - attrs.set_attrs($`.length + $1.length, $2.length, attr) - NULL * $1.length + $2 + NULL * $3.length + if !exclusive + next if exclusive?(attr) + else + next if !exclusive?(attr) + end + 1 while str.gsub!(regexp) { |orig| + updated = attrs.set_attrs($`.length + $1.length, $2.length, attr) + if updated + NULL * $1.length + $2 + NULL * $3.length + else + orig + end } end end @@ -153,10 +196,18 @@ class RDoc::Markup::AttributeManager ## # Converts HTML tags to RDoc attributes - def convert_html(str, attrs) - tags = @html_tags.keys.join '|' + def convert_html(str, attrs, exclusive = false) + tags = @html_tags.select { |start, bitmap| + if exclusive && exclusive?(bitmap) + true + elsif !exclusive && !exclusive?(bitmap) + true + else + false + end + }.keys.join '|' - 1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) { + 1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) { |orig| attr = @html_tags[$1.downcase] html_length = $1.length + 2 seq = NULL * html_length @@ -168,8 +219,13 @@ class RDoc::Markup::AttributeManager ## # Converts regexp handling sequences to RDoc attributes - def convert_regexp_handlings str, attrs + def convert_regexp_handlings str, attrs, exclusive = false @regexp_handlings.each do |regexp, attribute| + if exclusive + next if !exclusive?(attribute) + else + next if exclusive?(attribute) + end str.scan(regexp) do capture = $~.size == 1 ? 0 : 1 @@ -205,7 +261,7 @@ class RDoc::Markup::AttributeManager # # am.add_word_pair '*', '*', :BOLD - def add_word_pair(start, stop, name) + def add_word_pair(start, stop, name, exclusive = false) raise ArgumentError, "Word flags may not start with '<'" if start[0,1] == '<' @@ -220,6 +276,8 @@ class RDoc::Markup::AttributeManager @protectable << start[0,1] @protectable.uniq! + + @exclusive_bitmap |= bitmap if exclusive end ## @@ -228,8 +286,10 @@ class RDoc::Markup::AttributeManager # # am.add_html 'em', :EM - def add_html(tag, name) - @html_tags[tag.downcase] = @attributes.bitmap_for name + def add_html(tag, name, exclusive = false) + bitmap = @attributes.bitmap_for name + @html_tags[tag.downcase] = bitmap + @exclusive_bitmap |= bitmap if exclusive end ## @@ -238,8 +298,10 @@ class RDoc::Markup::AttributeManager # # @am.add_regexp_handling(/((https?:)\S+\w)/, :HYPERLINK) - def add_regexp_handling pattern, name - @regexp_handlings << [pattern, @attributes.bitmap_for(name)] + def add_regexp_handling pattern, name, exclusive = false + bitmap = @attributes.bitmap_for(name) + @regexp_handlings << [pattern, bitmap] + @exclusive_bitmap |= bitmap if exclusive end ## @@ -250,8 +312,11 @@ class RDoc::Markup::AttributeManager mask_protected_sequences - @attrs = RDoc::Markup::AttrSpan.new @str.length + @attrs = RDoc::Markup::AttrSpan.new @str.length, @exclusive_bitmap + convert_attrs @str, @attrs, true + convert_html @str, @attrs, true + convert_regexp_handlings @str, @attrs, true convert_attrs @str, @attrs convert_html @str, @attrs convert_regexp_handlings @str, @attrs diff --git a/lib/rdoc/markup/table.rb b/lib/rdoc/markup/table.rb new file mode 100644 index 0000000000..7bcb10aff3 --- /dev/null +++ b/lib/rdoc/markup/table.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true +## +# A section of table + +class RDoc::Markup::Table + attr_accessor :header, :align, :body + + def initialize header, align, body + @header, @align, @body = header, align, body + end + + def == other + self.class == other.class and + @header == other.header and + @align == other.align and + @body == other.body + end + + def accept visitor + visitor.accept_table @header, @body, @align + end + + def pretty_print q # :nodoc: + q.group 2, '[Table: ', ']' do + q.group 2, '[Head: ', ']' do + q.seplist @header.zip(@align) do |text, align| + q.pp text + if align + q.text ":" + q.breakable + q.text align.to_s + end + end + end + q.breakable + q.group 2, '[Body: ', ']' do + q.seplist @body do |body| + q.group 2, '[', ']' do + q.seplist body do |text| + q.pp text + end + end + end + end + end + end +end diff --git a/lib/rdoc/markup/to_html.rb b/lib/rdoc/markup/to_html.rb index 3b1b0e9d40..8ae4dd4720 100644 --- a/lib/rdoc/markup/to_html.rb +++ b/lib/rdoc/markup/to_html.rb @@ -314,6 +314,29 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter @res << raw.parts.join("\n") end + ## + # Adds +table+ to the output + + def accept_table header, body, aligns + @res << "\n<table role=\"table\">\n<thead>\n<tr>\n" + header.zip(aligns) do |text, align| + @res << '<th' + @res << ' align="' << align << '"' if align + @res << '>' << CGI.escapeHTML(text) << "</th>\n" + end + @res << "</tr>\n</thead>\n<tbody>\n" + body.each do |row| + @res << "<tr>\n" + row.zip(aligns) do |text, align| + @res << '<td' + @res << ' align="' << align << '"' if align + @res << '>' << CGI.escapeHTML(text) << "</td>\n" + end + @res << "</tr>\n" + end + @res << "</tbody>\n</table>\n" + end + # :section: Utilities ## @@ -334,6 +357,10 @@ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then "<img src=\"#{url}\" />" else + if scheme != 'link' and /\.(?:rb|rdoc|md)\z/i =~ url + url = url.sub(%r%\A([./]*)(.*)\z%) { "#$1#{$2.tr('.', '_')}.html" } + end + text = text.sub %r%^#{scheme}:/*%i, '' text = text.sub %r%^[*\^](\d+)$%, '\1' diff --git a/lib/rdoc/markup/to_joined_paragraph.rb b/lib/rdoc/markup/to_joined_paragraph.rb index 795f3f62ee..46e07c94ad 100644 --- a/lib/rdoc/markup/to_joined_paragraph.rb +++ b/lib/rdoc/markup/to_joined_paragraph.rb @@ -41,6 +41,7 @@ class RDoc::Markup::ToJoinedParagraph < RDoc::Markup::Formatter alias accept_raw ignore alias accept_rule ignore alias accept_verbatim ignore + alias accept_table ignore end diff --git a/lib/rdoc/markup/to_rdoc.rb b/lib/rdoc/markup/to_rdoc.rb index 81b16c4973..3cdf4fd08b 100644 --- a/lib/rdoc/markup/to_rdoc.rb +++ b/lib/rdoc/markup/to_rdoc.rb @@ -238,6 +238,34 @@ class RDoc::Markup::ToRdoc < RDoc::Markup::Formatter end ## + # Adds +table+ to the output + + def accept_table header, body, aligns + widths = header.zip(body) do |h, b| + [h.size, b.size].max + end + aligns = aligns.map do |a| + case a + when nil + :center + when :left + :ljust + when :right + :rjust + end + end + @res << header.zip(widths, aligns) do |h, w, a| + h.__send__(a, w) + end.join("|").rstrip << "\n" + @res << widths.map {|w| "-" * w }.join("|") << "\n" + body.each do |row| + @res << row.zip(widths, aligns) do |t, w, a| + t.__send__(a, w) + end.join("|").rstrip << "\n" + end + end + + ## # Applies attribute-specific markup to +text+ using RDoc::AttributeManager def attributes text diff --git a/lib/rdoc/markup/to_table_of_contents.rb b/lib/rdoc/markup/to_table_of_contents.rb index f68b90bcf6..eb8e8faa16 100644 --- a/lib/rdoc/markup/to_table_of_contents.rb +++ b/lib/rdoc/markup/to_table_of_contents.rb @@ -82,6 +82,7 @@ class RDoc::Markup::ToTableOfContents < RDoc::Markup::Formatter alias accept_list_item_end ignore alias accept_list_end_bullet ignore alias accept_list_start ignore + alias accept_table ignore # :startdoc: end diff --git a/lib/rdoc/options.rb b/lib/rdoc/options.rb index 13c1abae0a..13b7ba5c6c 100644 --- a/lib/rdoc/options.rb +++ b/lib/rdoc/options.rb @@ -338,8 +338,9 @@ class RDoc::Options attr_reader :visibility - def initialize # :nodoc: + def initialize loaded_options = nil # :nodoc: init_ivars + override loaded_options if loaded_options end def init_ivars # :nodoc: @@ -417,6 +418,37 @@ class RDoc::Options init_with map end + def override map # :nodoc: + if map.has_key?('encoding') + encoding = map['encoding'] + @encoding = encoding ? Encoding.find(encoding) : encoding + end + + @charset = map['charset'] if map.has_key?('charset') + @exclude = map['exclude'] if map.has_key?('exclude') + @generator_name = map['generator_name'] if map.has_key?('generator_name') + @hyperlink_all = map['hyperlink_all'] if map.has_key?('hyperlink_all') + @line_numbers = map['line_numbers'] if map.has_key?('line_numbers') + @locale_name = map['locale_name'] if map.has_key?('locale_name') + @locale_dir = map['locale_dir'] if map.has_key?('locale_dir') + @main_page = map['main_page'] if map.has_key?('main_page') + @markup = map['markup'] if map.has_key?('markup') + @op_dir = map['op_dir'] if map.has_key?('op_dir') + @show_hash = map['show_hash'] if map.has_key?('show_hash') + @tab_width = map['tab_width'] if map.has_key?('tab_width') + @template_dir = map['template_dir'] if map.has_key?('template_dir') + @title = map['title'] if map.has_key?('title') + @visibility = map['visibility'] if map.has_key?('visibility') + @webcvs = map['webcvs'] if map.has_key?('webcvs') + + if map.has_key?('rdoc_include') + @rdoc_include = sanitize_path map['rdoc_include'] + end + if map.has_key?('static_path') + @static_path = sanitize_path map['static_path'] + end + end + def == other # :nodoc: self.class === other and @encoding == other.encoding and diff --git a/lib/rdoc/parser/changelog.rb b/lib/rdoc/parser/changelog.rb index 167892f543..9245d49376 100644 --- a/lib/rdoc/parser/changelog.rb +++ b/lib/rdoc/parser/changelog.rb @@ -1,5 +1,4 @@ # frozen_string_literal: true -require 'time' ## # A ChangeLog file parser. @@ -106,15 +105,33 @@ class RDoc::Parser::ChangeLog < RDoc::Parser entries.group_by do |title, _| begin time = @time_cache[title] - (time || Time.parse(title)).strftime '%Y-%m-%d' + (time || parse_date(title)).strftime '%Y-%m-%d' rescue NoMethodError, ArgumentError time, = title.split ' ', 2 - Time.parse(time).strftime '%Y-%m-%d' + parse_date(time).strftime '%Y-%m-%d' end end end ## + # Parse date in ISO-8601, RFC-2822, or default of Git + + def parse_date(date) + case date + when /\A\s*(\d+)-(\d+)-(\d+)(?:[ T](\d+):(\d+):(\d+) *([-+]\d\d):?(\d\d))?\b/ + Time.new($1, $2, $3, $4, $5, $6, ("#{$7}:#{$8}" if $7)) + when /\A\s*\w{3}, +(\d+) (\w{3}) (\d+) (\d+):(\d+):(\d+) *(?:([-+]\d\d):?(\d\d))\b/ + Time.new($3, $2, $1, $4, $5, $6, ("#{$7}:#{$8}" if $7)) + when /\A\s*\w{3} (\w{3}) +(\d+) (\d+) (\d+):(\d+):(\d+) *(?:([-+]\d\d):?(\d\d))\b/ + Time.new($3, $1, $2, $4, $5, $6, ("#{$7}:#{$8}" if $7)) + when /\A\s*\w{3} (\w{3}) +(\d+) (\d+):(\d+):(\d+) (\d+)\b/ + Time.new($6, $1, $2, $3, $4, $5) + else + raise ArgumentError, "bad date: #{date}" + end + end + + ## # Parses the entries in the ChangeLog. # # Returns an Array of each ChangeLog entry in order of parsing. @@ -131,6 +148,13 @@ class RDoc::Parser::ChangeLog < RDoc::Parser def parse_entries @time_cache ||= {} + + if /\A((?:.*\n){,3})commit\s/ =~ @content + class << self; prepend Git; end + parse_info($1) + return parse_entries + end + entries = [] entry_name = nil entry_body = [] @@ -145,19 +169,10 @@ class RDoc::Parser::ChangeLog < RDoc::Parser entry_name = $& begin - time = Time.parse entry_name + time = parse_date entry_name @time_cache[entry_name] = time - # HACK Ruby 1.8 does not raise ArgumentError for Time.parse "Other" - entry_name = nil unless entry_name =~ /#{time.year}/ - rescue NoMethodError - # HACK Ruby 2.1.2 and earlier raises NoMethodError if time part is absent - entry_name.split ' ', 2 rescue ArgumentError - if /out of range/ =~ $!.message - Time.parse(entry_name.split(' ', 2)[0]) rescue entry_name = nil - else - entry_name = nil - end + entry_name = nil end entry_body = [] @@ -190,6 +205,7 @@ class RDoc::Parser::ChangeLog < RDoc::Parser def scan @time_cache = {} + entries = parse_entries grouped_entries = group_entries entries @@ -200,5 +216,120 @@ class RDoc::Parser::ChangeLog < RDoc::Parser @top_level end + module Git + def parse_info(info) + /^\s*base-url\s*=\s*(.*\S)/ =~ info + @base_url = $1 + end + + def parse_entries + entries = [] + + @content.scan(/^commit\s+(\h{20})\h*\n((?:.+\n)*)\n((?: {4}.*\n+)*)/) do + entry_name, header, entry_body = $1, $2, $3.gsub(/^ {4}/, '') + # header = header.scan(/^ *(\S+?): +(.*)/).to_h + # date = header["CommitDate"] || header["Date"] + date = header[/^ *(?:Author)?Date: +(.*)/, 1] + author = header[/^ *Author: +(.*)/, 1] + begin + time = parse_date(header[/^ *CommitDate: +(.*)/, 1] || date) + @time_cache[entry_name] = time + author.sub!(/\s*<(.*)>/, '') + email = $1 + entries << [entry_name, [author, email, date, entry_body]] + rescue ArgumentError + end + end + + entries + end + + def create_entries entries + # git log entries have no strictly itemized style like the old + # style, just assume Markdown. + entries.map do |commit, entry| + LogEntry.new(@base_url, commit, *entry) + end + end + + LogEntry = Struct.new(:base, :commit, :author, :email, :date, :contents) do + HEADING_LEVEL = 3 + + def initialize(base, commit, author, email, date, contents) + case contents + when String + contents = RDoc::Markdown.parse(contents).parts.each do |body| + case body + when RDoc::Markup::Heading + body.level += HEADING_LEVEL + 1 + end + end + case first = contents[0] + when RDoc::Markup::Paragraph + contents[0] = RDoc::Markup::Heading.new(HEADING_LEVEL + 1, first.text) + end + end + super + end + + def level + HEADING_LEVEL + end + + def aref + "label-#{commit}" + end + + def label context = nil + aref + end + + def text + case base + when nil + "#{date}" + when /%s/ + "{#{date}}[#{base % commit}]" + else + "{#{date}}[#{base}#{commit}]" + end + " {#{author}}[mailto:#{email}]" + end + + def accept visitor + visitor.accept_heading self + begin + if visitor.respond_to?(:code_object=) + code_object = visitor.code_object + visitor.code_object = self + end + contents.each do |body| + body.accept visitor + end + ensure + if visitor.respond_to?(:code_object) + visitor.code_object = code_object + end + end + end + + def pretty_print q # :nodoc: + q.group(2, '[log_entry: ', ']') do + q.text commit + q.text ',' + q.breakable + q.group(2, '[date: ', ']') { q.text date } + q.text ',' + q.breakable + q.group(2, '[author: ', ']') { q.text author } + q.text ',' + q.breakable + q.group(2, '[email: ', ']') { q.text email } + q.text ',' + q.breakable + q.pp contents + end + end + end + end end diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb index 93e764c462..bb58513d2c 100644 --- a/lib/rdoc/rdoc.rb +++ b/lib/rdoc/rdoc.rb @@ -166,8 +166,15 @@ class RDoc::RDoc rescue Psych::SyntaxError end + return RDoc::Options.new if options == false # Allow empty file. + raise RDoc::Error, "#{options_file} is not a valid rdoc options file" unless - RDoc::Options === options + RDoc::Options === options or Hash === options + + if Hash === options + # Override the default values with the contents of YAML file. + options = RDoc::Options.new options + end options end @@ -436,7 +443,7 @@ The internal error was: files.reject do |file, *| file =~ /\.(?:class|eps|erb|scpt\.txt|svg|ttf|yml)$/i or (file =~ /tags$/i and - open(file, 'rb') { |io| + File.open(file, 'rb') { |io| io.read(100) =~ /\A(\f\n[^,]+,\d+$|!_TAG_)/ }) end diff --git a/lib/rdoc/version.rb b/lib/rdoc/version.rb index bfb442c5c9..f6e18f7d4d 100644 --- a/lib/rdoc/version.rb +++ b/lib/rdoc/version.rb @@ -3,6 +3,6 @@ module RDoc ## # RDoc version you are using - VERSION = '6.3.0' + VERSION = '6.3.1' end diff --git a/test/rdoc/test_rdoc_markdown.rb b/test/rdoc/test_rdoc_markdown.rb index 9c7a406224..ad53e9473c 100644 --- a/test/rdoc/test_rdoc_markdown.rb +++ b/test/rdoc/test_rdoc_markdown.rb @@ -143,7 +143,7 @@ a block quote end def test_parse_code_github - doc = parse <<-MD + doc = <<-MD Example: ``` @@ -156,11 +156,25 @@ code goes here para("Example:"), verb("code goes here\n")) - assert_equal expected, doc + assert_equal expected, parse(doc) + assert_equal expected, parse(doc.sub(/^\n/, '')) + + @parser.github = false + + expected = + doc(para("Example:"), + para("<code>\n""code goes here\n</code>")) + + assert_equal expected, parse(doc) + + expected = + doc(para("Example:\n<code>\n""code goes here\n</code>")) + + assert_equal expected, parse(doc.sub(/^\n/, '')) end def test_parse_code_github_format - doc = parse <<-MD + doc = <<-MD Example: ``` ruby @@ -176,7 +190,21 @@ code goes here para("Example:"), code) - assert_equal expected, doc + assert_equal expected, parse(doc) + assert_equal expected, parse(doc.sub(/^\n/, '')) + + @parser.github = false + + expected = + doc(para("Example:"), + para("<code>ruby\n""code goes here\n</code>")) + + assert_equal expected, parse(doc) + + expected = + doc(para("Example:\n<code>ruby\n""code goes here\n</code>")) + + assert_equal expected, parse(doc.sub(/^\n/, '')) end def test_parse_definition_list @@ -1012,6 +1040,29 @@ and an extra note.[^2] assert_equal expected, doc end + def test_gfm_table + doc = parse <<~MD + | | |compare-ruby|built-ruby| + |------|:----------------|-----------:|---------:| + |test | 1 | 11.618M| 10.757M| + | | | 1.08x| -| + |test | 10 | 1.849M| 1.723M| + | | | 1.07x| -| + MD + + head = ["", "", "compare-ruby", "built-ruby"] + align = [nil, :left, :right, :right] + body = [ + ["test", "1", "11.618M", "10.757M"], + ["", "", "1.08x", "-"], + ["test", "10", "1.849M", "1.723M"], + ["", "", "1.07x", "-"], + ] + expected = doc(@RM::Table.new(head, align, body)) + + assert_equal expected, doc + end + def parse text @parser.parse text end diff --git a/test/rdoc/test_rdoc_markup_attribute_manager.rb b/test/rdoc/test_rdoc_markup_attribute_manager.rb index a180666867..944364ba89 100644 --- a/test/rdoc/test_rdoc_markup_attribute_manager.rb +++ b/test/rdoc/test_rdoc_markup_attribute_manager.rb @@ -172,22 +172,25 @@ class TestRDocMarkupAttributeManager < RDoc::TestCase def test_convert_attrs str = '+foo+'.dup - attrs = RDoc::Markup::AttrSpan.new str.length + attrs = RDoc::Markup::AttrSpan.new str.length, @am.exclusive_bitmap + @am.convert_attrs str, attrs, true @am.convert_attrs str, attrs assert_equal "\000foo\000", str str = '+:foo:+'.dup - attrs = RDoc::Markup::AttrSpan.new str.length + attrs = RDoc::Markup::AttrSpan.new str.length, @am.exclusive_bitmap + @am.convert_attrs str, attrs, true @am.convert_attrs str, attrs assert_equal "\000:foo:\000", str str = '+x-y+'.dup - attrs = RDoc::Markup::AttrSpan.new str.length + attrs = RDoc::Markup::AttrSpan.new str.length, @am.exclusive_bitmap + @am.convert_attrs str, attrs, true @am.convert_attrs str, attrs assert_equal "\000x-y\000", str @@ -243,6 +246,22 @@ class TestRDocMarkupAttributeManager < RDoc::TestCase output('unhandled <p>tag</p> unchanged') end + def test_exclude_tag + assert_equal '<CODE>aaa</CODE>[:symbol]', output('+aaa+[:symbol]') + assert_equal '<CODE>aaa[:symbol]</CODE>', output('+aaa[:symbol]+') + assert_equal 'aaa[:symbol]', output('aaa[:symbol]') + assert_equal '<B><CODE>index</CODE></B>', output('<b><tt>index</tt></b>') + end + + def test_exclude_tag_flow + assert_equal [@tt_on, "aaa", @tt_off, "[:symbol]"], + @am.flow("+aaa+[:symbol]") + assert_equal [@tt_on, "aaa[:symbol]", @tt_off], + @am.flow("+aaa[:symbol]+") + assert_equal ["aaa[:symbol]"], + @am.flow("aaa[:symbol]") + end + def test_html_like_em_bold assert_equal ["cat ", @em_on, "and ", @em_to_bold, "dog", @bold_off], @am.flow("cat <i>and </i><b>dog</b>") diff --git a/test/rdoc/test_rdoc_markup_to_html.rb b/test/rdoc/test_rdoc_markup_to_html.rb index fb94269064..29da968abc 100644 --- a/test/rdoc/test_rdoc_markup_to_html.rb +++ b/test/rdoc/test_rdoc_markup_to_html.rb @@ -704,6 +704,17 @@ EXPECTED assert_equal "\n<p><a href=\"irc://irc.freenode.net/#ruby-lang\">ruby-lang</a></p>\n", result end + def test_convert_with_exclude_tag + assert_equal "\n<p><code>aaa</code>[:symbol]</p>\n", @to.convert('+aaa+[:symbol]') + assert_equal "\n<p><code>aaa[:symbol]</code></p>\n", @to.convert('+aaa[:symbol]+') + assert_equal "\n<p><a href=\":symbol\">aaa</a></p>\n", @to.convert('aaa[:symbol]') + end + + def test_convert_underscore_adjacent_to_code + assert_equal "\n<p><code>aaa</code>_</p>\n", @to.convert(%q{+aaa+_}) + assert_equal "\n<p>`<code>i386-mswin32_</code><em>MSRTVERSION</em>'</p>\n", @to.convert(%q{`+i386-mswin32_+_MSRTVERSION_'}) + end + def test_gen_url assert_equal '<a href="example">example</a>', @to.gen_url('link:example', 'example') @@ -727,6 +738,27 @@ EXPECTED assert_equal '<img src="https://example.com/image.png" />', @to.gen_url('https://example.com/image.png', 'ignored') end + def test_gen_url_rdoc_file + assert_equal '<a href="doc/example_rdoc.html">example</a>', + @to.gen_url('doc/example.rdoc', 'example') + assert_equal '<a href="../ex_doc/example_rdoc.html">example</a>', + @to.gen_url('../ex.doc/example.rdoc', 'example') + end + + def test_gen_url_md_file + assert_equal '<a href="doc/example_md.html">example</a>', + @to.gen_url('doc/example.md', 'example') + assert_equal '<a href="../ex_doc/example_md.html">example</a>', + @to.gen_url('../ex.doc/example.md', 'example') + end + + def test_gen_url_rb_file + assert_equal '<a href="doc/example_rb.html">example</a>', + @to.gen_url('doc/example.rb', 'example') + assert_equal '<a href="../ex_doc/example_rb.html">example</a>', + @to.gen_url('../ex.doc/example.rb', 'example') + end + def test_handle_regexp_HYPERLINK_link target = RDoc::Markup::RegexpHandling.new 0, 'link:README.txt' diff --git a/test/rdoc/test_rdoc_parser_changelog.rb b/test/rdoc/test_rdoc_parser_changelog.rb index d93cb7deca..6584840572 100644 --- a/test/rdoc/test_rdoc_parser_changelog.rb +++ b/test/rdoc/test_rdoc_parser_changelog.rb @@ -212,6 +212,8 @@ Mon Dec 3 20:28:02 2012 Koichi Sasada <ko1@atdot.net> change condition of using `opt_send_simple'. More method invocations can be simple. +commit\ 8187228de0142d3ac7950b7d977c2849e934c637 + Other note that will be ignored ChangeLog @@ -270,6 +272,24 @@ Other note that will be ignored assert_equal expected, parser.parse_entries end + def test_parse_entries_git + parser = util_parser <<-ChangeLog +commit\ 709bed2afaee50e2ce803f87bf1ee8291bea41e3 + Author: git <svn-admin@ruby-lang.org> + Date: 2021-01-21 01:03:52 +0900 + + * 2021-01-21 [ci skip] +ChangeLog + + expected = [ + [ "709bed2afaee50e2ce80", + [ "git", "svn-admin@ruby-lang.org", + "2021-01-21 01:03:52 +0900", + "* 2021-01-21 [ci skip]\n"]]] + + assert_equal expected, parser.parse_entries + end + def test_scan parser = util_parser <<-ChangeLog Tue Dec 4 08:32:10 2012 Eric Hodel <drbrain@segment7.net> @@ -309,10 +329,157 @@ Mon Dec 3 20:37:22 2012 Koichi Sasada <ko1@atdot.net> assert_equal expected, @top_level.comment end + def test_scan_git + parser = util_parser <<-ChangeLog +commit\ 38816887962ec167ee46acf500f68df5c3013163 +Author: git <svn-admin@ruby-lang.org> +Date: Sun Jan 24 14:35:51 2021 +0900 + + * 2021-01-24 [ci skip] + +commit\ db7d0b89f6eca66cc7eb155c95f9123133da1ffc +Author: git <svn-admin@ruby-lang.org> +Date: Sat, 23 Jan 2021 06:01:39 +0900 + + * 2021-01-23 [ci skip] + +commit\ a3efbda7128ef20b55505b32d1608ea48f80af4a +Author: git <svn-admin@ruby-lang.org> +Date: 2021-01-22T02:49:39+09:00 + + * 2021-01-22 [ci skip] + +commit\ 709bed2afaee50e2ce803f87bf1ee8291bea41e3 + Author: git <svn-admin@ruby-lang.org> + Date: 2021-01-21 01:03:52 +0900 + + * 2021-01-21 [ci skip] + +commit\ a8dc5156e183489c5121fb1759bda5d9406d9175 + Author: git <svn-admin@ruby-lang.org> + Date: 2021-01-20 01:58:26 +0900 + + * 2021-01-20 [ci skip] + +commit\ de5f8a92d5001799bedb3b1a271a2d9b23c6c8fb + Author: Masataka Pocke Kuwabara <kuwabara@pocke.me> + Date: 2021-01-01 14:25:08 +0900 + + Make args info for RubyVM::AST to available on endless method without parens + + Problem + === + + Arguments information is missing for endless method without parens. + For example: + + ```ruby + # ok + ``` + + It causes an error if a program expects `args` node exists. + + Solution + === + + Call `new_args` on this case. +ChangeLog + + parser.scan + + expected = doc( + head(1, File.basename(@tempfile.path)), + blank_line, + head(2, '2021-01-24'), + blank_line, + log_entry(nil, '38816887962ec167ee46', + 'git', 'svn-admin@ruby-lang.org', 'Sun Jan 24 14:35:51 2021 +0900', + [list(:BULLET, item(nil, para('2021-01-24 [ci skip]')))]), + head(2, '2021-01-23'), + blank_line, + log_entry(nil, 'db7d0b89f6eca66cc7eb', + 'git', 'svn-admin@ruby-lang.org', 'Sat, 23 Jan 2021 06:01:39 +0900', + [list(:BULLET, item(nil, para('2021-01-23 [ci skip]')))]), + head(2, '2021-01-22'), + blank_line, + log_entry(nil, 'a3efbda7128ef20b5550', + 'git', 'svn-admin@ruby-lang.org', '2021-01-22T02:49:39+09:00', + [list(:BULLET, item(nil, para('2021-01-22 [ci skip]')))]), + head(2, '2021-01-21'), + blank_line, + log_entry(nil, '709bed2afaee50e2ce80', + 'git', 'svn-admin@ruby-lang.org', '2021-01-21 01:03:52 +0900', + [list(:BULLET, item(nil, para('2021-01-21 [ci skip]')))]), + head(2, '2021-01-20'), + blank_line, + log_entry(nil, 'a8dc5156e183489c5121', + 'git', 'svn-admin@ruby-lang.org', '2021-01-20 01:58:26 +0900', + [list(:BULLET, item(nil, para('2021-01-20 [ci skip]')))]), + head(2, '2021-01-01'), + blank_line, + log_entry(nil, 'de5f8a92d5001799bedb', + 'Masataka Pocke Kuwabara', 'kuwabara@pocke.me', '2021-01-01 14:25:08 +0900', + [head(4, 'Make args info for RubyVM::AST to available on endless method without parens'), + head(5, 'Problem'), + para("Arguments information is missing for endless method without parens.\n" + + "For example:"), + verb("# ok\n").tap {|v| v.format = :ruby}, + para('It causes an error if a program expects <code>args</code> node exists.'), + head(5, 'Solution'), + para('Call <code>new_args</code> on this case.')])) + + expected.file = @top_level + + assert_equal expected, @top_level.comment + end + + def test_scan_git_commit_date + parser = util_parser <<-ChangeLog +commit\ ee1e690a2df901adb279d7a63fbd92c64e0a5ae6 + Author: Igor Zubkov <igor.zubkov@gmail.com> + AuthorDate: 2016-10-25 03:56:11 +0900 + Commit: Nobuyoshi Nakada <nobu@ruby-lang.org> + CommitDate: 2021-01-07 13:40:42 +0900 + + We don't need "require 'uri'" after "require 'net/http'". + +commit\ 4d0985a7bd8f591dff4b430e288bfd83af782e51 + Author: git <svn-admin@ruby-lang.org> + AuthorDate: 2021-01-07 10:21:34 +0900 + Commit: git <svn-admin@ruby-lang.org> + CommitDate: 2021-01-07 10:21:34 +0900 + + * 2021-01-07 [ci skip] +ChangeLog + + parser.scan + + expected = doc( + head(1, File.basename(@tempfile.path)), + blank_line, + head(2, "2021-01-07"), + blank_line, + log_entry(nil, 'ee1e690a2df901adb279', + 'Igor Zubkov', 'igor.zubkov@gmail.com', + '2016-10-25 03:56:11 +0900', + [head(4, %[We don't need "require 'uri'" after "require 'net/http'".])]), + log_entry(nil, '4d0985a7bd8f591dff4b', + 'git', 'svn-admin@ruby-lang.org', + '2021-01-07 10:21:34 +0900', + [list(:BULLET, item(nil, para("2021-01-07 [ci skip]")))])) + + expected.file = @top_level + + assert_equal expected, @top_level.comment + end + def util_parser content = '' RDoc::Parser::ChangeLog.new \ @top_level, @tempfile.path, content, @options, @stats end + def log_entry(*a) + RDoc::Parser::ChangeLog::Git::LogEntry.new(*a) + end end diff --git a/test/rdoc/test_rdoc_rdoc.rb b/test/rdoc/test_rdoc_rdoc.rb index f7d9b8659f..a83d5a1b88 100644 --- a/test/rdoc/test_rdoc_rdoc.rb +++ b/test/rdoc/test_rdoc_rdoc.rb @@ -133,6 +133,29 @@ class TestRDocRDoc < RDoc::TestCase end end + def test_load_options_empty_file + temp_dir do + File.open '.rdoc_options', 'w' do |io| + end + + options = @rdoc.load_options + + assert_equal 'rdoc', options.markup + end + end + + def test_load_options_partial_override + temp_dir do + File.open '.rdoc_options', 'w' do |io| + io.write "markup: Markdown" + end + + options = @rdoc.load_options + + assert_equal 'Markdown', options.markup + end + end + def load_options_no_file temp_dir do options = @rdoc.load_options @@ -433,6 +456,18 @@ class TestRDocRDoc < RDoc::TestCase end end + def test_remove_unparseable_CVE_2021_31799 + temp_dir do + file_list = ['| touch evil.txt && echo tags'] + file_list.each do |f| + FileUtils.touch f + end + + assert_equal file_list, @rdoc.remove_unparseable(file_list) + assert_equal file_list, Dir.children('.') + end + end + def test_setup_output_dir Dir.mktmpdir {|d| path = File.join d, 'testdir' diff --git a/test/rdoc/test_rdoc_top_level.rb b/test/rdoc/test_rdoc_top_level.rb index e396791ab8..a954fde981 100644 --- a/test/rdoc/test_rdoc_top_level.rb +++ b/test/rdoc/test_rdoc_top_level.rb @@ -157,6 +157,9 @@ class TestRDocTopLevel < XrefTestCase def test_http_url assert_equal 'prefix/path/top_level_rb.html', @top_level.http_url('prefix') + + other_level = @store.add_file 'path.other/level.rb' + assert_equal 'prefix/path_other/level_rb.html', other_level.http_url('prefix') end def test_last_modified @@ -12,11 +12,11 @@ # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR #define RUBY_VERSION_TEENY 2 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 75 +#define RUBY_PATCHLEVEL 76 #define RUBY_RELEASE_YEAR 2021 -#define RUBY_RELEASE_MONTH 4 -#define RUBY_RELEASE_DAY 29 +#define RUBY_RELEASE_MONTH 5 +#define RUBY_RELEASE_DAY 22 #include "ruby/version.h" |