diff --git a/lib/coradoc/element/block.rb b/lib/coradoc/element/block.rb index 4be04ec..a9153fa 100644 --- a/lib/coradoc/element/block.rb +++ b/lib/coradoc/element/block.rb @@ -8,8 +8,10 @@ module Block require_relative "block/core" require_relative "block/example" require_relative "block/literal" -require_relative "block/quote" +require_relative "block/listing" +require_relative "block/open" require_relative "block/pass" +require_relative "block/quote" require_relative "block/side" require_relative "block/sourcecode" require_relative "block/reviewer_comment" \ No newline at end of file diff --git a/lib/coradoc/element/block/core.rb b/lib/coradoc/element/block/core.rb index f4c4275..50314bb 100644 --- a/lib/coradoc/element/block/core.rb +++ b/lib/coradoc/element/block/core.rb @@ -61,12 +61,13 @@ def type_from_delimiter def type_hash @type_hash ||= { - "____" => :quote, - "****" => :side, - "----" => :source, "====" => :example, "...." => :literal, + "--" => :open, "++++" => :pass, + "____" => :quote, + "****" => :side, + "----" => :source, } end end diff --git a/lib/coradoc/element/block/example.rb b/lib/coradoc/element/block/example.rb index ffc3b19..a31b3d8 100644 --- a/lib/coradoc/element/block/example.rb +++ b/lib/coradoc/element/block/example.rb @@ -15,7 +15,7 @@ def initialize(title, options = {}) end def to_adoc - "\n\n#{gen_anchor}#{gen_title}#{gen_delimiter}\n" << gen_lines << "\n#{gen_delimiter}\n\n" + "\n\n#{gen_anchor}#{gen_title}#{gen_attributes}#{gen_delimiter}\n" << gen_lines << "\n#{gen_delimiter}\n\n" end end end diff --git a/lib/coradoc/element/block/listing.rb b/lib/coradoc/element/block/listing.rb new file mode 100644 index 0000000..86dc8ba --- /dev/null +++ b/lib/coradoc/element/block/listing.rb @@ -0,0 +1,21 @@ +module Coradoc + module Element + module Block + class Listing < Core + def initialize(_title, options = {}) + @id = options.fetch(:id, nil) + @anchor = @id.nil? ? nil : Inline::Anchor.new(@id) + @lang = options.fetch(:lang, "") + @attributes = options.fetch(:attributes, AttributeList.new) + @lines = options.fetch(:lines, []) + @delimiter_char = "-" + @delimiter_len = options.fetch(:delimiter_len, 4) + end + + def to_adoc + "\n\n#{gen_anchor}#{gen_attributes}\n#{gen_delimiter}\n" << gen_lines << "\n#{gen_delimiter}\n\n" + end + end + end + end +end diff --git a/lib/coradoc/element/block/literal.rb b/lib/coradoc/element/block/literal.rb index 6a95470..4d1752b 100644 --- a/lib/coradoc/element/block/literal.rb +++ b/lib/coradoc/element/block/literal.rb @@ -2,16 +2,18 @@ module Coradoc module Element module Block class Literal < Core - def initialize(_title, options = {}) + def initialize(title, options = {}) + @title = title @id = options.fetch(:id, nil) @anchor = @id.nil? ? nil : Inline::Anchor.new(@id) + @attributes = options.fetch(:attributes, AttributeList.new) @lines = options.fetch(:lines, []) @delimiter_char = "." @delimiter_len = options.fetch(:delimiter_len, 4) end def to_adoc - "\n\n#{gen_anchor}#{gen_delimiter}\n" << gen_lines << "\n#{gen_delimiter}\n\n" + "\n\n#{gen_anchor}#{gen_title}#{gen_attributes}#{gen_delimiter}\n" << gen_lines << "\n#{gen_delimiter}\n\n" end end end diff --git a/lib/coradoc/element/block/open.rb b/lib/coradoc/element/block/open.rb new file mode 100644 index 0000000..9a84011 --- /dev/null +++ b/lib/coradoc/element/block/open.rb @@ -0,0 +1,22 @@ +module Coradoc + module Element + module Block + class Open < Core + def initialize(title, options = {}) + @title = title + @id = options.fetch(:id, nil) + @anchor = @id.nil? ? nil : Inline::Anchor.new(@id) + @lang = options.fetch(:lang, "") + @attributes = options.fetch(:attributes, AttributeList.new) + @lines = options.fetch(:lines, []) + @delimiter_char = "-" + @delimiter_len = options.fetch(:delimiter_len, 2) + end + + def to_adoc + "\n\n#{gen_anchor}#{gen_attributes}#{gen_delimiter}\n" << gen_lines << "\n#{gen_delimiter}\n\n" + end + end + end + end +end \ No newline at end of file diff --git a/lib/coradoc/element/list/core.rb b/lib/coradoc/element/list/core.rb index d0c5791..37821ea 100644 --- a/lib/coradoc/element/list/core.rb +++ b/lib/coradoc/element/list/core.rb @@ -19,8 +19,8 @@ def initialize(items, options = {}) m = @items.select do |i| i.is_a?(Coradoc::Element::ListItem) && !i.marker.nil? - end.first&.marker - @ol_count = m.size if m.is_a?(String) + end.first&.marker.to_s + @ol_count = m.size end @ol_count = 1 if @ol_count.nil? @attrs = options.fetch(:attrs, AttributeList.new) diff --git a/lib/coradoc/element/list/ordered.rb b/lib/coradoc/element/list/ordered.rb index 97e71bd..49bc542 100644 --- a/lib/coradoc/element/list/ordered.rb +++ b/lib/coradoc/element/list/ordered.rb @@ -7,6 +7,7 @@ def initialize(items, options = {}) end def prefix + return @marker if @marker "." * [@ol_count, 1].max end end diff --git a/lib/coradoc/element/list/unordered.rb b/lib/coradoc/element/list/unordered.rb index 98135e3..e2b07aa 100644 --- a/lib/coradoc/element/list/unordered.rb +++ b/lib/coradoc/element/list/unordered.rb @@ -7,6 +7,7 @@ def initialize(items, options = {}) end def prefix + return @marker if @marker "*" * [@ol_count, 1].max end end diff --git a/lib/coradoc/element/list_item.rb b/lib/coradoc/element/list_item.rb index c97a241..bfb92da 100644 --- a/lib/coradoc/element/list_item.rb +++ b/lib/coradoc/element/list_item.rb @@ -1,7 +1,7 @@ module Coradoc module Element class ListItem < Base - attr_accessor :marker, :id, :anchor, :content, :line_break + attr_accessor :marker, :id, :anchor, :content, :subitem, :line_break declare_children :content, :id, :anchor @@ -10,11 +10,14 @@ def initialize(content, options = {}) @id = options.fetch(:id, nil) @anchor = @id.nil? ? nil : Inline::Anchor.new(@id) @content = content + @attached = options.fetch(:attached, []) + @nested = options.fetch(:nested, nil) @line_break = options.fetch(:line_break, "\n") end def to_adoc - anchor = @anchor.nil? ? "" : @anchor.to_adoc.to_s + anchor = @anchor.nil? ? "" : " #{@anchor.to_adoc.to_s} " + # text = Coradoc::Generator.gen_adoc(@content) content = Array(@content).map do |subitem| next if subitem.is_a? Inline::HardLineBreak @@ -24,10 +27,15 @@ def to_adoc if Coradoc.a_single?(subitem, Coradoc::Element::TextElement) subcontent = Coradoc.strip_unicode(subcontent) end - subcontent.chomp + subcontent end.compact.join("\n+\n") - - " #{anchor}#{content.chomp}#{@line_break}" + # attach = Coradoc::Generator.gen_adoc(@attached) + attach = @attached.map do |elem| + "+\n" + Coradoc::Generator.gen_adoc(elem) + end.join + nest = Coradoc::Generator.gen_adoc(@nested) + out = " #{anchor}#{content}#{@line_break}" + out + attach + nest end end end diff --git a/lib/coradoc/element/text_element.rb b/lib/coradoc/element/text_element.rb index 1ef003a..ce4efeb 100644 --- a/lib/coradoc/element/text_element.rb +++ b/lib/coradoc/element/text_element.rb @@ -20,7 +20,7 @@ def inspect str += "(#{@id})" if @id str += ": " str += @content.inspect - str += " + #{@line_break.inspect}" unless line_break.empty? + str += " + #{@line_break.inspect}" unless line_break.to_s.empty? str end diff --git a/lib/coradoc/parser/asciidoc/attribute_list.rb b/lib/coradoc/parser/asciidoc/attribute_list.rb index aaabcc6..1a47205 100644 --- a/lib/coradoc/parser/asciidoc/attribute_list.rb +++ b/lib/coradoc/parser/asciidoc/attribute_list.rb @@ -11,8 +11,13 @@ def named_attribute_value match('[^\],]').repeat(1) end + def named_key + (str('reviewer') | + match('[a-zA-Z0-9_-]').repeat(1)).as(:named_key) + end + def named_attribute - (match('[a-zA-Z0-9_-]').repeat(1).as(:named_key) >> + ( named_key >> str(' ').maybe >> str("=") >> str(' ').maybe >> match['a-zA-Z0-9_\- \"'].repeat(1).as(:named_value) >> str(' ').maybe @@ -51,6 +56,7 @@ def positional_zero_or_one end def attribute_list(name = :attribute_list) + str('[').present? >> str('[') >> str("[").absent? >> ( named_many | positional_one_named_many | diff --git a/lib/coradoc/parser/asciidoc/base.rb b/lib/coradoc/parser/asciidoc/base.rb index 51999dd..88f401e 100644 --- a/lib/coradoc/parser/asciidoc/base.rb +++ b/lib/coradoc/parser/asciidoc/base.rb @@ -1,3 +1,7 @@ +require "parslet" +require "parslet/convenience" + + require_relative "admonition" require_relative "attribute_list" require_relative "bibliography" @@ -12,11 +16,12 @@ require_relative "section" require_relative "table" require_relative "term" +require_relative "text" module Coradoc module Parser module Asciidoc - module Base + class Base < Parslet::Parser include Coradoc::Parser::Asciidoc::Admonition include Coradoc::Parser::Asciidoc::AttributeList include Coradoc::Parser::Asciidoc::Bibliography @@ -31,141 +36,54 @@ module Base include Coradoc::Parser::Asciidoc::Section include Coradoc::Parser::Asciidoc::Table include Coradoc::Parser::Asciidoc::Term - - def space? - space.maybe - end - - def space - str(' ').repeat(1) - end - - def text - match("[^\n]").repeat(1) - end - - def line_ending - str("\n") - end - - def endline - newline | any.absent? - end - - def newline - (str("\n") | str("\r\n")).repeat(1) - end - - def newline_single - (str("\n") | str("\r\n")) - end - - def keyword - (match('[a-zA-Z0-9_\-.,]') | str(".")).repeat(1) - end - - def empty_line - match("^\n") - end - - def digit - match("[0-9]") - end - - def digits - match("[0-9]").repeat(1) - end - - def word - match("[a-zA-Z0-9_-]").repeat(1) - end - - def words - word >> (space? >> word).repeat - end - - def rich_texts - rich_text >> (space? >> rich_text).repeat - end - - def rich_text - (match("[a-zA-Z0-9_-]") | str(".") | str("*") | match("@")).repeat(1) - end - - def email - word >> str("@") >> word >> str(".") >> word - end - - def special_character - match("^[*:=-]") | str("[#") | str("[[") - end - - def date - digit.repeat(2, 4) >> str("-") >> - digit.repeat(1, 2) >> str("-") >> digit.repeat(1, 2) - end - - def attr_name - match("[^\t\s]").repeat(1) + include Coradoc::Parser::Asciidoc::Text + + def rule_dispatch(rule_name, *args, **kwargs) + @dispatch_data = {} unless @dispatch_data + dispatch_key = [rule_name, args, kwargs.to_a.sort] + dispatch_hash = dispatch_key.hash.abs + unless @dispatch_data.has_key?(dispatch_hash) + alias_name = "#{rule_name}_#{dispatch_hash}".to_sym + Coradoc::Parser::Asciidoc::Base.class_exec do + rule(alias_name) do + send(rule_name, *args, **kwargs) + end + end + @dispatch_data[dispatch_hash] = alias_name + end + dispatch_method = @dispatch_data[dispatch_hash] + send(dispatch_method) + end + + add_dispatch = true + with_params = true + + parser_methods = (Coradoc::Parser::Asciidoc.constants - [:Base]).map{ |const| + Coradoc::Parser::Asciidoc.const_get(const).instance_methods + }.flatten.uniq + + parser_methods.each do |rule_name| + params = Coradoc::Parser::Asciidoc::Base.instance_method(rule_name).parameters + if add_dispatch && params == [] + alias_name = "alias_nondispatch_#{rule_name}".to_sym + Coradoc::Parser::Asciidoc::Base.class_exec do + alias_method alias_name, rule_name + rule(rule_name) do + send(alias_name) + end + end + elsif add_dispatch && with_params + alias_name = "alias_dispatch_#{rule_name}".to_sym + Coradoc::Parser::Asciidoc::Base.class_exec do + alias_method alias_name, rule_name + define_method(rule_name) do |*args, **kwargs| + rule_dispatch(alias_name, *args, **kwargs) + end + + end + end end - def file_path - match('[^\[]').repeat(1) - end - - def include_directive - (str("include::") >> - file_path.as(:path) >> - attribute_list >> - (newline | str("")).as(:line_break) - ).as(:include) - end - - def inline_image - (str("image::") >> - file_path.as(:path) >> - attribute_list >> - (line_ending) - ).as(:inline_image) - end - - def block_image - (block_id.maybe >> - block_title.maybe >> - (attribute_list >> newline).maybe >> - match('^i') >> str("mage::") >> - file_path.as(:path) >> - attribute_list(:attribute_list_macro) >> - newline.as(:line_break) - ).as(:block_image) - end - - def comment_line - tag.absent? >> - (str('//') >> str("/").absent? >> - space? >> - text.as(:comment_text) - ).as(:comment_line) - end - - def tag - (str('//') >> str('/').absent? >> - space? >> - (str('tag') | str('end')).as(:prefix) >> - str('::') >> str(':').absent? >> - match('[^\[]').repeat(1).as(:name) >> - attribute_list >> - line_ending.maybe.as(:line_break) - ).as(:tag) - end - - def comment_block - ( str('////') >> line_ending >> - ((line_ending >> str('////')).absent? >> any - ).repeat.as(:comment_text) >> - line_ending >> str('////') - ).as(:comment_block) - end end end end diff --git a/lib/coradoc/parser/asciidoc/block.rb b/lib/coradoc/parser/asciidoc/block.rb index 2607fee..affda9c 100644 --- a/lib/coradoc/parser/asciidoc/block.rb +++ b/lib/coradoc/parser/asciidoc/block.rb @@ -3,77 +3,90 @@ module Parser module Asciidoc module Block - def block - sidebar_block | - example_block | - source_block | - quote_block | - pass_block + def block(n_deep = 3) + (example_block(n_deep) | + sidebar_block(n_deep) | + source_block(n_deep) | + quote_block(n_deep) | + pass_block(n_deep)).as(:block) end - def source_block - block_style("-", 2) + def example_block(n_deep) + block_style(n_deep, "=") end - def pass_block - block_style("+", 4, :pass) + def pass_block(n_deep) + block_style(n_deep, "+", 4, :pass) end - def source_block - block_style("-", 2) + def quote_block(n_deep) + block_style(n_deep, "_") end - def quote_block - block_style("_") + def sidebar_block(n_deep) + block_style(n_deep, "*") end - def block_content(n_deep = 2) - c = block_image | - list | - text_line | - empty_line.as(:line_break) - c = c | block_content(n_deep - 1) if (n_deep > 0) - c.repeat(1) - end - - def sidebar_block - block_style("*") - end - - def example_block - block_style("=") + def source_block(n_deep) + block_style(n_deep, "-", 2) end def block_title - match('^\\.') >> space.absent? >> text.as(:title) >> newline + str('.') >> space.absent? >> text.as(:title) >> newline end def block_type(type) - (match('^\[') >> str("[").absent? >> + (str("[") >> str("[").absent? >> str(type).as(:type) >> str("]")) | (match('^\[') >> keyword.as(:type) >> str("]")) >> newline end def block_id - (match('^\[') >> str("[") >> str('[').absent? >> keyword.as(:id) >> str("]]") | + line_start? >> + (str("[[") >> str('[').absent? >> keyword.as(:id) >> str("]]") | str("[#") >> keyword.as(:id) >> str("]")) >> newline end - def block_style(delimiter = "*", repeater = 4, type = nil) + def block_content(n_deep = 3) + c = block_image | + list | + text_line | + empty_line.as(:line_break) + c = c | block(n_deep - 1) if (n_deep > 0) + c.repeat(1) + end + + def block_delimiter + line_start? >> + ((str("*") | + str("=") | + str("_") | + str("+") | + str("-")).repeat(4) | + str("-").repeat(2,2)) >> + newline + end + + def block_style(n_deep = 3, delimiter = "*", repeater = 4, type = nil) + block_title.maybe >> block_id.maybe >> + (attribute_list >> newline ).maybe >> block_title.maybe >> newline.maybe >> - (attribute_list >> newline ).maybe >> + (line_start? >> str('[').present? >> attribute_list >> newline ).maybe >> block_id.maybe >> - (attribute_list >> newline ).maybe >> - str(delimiter).repeat(repeater).as(:delimiter) >> newline >> + (str('[').present? >> attribute_list >> newline ).maybe >> + line_start? >> + str(delimiter).repeat(repeater).capture(:delimit).as(:delimiter) >> newline >> if type == :pass (text_line | empty_line.as(:line_break)).repeat(1).as(:lines) else - block_content.as(:lines) + block_delimiter.absent? >> block_content(n_deep-1).as(:lines) end >> - str(delimiter).repeat(repeater) >> newline + line_start? >> + dynamic { |s,c| str(c.captures[:delimit].to_s.strip) } >> newline + # str(delimiter).repeat(repeater) >> str(delimiter).absent? >> newline end end diff --git a/lib/coradoc/parser/asciidoc/content.rb b/lib/coradoc/parser/asciidoc/content.rb index 0039fc7..319117f 100644 --- a/lib/coradoc/parser/asciidoc/content.rb +++ b/lib/coradoc/parser/asciidoc/content.rb @@ -25,9 +25,19 @@ def literal_space? literal_space.maybe end + def list_prefix + (line_start? >> match('^[*\.]') >> str(' ')) + end + + def section_prefix + (line_start? >> match('^[=]') >> str('=').repeat(0) >> match('[^\n]')) + end + # Text - def text_line(many_breaks = false) - tl = (asciidoc_char_with_id.absent? | text_id) >> literal_space? >> + def text_line(many_breaks = false) #:zero :one :many + tl = #section_prefix.absent? >> + # list_prefix.absent? >> + (asciidoc_char_with_id.absent? | text_id) >> literal_space? >> text.as(:text) if many_breaks tl >> line_ending.repeat(1).as(:line_break) @@ -37,7 +47,7 @@ def text_line(many_breaks = false) end def asciidoc_char - match('^[*_:=\-+]') + line_start? >> match['*_:+=\-'] end def asciidoc_char_with_id diff --git a/lib/coradoc/parser/asciidoc/list.rb b/lib/coradoc/parser/asciidoc/list.rb index 8a8529a..768eae5 100644 --- a/lib/coradoc/parser/asciidoc/list.rb +++ b/lib/coradoc/parser/asciidoc/list.rb @@ -1,30 +1,30 @@ +# $DEBUG = true module Coradoc module Parser module Asciidoc module List - def list - ( - unordered_list | - ordered_list # definition_list | + def list(nesting_level = 1) + ( + unordered_list(nesting_level) | + ordered_list(nesting_level) | + definition_list ).as(:list) end + def list_continuation + line_start? >> str("+\n") + end + def ordered_list(nesting_level = 1) attrs = (attribute_list >> newline).maybe r = olist_item(nesting_level) - if nesting_level <= 8 - r = r | ordered_list(nesting_level + 1) - end - attrs >> r.repeat(1).as(:ordered) + attrs >> olist_item(nesting_level).present? >> r.repeat(1).as(:ordered) end def unordered_list(nesting_level = 1) attrs = (attribute_list >> newline).maybe r = ulist_item(nesting_level) - if nesting_level <= 8 - r = r | unordered_list(nesting_level + 1) - end attrs >> r.repeat(1).as(:unordered) end @@ -34,23 +34,57 @@ def definition_list(delimiter = "::") dlist_item(delimiter).absent? end + def list_marker(nesting_level = 1) + olist_marker(nesting_level) | ulist_marker(nesting_level) + end + + def olist_marker(nesting_level = 1) + line_start? >> str('.' * nesting_level) >> str('.').absent? + end + def olist_item(nesting_level = 1) - nl2 = nesting_level - 1 - marker = match(/^\./) - marker = marker >> str(".").repeat(nl2, nl2) if nl2 > 0 - str("").as(:list_item) >> - marker.as(:marker) >> str(".").absent? >> - match("\n").absent? >> space >> text_line(true) + item = olist_marker(nesting_level).as(:marker) >> + match("\n").absent? >> space >> text_line(true)# >> + # (list_continuation.present? >> list_continuation >> + # paragraph #| example_block(n_deep: 1) + # ).repeat(0).as(:attached) + + att = (list_continuation.present? >> + list_continuation >> + (admonition_line | paragraph | block) #(n_deep: 1)) + ).repeat(0).as(:attached) + item = item >> att.maybe + + + if nesting_level <= 4 + item = item >> + (list_marker(nesting_level + 1).present? >> + list(nesting_level + 1)).repeat(0).as(:nested)#).maybe + end + olist_marker(nesting_level).present? >> item.as(:list_item) + end + + def ulist_marker(nesting_level = 1) + line_start? >> str('*' * nesting_level) >> str('*').absent? end def ulist_item(nesting_level = 1) - nl2 = nesting_level - 1 - marker = match(/^\*/) - marker = marker >> str("*").repeat(nl2, nl2) if nl2 > 0 - str("").as(:list_item) >> - marker.as(:marker) >> str("*").absent? >> + item = ulist_marker(nesting_level).as(:marker) >> str(' [[[').absent? >> match("\n").absent? >> space >> text_line(true) + + att = (list_continuation.present? >> + list_continuation >> + (admonition_line | paragraph | block) #(n_deep: 1)) + ).repeat(0).as(:attached) + item = item >> att.maybe + + if nesting_level <= 4 + item = item >> + (list_marker(nesting_level + 1).present? >> + list(nesting_level + 1)).repeat(0).as(:nested)#).maybe + end + ulist_marker(nesting_level).present? >> item.as(:list_item) end def dlist_delimiter diff --git a/lib/coradoc/parser/asciidoc/paragraph.rb b/lib/coradoc/parser/asciidoc/paragraph.rb index 8d37f8c..a14a93b 100644 --- a/lib/coradoc/parser/asciidoc/paragraph.rb +++ b/lib/coradoc/parser/asciidoc/paragraph.rb @@ -3,10 +3,22 @@ module Parser module Asciidoc module Paragraph + def line_not_text? + line_start? >> + attribute_list.absent? >> + block_delimiter.absent? >> + list.absent? >> + # list_prefix.absent? >> + section_id.absent? >> + section_prefix.absent? + end + def paragraph_text_line + line_not_text? >> (asciidoc_char_with_id.absent? | text_id ) >> literal_space? >> - (text_formatted.as(:text) # >> + (line_not_text? >> + text_formatted.as(:text) # >> ) | term | term2 end @@ -14,9 +26,9 @@ def paragraph ( block_id.maybe >> block_title.maybe >> (attribute_list >> newline).maybe >> - (paragraph_text_line.repeat(1,1) >> any.absent? | - (paragraph_text_line >> newline_single.as(:line_break)).repeat(1) >> - (paragraph_text_line.repeat(1,1)).repeat(0,1) + (line_not_text? >> paragraph_text_line.repeat(1,1) >> any.absent? | + (line_not_text? >> paragraph_text_line >> newline_single.as(:line_break)).repeat(1) >> + (line_not_text? >> paragraph_text_line.repeat(1,1)).repeat(0,1) ).as(:lines) >> newline.repeat(0) ).as(:paragraph) diff --git a/lib/coradoc/parser/asciidoc/section.rb b/lib/coradoc/parser/asciidoc/section.rb index 8f3c5fb..b1e404a 100644 --- a/lib/coradoc/parser/asciidoc/section.rb +++ b/lib/coradoc/parser/asciidoc/section.rb @@ -14,7 +14,7 @@ def contents comment_line | include_directive | admonition_line | - block.as(:block) | + block | table.as(:table) | highlight.as(:highlight) | glossaries.as(:glossaries) | @@ -35,12 +35,14 @@ def section_block(level = 2) # Section id def section_id + line_start? >> (str("[[") >> keyword.as(:id) >> str("]]") | str("[#") >> keyword.as(:id) >> str("]")) >> newline end # Heading def section_title(level = 2, max_level = 8) + line_start? >> match("=").repeat(level, max_level).as(:level) >> str('=').absent? >> space? >> text.as(:text) >> endline.as(:line_break) diff --git a/lib/coradoc/parser/asciidoc/term.rb b/lib/coradoc/parser/asciidoc/term.rb index 74fbdde..5efcc87 100644 --- a/lib/coradoc/parser/asciidoc/term.rb +++ b/lib/coradoc/parser/asciidoc/term.rb @@ -7,12 +7,14 @@ def term_type end def term + line_start? >> term_type >> str(':[') >> match('[^\]]').repeat(1).as(:term) >> str("]") >> str("\n").repeat(1).as(:line_break) end def term2 + line_start? >> match('^\[') >> term_type >> str(']#') >> match('[^\#]').repeat(1).as(:term2) >> str('#') >> str("\n").repeat(1).as(:line_break) diff --git a/lib/coradoc/parser/asciidoc/text.rb b/lib/coradoc/parser/asciidoc/text.rb new file mode 100644 index 0000000..65c67c2 --- /dev/null +++ b/lib/coradoc/parser/asciidoc/text.rb @@ -0,0 +1,161 @@ +module Coradoc + module Parser + module Asciidoc + module Text + + def space? + space.maybe + end + + def space + str(' ').repeat(1) + end + + def text + match("[^\n]").repeat(1) + end + + def line_start? + match('^[^\n]').present? + end + + def line_ending + str("\n") #| match('[\z]')# | match('$') + end + + def eof? + any.absent? + end + + def line_end + str("\n") | str("\r\n") | eof? + end + + def endline + newline | any.absent? + end + + # def endline_single + # newline_single | any.absent? + # end + + def newline + (str("\n") | str("\r\n")).repeat(1) + end + + def newline_single + (str("\n") | str("\r\n")) + end + + def keyword + (match('[a-zA-Z0-9_\-.,]') | str(".")).repeat(1) + end + + def empty_line + match("^\n") + end + + def digit + match("[0-9]") + end + + def digits + match("[0-9]").repeat(1) + end + + def word + match("[a-zA-Z0-9_-]").repeat(1) + end + + def words + word >> (space? >> word).repeat + end + + def rich_texts + rich_text >> (space? >> rich_text).repeat + end + + def rich_text + (match("[a-zA-Z0-9_-]") | str(".") | str("*") | match("@")).repeat(1) + end + + def email + word >> str("@") >> word >> str(".") >> word + end + + def special_character + match("^[*:=-]") | str("[#") | str("[[") + end + + def date + digit.repeat(2, 4) >> str("-") >> + digit.repeat(1, 2) >> str("-") >> digit.repeat(1, 2) + end + + def attr_name + match("[^\t\s]").repeat(1) + end + + def file_path + match('[^\[]').repeat(1) + end + + def include_directive + (str("include::") >> + file_path.as(:path) >> + attribute_list >> + (newline | str("")).as(:line_break) + ).as(:include) + end + + def inline_image + (str("image::") >> + file_path.as(:path) >> + attribute_list >> + (line_ending) + ).as(:inline_image) + end + + def block_image + (block_id.maybe >> + block_title.maybe >> + (attribute_list >> newline).maybe >> + match('^i') >> str("mage::") >> + file_path.as(:path) >> + attribute_list(:attribute_list_macro) >> + newline.as(:line_break) + ).as(:block_image) + end + + def comment_line + tag.absent? >> + (str('//') >> str("/").absent? >> + space? >> + text.as(:comment_text) + ).as(:comment_line) + end + + def tag + (str('//') >> str('/').absent? >> + space? >> + (str('tag') | str('end')).as(:prefix) >> + str('::') >> str(':').absent? >> + match('[^\[]').repeat(1).as(:name) >> + attribute_list >> + line_ending.maybe.as(:line_break) + ).as(:tag) + end + + def comment_block + ( str('////') >> line_ending >> + ((line_ending >> str('////')).absent? >> any + ).repeat.as(:comment_text) >> + line_ending >> str('////') + ).as(:comment_block) + end + end + end + end +end + + diff --git a/lib/coradoc/parser/base.rb b/lib/coradoc/parser/base.rb index 6bbe4a6..4df2f53 100644 --- a/lib/coradoc/parser/base.rb +++ b/lib/coradoc/parser/base.rb @@ -1,36 +1,12 @@ +require "digest" require "parslet" require "parslet/convenience" -require_relative "asciidoc/attribute_list" require_relative "asciidoc/base" -require_relative "asciidoc/block" -require_relative "asciidoc/citation" -require_relative "asciidoc/content" -require_relative "asciidoc/document_attributes" -require_relative "asciidoc/header" -require_relative "asciidoc/inline" -require_relative "asciidoc/list" -require_relative "asciidoc/paragraph" -require_relative "asciidoc/section" -require_relative "asciidoc/table" -require_relative "asciidoc/term" module Coradoc module Parser - class Base < Parslet::Parser - include Coradoc::Parser::Asciidoc::AttributeList - include Coradoc::Parser::Asciidoc::Base - include Coradoc::Parser::Asciidoc::Block - include Coradoc::Parser::Asciidoc::Citation - include Coradoc::Parser::Asciidoc::Content - include Coradoc::Parser::Asciidoc::DocumentAttributes - include Coradoc::Parser::Asciidoc::Header - include Coradoc::Parser::Asciidoc::Inline - include Coradoc::Parser::Asciidoc::List - include Coradoc::Parser::Asciidoc::Paragraph - include Coradoc::Parser::Asciidoc::Section - include Coradoc::Parser::Asciidoc::Table - include Coradoc::Parser::Asciidoc::Term + class Base < Coradoc::Parser::Asciidoc::Base root :document rule(:document) do @@ -43,8 +19,8 @@ class Base < Parslet::Parser tag | comment_block | comment_line | - block.as(:block) | - section.as(:section) | + block | + section | include_directive | document_attributes | list | diff --git a/lib/coradoc/transformer.rb b/lib/coradoc/transformer.rb index 44e7115..9404598 100644 --- a/lib/coradoc/transformer.rb +++ b/lib/coradoc/transformer.rb @@ -156,27 +156,25 @@ class NamedAttribute < Struct.new(:key, :value); end ) } - rule(bold_constrained: { - content: simple(:text) - }){ + rule(bold_constrained: sequence(:text)){ Element::Inline::Bold.new(text, unconstrained: false) } - rule(bold_unconstrained: {content: simple(:text)}) { + rule(bold_unconstrained: sequence(:text)) { Element::Inline::Bold.new(text, unconstrained: true) } - rule(highlight_constrained: {content: simple(:text)}) { + rule(highlight_constrained: sequence(:text)) { Element::Inline::Highlight.new(text, unconstrained: false) } - rule(highlight_unconstrained: {content: simple(:text)}) { + rule(highlight_unconstrained: sequence(:text)) { Element::Inline::Highlight.new(text, unconstrained: true) } - rule(italic_constrained: {content: simple(:text)}) { + rule(italic_constrained: sequence(:text)) { Element::Inline::Italic.new(text, unconstrained: false) } - rule(italic_unconstrained: {content: simple(:text)}) { + rule(italic_unconstrained: sequence(:text)) { Element::Inline::Italic.new(text, unconstrained: true) } @@ -300,25 +298,24 @@ class NamedAttribute < Struct.new(:key, :value); end if attribute_list if (attribute_list.positional == [] && attribute_list.named.keys[0] == "reviewer") - Element::Block::ReviewerComment.new( - opts - ) + Element::Block::ReviewerComment.new(opts) elsif (attribute_list.positional[0] == "sidebar" && attribute_list.named == {}) - Element::Block::Side.new( - opts - ) + Element::Block::Side.new(opts) + else + Element::Block::Side.new(opts) end else + Element::Block::Side.new(opts) end elsif delimiter_c == "=" Element::Block::Example.new(title, opts) elsif delimiter_c == "+" Element::Block::Pass.new(opts) - elsif delimiter_c == "-" - if (attribute_list.positional[0] == "quote") - Element::Block::Quote.new(title, opts) - end + elsif delimiter_c == "-" && delimiter.size == 2 + Element::Block::Open.new(title, opts) + elsif delimiter_c == "-"&& delimiter.size >= 4 + Element::Block::SourceCode.new(title, opts) elsif delimiter_c == "_" Element::Block::Quote.new(title, opts) end @@ -379,28 +376,15 @@ class NamedAttribute < Struct.new(:key, :value); end Element::Table.new(title, rows, opts) end - rule(list_item: simple(:list_item), - marker: simple(:marker), - text: simple(:text), - line_break: simple(:line_break)) do - Element::ListItem.new( - text, - marker: marker.to_s, - line_break: line_break - ) - end - - rule(list_item: simple(:list_item), - marker: simple(:marker), - id: simple(:id), - text: simple(:text), - line_break: simple(:line_break)) do + rule(list_item: subtree(:list_item)) do + marker = list_item[:marker] + id = list_item[:id] + text = list_item[:text] + attached = list_item[:attached] + nested = list_item[:nested] + line_break = list_item[:line_break] Element::ListItem.new( - text, - id: id, - marker: marker.to_s, - line_break: line_break - ) + text, id:, marker:, attached:, nested:, line_break: ) end diff --git a/spec/coradoc/parser/asciidoc/admonition_spec.rb b/spec/coradoc/parser/asciidoc/admonition_spec.rb index 6f8bc69..afa9876 100644 --- a/spec/coradoc/parser/asciidoc/admonition_spec.rb +++ b/spec/coradoc/parser/asciidoc/admonition_spec.rb @@ -6,34 +6,26 @@ parser = Asciidoc::AdmonitionTester ast = parser.parse("NOTE: some text\n") - exp = [{:admonition_type => "NOTE", - :content => [ - {:text => "some text", :line_break => "\n"} - ] - }] + exp = [{:admonition_type=>"NOTE", + :content=>[{:text=>"some text", :line_break=>"\n"}]}] expect(ast).to eq(exp) end it "parses multi line admonition" do parser = Asciidoc::AdmonitionTester ast = parser.parse("NOTE: some text\ncontinued\n") - exp = [{:admonition_type => "NOTE", - :content => [ - {:text => "some text", :line_break => "\n"}, - {:text => "continued", :line_break => "\n"} - ] - }] - + exp = [{:admonition_type=>"NOTE", + :content=> + [{:text=>"some text", :line_break=>"\n"}, + {:text=>"continued", :line_break=>"\n"}]}] + expect(ast).to eq(exp) end end end - module Asciidoc - class AdmonitionTester < Parslet::Parser - include Coradoc::Parser::Asciidoc::Base - + class AdmonitionTester < Coradoc::Parser::Asciidoc::Base rule(:document) { (admonition_line | any.as(:unparsed)).repeat(1) } root :document diff --git a/spec/coradoc/parser/asciidoc/attribute_list_spec.rb b/spec/coradoc/parser/asciidoc/attribute_list_spec.rb index 0fcfbf3..3befc4d 100644 --- a/spec/coradoc/parser/asciidoc/attribute_list_spec.rb +++ b/spec/coradoc/parser/asciidoc/attribute_list_spec.rb @@ -8,35 +8,38 @@ expect(ast).to eq([]) ast = parser.parse("[a]") - expect(ast).to eq([{positional: "a"}]) + expect(ast).to eq([{ positional: "a" }]) ast = parser.parse("[a,b]") - expect(ast).to eq([{positional: "a"}, {positional: "b"}]) + expect(ast).to eq([{ positional: "a" }, { positional: "b" }]) ast = parser.parse("[a,b,c]") - expect(ast).to eq([{positional: "a"}, {positional: "b"}, {positional: "c"}]) + expect(ast).to eq([{ positional: "a" }, { positional: "b" }, + { positional: "c" }]) ast = parser.parse("[a=b]") - expect(ast).to eq([{named: {named_key:"a", named_value:"b"}}]) + expect(ast).to eq([{ named: { named_key: "a", named_value: "b" } }]) ast = parser.parse("[a,b=c]") - expect(ast).to eq([{positional: "a"}, {named: {named_key:"b", named_value:"c"}}]) + expect(ast).to eq([{ positional: "a" }, + { named: { named_key: "b", named_value: "c" } }]) ast = parser.parse("[a,b,c=d]") - expect(ast).to eq([{positional: "a"}, {positional: "b"}, - {named: {named_key:"c", named_value:"d"}}]) + expect(ast).to eq([{ positional: "a" }, { positional: "b" }, + { named: { named_key: "c", named_value: "d" } }]) ast = parser.parse("[a,b=c,d=e]") - expect(ast).to eq([{positional: "a"}, - {named: {named_key:"b", named_value:"c"}}, - {named: {named_key:"d", named_value:"e"}}]) + obj = [{ positional: "a" }, + { named: { named_key: "b", named_value: "c" } }, + { named: { named_key: "d", named_value: "e" } }] + expect(ast).to eq(obj) + ast = parser.parse("[a,b,c=d,e=f]") - expect(ast).to eq([{positional: "a"}, {positional: "b"}, - {named: {named_key:"c", named_value:"d"}}, - {named: {named_key:"e", named_value:"f"}}]) + obj = [{ positional: "a" }, + { positional: "b" }, + { named: { named_key: "c", named_value: "d" } }, + { named: { named_key: "e", named_value: "f" } }] + expect(ast).to eq(obj) end end end - module Asciidoc - class AttributeListTester < Parslet::Parser - include Coradoc::Parser::Asciidoc::Base - + class AttributeListTester < Coradoc::Parser::Asciidoc::Base rule(:document) { (attribute_list | any.as(:unparsed)).repeat(1) } root :document diff --git a/spec/coradoc/parser/asciidoc/block_spec.rb b/spec/coradoc/parser/asciidoc/block_spec.rb new file mode 100644 index 0000000..ca7c81a --- /dev/null +++ b/spec/coradoc/parser/asciidoc/block_spec.rb @@ -0,0 +1,38 @@ +require "spec_helper" + +RSpec.describe "Coradoc::Parser::Asciidoc::AttributeList" do + describe ".parse" do + it "parses attribute list attached to a block" do + parser = Asciidoc::BlockTester + content = <<~TEXT + + [reviewer=ISO] + **** + block content + **** + TEXT + ast = parser.parse(content) + obj = [{:block=> + {:attribute_list=> + {:attribute_array=> + [{:named=>{:named_key=>"reviewer", :named_value=>"ISO"}}]}, + :delimiter=>"****", + :lines=>[{:text=>"block content", :line_break=>"\n"}]}}] + + expect(ast).to eq(obj) + end + +end +end + + +module Asciidoc + class BlockTester < Coradoc::Parser::Asciidoc::Base + rule(:document) { (block| any.as(:unparsed)).repeat(1) } + root :document + + def self.parse(text) + new.parse_with_debug(text) + end + end +end diff --git a/spec/coradoc/parser/asciidoc/bugs_spec.rb b/spec/coradoc/parser/asciidoc/bugs_spec.rb new file mode 100644 index 0000000..2a1ef27 --- /dev/null +++ b/spec/coradoc/parser/asciidoc/bugs_spec.rb @@ -0,0 +1,41 @@ +require "spec_helper" + +RSpec.describe "Coradoc::Parser::Asciidoc::List" do + describe "parsing problem not fixed yet: " do + + xit "problem with parsing attribute_list before block in between two lists latter of which has block attached to it" do + + content = <<~ADOC + . Unordered list item 1 + + [reviewer=ISO] + **** + block content + **** + + * Unordered list item 1 + + + ==== + block attached + ==== + ADOC + ast = Coradoc::Parser::Base.new.parse(content) + # pp ast + + end + + xit "some problem with nested blocks" do + content =<<~TEXT + .Source block (open block syntax) + [source] + -- + ==== + Text inside of a block. + ==== + -- + TEXT + ast = Coradoc::Parser::Base.new.parse(content) + # pp ast + end + end +end diff --git a/spec/coradoc/parser/asciidoc/citation_spec.rb b/spec/coradoc/parser/asciidoc/citation_spec.rb index f38ec6c..acc53d6 100644 --- a/spec/coradoc/parser/asciidoc/citation_spec.rb +++ b/spec/coradoc/parser/asciidoc/citation_spec.rb @@ -21,10 +21,20 @@ expect(ast).to eq([{:citation=>{:cross_reference=>[{:href_arg=>"xref_anchor"}, {:key=>"section", :delimiter=>"=", :value=>"1"}]}}]) ast = parser.parse("[.source]\n<>some reference\n") - expect(ast).to eq([{:citation=>{:cross_reference=>[{:href_arg=>"xref_anchor"}], :comment=>[{:text=>"some reference", :line_break=>"\n"}]}}]) + obj = [{:citation=> + {:cross_reference=>[{:href_arg=>"xref_anchor"}], + :comment=>[{:text=>"some reference", :line_break=>"\n"}]}}] + + expect(ast).to eq(obj) ast = parser.parse("[.source]\n<>some reference\nsecond line\n") - expect(ast).to eq([{:citation=>{:cross_reference=>[{:href_arg=>"xref_anchor"}], :comment=>[{:text=>"some reference", :line_break=>"\n"}, {:text=>"second line", :line_break=>"\n"}]}}]) + obj = [{:citation=> + {:cross_reference=>[{:href_arg=>"xref_anchor"}], + :comment=> + [{:text=>"some reference", :line_break=>"\n"}, + {:text=>"second line", :line_break=>"\n"}]}}] + + expect(ast).to eq(obj) end end @@ -32,8 +42,7 @@ module Asciidoc - class CitationTester < Parslet::Parser - include Coradoc::Parser::Asciidoc::Base + class CitationTester < Coradoc::Parser::Asciidoc::Base rule(:document) { (citation | any.as(:unparsed)).repeat(1) } root :document diff --git a/spec/coradoc/parser/asciidoc/content_spec.rb b/spec/coradoc/parser/asciidoc/content_spec.rb index 017fc82..8063775 100644 --- a/spec/coradoc/parser/asciidoc/content_spec.rb +++ b/spec/coradoc/parser/asciidoc/content_spec.rb @@ -119,6 +119,34 @@ expect(block_two[:lines][0][:text]).to eq("This renders in monospace.") end + it "parses source type block" do + content = <<~TEXT + .Source block (open block syntax) + [source] + -- + This renders in monospace. + -- + + .Source block (with block perimeter type) + ---- + This renders in monospace. + ---- + TEXT + + ast = Asciidoc::ContentTester.parse(content) + + obj = [{:block=> + {:title=>"Source block (open block syntax)", + :attribute_list=>{:attribute_array=>[{:positional=>"source"}]}, + :delimiter=>"--", + :lines=>[{:text=>"This renders in monospace.", :line_break=>"\n"}]}}, + {:block=> + {:title=>"Source block (with block perimeter type)", + :delimiter=>"----", + :lines=>[{:text=>"This renders in monospace.", :line_break=>"\n"}]}}] + expect(ast).to eq(obj) + end + it "parses quote type block" do content = <<~TEXT .Quote block (open block syntax) @@ -206,21 +234,26 @@ end end - it "parses list embeded in the content" do - content = <<~DOC - * Unordered list item 1 - * Unordered list item 2 - * [[list_item_id]] Unordered list item 3 - DOC + context "paragraph" do + it "parses paragraph with id 2" do + content = <<~TEXT + [id=myblock] + This is my block with a defined ID. + this is going to be the next line + TEXT + ast = Asciidoc::ContentTester.parse(content) + obj = [{:paragraph=> + {:attribute_list=> + {:attribute_array=> + [{:named=>{:named_key=>"id", :named_value=>"myblock"}}]}, + :lines=> + [{:text=>"This is my block with a defined ID.", :line_break=>"\n"}, + {:text=>"this is going to be the next line", :line_break=>"\n"}]}}] + expect(ast).to eq(obj) + end + end - ast = Asciidoc::ContentTester.parse(content) - list_items = ast[0][:list][:unordered] - expect(list_items.count).to eq(3) - expect(list_items[0][:text]).to eq("Unordered list item 1") - expect(list_items[2][:id]).to eq("list_item_id") - expect(list_items[2][:text]).to eq("Unordered list item 3") - end it "parses the table block" do content = <<~DOC @@ -242,6 +275,33 @@ expect(table[:rows][1][:cols][2][:text]).to eq("john.doe@example.com") end + it "parses the table block 2" do + content = <<~DOC + .Person table + |=== + | *first_name* | last_name | email + | John | Doe | john.doe@example.com + | | doe | jennie.doe@example.com + |=== + DOC + + ast = Asciidoc::ContentTester.parse(content) + table = ast.first[:table] + + + obj = {:table=> + {:title=>"Person table", + :rows=> + [{:cols=>[{:text=>"*first_name*"}, {:text=>"last_name"}, {:text=>"email"}]}, + {:cols=> + [{:text=>"John"}, {:text=>"Doe"}, {:text=>"john.doe@example.com"}]}, + {:cols=> + [{:text=>" "}, {:text=>"doe"}, {:text=>"jennie.doe@example.com"}]}]}} + + expect(ast.first).to eq(obj) + + end + it "parses highlighted text block" do content = <<~DOC [[scls_5-9]] @@ -260,8 +320,7 @@ end module Asciidoc - class ContentTester < Parslet::Parser - include Coradoc::Parser::Asciidoc::Base + class ContentTester < Coradoc::Parser::Asciidoc::Base rule(:document) { (contents | any.as(:unparsed)).repeat(1) } root :document diff --git a/spec/coradoc/parser/asciidoc/document_attributes_spec.rb b/spec/coradoc/parser/asciidoc/document_attributes_spec.rb index 8aa5745..2e2d1c3 100644 --- a/spec/coradoc/parser/asciidoc/document_attributes_spec.rb +++ b/spec/coradoc/parser/asciidoc/document_attributes_spec.rb @@ -38,8 +38,7 @@ end module Asciidoc - class DocumentAttributesTester < Parslet::Parser - include Coradoc::Parser::Asciidoc::Base + class DocumentAttributesTester < Coradoc::Parser::Asciidoc::Base rule(:document) do (document_attributes | any.as(:unparsed)).repeat(1) diff --git a/spec/coradoc/parser/asciidoc/header_spec.rb b/spec/coradoc/parser/asciidoc/header_spec.rb index 9e14d65..f2a7608 100644 --- a/spec/coradoc/parser/asciidoc/header_spec.rb +++ b/spec/coradoc/parser/asciidoc/header_spec.rb @@ -30,8 +30,7 @@ end module Asciidoc - class HeaderTester < Parslet::Parser - include Coradoc::Parser::Asciidoc::Base + class HeaderTester < Coradoc::Parser::Asciidoc::Base rule(:document) { (header | any.as(:unparsed)).repeat(1) } root :document diff --git a/spec/coradoc/parser/asciidoc/inline_spec.rb b/spec/coradoc/parser/asciidoc/inline_spec.rb index 8221cdf..cf091fe 100644 --- a/spec/coradoc/parser/asciidoc/inline_spec.rb +++ b/spec/coradoc/parser/asciidoc/inline_spec.rb @@ -7,9 +7,9 @@ ast = parser.parse("*bold*") - # expect(ast).to eq([{:bold_constrained=>[{:text=>"bold"}]}]) + expect(ast).to eq([{:bold_constrained=>[{:text=>"bold"}]}]) ast = parser.parse("**bold**") - # expect(ast).to eq([{:bold_unconstrained=>[{:text=>"bold"}]}]) + expect(ast).to eq([{:bold_unconstrained=>[{:text=>"bold"}]}]) ast = parser.parse("line with *bold*") exp = [{:text=>[ "line with ", @@ -30,8 +30,7 @@ module Asciidoc - class InlineTextFormattingTester < Parslet::Parser - include Coradoc::Parser::Asciidoc::Base + class InlineTextFormattingTester < Coradoc::Parser::Asciidoc::Base rule(:document) { (text_formatted | any.as(:unparsed)).repeat(1) } root :document diff --git a/spec/coradoc/parser/asciidoc/list_spec.rb b/spec/coradoc/parser/asciidoc/list_spec.rb new file mode 100644 index 0000000..d4f0993 --- /dev/null +++ b/spec/coradoc/parser/asciidoc/list_spec.rb @@ -0,0 +1,358 @@ +require "spec_helper" + +RSpec.describe "Coradoc::Parser::Asciidoc::List" do + describe ".parse" do + + it "parses ordered list" do + content = <<~DOC + . Ordered list item 1 + . Ordered list item 2 + . [[list_item_id]] Ordered list item 3 + DOC + + ast = Asciidoc::ListTester.parse(content) + list_items = ast[:list][:ordered] + + expect(list_items.count).to eq(3) + expect(list_items[0][:list_item][:text]).to eq("Ordered list item 1") + expect(list_items[2][:list_item][:id]).to eq("list_item_id") + expect(list_items[2][:list_item][:text]).to eq("Ordered list item 3") + end + + + it "parser ordered list with empty lines between items" do + content = <<~DOC + . Ordered list item 1 + + . Ordered list item 2 + DOC + + ast = Asciidoc::ListTester.parse(content) + obj = {:list=> + {:ordered=> + [{:list_item=> + {:marker=>".", + :text=>"Ordered list item 1", + :line_break=>"\n\n", + :attached=>[], + :nested=>[]}}, + {:list_item=> + {:marker=>".", + :text=>"Ordered list item 2", + :line_break=>"\n", + :attached=>[], + :nested=>[]}}]}} + expect(ast).to eq(obj) + end + + + it "parses unordered list" do + content = <<~DOC + * Unordered list item 1 + * Unordered list item 2 + * [[list_item_id]] Unordered list item 3 + DOC + + ast = Asciidoc::ListTester.parse(content) + list_items = ast[:list][:unordered] + + expect(list_items.count).to eq(3) + expect(list_items[0][:list_item][:text]).to eq("Unordered list item 1") + expect(list_items[2][:list_item][:id]).to eq("list_item_id") + expect(list_items[2][:list_item][:text]).to eq("Unordered list item 3") + + obj = {:list=> + {:unordered=> + [{:list_item=> + {:marker=>"*", + :text=>"Unordered list item 1", + :line_break=>"\n", + :attached=>[], + :nested=>[]}}, + {:list_item=> + {:marker=>"*", + :text=>"Unordered list item 2", + :line_break=>"\n", + :attached=>[], + :nested=>[]}}, + {:list_item=> + {:marker=>"*", + :id=>"list_item_id", + :text=>"Unordered list item 3", + :line_break=>"\n", + :attached=>[], + :nested=>[]}}]}} + + expect(ast).to eq(obj) + end + + it "parses ordered list with nesting" do + content = <<~DOC + . Ordered list item 1 + . Ordered list item 2 + .. Nested list item A + DOC + + ast = Asciidoc::ListTester.parse(content) + list_items = ast[:list][:ordered] + + expect(list_items.count).to eq(2) + expect(list_items[1][:list_item][:nested][0][:list][:ordered][0][:list_item][:text]).to eq("Nested list item A") + + obj = {:list=> + {:ordered=> + [{:list_item=> + {:marker=>".", + :text=>"Ordered list item 1", + :line_break=>"\n", + :attached=>[], + :nested=>[]}}, + {:list_item=> + {:marker=>".", + :text=>"Ordered list item 2", + :line_break=>"\n", + :attached=>[], + :nested=> + [{:list=> + {:ordered=> + [{:list_item=> + {:marker=>"..", + :text=>"Nested list item A", + :line_break=>"\n", + :attached=>[], + :nested=>[]}}]}}]}}]}} + + expect(ast).to eq(obj) + end + + + + it "parses list with attached paragraph" do + content = <<~TEXT + . This is a list item + + + With attached paragraph. + TEXT + + ast = Asciidoc::ListTester.parse(content) + items = ast[:list][:ordered] + expect(items[0][:list_item][:text]).to eq("This is a list item") + + obj = {:list=> + {:ordered=> + [{:list_item=> + {:marker=>".", + :text=>"This is a list item", + :line_break=>"\n", + :attached=> + [{:paragraph=> + {:lines=> + [{:text=>"With attached paragraph.", + :line_break=>"\n"}]}}], + :nested=>[]}}]}} + expect(ast).to eq(obj) + end + + + it "parses list with attached admonition" do + content = <<~TEXT + . This is a list item + + + NOTE: attached admonition. + TEXT + + ast = Asciidoc::ListTester.parse(content) + first_item = ast[:list][:ordered][0][:list_item] + expect(first_item[:text]).to eq("This is a list item") + expect(first_item[:attached][0][:admonition_type]).to eq("NOTE") + + obj = {:list=> + {:ordered=> + [{:list_item=> + {:marker=>".", + :text=>"This is a list item", + :line_break=>"\n", + :attached=> + [{:admonition_type=>"NOTE", + :content=>[{:text=>"attached admonition.", :line_break=>"\n"}]}], + :nested=>[]}}]}} + expect(ast).to eq(obj) + end + + + + it "parses list with attached paragraphs" do + content = <<~TEXT + . This is a list item + + + With attached paragraph. + + + And another attached paragraph. + TEXT + + ast = Asciidoc::ListTester.parse(content) + items = ast[:list][:ordered] + expect(items[0][:list_item][:text]).to eq("This is a list item") + + obj = {:list=> + {:ordered=> + [{:list_item=> + {:marker=>".", + :text=>"This is a list item", + :line_break=>"\n", + :attached=> + [{:paragraph=> + {:lines=> + [{:text=>"With attached paragraph.", :line_break=>"\n"}]}}, + {:paragraph=> + {:lines=> + [{:text=>"And another attached paragraph.", + :line_break=>"\n"}]}}], + :nested=>[]}}]}} + expect(ast).to eq(obj) + end + + + it "parses nested list with paragraph attached" do + content = <<~TEXT + . Ordered list item 1 + . Ordered list item 2 + ** Nested list item A + + + Attached paragraph. + TEXT + + ast = Asciidoc::ListTester.parse(content) + items = ast[:list][:ordered] + expect(items[0][:list_item][:text]).to eq("Ordered list item 1") + expect(items[1][:list_item][:nested][0][:list][:unordered][0][:list_item][:text]).to eq("Nested list item A") + + obj = {:list=> + {:ordered=> + [{:list_item=> + {:marker=>".", + :text=>"Ordered list item 1", + :line_break=>"\n", + :attached=>[], + :nested=>[]}}, + {:list_item=> + {:marker=>".", + :text=>"Ordered list item 2", + :line_break=>"\n", + :attached=>[], + :nested=> + [{:list=> + {:unordered=> + [{:list_item=> + {:marker=>"**", + :text=>"Nested list item A", + :line_break=>"\n", + :attached=> + [{:paragraph=> + {:lines=> + [{:text=>"Attached paragraph.", :line_break=>"\n"}]}}], + :nested=>[]}}]}}]}}]}} + + expect(ast).to eq(obj) + end + + + it "parses nested list with multiline paragraph attached" do + content = <<~TEXT + . Ordered list item 1 + . Ordered list item 2 + ** Nested list item A + + + Attached paragraph + that is also + multiline. + TEXT + + ast = Asciidoc::ListTester.parse(content) + items = ast[:list][:ordered] + expect(items[0][:list_item][:text]).to eq("Ordered list item 1") + expect(items[1][:list_item][:nested][0][:list][:unordered][0][:list_item][:text]).to eq("Nested list item A") + + obj = {:list=> + {:ordered=> + [{:list_item=> + {:marker=>".", + :text=>"Ordered list item 1", + :line_break=>"\n", + :attached=>[], + :nested=>[]}}, + {:list_item=> + {:marker=>".", + :text=>"Ordered list item 2", + :line_break=>"\n", + :attached=>[], + :nested=> + [{:list=> + {:unordered=> + [{:list_item=> + {:marker=>"**", + :text=>"Nested list item A", + :line_break=>"\n", + :attached=> + [{:paragraph=> + {:lines=> + [{:text=>"Attached paragraph", :line_break=>"\n"}, + {:text=>"that is also", :line_break=>"\n"}, + {:text=>"multiline.", :line_break=>"\n"}]}}], + :nested=>[]}}]}}]}}]}} + + expect(ast).to eq(obj) + end + + + it "parses item with both attached and nested" do + content =<<~ADOC + . Ordered list item 1 + . Ordered list item 2 + + + Attached paragraph. + ** Nested list item A + ADOC + + ast = Asciidoc::ListTester.parse(content) + obj = {:list=> + {:ordered=> + [{:list_item=> + {:marker=>".", + :text=>"Ordered list item 1", + :line_break=>"\n", + :attached=>[], + :nested=>[]}}, + {:list_item=> + {:marker=>".", + :text=>"Ordered list item 2", + :line_break=>"\n", + :attached=> + [{:paragraph=> + {:lines=>[{:text=>"Attached paragraph.", :line_break=>"\n"}]}}], + :nested=> + [{:list=> + {:unordered=> + [{:list_item=> + {:marker=>"**", + :text=>"Nested list item A", + :line_break=>"\n", + :attached=>[], + :nested=>[]}}]}}]}}]}} + expect(ast).to eq(obj) + end + end +end + + +module Asciidoc + class ListTester < Coradoc::Parser::Asciidoc::Base + + rule(:document) { (list | any.as(:unparsed)).repeat(1) } + root :document + + def self.parse(text) + new.parse_with_debug(text).first + end + end +end diff --git a/spec/coradoc/parser/asciidoc/section_spec.rb b/spec/coradoc/parser/asciidoc/section_spec.rb index c728cb0..ac77790 100644 --- a/spec/coradoc/parser/asciidoc/section_spec.rb +++ b/spec/coradoc/parser/asciidoc/section_spec.rb @@ -149,9 +149,9 @@ expect(section[:id]).to eq("section_id") expect(section[:title][:text]).to eq("Section title") - expect(list_items[0][:text]).to eq("List item one") - expect(list_items[1][:id]).to eq("list_item_id") - expect(list_items[1][:text]).to eq("List item two") + expect(list_items[0][:list_item][:text]).to eq("List item one") + expect(list_items[1][:list_item][:id]).to eq("list_item_id") + expect(list_items[1][:list_item][:text]).to eq("List item two") end it "parses blocks with different types" do @@ -189,8 +189,7 @@ end module Asciidoc - class SectionTester < Parslet::Parser - include Coradoc::Parser::Asciidoc::Base + class SectionTester < Coradoc::Parser::Asciidoc::Base rule(:document) { (section | any.as(:unparsed)).repeat(1) } root :document diff --git a/spec/coradoc/parser/asciidoc/tag_spec.rb b/spec/coradoc/parser/asciidoc/tag_spec.rb index 657bccd..0bd313d 100644 --- a/spec/coradoc/parser/asciidoc/tag_spec.rb +++ b/spec/coradoc/parser/asciidoc/tag_spec.rb @@ -22,8 +22,7 @@ module Asciidoc - class TagTester < Parslet::Parser - include Coradoc::Parser::Asciidoc::Base + class TagTester < Coradoc::Parser::Asciidoc::Base rule(:document) { (tag | any.as(:unparsed)).repeat(1) } root :document diff --git a/spec/coradoc/parser_spec.rb b/spec/coradoc/parser_spec.rb index 2a85af5..52a121f 100644 --- a/spec/coradoc/parser_spec.rb +++ b/spec/coradoc/parser_spec.rb @@ -16,7 +16,7 @@ expect(ast[1][:document_attributes][0][:key]).to eq("published") expect(ast[1][:document_attributes][0][:value]).to eq("'2023-03-08T09:51:08+08:00'") - section = ast[3][:section][:section] + section = ast[3][:section] clause_5_1 = section[:sections][0][:section] content = clause_5_1[:contents].first @@ -44,8 +44,8 @@ list_one_items = guidance[:contents][2][:list][:unordered] expect(list_one_items.count).to eq(3) - expect(list_one_items[0][:text]).not_to be_nil - expect(list_one_items[0][:id]).to eq("guidance_5.1_part_2_1") + expect(list_one_items[0][:list_item][:text]).not_to be_nil + expect(list_one_items[0][:list_item][:id]).to eq("guidance_5.1_part_2_1") expect(guidance[:contents][3][:paragraph][:lines][0][:id]).not_to be_nil expect(guidance[:contents][4][:list][:unordered].count).to eq(7) diff --git a/utils/round_trip.rb b/utils/round_trip.rb index 9400403..7576dd0 100644 --- a/utils/round_trip.rb +++ b/utils/round_trip.rb @@ -30,7 +30,7 @@ generated_adoc = Coradoc::Generator.gen_adoc(doc) cleaned_adoc = Coradoc::Input::HTML.cleaner.tidy(generated_adoc) File.open("#{file_path}.roundtrip","w"){|f| f.write(cleaned_adoc)} - `diff -B #{file_path} #{file_path}.roundtrip > #{file_path}.roundtrip.diff` + `diff -BNaur #{file_path} #{file_path}.roundtrip > #{file_path}.roundtrip.diff` # rescue # puts "unsuccessful..." # end