Public: Methods to perform substitutions on lines of AsciiDoc text. This module is intented to be mixed-in to Section and Block to provide operations for performing the necessary substitutions.
Internal: A String Array of passthough (unprocessed) text captured from this block
Public: Apply substitutions for header metadata and attribute assignments
text - String containing the text process
returns - A String with header substitutions performed
# File lib/asciidoctor/substituters.rb, line 118 def apply_header_subs(text) apply_subs text, SUBS[:header] end
Public: Apply normal substitutions.
lines - The lines of text to process. Can be a String or a String Array
returns - A String with normal substitutions performed
# File lib/asciidoctor/substituters.rb, line 100 def apply_normal_subs(lines) apply_subs lines.is_a?(Array) ? lines.join : lines end
Public: Apply the specified substitutions to the lines of text
source - The String or String Array of text to process subs - The substitutions to perform. Can be a Symbol or a Symbol Array (default: :normal) expand - A Boolean to control whether sub aliases are expanded (default: true)
returns Either a String or String Array, whichever matches the type of the first argument
# File lib/asciidoctor/substituters.rb, line 37 def apply_subs source, subs = :normal, expand = false if subs == :normal subs = SUBS[:normal] elsif subs.nil? return source elsif expand if subs.is_a? Symbol subs = COMPOSITE_SUBS[subs] || [subs] else effective_subs = [] subs.each do |key| if COMPOSITE_SUBS.has_key? key effective_subs.push(*COMPOSITE_SUBS[key]) else effective_subs << key end end subs = effective_subs end end return source if subs.empty? multiline = source.is_a?(Array) text = multiline ? source.join : source if (has_passthroughs = subs.include?(:macros)) text = extract_passthroughs(text) end subs.each do |type| case type when :specialcharacters text = sub_specialcharacters(text) when :quotes text = sub_quotes(text) when :attributes text = sub_attributes(text.lines.entries).join when :replacements text = sub_replacements(text) when :macros text = sub_macros(text) when :highlight text = highlight_source(text, subs.include?(:callouts)) when :callouts text = sub_callouts(text) unless subs.include?(:highlight) when :post_replacements text = sub_post_replacements(text) else warn "asciidoctor: WARNING: unknown substitution type #{type}" end end text = restore_passthroughs(text) if has_passthroughs multiline ? text.lines.entries : text end
Public: Apply substitutions for titles.
title - The String title to process
returns - A String with title substitutions performed
# File lib/asciidoctor/substituters.rb, line 109 def apply_title_subs(title) apply_subs title, SUBS[:title] end
# Public: Apply explicit substitutions, if specified, otherwise normal substitutions. # # lines - The lines of text to process. Can be a String or a String Array # # returns - A String with substitutions applied def apply_para_subs(lines) if (subs = attr('subs', nil, false)) apply_subs lines.join, resolve_subs(subs) else apply_subs lines.join end end # Public: Apply substitutions for titles # # lines - A String Array containing the lines of text process # # returns - A String with literal (verbatim) substitutions performed def apply_literal_subs(lines) if (subs = attr('subs', nil, false)) apply_subs lines.join, resolve_subs(subs) elsif @style == 'source' && @document.attributes['basebackend'] == 'html' && ((highlighter = @document.attributes['source-highlighter']) == 'coderay' || highlighter == 'pygments') && attr?('language') highlight_source lines.join, highlighter, callouts else apply_subs lines.join, SUBS[:verbatim] end end # Public: Apply substitutions for passthrough text # # lines - A String Array containing the lines of text process # # returns - A String with passthrough substitutions performed def apply_passthrough_subs(lines) if (subs = attr('subs', nil, false)) subs = resolve_subs(subs) else subs = SUBS[:pass] end apply_subs lines.join, subs end
# Internal: Extract the passthrough text from the document for reinsertion after processing. # # text - The String from which to extract passthrough fragements # # returns - The text with the passthrough region substituted with placeholders
# File lib/asciidoctor/substituters.rb, line 173 def extract_passthroughs(text) result = text.dup result.gsub!(REGEXP[:pass_macro]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if m[0].start_with? '\' next m[0][1..-1] end if m[1] == '$$' subs = [:specialcharacters] elsif m[3].nil? || m[3].empty? subs = [] else subs = resolve_pass_subs m[3] end # TODO move unescaping closing square bracket to an operation @passthroughs << {:text => m[2] || m[4].gsub('\]', ']'), :subs => subs} index = @passthroughs.size - 1 "\e#{index}\e" } unless !(result.include?('+++') || result.include?('$$') || result.include?('pass:')) result.gsub!(REGEXP[:pass_lit]) { # alias match for Ruby 1.8.7 compat m = $~ unescaped_attrs = nil # honor the escape if m[3].start_with? '\' next m[2].nil? ? "#{m[1]}#{m[3][1..-1]}" : "#{m[1]}[#{m[2]}]#{m[3][1..-1]}" elsif m[1] == '\' && !m[2].nil? unescaped_attrs = "[#{m[2]}]" end if unescaped_attrs.nil? && !m[2].nil? attributes = parse_attributes(m[2]) else attributes = {} end @passthroughs << {:text => m[4], :subs => [:specialcharacters], :attributes => attributes, :literal => true} index = @passthroughs.size - 1 "#{unescaped_attrs || m[1]}\e#{index}\e" } unless !result.include?('`') result end
Public: Highlight the source code if a source highlighter is defined on the document, otherwise return the text unprocessed
Callout marks are stripped from the source prior to passing it to the highlighter, then later restored in rendered form, so they are not incorrectly processed by the source highlighter.
source - the source code String to highlight #sub_callouts - a Boolean flag indicating whether callout marks should be substituted
returns the highlighted source code, if a source highlighter is defined on the document, otherwise the unprocessed text
# File lib/asciidoctor/substituters.rb, line 993 def highlight_source(source, sub_callouts, highlighter = nil) highlighter ||= @document.attributes['source-highlighter'] Helpers.require_library highlighter, (highlighter == 'pygments' ? 'pygments.rb' : highlighter) callout_marks = {} lineno = 0 callout_on_last = false if sub_callouts last = -1 # extract callout marks, indexed by line number source = source.split(EOL).map {|line| lineno = lineno + 1 line.gsub(REGEXP[:callout_scan]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if m[1] == '\' m[0].sub('\', '') else (callout_marks[lineno] ||= []) << m[3] last = lineno nil end } } * EOL callout_on_last = (last == lineno) end linenums_mode = nil case highlighter when 'coderay' result = ::CodeRay::Duo[attr('language', 'text').to_sym, :html, { :css => @document.attributes.fetch('coderay-css', 'class').to_sym, :line_numbers => (linenums_mode = (attr?('linenums') ? @document.attributes.fetch('coderay-linenums-mode', 'table').to_sym : nil)), :line_number_anchors => false}].highlight(source) when 'pygments' lexer = ::Pygments::Lexer[attr('language')] if lexer opts = { :cssclass => 'pyhl', :classprefix => 'tok-', :nobackground => true } opts[:noclasses] = true unless @document.attributes.fetch('pygments-css', 'class') == 'class' if attr? 'linenums' opts[:linenos] = (linenums_mode = @document.attributes.fetch('pygments-linenums-mode', 'table').to_sym).to_s end # FIXME stick these regexs into constants if linenums_mode == :table result = lexer.highlight(source, :options => opts). sub(/<div class="pyhl">(.*)<\/div>/, '\1'). gsub(/<pre[^>]*>(.*?)<\/pre>\s*/, '\1') else result = lexer.highlight(source, :options => opts). sub(/<div class="pyhl"><pre[^>]*>(.*?)<\/pre><\/div>/, '\1') end else result = source end end if !sub_callouts || callout_marks.empty? result else lineno = 0 reached_code = linenums_mode != :table result.split(EOL).map {|line| unless reached_code unless line.include?('<td class="code">') next line end reached_code = true end lineno = lineno + 1 if (conums = callout_marks.delete(lineno)) tail = nil if callout_on_last && callout_marks.empty? && (pos = line.index '</pre>') tail = line[pos..-1] line = line[0...pos] end if conums.size == 1 %Q(#{line}#{Inline.new(self, :callout, conums.first, :id => @document.callouts.read_next_id).render }#{tail}) else conums_markup = conums.map {|conum| Inline.new(self, :callout, conum, :id => @document.callouts.read_next_id).render } * ' ' %Q(#{line}#{conums_markup}#{tail}) end else line end } * EOL end end
Internal: Parse the attributes in the attribute line
attrline - A String of unprocessed attributes (key/value pairs) posattrs - The keys for positional attributes
returns nil if attrline is nil, an empty Hash if attrline is empty, otherwise a Hash of parsed attributes
# File lib/asciidoctor/substituters.rb, line 921 def parse_attributes(attrline, posattrs = ['role'], opts = {}) return nil if attrline.nil? return {} if attrline.empty? attrline = @document.sub_attributes(attrline) if opts[:sub_input] attrline = unescape_bracketed_text(attrline) if opts[:unescape_input] block = nil if opts.fetch(:sub_result, true) # substitutions are only performed on attribute values if block is not nil block = self end if opts.has_key?(:into) AttributeList.new(attrline, block).parse_into(opts[:into], posattrs) else AttributeList.new(attrline, block).parse(posattrs) end end
Internal: Parse the attributes that are defined on quoted text
str - A String of unprocessed attributes (space-separated roles or the id/role shorthand syntax)
returns nil if str is nil, an empty Hash if str is empty, otherwise a Hash of attributes (role and id only)
# File lib/asciidoctor/substituters.rb, line 877 def parse_quoted_text_attributes(str) return nil if str.nil? return {} if str.empty? str = sub_attributes(str) if str.include?('{') str = str.strip # for compliance, only consider first positional attribute str, _ = str.split(',', 2) if str.include?(',') if str.empty? {} elsif str.start_with?('.') || str.start_with?('#') segments = str.split('#', 2) if segments.length > 1 id, *more_roles = segments[1].split('.') else id = nil more_roles = [] end roles = segments[0].empty? ? [] : segments[0].split('.') if roles.length > 1 roles.shift end if more_roles.length > 0 roles.concat more_roles end attrs = {} attrs['id'] = id unless id.nil? attrs['role'] = roles.empty? ? nil : (roles * ' ') attrs else {'role' => str} end end
# File lib/asciidoctor/substituters.rb, line 973 def resolve_block_subs subs, subject resolve_subs subs, :block, subject end
# File lib/asciidoctor/substituters.rb, line 977 def resolve_pass_subs subs resolve_subs subs, :inline, 'passthrough macro' end
Internal: Resolve the list of comma-delimited subs against the possible options.
subs - A comma-delimited String of substitution aliases
returns An Array of Symbols representing the substitution operation
# File lib/asciidoctor/substituters.rb, line 951 def resolve_subs subs, type = :block, subject = nil return [] if subs.nil? || subs.empty? candidates = [] subs.split(',').each do |val| key = val.strip.to_sym # special case to disable callouts for inline subs if key == :verbatim && type == :inline candidates << :specialcharacters elsif COMPOSITE_SUBS.has_key? key candidates.push(*COMPOSITE_SUBS[key]) else candidates << key end end # weed out invalid options and remove duplicates (first wins) resolved = candidates & SUB_OPTIONS[type] if (invalid = candidates - resolved).size > 0 warn "asciidoctor: WARNING: invalid substitution type#{invalid.size > 1 ? 's' : ''}#{subject ? ' for ' : nil}#{subject}: #{invalid * ', '}" end resolved end
Internal: Restore the passthrough text by reinserting into the placeholder positions
text - The String text into which to restore the passthrough text
returns The String text with the passthrough text restored
# File lib/asciidoctor/substituters.rb, line 229 def restore_passthroughs(text) return text if @passthroughs.nil? || @passthroughs.empty? || !text.include?("\e") text.gsub(REGEXP[:pass_placeholder]) { pass = @passthroughs[$1.to_i]; text = apply_subs(pass[:text], pass.fetch(:subs, [])) pass[:literal] ? Inline.new(self, :quoted, text, :type => :monospaced, :attributes => pass.fetch(:attributes, {})).render : text } end
Public: Substitute attribute references
Attribute references are in the format {name}.
If an attribute referenced in the line is missing, the line is dropped.
text - The String text to process
returns The String text with the attribute references replaced with attribute values
# File lib/asciidoctor/substituters.rb, line 312 def sub_attributes(data, opts = {}) return data if data.nil? || data.empty? string_data = data.is_a? String # normalizes data type to an array (string becomes single-element array) lines = string_data ? [data] : data result = [] lines.each {|line| reject = false line = line.gsub(REGEXP[:attr_ref]) { # alias match for Ruby 1.8.7 compat m = $~ # escaped attribute, return unescaped if !m[1].nil? || !m[4].nil? "{#{m[2]}}" elsif (directive = m[3]) offset = directive.length + 1 expr = m[2][offset..-1] case directive when 'set' args = expr.split(':') _, value = Lexer::store_attribute(args[0], args[1] || '', @document) if value.nil? # since this is an assignment, only drop-line applies here (skip and drop imply the same result) if @document.attributes.fetch('attribute-undefined', COMPLIANCE[:attribute_undefined]) == 'drop-line' Debug.debug { "Undefining attribute: #{key}, line marked for removal" } break '' end end '' when 'counter', 'counter2' args = expr.split(':') val = @document.counter(args[0], args[1]) directive == 'counter2' ? '' : val else # if we get here, our attr_ref regex is too loose warn "asciidoctor: WARNING: illegal attribute directive: #{m[2]}" '' end elsif (key = m[2].downcase) && @document.attributes.has_key?(key) @document.attributes[key] elsif INTRINSICS.has_key? key INTRINSICS[key] else case (opts[:attribute_missing] || @document.attributes.fetch('attribute-missing', COMPLIANCE[:attribute_missing])) when 'skip' "{#{key}}" when 'drop-line' Debug.debug { "Missing attribute: #{key}, line marked for removal" } break '' else # 'drop' '' end end } if line.include? '{' result << line unless reject } string_data ? result.join : result end
Public: Substitute callout references
text - The String text to process
returns The String with the callout references rendered using the backend templates
# File lib/asciidoctor/substituters.rb, line 811 def sub_callouts(text) text.gsub(REGEXP[:callout_render]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if m[1] == '\' # we have to do a sub since we aren't sure it's the first char next m[0].sub('\', '') end Inline.new(self, :callout, m[3], :id => @document.callouts.read_next_id).render } end
Public: Substitute inline macros (e.g., links, images, etc)
Replace inline macros, which may span multiple lines, in the provided text
text - The String text to process
returns The String with the inline macros rendered using the backend templates
# File lib/asciidoctor/substituters.rb, line 382 def sub_macros(text) return text if text.nil? || text.empty? result = text.dup # some look ahead assertions to cut unnecessary regex calls found = {} found[:square_bracket] = result.include?('[') found[:round_bracket] = result.include?('(') found[:colon] = result.include?(':') found[:at] = result.include?('@') found[:macroish] = (found[:square_bracket] && found[:colon]) found[:macroish_short_form] = (found[:square_bracket] && found[:colon] && result.include?(':[')) found[:uri] = (found[:colon] && result.include?('://')) use_link_attrs = @document.attributes.has_key?('linkattrs') experimental = @document.attributes.has_key?('experimental') if experimental if found[:macroish_short_form] && (result.include?('kbd:') || result.include?('btn:')) result.gsub!(REGEXP[:kbd_btn_macro]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if (captured = m[0]).start_with? '\' next captured[1..-1] end if captured.start_with?('kbd') keys = unescape_bracketed_text m[1] if keys == '+' keys = ['+'] else # need to use closure to work around lack of negative lookbehind keys = keys.split(REGEXP[:kbd_delim]).inject([]) {|c, key| if key.end_with?('++') c << key[0..-3].strip c << '+' else c << key.strip end c } end Inline.new(self, :kbd, nil, :attributes => {'keys' => keys}).render elsif captured.start_with?('btn') label = unescape_bracketed_text m[1] Inline.new(self, :button, label).render end } end if found[:macroish] && result.include?('menu:') result.gsub!(REGEXP[:menu_macro]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if (captured = m[0]).start_with? '\' next captured[1..-1] end menu = m[1] items = m[2] if items.nil? submenus = [] menuitem = nil else if (delim = items.include?('>') ? '>' : (items.include?(',') ? ',' : nil)) submenus = items.split(delim).map(&:strip) menuitem = submenus.pop else submenus = [] menuitem = items.rstrip end end Inline.new(self, :menu, nil, :attributes => {'menu' => menu, 'submenus' => submenus, 'menuitem' => menuitem}).render } end if result.include?('"') && result.include?('>') result.gsub!(REGEXP[:menu_inline_macro]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if (captured = m[0]).start_with? '\' next captured[1..-1] end input = m[1] menu, *submenus = input.split('>').map(&:strip) menuitem = submenus.pop Inline.new(self, :menu, nil, :attributes => {'menu' => menu, 'submenus' => submenus, 'menuitem' => menuitem}).render } end end # FIXME this location is somewhat arbitrary, probably need to be able to control ordering # TODO this handling needs some cleanup if (extensions = @document.extensions) && extensions.inline_macros? && found[:macroish] extensions.load_inline_macro_processors(@document).each do |processor| result.gsub!(processor.regexp) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if m[0].start_with? '\' next m[0][1..-1] end target = m[1] if processor.options[:short_form] attributes = {} else posattrs = processor.options.fetch(:pos_attrs, []) attributes = parse_attributes(m[2], posattrs, :sub_input => true, :unescape_input => true) end processor.process self, target, attributes } end end if found[:macroish] && (result.include?('image:') || result.include?('icon:')) # image:filename.png[Alt Text] result.gsub!(REGEXP[:image_macro]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if m[0].start_with? '\' next m[0][1..-1] end raw_attrs = unescape_bracketed_text m[2] if m[0].start_with? 'icon:' type = 'icon' posattrs = ['size'] else type = 'image' posattrs = ['alt', 'width', 'height'] end target = sub_attributes(m[1]) unless type == 'icon' @document.register(:images, target) end attrs = parse_attributes(raw_attrs, posattrs) if !attrs['alt'] attrs['alt'] = File.basename(target, File.extname(target)) end Inline.new(self, :image, nil, :type => type, :target => target, :attributes => attrs).render } end if found[:macroish_short_form] || found[:round_bracket] # indexterm:[Tigers,Big cats] # (((Tigers,Big cats))) result.gsub!(REGEXP[:indexterm_macro]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if m[0].start_with? '\' next m[0][1..-1] end terms = unescape_bracketed_text(m[1] || m[2]).split(',').map(&:strip) @document.register(:indexterms, [*terms]) Inline.new(self, :indexterm, text, :attributes => {'terms' => terms}).render } # indexterm2:[Tigers] # ((Tigers)) result.gsub!(REGEXP[:indexterm2_macro]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if m[0].start_with? '\' next m[0][1..-1] end text = unescape_bracketed_text(m[1] || m[2]) @document.register(:indexterms, [text]) Inline.new(self, :indexterm, text, :type => :visible).render } end if found[:uri] # inline urls, target[text] (optionally prefixed with link: and optionally surrounded by <>) result.gsub!(REGEXP[:link_inline]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if m[2].start_with? '\' next "#{m[1]}#{m[2][1..-1]}#{m[3]}" # not a valid macro syntax w/o trailing square brackets # we probably shouldn't even get here...our regex is doing too much elsif m[1] == 'link:' && m[3].nil? next m[0] end prefix = (m[1] != 'link:' ? m[1] : '') target = m[2] suffix = '' # strip the <> around the link if prefix.start_with?('<') && target.end_with?('>') prefix = prefix[4..-1] target = target[0..-5] elsif prefix.start_with?('(') && target.end_with?(')') target = target[0..-2] suffix = ')' elsif target.end_with?('):') target = target[0..-3] suffix = '):' end @document.register(:links, target) attrs = nil #text = !m[3].nil? ? sub_attributes(m[3].gsub('\]', ']')) : '' if !m[3].to_s.empty? if use_link_attrs && (m[3].start_with?('"') || m[3].include?(',')) attrs = parse_attributes(sub_attributes(m[3].gsub('\]', ']')), []) text = attrs[1] else text = sub_attributes(m[3].gsub('\]', ']')) end if text.end_with? '^' text = text.chop attrs ||= {} attrs['window'] = '_blank' unless attrs.has_key?('window') end else text = '' end "#{prefix}#{Inline.new(self, :anchor, (!text.empty? ? text : target), :type => :link, :target => target, :attributes => attrs).render}#{suffix}" } end if found[:macroish] && (result.include?('link:') || result.include?('mailto:')) # inline link macros, link:target[text] result.gsub!(REGEXP[:link_macro]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if m[0].start_with? '\' next m[0][1..-1] end raw_target = m[1] mailto = m[0].start_with?('mailto:') target = mailto ? "mailto:#{raw_target}" : raw_target attrs = nil #text = sub_attributes(m[2].gsub('\]', ']')) if use_link_attrs && (m[2].start_with?('"') || m[2].include?(',')) attrs = parse_attributes(sub_attributes(m[2].gsub('\]', ']')), []) text = attrs[1] if mailto if attrs.has_key? 2 target = "#{target}?subject=#{Helpers.encode_uri(attrs[2])}" if attrs.has_key? 3 target = "#{target}&body=#{Helpers.encode_uri(attrs[3])}" end end end else text = sub_attributes(m[2].gsub('\]', ']')) end if text.end_with? '^' text = text.chop attrs ||= {} attrs['window'] = '_blank' unless attrs.has_key?('window') end # QUESTION should a mailto be registered as an e-mail address? @document.register(:links, target) Inline.new(self, :anchor, (!text.empty? ? text : raw_target), :type => :link, :target => target, :attributes => attrs).render } end if found[:at] result.gsub!(REGEXP[:email_inline]) { # alias match for Ruby 1.8.7 compat m = $~ address = m[0] case address[0..0] when '\' next address[1..-1] when '>', ':' next address end target = "mailto:#{address}" # QUESTION should this be registered as an e-mail address? @document.register(:links, target) Inline.new(self, :anchor, address, :type => :link, :target => target).render } end if found[:macroish_short_form] && result.include?('footnote') result.gsub!(REGEXP[:footnote_macro]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if m[0].start_with? '\' next m[0][1..-1] end if m[1] == 'footnote' # hmmmm text = restore_passthroughs(m[2]) id = nil index = @document.counter('footnote-number') @document.register(:footnotes, Document::Footnote.new(index, id, text)) type = nil target = nil else id, text = m[2].split(',', 2).map(&:strip) if !text.nil? # hmmmm text = restore_passthroughs(text) index = @document.counter('footnote-number') @document.register(:footnotes, Document::Footnote.new(index, id, text)) type = :ref target = nil else footnote = @document.references[:footnotes].find {|fn| fn.id == id } target = id id = nil index = footnote.index text = footnote.text type = :xref end end Inline.new(self, :footnote, text, :attributes => {'index' => index}, :id => id, :target => target, :type => type).render } end if found[:macroish] || result.include?('<<') result.gsub!(REGEXP[:xref_macro]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if m[0].start_with? '\' next m[0][1..-1] end if !m[1].nil? id, reftext = m[1].split(',', 2).map(&:strip) id.sub!(REGEXP[:dbl_quoted], '\2') reftext.sub!(REGEXP[:m_dbl_quoted], '\2') unless reftext.nil? else id = m[2] reftext = !m[3].empty? ? m[3] : nil end if id.include? '#' path, fragment = id.split('#') else path = nil fragment = id end # handles form: id if path.nil? refid = fragment target = "##{fragment}" # handles forms: doc#, doc.adoc#, doc#id and doc.adoc#id else path = Helpers.rootname(path) # the referenced path is this document, or its contents has been included in this document if @document.attr?('docname', path) || @document.references[:includes].include?(path) refid = fragment path = nil target = "##{fragment}" else refid = fragment.nil? ? path : "#{path}##{fragment}" path = "#{path}#{@document.attr 'outfilesuffix', '.html'}" target = fragment.nil? ? path : "#{path}##{fragment}" end end Inline.new(self, :anchor, reftext, :type => :xref, :target => target, :attributes => {'path' => path, 'fragment' => fragment, 'refid' => refid}).render } end if found[:square_bracket] && result.include?('[[[') result.gsub!(REGEXP[:biblio_macro]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if m[0].start_with? '\' next m[0][1..-1] end id = reftext = m[1] Inline.new(self, :anchor, reftext, :type => :bibref, :target => id).render } end if found[:square_bracket] && result.include?('[[') result.gsub!(REGEXP[:anchor_macro]) { # alias match for Ruby 1.8.7 compat m = $~ # honor the escape if m[0].start_with? '\' next m[0][1..-1] end id, reftext = m[1].split(',').map(&:strip) id.sub!(REGEXP[:dbl_quoted], '\2') if reftext.nil? reftext = "[#{id}]" else reftext.sub!(REGEXP[:m_dbl_quoted], '\2') end # NOTE the reftext should also match what's in our references dic if !@document.references[:ids].has_key? id Debug.debug { "Missing reference for anchor #{id}" } end Inline.new(self, :anchor, reftext, :type => :ref, :target => id).render } end result end
Public: Substitute post replacements
text - The String text to process
returns The String with the post replacements rendered using the backend templates
# File lib/asciidoctor/substituters.rb, line 829 def sub_post_replacements(text) if @document.attributes['hardbreaks'] lines = text.lines.entries return text if lines.size == 1 last = lines.pop lines.map {|line| Inline.new(self, :break, line.rstrip.chomp(LINE_BREAK), :type => :line).render }.push(last) * EOL else text.gsub(REGEXP[:line_break]) { Inline.new(self, :break, $1, :type => :line).render } end end
Public: Substitute quoted text (includes emphasis, strong, monospaced, etc)
text - The String text to process
returns The String text with quoted text rendered using the backend templates
# File lib/asciidoctor/substituters.rb, line 259 def sub_quotes(text) result = text.dup QUOTE_SUBS.each {|type, scope, pattern| result.gsub!(pattern) { transform_quoted_text($~, type, scope) } } result end
Public: Substitute replacement characters (e.g., copyright, trademark, etc)
text - The String text to process
returns The String text with the replacement characters substituted
# File lib/asciidoctor/substituters.rb, line 274 def sub_replacements(text) result = text.dup REPLACEMENTS.each {|pattern, replacement, restore| result.gsub!(pattern) { matched = $& head = $1 tail = $2 if matched.include?('\') matched.tr('\', '') else case restore when :none replacement when :leading "#{head}#{replacement}" when :bounding "#{head}#{replacement}#{tail}" end end } } result end
Public: Substitute special characters (i.e., encode XML)
Special characters are defined in the Asciidoctor::SPECIAL_CHARS Array constant
text - The String text to process
returns The String text with special characters replaced
# File lib/asciidoctor/substituters.rb, line 246 def sub_specialcharacters(text) # this syntax only available in Ruby 1.9 #text.gsub(SPECIAL_CHARS_PATTERN, SPECIAL_CHARS) text.gsub(SPECIAL_CHARS_PATTERN) { SPECIAL_CHARS[$&] } end
Internal: Transform (render) a quoted text region
match - The MatchData for the quoted text region type - The quoting type (single, double, strong, emphasis, monospaced, etc) scope - The scope of the quoting (constrained or unconstrained)
returns The rendered text for the quoted text region
# File lib/asciidoctor/substituters.rb, line 847 def transform_quoted_text(match, type, scope) unescaped_attrs = nil if match[0].start_with? '\' if scope == :constrained && !match[2].nil? unescaped_attrs = "[#{match[2]}]" else return match[0][1..-1] end end if scope == :constrained if unescaped_attrs.nil? attributes = parse_quoted_text_attributes(match[2]) id = attributes.nil? ? nil : attributes.delete('id') "#{match[1]}#{Inline.new(self, :quoted, match[3], :type => type, :id => id, :attributes => attributes).render}" else "#{unescaped_attrs}#{Inline.new(self, :quoted, match[3], :type => type, :attributes => {}).render}" end else attributes = parse_quoted_text_attributes(match[1]) id = attributes.nil? ? nil : attributes.delete('id') Inline.new(self, :quoted, match[2], :type => type, :id => id, :attributes => attributes).render end end
Internal: Strip bounding whitespace, fold endlines and unescaped closing square brackets from text extracted from brackets
# File lib/asciidoctor/substituters.rb, line 941 def unescape_bracketed_text(text) return '' if text.empty? text.strip.tr(EOL, ' ').gsub('\]', ']') end