Class: Kramdown::AsciiDoc::Converter

Inherits:
Converter::Base
  • Object
show all
Defined in:
lib/kramdown-asciidoc/converter.rb

Constant Summary collapse

RESOLVE_ENTITY_TABLE =
{ 38 => '&', 60 => '<', 62 => '>', 124 => '|' }
ADMON_LABELS =
%w(Note Tip Caution Warning Important Attention Hint).map {|l| [l, l] }.to_h
ADMON_MARKERS =
ADMON_LABELS.map {|l, _| %(#{l}: ) }
ADMON_MARKERS_ASCIIDOC =
%w(NOTE TIP CAUTION WARNING IMPORTANT).map {|l| %(#{l}: ) }
ADMON_FORMATTED_MARKERS =
ADMON_LABELS.map {|l, _| [%(#{l}:), l] }.to_h
ADMON_TYPE_MAP =
ADMON_LABELS.map {|l, _| [l, l.upcase] }.to_h.merge 'Attention' => 'IMPORTANT', 'Hint' => 'TIP'
BLOCK_TYPES =
[:p, :blockquote, :codeblock, :table]
DLIST_MARKERS =
%w(:: ;; ::: ::::)
NON_DEFAULT_TABLE_ALIGNMENTS =
[:center, :right]
STOP_PUNCTUATION =
%w(. ? ! ;)
SMART_QUOTE_ENTITY_TO_MARKUP =

FIXME here we reverse the smart quotes; add option to allow them (needs to be handled carefully)

{ ldquo: ?", rdquo: ?", lsquo: ?', rsquo: ?' }
TYPOGRAPHIC_SYMBOL_TO_MARKUP =
{
  '' => '"`',
  '' => '`"',
  '' => '\'`',
  '' => '`\'',
  # FIXME in the future, mdash will be three dashes in AsciiDoc; for now, down-convert
  '' => '--',
  '' => '--',
  '' => '...',
}
TYPOGRAPHIC_ENTITY_TO_MARKUP =
{
  # FIXME in the future, mdash will be three dashes in AsciiDoc; for now, down-convert
  mdash: '--',
  ndash: '--',
  hellip: '...',
  laquo: '<<',
  raquo: '>>',
  laquo_scape: '<< ',
  raquo_space: ' >>',
}
TABLE_ALIGNMENTS =
{ left: '<', center: '^', right: '>' }
UNIQUE_ID_START_INDEX =

NOTE assumes default Asciidoctor::Compliance.unique_id_start_index value

1
CommentPrefixRx =
/^ *! ?/m
CssPropDelimRx =
/\s*;\s*/
InadvertentReplacementsRx =
/[-=]>|<[-=]|\.\.\.|\{\p{Word}[\p{Word}-]*\}/
InvalidIdCharsRx =
/&(?:[a-z][a-z]+\d{0,2}|#\d\d\d{0,4}|#x[\da-f][\da-f][\da-f]{0,3});|[^ \p{Word}\-.]+?/
ListMarkerRx =
/^[ \t]*(?:(?:-|\*\*{0,4}|\.\.{0,4}|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]|.*?(?::::{0,2}|;;)(?:$|[ \t]))/
/^([\p{Word}&].*?)\s>\s([\p{Word}&].*(?:\s>\s|$))+/
ReplaceableTextRx =
%r([-=]>|<[-=]| -- |\p{Word}--\p{Word}|\*\*|\.\.\.|&\S+;|\{\p{Word}[\p{Word}-]*\}|(?:https?|ftp)://\p{Word}|\((?:C|R|TM)\))
SmartApostropheRx =
/\b’\b/
StopPunctRx =
/(?<=\S[.;]|.[?!])\p{Blank}+/
TrailingSpaceRx =
/ +$/
TypographicSymbolRx =
/[“”‘’—–…]/
UriSchemeRx =
%r((?:https?|ftp)://\p{Word})
WordishRx =
/[\p{Word};:<>&]/
WordRx =
/\p{Word}/
XmlCommentRx =
/\A<!--(.*)-->\Z/m
VoidElement =
Element.new nil
LF =
?\n
NBSP =
?\u00a0

Instance Method Summary collapse

Constructor Details

#initialize(root, opts) ⇒ Converter

Returns a new instance of Converter.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/kramdown-asciidoc/converter.rb', line 65

def initialize root, opts
  super
  @attributes = opts[:attributes] || {}
  @auto_ids = opts[:auto_ids]
  @lazy_ids = opts[:lazy_ids]
  if @auto_ids || @lazy_ids
    if @auto_ids
      @id_pre = opts[:auto_id_prefix]
      sep = opts[:auto_id_separator] || '-'
      if @lazy_ids
        # NOTE only need to set idprefix when lazy_ids is set since otherwise all IDs are explicit
        @attributes['idprefix'] = @id_pre unless @id_pre == '_'
        # NOTE only need to set idseparator when lazy_ids is set since otherwise all IDs are explicit
        @attributes['idseparator'] = sep unless sep == '_'
      end
    else
      @id_pre = @attributes['idprefix'] || '_'
      sep = @attributes['idseparator']
    end
    if sep
      sep_replace = (sep = sep.chr) == '-' || sep == '.' ? ' .-' : %( #{sep}.-) unless sep.empty?
    else
      sep, sep_replace = '_', ' _.-'
    end
    @id_sep = sep
    @id_sep_replace = sep_replace
  end
  @ids_seen = {}
  @footnote_ids = ::Set.new
  @auto_links = opts.fetch :auto_links, true
  @diagram_languages = opts[:diagram_languages] || %w(plantuml mermaid)
  @heading_offset = opts[:heading_offset] || 0
  @imagesdir = opts[:imagesdir] || @attributes['imagesdir']
  @wrap = opts[:wrap] || :preserve
  @current_heading_level = nil
end

Instance Method Details

#_convert_list(el, opts) ⇒ Object Also known as: convert_ul, convert_ol, convert_dl



317
318
319
320
321
322
323
# File 'lib/kramdown-asciidoc/converter.rb', line 317

def _convert_list el, opts
  kin = el.type == :dl ? :dlist : :list
  (writer = opts[:writer]).start_list (parent = opts[:parent]).type == :dd || parent.options[:compound], kin
  traverse el, opts
  writer.end_list kin
  writer.add_blank_line if writer.in_list? && opts[:next]
end

#convert(el, opts = {}) ⇒ Object



102
103
104
# File 'lib/kramdown-asciidoc/converter.rb', line 102

def convert el, opts = {}
  send %(convert_#{el.type}), el, opts if el
end

#convert_a(el, opts) ⇒ Object



432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/kramdown-asciidoc/converter.rb', line 432

def convert_a el, opts
  if (url = el.attr['href']).start_with? '#'
    opts[:writer].append %(<<#{url.slice 1, url.length},#{compose_text el, strip: true}>>)
  elsif url.start_with? 'https://', 'http://'
    if (children = el.children).size == 1 && (child_i = children[0]).type == :img
      convert_img child_i, parent: opts[:parent], index: 0, url: url, writer: opts[:writer]
    else
      bare = ((text = compose_text el, strip: true).chomp '/') == (url.chomp '/')
      url = url.gsub '__', '%5F%5F' if url.include? '__'
      opts[:writer].append bare ? url : %(#{url}[#{text.gsub ']', '\]'}])
    end
  elsif url.end_with? '.md'
    text = (compose_text el, strip: true).gsub ']', '\]'
    text = %(#{text.slice 0, text.length - 3}.adoc) if text.end_with? '.md'
    opts[:writer].append %(xref:#{url.slice 0, url.length - 3}.adoc[#{text}])
  else
    opts[:writer].append %(link:#{url}[#{(compose_text el, strip: true).gsub ']', '\]'}])
  end
end

#convert_abbreviation(el, opts) ⇒ Object



509
510
511
# File 'lib/kramdown-asciidoc/converter.rb', line 509

def convert_abbreviation el, opts
  opts[:writer].append el.value
end

#convert_blank(el, opts) ⇒ Object



173
# File 'lib/kramdown-asciidoc/converter.rb', line 173

def convert_blank el, opts; end

#convert_blockquote(el, opts) ⇒ Object

Q: should we delete blank line between blocks in a nested conversation? TODO use shorthand for blockquote when contents is paragraph only; or always?



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/kramdown-asciidoc/converter.rb', line 204

def convert_blockquote el, opts
  (writer = opts[:writer]).start_block
  traverse el, (opts.merge writer: (block_writer = Writer.new), blockquote_depth: (depth = opts[:blockquote_depth] || 0) + 1)
  contents = block_writer.body
  if contents[0].start_with?(*ADMON_MARKERS_ASCIIDOC)
    if contents.include? ''
      style, _, contents[0] = contents[0].partition ': '
      writer.add_line %([#{style}])
      writer.start_delimited_block '='
      writer.add_lines contents
      writer.end_delimited_block
    else
      writer.add_lines contents
    end
  else
    if contents.size > 1 && ::String === (last_line = contents[-1]) && (last_line.start_with? '-- ')
      attribution = (attribution_line = contents.pop).slice 3, attribution_line.length
      writer.add_line %([,#{attribution}])
      # NOTE there will be at least one non-blank line, but coerce .to_s just to be safe
      contents.pop while contents[-1].to_s.empty?
    end
    # Q: should writer handle delimited block nesting?
    delimiter = depth > 0 ? ('____' + '__' * depth) : '_'
    writer.start_delimited_block delimiter
    writer.add_lines contents
    writer.end_delimited_block
  end
end

#convert_br(el, opts) ⇒ Object

NOTE this logic assumes the :hard_wrap option is disabled in the parser



542
543
544
545
546
547
548
549
550
# File 'lib/kramdown-asciidoc/converter.rb', line 542

def convert_br el, opts
  writer = opts[:writer]
  if writer.empty?
    writer.append '{blank} +'
  else
    writer.append %(#{(writer.current_line.end_with? ' ') ? '' : ' '}+)
  end
  writer.add_blank_line if el.options[:html_tag] && ((next_el = to_element opts[:next]).type != :text || !(next_el.value.start_with? LF))
end

#convert_codeblock(el, opts) ⇒ Object

TODO match logic from ditarx



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/kramdown-asciidoc/converter.rb', line 234

def convert_codeblock el, opts
  writer = opts[:writer]
  # NOTE hack to down-convert level-5 heading to block title
  if (current_line = writer.current_line) &&
      ((current_line.start_with? '.') ? (current_line == '....' || (current_line.start_with? '. ')) : true)
    writer.start_block
  end
  lines = el.value.rstrip.split LF
  first_line = lines[0]
  if (lang = el.attr['class'])
    # NOTE Kramdown always prefixes class with language-
    # TODO remap lang if requested
    lang = lang.slice 9, lang.length
    if @diagram_languages.include? lang
      diagram = true
      writer.add_line %([#{lang}])
    else
      writer.add_line %([,#{lang}])
    end
  elsif (prompt = first_line && (first_line.start_with? '$ '))
    writer.add_line %([,#{lang = 'console'}]) if lines.include? ''
  end
  if lang || (el.options[:fenced] && !prompt)
    delimiter = diagram ? '....' : '----'
    writer.add_line delimiter
    writer.add_lines lines
    writer.add_line delimiter
  elsif !prompt && ((lines.include? '') || (first_line && (ListMarkerRx.match? first_line)))
    writer.add_line '....'
    writer.add_lines lines
    writer.add_line '....'
  else
    # NOTE clear the list continuation as it isn't required
    writer.clear_line if writer.current_line == '+'
    writer.add_line(lines.map {|l| %( #{l}) })
  end
end

#convert_codespan(el, opts) ⇒ Object



452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# File 'lib/kramdown-asciidoc/converter.rb', line 452

def convert_codespan el, opts
  attrlist, mark = '', '`'
  if unconstrained? (prev_el = opts[:prev]), (next_el = opts[:next])
    mark = '``'
  elsif next_el
    case next_el.type
    when :smart_quote
      if prev_el && prev_el.type == :smart_quote
        attrlist, mark = '[.code]', '``'
      else
        mark = '``'
      end
    when :text
      mark = '``' if (next_el.value.chr == ?') || (prev_el && prev_el.type == :smart_quote)
    end
  end
  text = el.value
  pass = (replaceable? text) ? :shorthand : nil
  pass = :macro if text.include? '++'
  case pass
  when :shorthand
    opts[:writer].append %(#{mark}+#{text}+#{mark})
  when :macro
    opts[:writer].append %(#{mark}pass:c[#{text}]#{mark})
  else
    opts[:writer].append %(#{attrlist}#{mark}#{text}#{mark})
  end
end

#convert_dd(el, opts) ⇒ Object



361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/kramdown-asciidoc/converter.rb', line 361

def convert_dd el, opts
  if el.options[:first_as_para] == false
    remaining = el.children
  else
    remaining = (children = el.children).drop 1
    primary_lines = compose_text [children[0]], parent: el, strip: true, split: true, wrap: @wrap
    if primary_lines.size == 1
      opts[:writer].append %( #{primary_lines[0]})
    else
      el.options[:compound] = true
      opts[:writer].add_lines primary_lines
    end
  end
  unless remaining.empty?
    if remaining.find {|n| (type = n.type) == :blank ? nil : ((BLOCK_TYPES.include? type) ? true : break) }
      el.options[:compound] = true
    end
    traverse remaining, (opts.merge parent: el)
  end
end

#convert_dt(el, opts) ⇒ Object



351
352
353
354
355
356
357
358
359
# File 'lib/kramdown-asciidoc/converter.rb', line 351

def convert_dt el, opts
  writer = opts[:writer]
  # NOTE kramdown removes newlines from term
  term = compose_text el, strip: true
  marker = DLIST_MARKERS[(writer.list_level :dlist) - 1]
  #writer.add_blank_line if (prev = opts[:prev]) && prev.options[:compound]
  writer.add_blank_line if opts[:prev]
  writer.add_line %(#{term}#{marker})
end

#convert_em(el, opts) ⇒ Object



481
482
483
484
485
# File 'lib/kramdown-asciidoc/converter.rb', line 481

def convert_em el, opts
  composed_text = compose_text el
  mark = (unconstrained? opts[:prev], opts[:next]) ? '__' : '_'
  opts[:writer].append %(#{mark}#{composed_text}#{mark})
end

#convert_entity(el, opts) ⇒ Object



552
553
554
# File 'lib/kramdown-asciidoc/converter.rb', line 552

def convert_entity el, opts
  opts[:writer].append RESOLVE_ENTITY_TABLE[el.value.code_point] || el.options[:original]
end

#convert_footnote(el, opts) ⇒ Object



556
557
558
559
560
# File 'lib/kramdown-asciidoc/converter.rb', line 556

def convert_footnote el, opts
  id = el.options[:name]
  composed_text = (@footnote_ids.add? id) ? ((compose_text el.value).gsub ']', '\]') : ''
  opts[:writer].append %(footnote:#{id}[#{composed_text}])
end

#convert_heading(el, opts) ⇒ Object Also known as: convert_header



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/kramdown-asciidoc/converter.rb', line 120

def convert_heading el, opts
  (writer = opts[:writer]).start_block
  level = el.options[:level] + @heading_offset
  attrs = []
  style = []
  # Q: should writer track last heading level?
  if (discrete = @current_heading_level && level > @current_heading_level + 1)
    # TODO make block title promotion an option (allow certain levels and titles)
    if level == 5 && (next_2_siblings = (siblings = opts[:parent].children).slice (siblings.index el) + 1, 2) &&
        next_2_siblings.any? {|sibling| sibling.type == :codeblock }
      writer.add_line %(.#{compose_text el, strip: true})
      return
    end
    style << 'discrete'
  end
  if (child_i = to_element el.children[0]).type == :html_element && child_i.value == 'a' && (id = child_i.attr['id'])
    el = clone el, children: child_i.children + (el.children.drop 1)
    unless @lazy_ids && id == (generate_unique_id (extract_raw_text el), false)
      (id.include? '.') ? (attrs << %(id=#{id})) : (style << %(##{id}))
    end
    record_id id
  elsif (id = el.attr['id'])
    # NOTE no need to check for '.' in this case since it's not recognized as a valid ID character by kramdown
    style << %(##{id}) unless @lazy_ids && id == (generate_unique_id (extract_raw_text el), false)
    record_id id
  elsif @auto_ids
    unless @lazy_ids
      ((id = generate_unique_id (extract_raw_text el)).include? '.') ? (attrs << %(id=#{id})) : (style << %(##{id}))
    end
  end
  if (role = el.attr['class'])
    style << %(.#{role.tr ' ', '.'})
  end
  attrs.unshift style.join unless style.empty?
  attrlist = %([#{attrs.join ','}]) unless attrs.empty?
  # NOTE kramdown has already removed newlines
  title = compose_text el, strip: true
  if level == 1 && writer.empty? && @current_heading_level != 1
    writer.add_prologue_line attrlist if attrlist
    writer.doctitle = title
    nil
  else
    @attributes['doctype'] = 'book' if level == 1
    writer.add_line attrlist if attrlist
    writer.add_line %(#{'=' * level} #{title})
  end
  @current_heading_level = level unless discrete
  nil
end

#convert_hr(_el, opts) ⇒ Object



427
428
429
430
# File 'lib/kramdown-asciidoc/converter.rb', line 427

def convert_hr _el, opts
  (writer = opts[:writer]).start_block
  writer.add_line '\'\'\''
end

#convert_html_element(el, opts) ⇒ Object



571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
# File 'lib/kramdown-asciidoc/converter.rb', line 571

def convert_html_element el, opts
  if (tag = el.value) == 'script'
    opts[:writer].start_block
    opts[:writer].add_lines ['++++', '<script>', el.children[0].value.strip, '</script>', '++++']
    return
  elsif tag == 'div' && (child_i = el.children[0]) && child_i.options[:transparent] && (child_i_i = child_i.children[0])
    if child_i_i.value == 'span' && ((role = el.attr['class'].to_s).start_with? 'note') && child_i_i.attr['class'] == 'notetitle'
      marker = ADMON_FORMATTED_MARKERS[(to_element child_i_i.children[0]).value] || 'Note'
      lines = compose_text (child_i.children.drop 1), parent: child_i, strip: true, split: true, wrap: @wrap
      lines.unshift %(#{ADMON_TYPE_MAP[marker]}: #{lines.shift})
      opts[:writer].start_block
      opts[:writer].add_lines lines
      return
    else
      return convert_p child_i, (opts.merge parent: el, index: 0)
    end
  end

  contents = compose_text el, (opts.merge strip: el.options[:category] == :block)
  case tag
  when 'del'
    opts[:writer].append %([.line-through]##{contents}#)
  when 'mark'
    opts[:writer].append %(##{contents}#)
  when 'span'
    if (role = el.attr['class'])
      opts[:writer].append %([.#{role.tr ' ', '.'}]##{contents}#)
    else
      opts[:writer].append contents
    end
  when 'sup'
    opts[:writer].append %(^#{contents}^)
  when 'sub'
    opts[:writer].append %(~#{contents}~)
  else
    if tag == 'input' && el.attr['type'] == 'checkbox' && el.attr['class'] == 'task-list-item-checkbox'
      opts[:writer].append %([#{el.attr['checked'] ? 'x' : ' '}] )
    else
      attrs = (attrs = el.attr).empty? ? '' : attrs.map {|k, v| %( #{k}="#{v}") }.join
      opts[:writer].append %(+++<#{tag}#{attrs}>+++#{contents}+++</#{tag}>+++)
    end
  end
end

#convert_img(el, opts) ⇒ Object



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/kramdown-asciidoc/converter.rb', line 272

def convert_img el, opts
  if (parent = opts[:parent]).type == :p && parent.children.size == 1
    attrs = []
    style = []
    if (id = el.attr['id'])
      (id.include? '.') ? (attrs << %(id=#{id})) : (style << %(##{id}))
    end
    if (role = el.attr['class'])
      style << %(.#{role.tr ' ', '.'})
    end
    attrs.unshift style.join unless style.empty?
    attrlist = %([#{attrs.join ','}]) unless attrs.empty?
    block = true
  end
  macro_attrs = [nil]
  if (alt_text = el.attr['alt']) && !alt_text.empty?
    macro_attrs[0] = alt_text
  end
  if (width = el.attr['width'])
    macro_attrs << width
  elsif (css = el.attr['style']) && (width_css = (css.split CssPropDelimRx).find {|p| p.start_with? 'width:' })
    width = (width_css.slice (width_css.index ':') + 1, width_css.length).strip
    width = width.to_f.round unless width.end_with? '%'
    macro_attrs << width
  end
  if macro_attrs.size == 1 && (alt_text = macro_attrs.pop)
    macro_attrs << alt_text
  end
  if (url = opts[:url])
    macro_attrs << %(link=#{url})
  end
  src = el.attr['src']
  if (imagesdir = @imagesdir) && (src.start_with? %(#{imagesdir}/))
    src = src.slice imagesdir.length + 1, src.length
  end
  writer = opts[:writer]
  if block
    writer.start_block
    writer.add_line attrlist if attrlist
    writer.add_line %(image::#{src}[#{macro_attrs.join ','}])
  else
    writer.append %(image:#{src}[#{macro_attrs.join ','}])
  end
end

#convert_li(el, opts) ⇒ Object



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/kramdown-asciidoc/converter.rb', line 329

def convert_li el, opts
  writer = opts[:writer]
  writer.add_blank_line if (prev = opts[:prev]) && prev.options[:compound]
  marker = opts[:parent].type == :ol ? '.' : '*'
  indent = (level = writer.list_level) - 1
  if !(children = el.children).empty? && children[0].type == :p
    primary, remaining = [(children = children.dup).shift, children]
    primary_lines = compose_text [primary], parent: el, strip: true, split: true, wrap: @wrap
  else
    remaining = children
    primary_lines = ['{blank}']
  end
  primary_lines.unshift %(#{indent > 0 ? ' ' * indent : ''}#{marker * level} #{primary_lines.shift})
  writer.add_lines primary_lines
  unless remaining.empty?
    if remaining.find {|n| (type = n.type) == :blank ? nil : ((BLOCK_TYPES.include? type) ? true : break) }
      el.options[:compound] = true
    end
    traverse remaining, (opts.merge parent: el)
  end
end

#convert_math(el, opts) ⇒ Object



653
654
655
656
657
658
659
660
661
662
663
664
665
# File 'lib/kramdown-asciidoc/converter.rb', line 653

def convert_math el, opts
  writer = opts[:writer]
  @attributes['stem'] = 'latexmath'
  if el.options[:category] == :span
    writer.append %(stem:[#{el.value.gsub ']', '\]'}])
  else
    writer.start_block
    writer.add_line '[stem]'
    writer.add_line '++++'
    writer.add_lines el.value.rstrip.split LF
    writer.add_line '++++'
  end
end

#convert_p(el, opts) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/kramdown-asciidoc/converter.rb', line 175

def convert_p el, opts
  (writer = opts[:writer]).start_block
  if (children = el.children).empty?
    lines = ['{blank}']
  # NOTE detect plain admonition marker (e.g, Note: ...)
  # TODO these conditionals could be optimized
  elsif (child_i = children[0]).type == :text && (child_i_text = child_i.value).start_with?(*ADMON_MARKERS)
    marker, child_i_text = child_i_text.split ': ', 2
    children = [(clone child_i, value: %(#{ADMON_TYPE_MAP[marker]}: #{child_i_text}))] + (children.drop 1)
    lines = compose_text children, parent: el, strip: true, split: true, wrap: @wrap
  # NOTE detect formatted admonition marker (e.g., *Note:* ...)
  elsif (child_i.type == :strong || child_i.type == :em) &&
      (marker_el = child_i.children[0]) && ((marker = ADMON_FORMATTED_MARKERS[marker_el.value]) ||
      ((marker = ADMON_LABELS[marker_el.value]) && (child_ii = to_element children[1]).type == :text &&
      (((child_ii_text = child_ii.value).start_with? ': ') ||
      (opts[:parent].type == :blockquote && (child_ii_text.start_with? ?\n)))))
    children = children.drop 1
    children[0] = clone child_ii, value: (child_ii_text.slice 1, child_ii_text.length) if child_ii
    # Q: should we only rstrip?
    lines = compose_text children, parent: el, strip: true, split: true, wrap: @wrap
    lines.unshift %(#{ADMON_TYPE_MAP[marker]}: #{lines.shift})
  else
    lines = compose_text el, strip: true, split: true, wrap: @wrap
  end
  writer.add_lines lines
end

#convert_root(el, opts) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/kramdown-asciidoc/converter.rb', line 106

def convert_root el, opts
  writer = Writer.new
  el = extract_prologue el, (opts.merge writer: writer)
  traverse el, (opts.merge writer: writer)
  if (fallback_doctitle = @attributes.delete 'title')
    writer.doctitle ||= fallback_doctitle
  end
  writer.add_attributes @attributes unless @attributes.empty?
  result = writer.to_s.gsub TrailingSpaceRx, ''
  # QUESTION should we add a preprocessor step to clean the source?
  result = result.tr NBSP, ' ' if result.include? NBSP
  result
end

#convert_smart_quote(el, opts) ⇒ Object



562
563
564
# File 'lib/kramdown-asciidoc/converter.rb', line 562

def convert_smart_quote el, opts
  opts[:writer].append SMART_QUOTE_ENTITY_TO_MARKUP[el.value]
end

#convert_strong(el, opts) ⇒ Object



487
488
489
490
491
492
493
494
495
# File 'lib/kramdown-asciidoc/converter.rb', line 487

def convert_strong el, opts
  if ((composed_text = compose_text el).include? ' > ') && MenuRefRx =~ composed_text
    @attributes['experimental'] = ''
    opts[:writer].append %(menu:#{$1}[#{$2}])
  else
    mark = (unconstrained? opts[:prev], opts[:next]) ? '**' : '*'
    opts[:writer].append %(#{mark}#{composed_text}#{mark})
  end
end

#convert_table(el, opts) ⇒ Object



382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
# File 'lib/kramdown-asciidoc/converter.rb', line 382

def convert_table el, opts
  head = nil
  cols = (alignments = el.options[:alignment]).size
  if alignments.any? {|align| NON_DEFAULT_TABLE_ALIGNMENTS.include? align }
    colspecs = alignments.map {|align| TABLE_ALIGNMENTS[align] }.join ','
    colspecs = %("#{colspecs}") if cols > 1
  end
  table_buffer = ['|===']
  ventilate = @wrap == :ventilate
  el.children.each do |container|
    container.children.each do |row|
      row_buffer = []
      row.children.each do |cell|
        if ventilate
          cell_contents = (compose_text cell, split: true, wrap: :ventilate).map do |line|
            (line.include? '|') ? (line.gsub '|', '\|') : line
          end
          cell_contents[0] = %(| #{cell_contents[0]})
          row_buffer += cell_contents
        else
          cell_contents = compose_text cell
          cell_contents = cell_contents.gsub '|', '\|' if cell_contents.include? '|'
          row_buffer << %(| #{cell_contents})
        end
      end
      if container.type == :thead
        head = true
        row_buffer = [row_buffer * ' ', '']
      elsif cols > 1
        row_buffer << ''
      end
      table_buffer.concat row_buffer
    end
  end
  table_buffer.pop if table_buffer[-1] == ''
  table_buffer << '|==='
  (writer = opts[:writer]).start_block
  if colspecs
    writer.add_line %([cols=#{colspecs}])
  elsif !head && cols > 1
    writer.add_line %([cols=#{cols}*])
  end
  opts[:writer].add_lines table_buffer
end

#convert_text(el, opts) ⇒ Object



513
514
515
516
517
518
519
520
521
522
523
524
# File 'lib/kramdown-asciidoc/converter.rb', line 513

def convert_text el, opts
  text = escape_replacements el.value
  if text.include? '++'
    @attributes['pp'] = '{plus}{plus}'
    text = text.gsub '++', '{pp}'
  end
  if (writer = opts[:writer]).current_line.to_s.empty?
    writer.append text.lstrip
  else
    writer.append text
  end
end

#convert_typographic_sym(el, opts) ⇒ Object

NOTE leave enabled so we can down-convert mdash to –



567
568
569
# File 'lib/kramdown-asciidoc/converter.rb', line 567

def convert_typographic_sym el, opts
  opts[:writer].append TYPOGRAPHIC_ENTITY_TO_MARKUP[el.value]
end

#convert_xml_comment(el, opts) ⇒ Object



615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
# File 'lib/kramdown-asciidoc/converter.rb', line 615

def convert_xml_comment el, opts
  writer = opts[:writer]
  if (val = (XmlCommentRx.match el.value)[1]).include? ' !'
    lines = (val.gsub CommentPrefixRx, '').strip.split LF
  elsif (val = val.strip).empty? && !writer.follows_list?
    return
  else
    lines = val.split LF
  end
  #siblings = (parent = opts[:parent]) ? parent.children : []
  if el.options[:category] == :block # || (!opts[:result][-1] && siblings[-1] == el)
    writer.start_block
    if lines.empty?
      writer.add_line '//'
    # Q: should we only use block form if empty line is present?
    elsif lines.size > 1
      writer.add_line '////'
      writer.add_lines lines
      writer.add_line '////'
    else
      writer.add_line %(// #{lines[0]})
    end
  elsif !lines.empty?
    if (current_line = writer.current_line) && !(current_line.end_with? LF)
      start_new_line = true
      writer.replace_line current_line.rstrip if current_line.end_with? ' '
    end
    lines = lines.map {|l| %(// #{l}) }
    if start_new_line
      writer.add_lines lines
    else
      writer.append lines.shift
      writer.add_lines lines unless lines.empty?
    end
    writer.add_blank_line
  end
end

#escape_replacements(text) ⇒ Object



530
531
532
533
534
535
536
537
538
539
# File 'lib/kramdown-asciidoc/converter.rb', line 530

def escape_replacements text
  # NOTE the replacement \\\\\& inserts a single backslash in front of the matched text
  text = text.gsub InadvertentReplacementsRx, '\\\\\&' if InadvertentReplacementsRx.match? text
  text = text.gsub UriSchemeRx, '\\\\\&' if !@auto_links && (text.include? '://')
  text = text.gsub '^', '{caret}' if (text.include? '^') && text != '^'
  unless text.ascii_only?
    text = (text.gsub SmartApostropheRx, ?').gsub TypographicSymbolRx, TYPOGRAPHIC_SYMBOL_TO_MARKUP
  end
  text
end

#next_char_word?(next_el) ⇒ Boolean

Returns:

  • (Boolean)


505
506
507
# File 'lib/kramdown-asciidoc/converter.rb', line 505

def next_char_word? next_el
  next_el && next_el.type == :text && (WordRx.match? next_el.value.chr)
end

#prev_char_wordish?(prev_el) ⇒ Boolean

Returns:

  • (Boolean)


501
502
503
# File 'lib/kramdown-asciidoc/converter.rb', line 501

def prev_char_wordish? prev_el
  prev_el && (prev_el.type == :entity || (prev_el.type == :text && (WordishRx.match? prev_el.value[-1])))
end

#replaceable?(text) ⇒ Boolean

Returns:

  • (Boolean)


526
527
528
# File 'lib/kramdown-asciidoc/converter.rb', line 526

def replaceable? text
  (ReplaceableTextRx.match? text) || (text != '^' && (text.include? '^'))
end

#unconstrained?(prev_el, next_el) ⇒ Boolean

Returns:

  • (Boolean)


497
498
499
# File 'lib/kramdown-asciidoc/converter.rb', line 497

def unconstrained? prev_el, next_el
  (next_char_word? next_el) || (prev_char_wordish? prev_el)
end