module Fabricator
Public Class Methods
canonicalise_chunk_name(raw_name)
click to toggle source
# File lib/mau/fabricator.rb, line 996 def canonicalise_chunk_name raw_name name = '' raw_name.strip.split(/(\[\[.*?\]*\]\])/, -1). each_with_index do |part, i| part.gsub! /\s+/, ' ' if i.even? name << part end return name end
commatise_oxfordly(items)
click to toggle source
# File lib/mau/fabricator.rb, line 1593 def commatise_oxfordly items result = [] items.each_with_index do |item, i| unless i.zero? then unless items.length == 2 then result.push OpenStruct.new(:type => :plain, :data => ',') end result.push OpenStruct.new(:type => :space) if i == items.length - 1 then result.push OpenStruct.new(:type => :plain, :data => 'and') result.push OpenStruct.new(:type => :space) end end result.push *item end return result end
format_location(h)
click to toggle source
# File lib/mau/fabricator.rb, line 964 def format_location h if h.column then return "%s:%i.%i" % [h.filename, h.line, h.column] else return "%s:%i" % [h.filename, h.line] end end
format_location_range(h, dash: "-")
click to toggle source
# File lib/mau/fabricator.rb, line 972 def format_location_range h, dash: "-" if h.from.filename != h.to.filename then return format_location(h.from) + dash + format_location(h.to) else if h.from.line != h.to.line then result = h.from.filename + ":" result << h.from.line.to_s result << "." << h.from.column.to_s if h.from.column result << dash result << h.to.line.to_s result << "." << h.to.column.to_s if h.to.column else result = h.from.filename + ":" result << h.from.line.to_s if h.from.column or h.to.column then result << "." << h.from.column.to_s << dash << h.to.column.to_s end end return result end end
htmlify(nodes, port)
click to toggle source
# File lib/mau/fabricator.rb, line 2036 def htmlify nodes, port nodes.each do |node| case node.type when :plain then port.print node.data.to_xml when :space then port.print((node.data || ' ').to_xml) when :nbsp then port.print ' ' when :monospace, :bold, :italic, :underscore then html_tag = Fabricator::MARKUP2HTML[node.type] port.print "<%s>" % html_tag htmlify node.content, port port.print "</%s>" % html_tag when :mention_chunk then port.print "<span class='maui-chunk-mention'>\u00AB" htmlify( parse_markup(node.name, Fabricator::MF::LINK), port) port.print "\u00BB</span>" when :link then port.print "<a href='#{node.target.to_xml}'>" htmlify node.content, port port.print "</a>" else raise 'invalid node type' end end return end
link_like?(s)
click to toggle source
# File lib/mau/fabricator.rb, line 1149 def link_like? s return !!(s =~ /\A(?:#\s*)?[[:alnum:]]/) end
load_fabric(input, chunk_size_limit: 24)
click to toggle source
# File lib/mau/fabricator.rb, line 1192 def load_fabric input, chunk_size_limit: 24 vp = Fabricator::Vertical_Peeker.new input integrator = Fabricator::Integrator.new in_list = false loop do vertical_separation = 0 while vp.peek_line == '' do if vertical_separation == 2 then integrator.warn vp.location_ahead, "more than two consecutive blank lines" end vertical_separation += 1 vp.get_line end break if vp.eof? if vertical_separation >= 2 then integrator.force_section_break in_list = false end element_location = vp.location_ahead case vp.peek_line when /^\s+/ then if !in_list or vp.peek_line !~ /^ (?<margin> \s+ ) - (?<separator> \s+ ) /x then body_location = vp.location_ahead element = vp.get_indented_lines_with_skip element.type = :block element.body_loc = element_location else margin = $~['margin'] lines = [$~['separator'] + $'] vp.get_line while !vp.eof? and vp.peek_line.start_with? margin and vp.peek_line !~ /^\s*-\s/ do lines.push vp.get_line[margin.length .. -1] end element = OpenStruct.new( type: :item, lines: lines, content: parse_markup(lines.map(&:strip).join ' '), indent: margin.length, loc: element_location) end when /^<<\s* (?: (?<root-type> \.file|\.script)\s+ )? (?<raw-name> [^\s].*?) \s*>>:$/x then name = canonicalise_chunk_name $~['raw-name'] vp.get_line element = OpenStruct.new( type: :divert, root_type: $~['root-type'], name: name, header_loc: element_location) body_location = vp.location_ahead body = vp.get_indented_lines_with_skip if body then element.type = :chunk element.lines = body.lines element.indent = body.indent element.body_loc = body_location element.initial = element.final = true end when /^-\s/ then # We'll discard the leading dash but save the following # whitespace. lines = [vp.get_line[1 .. -1]] while !vp.eof? and vp.peek_line != '' and vp.peek_line !~ /^\s*-\s/ do lines.push vp.get_line end element = OpenStruct.new( type: :item, lines: lines, content: parse_markup(lines.map(&:strip).join ' '), indent: 0, loc: element_location) when /^[^\s]/ then lines = [] while vp.peek_line =~ /^[^\s]/ and vp.peek_line !~ /^-\s/ do lines.push vp.get_line end mode_flags_to_suppress = 0 case lines[0] when /^(==+)(\s+)/ then lines[0] = $2 + $' element = OpenStruct.new( type: :title, level: $1.length - 1, loc: element_location) mode_flags_to_suppress |= Fabricator::MF::LINK when /^\*\s+/ then lines[0] = $' element = OpenStruct.new( type: :rubric, loc: element_location) else element = OpenStruct.new( type: :paragraph, loc: element_location) end element.lines = lines element.content = parse_markup(lines.map(&:strip).join(' '), mode_flags_to_suppress) else raise 'assertion failed' end integrator.integrate element in_list = element.type == :item end integrator.clear_diversion integrator.check_root_type_consistency integrator.check_chunk_sizes(chunk_size_limit) integrator.tangle_roots return integrator.output end def weave_ctxt fabric, port, width: 80, pseudographics: Fabricator::UNICODE_PSEUDOGRAPHICS wr = Fabricator::Text_Wrapper.new port, width: width, pseudographics: pseudographics unless fabric.warnings.empty? then wr.styled :section_title do wr.add_plain 'Warnings' end wr.linebreak wr.linebreak weave_ctxt_warning_list fabric.warnings, wr wr.linebreak end toc_generated = false fabric.presentation.each do |element| case element.type when :title then if !toc_generated then weave_ctxt_toc fabric.toc, wr toc_generated = true end wr.styled :section_title do wr.add_plain "#{element.number}." wr.add_space wr.hang do wr.add_nodes element.content end end wr.linebreak wr.linebreak when :section then rubricated = element.elements[0].type == :rubric # If we're encountering the first rubric/title, output # the table of contents. if rubricated and !toc_generated then weave_ctxt_toc fabric.toc, wr toc_generated = true end start_index = 0 # index of the first non-special child if rubricated then start_index += 1 wr.styled :rubric do wr.add_plain "§%i." % element.section_number wr.add_space wr.add_nodes element.elements.first.content end else wr.styled :section_number do wr.add_plain "§%i." % element.section_number end end # If the rubric or the section sign is followed by a # paragraph, a chunk header, or a divert, we'll output # it in the same paragraph. starter = element.elements[start_index] if starter then case starter.type when :paragraph, :divert, :chunk then wr.add_space weave_ctxt_section_part starter, fabric, wr start_index += 1 else wr.linebreak end end # Finally, the blank line that separates the special # paragraph from the section's body, if any. wr.linebreak element.elements[start_index .. -1].each do |child| weave_ctxt_section_part child, fabric, wr wr.linebreak end unless (element.warnings || []).empty? then weave_ctxt_warning_list element.warnings, wr, inline: true, indent: false wr.linebreak end else raise 'data structure error' end end return end def weave_ctxt_warning_list list, wr, inline: false, indent: true list.to_a.each do |warning| wr.styled inline ? :inline_warning : :null do wr.add_plain (indent ? ' ' : '') + '!!! ' if inline wr.add_plain format_location(warning.loc) wr.add_plain ':' wr.add_space wr.hang do warning.message.split(/(\s+)/). each_with_index do |part, i| if i.even? then wr.add_plain part else wr.add_space part end end end end wr.linebreak end return end def weave_ctxt_section_part element, fabric, wr case element.type when :paragraph then wr.add_nodes element.content wr.linebreak when :divert, :chunk, :diverted_chunk then if [:divert, :chunk].include? element.type then weave_ctxt_chunk_header element, wr weave_ctxt_warning_list element.warnings, wr, inline: true end if [:chunk, :diverted_chunk].include? element.type then wr.styled :chunk_frame do wr.add_pseudographics element.initial ? :initial_chunk_margin : :chunk_margin end wr.styled :monospace do element.content.each do |node| case node.type when :verbatim then wr.add_plain node.data when :newline then wr.linebreak wr.styled :chunk_frame do wr.add_pseudographics :chunk_margin end when :use then weave_ctxt_use node, wr else raise 'data structure error' end end end wr.linebreak if element.final then wr.styled :chunk_frame do wr.add_pseudographics :final_chunk_marker end wr.linebreak end weave_ctxt_warning_list element.warnings, wr, inline: true if element.final then wr.styled :chunk_xref do wr.add_nodes xref_chain(element, fabric) end wr.linebreak end end when :list then weave_ctxt_list element.items, wr when :block then weave_ctxt_block element, wr else raise 'data structure error' end return end def weave_ctxt_chunk_header element, wr wr.styled :chunk_header do wr.add_pseudographics :before_chunk_name if element.root_type then wr.styled :root_type do wr.add_plain element.root_type end wr.add_space end wr.add_nodes( parse_markup(element.name, Fabricator::MF::LINK)) wr.add_pseudographics :after_chunk_name wr.add_plain ":" end wr.linebreak return end def weave_ctxt_block element, wr element.lines.each do |line| wr.styled :block_frame do wr.add_pseudographics :block_margin end wr.styled :monospace do wr.add_plain line end wr.linebreak end return end def weave_ctxt_use node, wr wr.styled :use do wr.add_pseudographics :before_chunk_name if node.clearindent then wr.add_plain ".clearindent " end wr.add_nodes parse_markup(node.name, Fabricator::MF::LINK) if node.vertical_separation then wr.add_plain " " + node.vertical_separation end if node.postprocess then wr.add_plain " " + node.postprocess end wr.add_pseudographics :after_chunk_name end return end # Given a chunk, prepare its transclusion summary as a list of # markup nodes. Should only be used on chunks that are the # last in a chunk chain (i.e., that have [[final]] set). def xref_chain element, fabric, dash: "-" xref = markup if element.initial then xref.words "This chunk is " else xref.words "These chunks are " end cbn_entry = fabric.chunks_by_name[element.name] transcluders = cbn_entry.transcluders if transcluders then xref.words "transcluded by " xref.push *commatise_oxfordly( transcluders.map{|ref| markup. node(:mention_chunk, name: ref.name). space. plain("(§%i)" % ref.section_number) }) else if cbn_entry.root_type then xref.words "solely a transclusion root" else xref.words "never transcluded" end end xref.words " and " tlocs = element.divert ? element.divert.chain_tangle_locs : element.tangle_locs if tlocs then xref. words("tangled to "). push(*commatise_oxfordly( tlocs.map{|range| markup. plain(format_location_range(range, dash: dash)) })). plain(".") else xref.words "never tangled." end return xref end def commatise_oxfordly items result = [] items.each_with_index do |item, i| unless i.zero? then unless items.length == 2 then result.push OpenStruct.new(:type => :plain, :data => ',') end result.push OpenStruct.new(:type => :space) if i == items.length - 1 then result.push OpenStruct.new(:type => :plain, :data => 'and') result.push OpenStruct.new(:type => :space) end end result.push *item end return result end def weave_ctxt_list items, wr items.each do |item| wr.add_pseudographics :bullet wr.add_plain " " wr.hang do wr.add_nodes item.content end wr.linebreak unless (item.warnings || []).empty? then wr.hang do weave_ctxt_warning_list item.warnings, wr, inline: true end end if item.sublist then wr.add_plain " " wr.hang do weave_ctxt_list item.sublist.items, wr end end end return end def weave_ctxt_toc toc, wr if toc.length >= 2 then wr.styled :section_title do wr.add_plain 'Contents' end wr.linebreak; wr.linebreak rubric_level = 0 toc.each do |entry| case entry.type when :title then rubric_level = entry.level - 1 + 1 wr.add_plain ' ' * (entry.level - 1) wr.add_plain entry.number + '.' wr.add_space wr.hang do wr.add_nodes entry.content end when :rubric then wr.add_plain ' ' * rubric_level wr.add_plain '§%i.' % entry.section_number wr.add_space wr.hang do wr.add_nodes entry.content end else raise 'assertion failed' end wr.linebreak end wr.linebreak end return end def weave_html fabric, port, title: nil, link_css: [] title ||= "(Untitled)" port.puts '<!doctype html>' port.puts '<html>' port.puts '<head>' port.puts "<meta http-equiv='Content-type' " + "content='text/html; charset=utf-8' />" port.puts "<title>#{title.to_xml}</title>" if link_css.empty? then port.puts "<style type='text/css'>" port.puts '/**** Fonts ****/ @import url("http://fonts.googleapis.com/css?family=Roboto"); @import url("http://fonts.googleapis.com/css?family=Cousine"); /**** Rules ****/ body, .maui-transclude { font-family: "Roboto", sans-serif; } pre, tt, code { font-family: "Cousine", monospace; } body { colour: black; background: white; } tt, code { color: forestgreen; } .maui-inline-warnings { color: red; } .maui-warnings tt { color: inherit; } .maui-rubric { color: crimson; } ul.maui-warnings { padding-left: 0; } ul.maui-warnings > li { list-style: none; } .maui-chunk-body { margin-left: 20px; border-left: 2px solid #cccccc; padding-left: 5px; } .maui-initial-chunk > .maui-chunk-body:before { content: ""; display: block; width: 22px; border-top: solid 2px #cccccc; margin-left: -27px; } .maui-final-chunk > .maui-chunk-body:after { content: ""; display: block; margin-left: -7px; width: 40px; border-bottom: solid 2px #cccccc; } .maui-chunk-body, .maui-chunk > .maui-warnings { margin-top: 0; margin-bottom: 0; } .maui-chunk { margin-top: 16px; margin-bottom: 16px; } .maui-chunk-xref { font-size: small; font-style: italic; margin-left: 22px; } /* Backwards compatibility with pre-HTML5 browsers */ section { display: block; }' port.puts "</style>" else link_css.each do |link| port.puts ("<link rel='stylesheet' type='text/css' " + "href='%s' />") % link.to_xml end end port.puts '</head>' port.puts '<body>' port.puts port.puts "<h1>#{title.to_xml}</h1>" unless fabric.warnings.empty? then port.puts "<h2>Warnings</h2>" port.puts weave_html_warning_list fabric.warnings, port port.puts end toc_generated = false fabric.presentation.each do |element| case element.type when :title then if !toc_generated then weave_html_toc fabric.toc, port toc_generated = true end port.print '<h%i' % (element.level + 1) port.print " id='%s'" % "T.#{element.number}" port.print '>' port.print "#{element.number}. " htmlify element.content, port port.puts '</h%i>' % (element.level + 1) when :section then rubricated = element.elements[0].type == :rubric # If we're encountering the first rubric/title, output # the table of contents. if rubricated and !toc_generated then weave_html_toc fabric.toc, port toc_generated = true end start_index = 0 port.puts "<section class='maui-section' id='%s'>" % "S.#{element.section_number}" port.puts port.print "<p>" port.print "<b class='%s'>" % (rubricated ? 'maui-rubric' : 'maui-section-number') port.print "\u00A7#{element.section_number}." if rubricated then port.print " " htmlify element.elements[start_index].content, port start_index += 1 end port.print "</b>" subelement = element.elements[start_index] warnings = nil case subelement && subelement.type when :paragraph then port.print " " htmlify subelement.content, port start_index += 1 when :divert then port.print " " weave_html_chunk_header subelement, 'maui-divert', port, tag: 'span' warnings = subelement.warnings start_index += 1 end port.puts "</p>" if warnings then weave_html_warning_list warnings, port, inline: true end port.puts element.elements[start_index .. -1].each do |child| weave_html_section_part child, fabric, port port.puts end unless (element.warnings || []).empty? then weave_html_warning_list element.warnings, port, inline: true port.puts end port.puts "</section>" else raise 'data structure error' end port.puts end port.puts '</html>' port.puts '</body>' port.puts '</html>' return end def weave_html_section_part element, fabric, port case element.type when :paragraph then port.print "<p>" htmlify element.content, port port.puts "</p>" when :list then weave_html_list element.items, port when :divert then weave_html_chunk_header element, 'maui-divert', port port.puts weave_html_warning_list element.warnings, port, inline: true when :chunk, :diverted_chunk then port.print "<div class='maui-chunk" port.print " maui-initial-chunk" if element.initial port.print " maui-final-chunk" if element.final port.print "'>" if element.type == :chunk then weave_html_chunk_header element, 'maui-chunk-header', port port.puts end weave_html_chunk_body element, port unless (element.warnings || []).empty? then weave_html_warning_list element.warnings, port, inline: true end if element.final then port.print "<div class='maui-chunk-xref'>" htmlify( xref_chain(element, fabric, dash: "\u2013"), port) port.puts "</div>" end port.puts "</div>" when :block then port.print "<pre class='maui-block'>" element.lines.each_with_index do |line, i| port.puts unless i.zero? port.print line.to_xml end port.puts "</pre>" else raise 'data structure error' end return end def weave_html_toc toc, port if toc.length >= 2 then port.puts "<h2>Contents</h2>" port.puts last_level = 0 # What level should the rubrics in the current # (sub(sub))chapter appear at? rubric_level = 1 toc.each do |entry| if entry.type == :rubric then level = rubric_level else level = entry.level rubric_level = entry.level + 1 end if level > last_level then raise 'assertion failed' \ unless level == last_level + 1 port.print "\n<ul><li>" elsif level == last_level then port.print "</li>\n<li>" else port.print "</li></ul>" * (last_level - level) + "\n<li>" end case entry.type when :title then port.print "#{entry.number}. " port.print "<a href='#T.#{entry.number}'>" htmlify entry.content, port port.print "</a>" when :rubric then port.print "\u00A7#{entry.section_number}. " port.print "<a href='#S.#{entry.section_number}'>" htmlify entry.content, port port.print "</a>" else raise 'assertion failed' end last_level = level end port.puts "</li></ul>" * last_level port.puts end return end def weave_html_list items, port port.puts "<ul>" items.each do |item| port.print "<li>" htmlify item.content, port if item.sublist then port.puts weave_html_list item.sublist.items, port end unless (item.warnings || []).empty? then port.puts weave_html_warning_list item.warnings, port, inline: true end port.puts "</li>" end port.puts "</ul>" return end def weave_html_chunk_header element, cls, port, tag: 'div' port.print "<#{tag} class='%s'>" % cls port.print "«" if element.root_type then port.print "<u>%s</u> " % element.root_type.to_xml end htmlify( parse_markup(element.name, Fabricator::MF::LINK), port) port.print "»:" port.print "</#{tag}>" # Note that we won't output a trailing linebreak here. return end def weave_html_chunk_body element, port port.print "<pre class='maui-chunk-body'>" element.content.each do |node| case node.type when :verbatim then port.print node.data.to_xml when :newline then port.puts when :use then port.print "<span class='maui-transclude'>" port.print "«" if node.clearindent then port.print ".clearindent " end htmlify( parse_markup(node.name, Fabricator::MF::LINK), port) if node.vertical_separation then port.print " " + node.vertical_separation.to_xml end if node.postprocess then port.print " " + node.postprocess.to_xml end port.print "»" port.print "</span>" else raise 'data structure error' end end port.puts "</pre>" return end def weave_html_warning_list list, port, inline: false if list and !list.empty? then port.print "<ul class='maui-warnings" port.print " maui-inline-warnings" if inline port.puts "'>" list.each do |warning| port.print "<li" port.print " id='W.#{warning.number}'" if inline port.print ">" port.print "!!! " if inline if !inline and warning.inline then port.print "<a href='#W.%i'>" % warning.number end port.print "<tt>%s</tt>" % format_location(warning.loc).to_xml port.print ": " + warning.message port.print "</a>" if !inline and warning.inline port.puts "</li>" end port.puts "</ul>" end return end def htmlify nodes, port nodes.each do |node| case node.type when :plain then port.print node.data.to_xml when :space then port.print((node.data || ' ').to_xml) when :nbsp then port.print ' ' when :monospace, :bold, :italic, :underscore then html_tag = Fabricator::MARKUP2HTML[node.type] port.print "<%s>" % html_tag htmlify node.content, port port.print "</%s>" % html_tag when :mention_chunk then port.print "<span class='maui-chunk-mention'>\u00AB" htmlify( parse_markup(node.name, Fabricator::MF::LINK), port) port.print "\u00BB</span>" when :link then port.print "<a href='#{node.target.to_xml}'>" htmlify node.content, port port.print "</a>" else raise 'invalid node type' end end return end
markup()
click to toggle source
# File lib/mau/fabricator.rb, line 1153 def markup return Fabricator::Markup_Constructor.new end
parse_markup(s, suppress_modes = 0)
click to toggle source
# File lib/mau/fabricator.rb, line 1006 def parse_markup s, suppress_modes = 0 ps = Fabricator::Pointered_String.new s stack = Fabricator::Markup_Parser_Stack.new suppress_modes while ps.pointer < s.length do if ps.at? "[[" and end_offset = s.index("]]", ps.pointer + 2) then while ps[end_offset + 2] == ?] do end_offset += 1 end monospaced_content = [] ps[ps.pointer + 2 ... end_offset].split(/(\s+)/). each_with_index do |part, i| monospaced_content.push OpenStruct.new( type: i.even? ? :plain : :space, data: part ) end stack.last.content.push OpenStruct.new( type: :monospace, content: monospaced_content) ps.pointer = end_offset + 2 elsif stack.last.mode & Fabricator::MF::BOLD != 0 and ps.biu_starter? ?* then stack.spawn '*', Fabricator::MF::BOLD, Fabricator::MF::END_BOLD ps.pointer += 1 elsif stack.last.mode & Fabricator::MF::ITALIC != 0 and ps.biu_starter? ?/ then stack.spawn '/', Fabricator::MF::ITALIC, Fabricator::MF::END_ITALIC ps.pointer += 1 elsif stack.last.mode & Fabricator::MF::UNDERSCORE \ != 0 and ps.biu_starter? ?_ then stack.spawn '_', Fabricator::MF::UNDERSCORE, Fabricator::MF::END_UNDERSCORE ps.pointer += 1 elsif stack.last.mode & Fabricator::MF::END_BOLD != 0 and ps.biu_terminator? ?* then stack.ennode :bold, Fabricator::MF::END_BOLD ps.pointer += 1 elsif stack.last.mode & Fabricator::MF::END_ITALIC \ != 0 and ps.biu_terminator? ?/ then stack.ennode :italic, Fabricator::MF::END_ITALIC ps.pointer += 1 elsif stack.last.mode & Fabricator::MF::END_UNDERSCORE \ != 0 and ps.biu_terminator? ?_ then stack.ennode :underscore, Fabricator::MF::END_UNDERSCORE ps.pointer += 1 elsif stack.last.mode & Fabricator::MF::LINK != 0 and ps.biu_starter? ?< then stack.spawn '<', Fabricator::MF::LINK, Fabricator::MF::END_LINK stack.last.start_offset = ps.pointer ps.pointer += 1 elsif stack.last.mode & Fabricator::MF::END_LINK != 0 and ps.at? '|' and end_offset = s.index(?>, ps.pointer + 1) then target = ps[ps.pointer + 1 ... end_offset] if link_like? target then stack.ennode(:link, Fabricator::MF::END_LINK).target = target ps.pointer = end_offset + 1 else # False alarm: this is not a link, after all. stack.cancel_link stack.last.content.push OpenStruct.new( type: :plain, data: '|', ) ps.pointer += 1 end elsif stack.last.mode & Fabricator::MF::END_LINK != 0 and ps.at? '>' then j = stack.rindex do |x| x.term_type == Fabricator::MF::END_LINK end target = ps[stack[j].start_offset + 1 ... ps.pointer] if link_like? target then stack[j .. -1] = [] stack.last.content.push OpenStruct.new( type: :link, implicit_face: true, target: target, content: [OpenStruct.new( type: :plain, data: target, )], ) else # False alarm: this is not a link, after all. stack.cancel_link stack.last.content.push OpenStruct.new( type: :plain, data: '>', ) end ps.pointer += 1 elsif ps.at? ' ' then ps.pointer += 1 while ps.at? ' ' do ps.pointer += 1 end stack.last.content.push OpenStruct.new(type: :space) elsif ps.at? "\u00A0" then stack.last.content.push OpenStruct.new(type: :nbsp) ps.pointer += 1 else j = ps.pointer + 1 while j < s.length and !" */<>[_|".include? ps[j] do j += 1 end stack.last.content.push OpenStruct.new( type: :plain, data: String.new(ps[ps.pointer ... j]), ) ps.pointer = j end end while stack.length > 1 do stack.unspawn end return stack.last.content end
plan_to_write_out(results)
click to toggle source
Take a [[results]] record from tangling and construct a matching [[proc]] to be stored in the [[writeout_plan]].
# File lib/mau/fabricator.rb, line 1159 def plan_to_write_out results return proc do |output_filename| File.write output_filename, results.content puts "Tangled #{results.filename}," if results.line_count != 1 then print " #{results.line_count} lines" else print " #{results.line_count} line" end puts " (#{results.nonblank_line_count} non-blank)," if results.longest_line_length != 1 then puts " longest #{results.longest_line_length} chars." else puts " longest #{results.longest_line_length} char." end if results.root_type == '.script' and !Fabricator::WINDOWS_HOSTED_P then stat = File.stat output_filename m = stat.mode uc = "" [(m |= 0o100), (uc << "u")] if m & 0o400 != 0 [(m |= 0o010), (uc << "g")] if m & 0o040 != 0 [(m |= 0o001), (uc << "o")] if m & 0o004 != 0 File.chmod m, output_filename puts "Set %s+x on %s, resulting in %03o" % [ uc, output_filename, m & 0o777, ] end end end
show_warnings(fabric)
click to toggle source
# File lib/mau/fabricator.rb, line 956 def show_warnings fabric fabric.warnings.each do |warning| $stderr.puts "%s: %s" % [format_location(warning.loc), warning.message] end return end
weave_ctxt(fabric, port, width: 80, pseudographics: Fabricator::UNICODE_PSEUDOGRAPHICS)
click to toggle source
# File lib/mau/fabricator.rb, line 1323 def weave_ctxt fabric, port, width: 80, pseudographics: Fabricator::UNICODE_PSEUDOGRAPHICS wr = Fabricator::Text_Wrapper.new port, width: width, pseudographics: pseudographics unless fabric.warnings.empty? then wr.styled :section_title do wr.add_plain 'Warnings' end wr.linebreak wr.linebreak weave_ctxt_warning_list fabric.warnings, wr wr.linebreak end toc_generated = false fabric.presentation.each do |element| case element.type when :title then if !toc_generated then weave_ctxt_toc fabric.toc, wr toc_generated = true end wr.styled :section_title do wr.add_plain "#{element.number}." wr.add_space wr.hang do wr.add_nodes element.content end end wr.linebreak wr.linebreak when :section then rubricated = element.elements[0].type == :rubric # If we're encountering the first rubric/title, output # the table of contents. if rubricated and !toc_generated then weave_ctxt_toc fabric.toc, wr toc_generated = true end start_index = 0 # index of the first non-special child if rubricated then start_index += 1 wr.styled :rubric do wr.add_plain "§%i." % element.section_number wr.add_space wr.add_nodes element.elements.first.content end else wr.styled :section_number do wr.add_plain "§%i." % element.section_number end end # If the rubric or the section sign is followed by a # paragraph, a chunk header, or a divert, we'll output # it in the same paragraph. starter = element.elements[start_index] if starter then case starter.type when :paragraph, :divert, :chunk then wr.add_space weave_ctxt_section_part starter, fabric, wr start_index += 1 else wr.linebreak end end # Finally, the blank line that separates the special # paragraph from the section's body, if any. wr.linebreak element.elements[start_index .. -1].each do |child| weave_ctxt_section_part child, fabric, wr wr.linebreak end unless (element.warnings || []).empty? then weave_ctxt_warning_list element.warnings, wr, inline: true, indent: false wr.linebreak end else raise 'data structure error' end end return end
weave_ctxt_block(element, wr)
click to toggle source
# File lib/mau/fabricator.rb, line 1517 def weave_ctxt_block element, wr element.lines.each do |line| wr.styled :block_frame do wr.add_pseudographics :block_margin end wr.styled :monospace do wr.add_plain line end wr.linebreak end return end
weave_ctxt_chunk_header(element, wr)
click to toggle source
# File lib/mau/fabricator.rb, line 1499 def weave_ctxt_chunk_header element, wr wr.styled :chunk_header do wr.add_pseudographics :before_chunk_name if element.root_type then wr.styled :root_type do wr.add_plain element.root_type end wr.add_space end wr.add_nodes( parse_markup(element.name, Fabricator::MF::LINK)) wr.add_pseudographics :after_chunk_name wr.add_plain ":" end wr.linebreak return end
weave_ctxt_list(items, wr)
click to toggle source
# File lib/mau/fabricator.rb, line 1613 def weave_ctxt_list items, wr items.each do |item| wr.add_pseudographics :bullet wr.add_plain " " wr.hang do wr.add_nodes item.content end wr.linebreak unless (item.warnings || []).empty? then wr.hang do weave_ctxt_warning_list item.warnings, wr, inline: true end end if item.sublist then wr.add_plain " " wr.hang do weave_ctxt_list item.sublist.items, wr end end end return end
weave_ctxt_section_part(element, fabric, wr)
click to toggle source
# File lib/mau/fabricator.rb, line 1437 def weave_ctxt_section_part element, fabric, wr case element.type when :paragraph then wr.add_nodes element.content wr.linebreak when :divert, :chunk, :diverted_chunk then if [:divert, :chunk].include? element.type then weave_ctxt_chunk_header element, wr weave_ctxt_warning_list element.warnings, wr, inline: true end if [:chunk, :diverted_chunk].include? element.type then wr.styled :chunk_frame do wr.add_pseudographics element.initial ? :initial_chunk_margin : :chunk_margin end wr.styled :monospace do element.content.each do |node| case node.type when :verbatim then wr.add_plain node.data when :newline then wr.linebreak wr.styled :chunk_frame do wr.add_pseudographics :chunk_margin end when :use then weave_ctxt_use node, wr else raise 'data structure error' end end end wr.linebreak if element.final then wr.styled :chunk_frame do wr.add_pseudographics :final_chunk_marker end wr.linebreak end weave_ctxt_warning_list element.warnings, wr, inline: true if element.final then wr.styled :chunk_xref do wr.add_nodes xref_chain(element, fabric) end wr.linebreak end end when :list then weave_ctxt_list element.items, wr when :block then weave_ctxt_block element, wr else raise 'data structure error' end return end
weave_ctxt_toc(toc, wr)
click to toggle source
# File lib/mau/fabricator.rb, line 1637 def weave_ctxt_toc toc, wr if toc.length >= 2 then wr.styled :section_title do wr.add_plain 'Contents' end wr.linebreak; wr.linebreak rubric_level = 0 toc.each do |entry| case entry.type when :title then rubric_level = entry.level - 1 + 1 wr.add_plain ' ' * (entry.level - 1) wr.add_plain entry.number + '.' wr.add_space wr.hang do wr.add_nodes entry.content end when :rubric then wr.add_plain ' ' * rubric_level wr.add_plain '§%i.' % entry.section_number wr.add_space wr.hang do wr.add_nodes entry.content end else raise 'assertion failed' end wr.linebreak end wr.linebreak end return end
weave_ctxt_use(node, wr)
click to toggle source
# File lib/mau/fabricator.rb, line 1530 def weave_ctxt_use node, wr wr.styled :use do wr.add_pseudographics :before_chunk_name if node.clearindent then wr.add_plain ".clearindent " end wr.add_nodes parse_markup(node.name, Fabricator::MF::LINK) if node.vertical_separation then wr.add_plain " " + node.vertical_separation end if node.postprocess then wr.add_plain " " + node.postprocess end wr.add_pseudographics :after_chunk_name end return end
weave_ctxt_warning_list(list, wr, inline: false, indent: true)
click to toggle source
# File lib/mau/fabricator.rb, line 1413 def weave_ctxt_warning_list list, wr, inline: false, indent: true list.to_a.each do |warning| wr.styled inline ? :inline_warning : :null do wr.add_plain (indent ? ' ' : '') + '!!! ' if inline wr.add_plain format_location(warning.loc) wr.add_plain ':' wr.add_space wr.hang do warning.message.split(/(\s+)/). each_with_index do |part, i| if i.even? then wr.add_plain part else wr.add_space part end end end end wr.linebreak end return end
weave_html(fabric, port, title: nil, link_css: [])
click to toggle source
# File lib/mau/fabricator.rb, line 1673 def weave_html fabric, port, title: nil, link_css: [] title ||= "(Untitled)" port.puts '<!doctype html>' port.puts '<html>' port.puts '<head>' port.puts "<meta http-equiv='Content-type' " + "content='text/html; charset=utf-8' />" port.puts "<title>#{title.to_xml}</title>" if link_css.empty? then port.puts "<style type='text/css'>" port.puts '/**** Fonts ****/ @import url("http://fonts.googleapis.com/css?family=Roboto"); @import url("http://fonts.googleapis.com/css?family=Cousine"); /**** Rules ****/ body, .maui-transclude { font-family: "Roboto", sans-serif; } pre, tt, code { font-family: "Cousine", monospace; } body { colour: black; background: white; } tt, code { color: forestgreen; } .maui-inline-warnings { color: red; } .maui-warnings tt { color: inherit; } .maui-rubric { color: crimson; } ul.maui-warnings { padding-left: 0; } ul.maui-warnings > li { list-style: none; } .maui-chunk-body { margin-left: 20px; border-left: 2px solid #cccccc; padding-left: 5px; } .maui-initial-chunk > .maui-chunk-body:before { content: ""; display: block; width: 22px; border-top: solid 2px #cccccc; margin-left: -27px; } .maui-final-chunk > .maui-chunk-body:after { content: ""; display: block; margin-left: -7px; width: 40px; border-bottom: solid 2px #cccccc; } .maui-chunk-body, .maui-chunk > .maui-warnings { margin-top: 0; margin-bottom: 0; } .maui-chunk { margin-top: 16px; margin-bottom: 16px; } .maui-chunk-xref { font-size: small; font-style: italic; margin-left: 22px; } /* Backwards compatibility with pre-HTML5 browsers */ section { display: block; }' port.puts "</style>" else link_css.each do |link| port.puts ("<link rel='stylesheet' type='text/css' " + "href='%s' />") % link.to_xml end end port.puts '</head>' port.puts '<body>' port.puts port.puts "<h1>#{title.to_xml}</h1>" unless fabric.warnings.empty? then port.puts "<h2>Warnings</h2>" port.puts weave_html_warning_list fabric.warnings, port port.puts end toc_generated = false fabric.presentation.each do |element| case element.type when :title then if !toc_generated then weave_html_toc fabric.toc, port toc_generated = true end port.print '<h%i' % (element.level + 1) port.print " id='%s'" % "T.#{element.number}" port.print '>' port.print "#{element.number}. " htmlify element.content, port port.puts '</h%i>' % (element.level + 1) when :section then rubricated = element.elements[0].type == :rubric # If we're encountering the first rubric/title, output # the table of contents. if rubricated and !toc_generated then weave_html_toc fabric.toc, port toc_generated = true end start_index = 0 port.puts "<section class='maui-section' id='%s'>" % "S.#{element.section_number}" port.puts port.print "<p>" port.print "<b class='%s'>" % (rubricated ? 'maui-rubric' : 'maui-section-number') port.print "\u00A7#{element.section_number}." if rubricated then port.print " " htmlify element.elements[start_index].content, port start_index += 1 end port.print "</b>" subelement = element.elements[start_index] warnings = nil case subelement && subelement.type when :paragraph then port.print " " htmlify subelement.content, port start_index += 1 when :divert then port.print " " weave_html_chunk_header subelement, 'maui-divert', port, tag: 'span' warnings = subelement.warnings start_index += 1 end port.puts "</p>" if warnings then weave_html_warning_list warnings, port, inline: true end port.puts element.elements[start_index .. -1].each do |child| weave_html_section_part child, fabric, port port.puts end unless (element.warnings || []).empty? then weave_html_warning_list element.warnings, port, inline: true port.puts end port.puts "</section>" else raise 'data structure error' end port.puts end port.puts '</html>' port.puts '</body>' port.puts '</html>' return end
weave_html_chunk_body(element, port)
click to toggle source
# File lib/mau/fabricator.rb, line 1980 def weave_html_chunk_body element, port port.print "<pre class='maui-chunk-body'>" element.content.each do |node| case node.type when :verbatim then port.print node.data.to_xml when :newline then port.puts when :use then port.print "<span class='maui-transclude'>" port.print "«" if node.clearindent then port.print ".clearindent " end htmlify( parse_markup(node.name, Fabricator::MF::LINK), port) if node.vertical_separation then port.print " " + node.vertical_separation.to_xml end if node.postprocess then port.print " " + node.postprocess.to_xml end port.print "»" port.print "</span>" else raise 'data structure error' end end port.puts "</pre>" return end
weave_html_chunk_header(element, cls, port, tag: 'div')
click to toggle source
# File lib/mau/fabricator.rb, line 1965 def weave_html_chunk_header element, cls, port, tag: 'div' port.print "<#{tag} class='%s'>" % cls port.print "«" if element.root_type then port.print "<u>%s</u> " % element.root_type.to_xml end htmlify( parse_markup(element.name, Fabricator::MF::LINK), port) port.print "»:" port.print "</#{tag}>" # Note that we won't output a trailing linebreak here. return end
weave_html_list(items, port)
click to toggle source
# File lib/mau/fabricator.rb, line 1945 def weave_html_list items, port port.puts "<ul>" items.each do |item| port.print "<li>" htmlify item.content, port if item.sublist then port.puts weave_html_list item.sublist.items, port end unless (item.warnings || []).empty? then port.puts weave_html_warning_list item.warnings, port, inline: true end port.puts "</li>" end port.puts "</ul>" return end
weave_html_section_part(element, fabric, port)
click to toggle source
# File lib/mau/fabricator.rb, line 1844 def weave_html_section_part element, fabric, port case element.type when :paragraph then port.print "<p>" htmlify element.content, port port.puts "</p>" when :list then weave_html_list element.items, port when :divert then weave_html_chunk_header element, 'maui-divert', port port.puts weave_html_warning_list element.warnings, port, inline: true when :chunk, :diverted_chunk then port.print "<div class='maui-chunk" port.print " maui-initial-chunk" if element.initial port.print " maui-final-chunk" if element.final port.print "'>" if element.type == :chunk then weave_html_chunk_header element, 'maui-chunk-header', port port.puts end weave_html_chunk_body element, port unless (element.warnings || []).empty? then weave_html_warning_list element.warnings, port, inline: true end if element.final then port.print "<div class='maui-chunk-xref'>" htmlify( xref_chain(element, fabric, dash: "\u2013"), port) port.puts "</div>" end port.puts "</div>" when :block then port.print "<pre class='maui-block'>" element.lines.each_with_index do |line, i| port.puts unless i.zero? port.print line.to_xml end port.puts "</pre>" else raise 'data structure error' end return end
weave_html_toc(toc, port)
click to toggle source
# File lib/mau/fabricator.rb, line 1898 def weave_html_toc toc, port if toc.length >= 2 then port.puts "<h2>Contents</h2>" port.puts last_level = 0 # What level should the rubrics in the current # (sub(sub))chapter appear at? rubric_level = 1 toc.each do |entry| if entry.type == :rubric then level = rubric_level else level = entry.level rubric_level = entry.level + 1 end if level > last_level then raise 'assertion failed' \ unless level == last_level + 1 port.print "\n<ul><li>" elsif level == last_level then port.print "</li>\n<li>" else port.print "</li></ul>" * (last_level - level) + "\n<li>" end case entry.type when :title then port.print "#{entry.number}. " port.print "<a href='#T.#{entry.number}'>" htmlify entry.content, port port.print "</a>" when :rubric then port.print "\u00A7#{entry.section_number}. " port.print "<a href='#S.#{entry.section_number}'>" htmlify entry.content, port port.print "</a>" else raise 'assertion failed' end last_level = level end port.puts "</li></ul>" * last_level port.puts end return end
weave_html_warning_list(list, port, inline: false)
click to toggle source
# File lib/mau/fabricator.rb, line 2012 def weave_html_warning_list list, port, inline: false if list and !list.empty? then port.print "<ul class='maui-warnings" port.print " maui-inline-warnings" if inline port.puts "'>" list.each do |warning| port.print "<li" port.print " id='W.#{warning.number}'" if inline port.print ">" port.print "!!! " if inline if !inline and warning.inline then port.print "<a href='#W.%i'>" % warning.number end port.print "<tt>%s</tt>" % format_location(warning.loc).to_xml port.print ": " + warning.message port.print "</a>" if !inline and warning.inline port.puts "</li>" end port.puts "</ul>" end return end
xref_chain(element, fabric, dash: "-")
click to toggle source
Given a chunk, prepare its transclusion summary as a list of markup nodes. Should only be used on chunks that are the last in a chunk chain (i.e., that have [[final]] set).
# File lib/mau/fabricator.rb, line 1551 def xref_chain element, fabric, dash: "-" xref = markup if element.initial then xref.words "This chunk is " else xref.words "These chunks are " end cbn_entry = fabric.chunks_by_name[element.name] transcluders = cbn_entry.transcluders if transcluders then xref.words "transcluded by " xref.push *commatise_oxfordly( transcluders.map{|ref| markup. node(:mention_chunk, name: ref.name). space. plain("(§%i)" % ref.section_number) }) else if cbn_entry.root_type then xref.words "solely a transclusion root" else xref.words "never transcluded" end end xref.words " and " tlocs = element.divert ? element.divert.chain_tangle_locs : element.tangle_locs if tlocs then xref. words("tangled to "). push(*commatise_oxfordly( tlocs.map{|range| markup. plain(format_location_range(range, dash: dash)) })). plain(".") else xref.words "never tangled." end return xref end