coradoc-adoc 2.0.22 → 2.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/coradoc/asciidoc/model/attribute_list.rb +31 -4
  3. data/lib/coradoc/asciidoc/model/base.rb +7 -0
  4. data/lib/coradoc/asciidoc/model/image/attribute_extractor.rb +142 -0
  5. data/lib/coradoc/asciidoc/model/image/block_image.rb +8 -0
  6. data/lib/coradoc/asciidoc/model/image/core.rb +35 -26
  7. data/lib/coradoc/asciidoc/model/image/inline_image.rb +7 -0
  8. data/lib/coradoc/asciidoc/model/image.rb +1 -0
  9. data/lib/coradoc/asciidoc/parser/attribute_list.rb +4 -1
  10. data/lib/coradoc/asciidoc/serializer/serializers/image/core.rb +3 -1
  11. data/lib/coradoc/asciidoc/transform/element_transformers/admonition_styles.rb +6 -1
  12. data/lib/coradoc/asciidoc/transform/element_transformers/block_transformer.rb +39 -16
  13. data/lib/coradoc/asciidoc/transform/element_transformers/document_transformer.rb +2 -27
  14. data/lib/coradoc/asciidoc/transform/element_transformers/include_transformer.rb +2 -1
  15. data/lib/coradoc/asciidoc/transform/element_transformers/inline_transformer.rb +12 -6
  16. data/lib/coradoc/asciidoc/transform/element_transformers/list_transformer.rb +10 -4
  17. data/lib/coradoc/asciidoc/transform/element_transformers/other_transformer.rb +25 -22
  18. data/lib/coradoc/asciidoc/transform/element_transformers/table_transformer.rb +6 -3
  19. data/lib/coradoc/asciidoc/transform/from_core_model.rb +16 -40
  20. data/lib/coradoc/asciidoc/transform/inline_transform_visitor.rb +23 -5
  21. data/lib/coradoc/asciidoc/transform/to_core_model.rb +30 -6
  22. data/lib/coradoc/asciidoc/transformer/block_rules.rb +19 -5
  23. data/lib/coradoc/asciidoc/transformer/header_rules.rb +12 -3
  24. data/lib/coradoc/asciidoc/transformer/inline_rules.rb +68 -20
  25. data/lib/coradoc/asciidoc/transformer/list_rules.rb +20 -5
  26. data/lib/coradoc/asciidoc/transformer/misc_rules.rb +31 -12
  27. data/lib/coradoc/asciidoc/transformer/source_line_extractor.rb +67 -0
  28. data/lib/coradoc/asciidoc/transformer/structural_rules.rb +7 -3
  29. data/lib/coradoc/asciidoc/transformer/text_rules.rb +33 -9
  30. data/lib/coradoc/asciidoc/transformer.rb +6 -1
  31. data/lib/coradoc/asciidoc/version.rb +1 -1
  32. metadata +3 -1
@@ -16,11 +16,15 @@ module Coradoc
16
16
  end
17
17
 
18
18
  if marker_type == 'definition'
19
- Coradoc::CoreModel::DefinitionList.new(items: items)
19
+ Coradoc::CoreModel::DefinitionList.new(
20
+ items: items,
21
+ source_line: list.source_line
22
+ )
20
23
  else
21
24
  Coradoc::CoreModel::ListBlock.new(
22
25
  marker_type: marker_type,
23
- items: items
26
+ items: items,
27
+ source_line: list.source_line
24
28
  )
25
29
  end
26
30
  end
@@ -45,7 +49,8 @@ module Coradoc
45
49
  term: ToCoreModel.extract_text_content(term_children),
46
50
  definitions: [ToCoreModel.extract_text_content(def_children)],
47
51
  term_children: term_children,
48
- definition_children: def_children
52
+ definition_children: def_children,
53
+ source_line: item.source_line
49
54
  )
50
55
  di.id = item.id if item.id
51
56
 
@@ -63,7 +68,8 @@ module Coradoc
63
68
 
64
69
  li = Coradoc::CoreModel::ListItem.new(
65
70
  content: ToCoreModel.extract_text_content(content_val),
66
- marker: item.marker
71
+ marker: item.marker,
72
+ source_line: item.source_line
67
73
  )
68
74
  li.children = children
69
75
 
@@ -10,7 +10,8 @@ module Coradoc
10
10
  Coradoc::CoreModel::Term.new(
11
11
  text: term.term.to_s,
12
12
  type: term.type&.to_s || 'preferred',
13
- lang: term.lang&.to_s || 'en'
13
+ lang: term.lang&.to_s || 'en',
14
+ source_line: term.source_line
14
15
  )
15
16
  end
16
17
 
@@ -19,31 +20,31 @@ module Coradoc
19
20
  children = ToCoreModel.transform_inline_content(admonition.content)
20
21
  block = Coradoc::CoreModel::AnnotationBlock.new(
21
22
  annotation_type: canonical,
22
- content: ToCoreModel.extract_text_content(admonition.content)
23
+ content: ToCoreModel.extract_text_content(admonition.content),
24
+ source_line: admonition.source_line
23
25
  )
24
26
  block.children = children
25
27
  block
26
28
  end
27
29
 
28
30
  def transform_image(image)
29
- src = image.src.to_s
30
- src = src[1..] if src.start_with?(':')
31
- positional = image.attributes&.positional || []
32
- positional_alt = positional.first&.value&.to_s || ''
33
- caption = positional[1]&.value&.to_s
34
- title = image.title&.to_s
35
- # AsciiDoc block-title (`.Caption`) is semantically a caption,
36
- # but for compatibility with the existing pipeline that treated
37
- # it as alt when no positional alt was supplied, fall back to it.
38
- alt = positional_alt.empty? ? title : positional_alt
39
- Coradoc::CoreModel::Image.new(
40
- src: src,
41
- alt: alt,
42
- caption: caption,
43
- title: title,
44
- width: image.attributes&.[]('width'),
45
- height: image.attributes&.[]('height')
46
- )
31
+ Coradoc::CoreModel::Image.new(**image_attributes(image))
32
+ end
33
+
34
+ def image_attributes(image)
35
+ {
36
+ src: normalize_image_src(image.src),
37
+ alt: image.alt, title: image.title, caption: image.caption,
38
+ width: image.width, height: image.height,
39
+ link: image.link, role: image.role,
40
+ inline: image.is_a?(Coradoc::AsciiDoc::Model::Image::InlineImage),
41
+ source_line: image.source_line
42
+ }
43
+ end
44
+
45
+ def normalize_image_src(src)
46
+ s = src.to_s
47
+ s.start_with?(':') ? s[1..] : s
47
48
  end
48
49
 
49
50
  def transform_bibliography(bib)
@@ -55,7 +56,8 @@ module Coradoc
55
56
  id: bib.id,
56
57
  title: bib.title.to_s,
57
58
  level: nil,
58
- entries: entries
59
+ entries: entries,
60
+ source_line: bib.source_line
59
61
  )
60
62
  end
61
63
 
@@ -63,7 +65,8 @@ module Coradoc
63
65
  Coradoc::CoreModel::BibliographyEntry.new(
64
66
  anchor_name: entry.anchor_name,
65
67
  document_id: entry.document_id,
66
- ref_text: entry.ref_text.to_s
68
+ ref_text: entry.ref_text.to_s,
69
+ source_line: entry.source_line
67
70
  )
68
71
  end
69
72
  end
@@ -14,7 +14,8 @@ module Coradoc
14
14
  Coradoc::CoreModel::Table.new(
15
15
  id: table.id,
16
16
  title: table.title&.to_s,
17
- rows: rows
17
+ rows: rows,
18
+ source_line: table.source_line
18
19
  )
19
20
  end
20
21
 
@@ -24,7 +25,8 @@ module Coradoc
24
25
  end
25
26
  Coradoc::CoreModel::TableRow.new(
26
27
  cells: cells,
27
- header: row.header
28
+ header: row.header,
29
+ source_line: row.source_line
28
30
  )
29
31
  end
30
32
 
@@ -38,7 +40,8 @@ module Coradoc
38
40
  colspan: cell.colspan,
39
41
  rowspan: cell.rowspan,
40
42
  style: cell.style_name,
41
- children: children
43
+ children: children,
44
+ source_line: cell.source_line
42
45
  )
43
46
  end
44
47
  end
@@ -38,18 +38,12 @@ module Coradoc
38
38
  Coradoc::AsciiDoc::Model::Header.new(title: '')
39
39
  end
40
40
 
41
- # Pull FrontmatterBlock out first so strip_title_heading sees
42
- # the body children in their natural order (frontmatter →
43
- # title heading → body). The reverse-direction strip then
44
- # matches on a level-0 HeaderElement wherever it sits among
45
- # the remaining children.
46
41
  without_frontmatter, frontmatter = extract_frontmatter(Array(element.children))
47
- without_title = strip_title_heading(without_frontmatter, element.title)
48
42
 
49
43
  Coradoc::AsciiDoc::Model::Document.new(
50
44
  id: element.id,
51
45
  header: header,
52
- sections: flatten_children(without_title),
46
+ sections: flatten_children(without_frontmatter),
53
47
  frontmatter: frontmatter
54
48
  )
55
49
  when CoreModel::SectionElement
@@ -68,27 +62,6 @@ module Coradoc
68
62
  end
69
63
  end
70
64
 
71
- # The forward direction (DocumentTransformer#insert_title_heading_after_frontmatter)
72
- # emits a level-0 HeaderElement after any FrontmatterBlock so
73
- # consumers that walk the children see the title. On the reverse
74
- # path, that HeaderElement would round-trip back as a separate
75
- # body element and the title would be emitted twice (once via
76
- # the document header, once via the heading child). Drop it
77
- # before serialization when it carries the same text as the
78
- # document title.
79
- def strip_title_heading(children, document_title)
80
- return children unless document_title && !document_title.strip.empty?
81
-
82
- index = children.find_index do |child|
83
- child.is_a?(CoreModel::HeaderElement) &&
84
- child.level.to_i.zero? &&
85
- child.title.to_s == document_title.to_s
86
- end
87
- return children unless index
88
-
89
- children.reject.with_index { |_, i| i == index }
90
- end
91
-
92
65
  # Transforms each CoreModel child and flattens one level so a
93
66
  # transform that returns multiple siblings (e.g. a source block
94
67
  # followed by its re-expanded callout paragraphs) stays in
@@ -327,11 +300,17 @@ module Coradoc
327
300
  end
328
301
 
329
302
  def transform_image(image)
330
- Coradoc::AsciiDoc::Model::Image::BlockImage.new(
331
- src: image.src,
332
- title: image.alt,
333
- attributes: build_image_attributes(image)
334
- )
303
+ target_class = image.inline ? Coradoc::AsciiDoc::Model::Image::InlineImage
304
+ : Coradoc::AsciiDoc::Model::Image::BlockImage
305
+ target_class.new(**image_attributes(image))
306
+ end
307
+
308
+ def image_attributes(image)
309
+ {
310
+ src: image.src, alt: image.alt, title: image.title,
311
+ caption: image.caption, width: image.width, height: image.height,
312
+ link: image.link, role: image.role
313
+ }
335
314
  end
336
315
 
337
316
  def transform_bibliography(bib)
@@ -530,6 +509,10 @@ module Coradoc
530
509
  content.map { |item| create_text_elements(item) }
531
510
  when Coradoc::CoreModel::InlineElement
532
511
  transform_inline(content)
512
+ when Coradoc::CoreModel::TextContent
513
+ Coradoc::AsciiDoc::Model::TextElement.new(content: content.text.to_s)
514
+ when Coradoc::CoreModel::Base
515
+ transform(content)
533
516
  when Coradoc::AsciiDoc::Model::Base
534
517
  content
535
518
  when Lutaml::Model::Serializable
@@ -555,13 +538,6 @@ module Coradoc
555
538
  attrs
556
539
  end
557
540
 
558
- def build_image_attributes(image)
559
- attrs = {}
560
- attrs['width'] = image.width if image.width
561
- attrs['height'] = image.height if image.height
562
- attrs
563
- end
564
-
565
541
  def default_marker(marker_type)
566
542
  case marker_type
567
543
  when 'ordered' then '.'
@@ -35,21 +35,39 @@ module Coradoc
35
35
  end
36
36
  end
37
37
 
38
+ # Folds soft source line breaks into a single text run: when two
39
+ # +Model::TextElement+s sit adjacent in the content array (the parser
40
+ # emits one per source line of a paragraph), a space is inserted
41
+ # between them so wrapped text renders as flowing prose rather than
42
+ # concatenated words.
43
+ #
44
+ # The previous item must ALSO be a TextElement — if a non-text
45
+ # inline (HardLineBreak, Passthrough, Image, etc.) sits between two
46
+ # TextElements, the source did not have a soft break there and no
47
+ # space should be synthesised. Without this guard, `foo +\nbar`
48
+ # would emit `["foo", hard_break, " ", "bar"]` (stray space) and
49
+ # `Before +pass:[RAW]+ after` would emit a double space around the
50
+ # passthrough.
38
51
  def visit_array(items)
39
52
  result = []
40
- items.each_with_index do |item, idx|
53
+ previous = nil
54
+ items.each do |item|
41
55
  transformed = visit_content(item)
42
56
  next if transformed.empty?
43
57
 
44
- needs_space = idx.positive? &&
45
- item.is_a?(Model::TextElement) &&
46
- item.line_break != '+'
47
- result << CoreModel::TextContent.new(text: ' ') if needs_space
58
+ result << CoreModel::TextContent.new(text: ' ') if soft_break_before?(previous, item)
48
59
  result.concat(transformed)
60
+ previous = item
49
61
  end
50
62
  result
51
63
  end
52
64
 
65
+ def soft_break_before?(previous, current)
66
+ previous.is_a?(Model::TextElement) &&
67
+ current.is_a?(Model::TextElement) &&
68
+ current.line_break != '+'
69
+ end
70
+
53
71
  def visit_model(model)
54
72
  @to_core_model.transform(model)
55
73
  end
@@ -23,14 +23,38 @@ module Coradoc
23
23
  transformer ? transformer.call(model) : model
24
24
  end
25
25
 
26
+ # Returns the source-line view of an AsciiDoc content array as
27
+ # an Array<String>. Single source of truth for source-line
28
+ # extraction — every transformer that needs to populate
29
+ # CoreModel::Block#lines goes through here so the filtering
30
+ # rules (skip Model::LineBreak and Model::Break::PageBreak,
31
+ # which carry no renderable text) apply consistently.
32
+ def extract_source_lines(content)
33
+ Array(content).filter_map do |item|
34
+ next if item.is_a?(Coradoc::AsciiDoc::Model::LineBreak) ||
35
+ item.is_a?(Coradoc::AsciiDoc::Model::Break::PageBreak)
36
+
37
+ extract_text_content(item).to_s
38
+ end
39
+ end
40
+
26
41
  def extract_block_lines(block)
27
- non_break_lines = Array(block.lines).reject do |line|
28
- line.is_a?(Coradoc::AsciiDoc::Model::LineBreak) ||
29
- line.is_a?(Coradoc::AsciiDoc::Model::Break::PageBreak)
42
+ extract_source_lines(block.lines).join("\n")
43
+ end
44
+
45
+ # Extracts the source_line of the first AsciiDoc::Model::Base in
46
+ # +content+. Single source of truth for source-line propagation
47
+ # from AsciiDoc::Model trees into CoreModel objects built from
48
+ # grouped content (e.g., paragraphs inside typed blocks).
49
+ # Returns nil when no model carries a source_line.
50
+ def extract_source_line(content)
51
+ Array(content).each do |item|
52
+ next unless item.is_a?(Coradoc::AsciiDoc::Model::Base)
53
+
54
+ line = item.source_line
55
+ return line if line
30
56
  end
31
- non_break_lines.map do |line|
32
- extract_text_content(line)
33
- end.join("\n")
57
+ nil
34
58
  end
35
59
 
36
60
  def extract_title_text(title)
@@ -23,7 +23,8 @@ module Coradoc
23
23
  title: title,
24
24
  delimiter_len: delimiter.size,
25
25
  lines: lines,
26
- ordering: ordering
26
+ ordering: ordering,
27
+ source_line: SourceLineExtractor.extract(block)
27
28
  }
28
29
  # Markdown fences carry the language tag inline (```ruby);
29
30
  # pass it through so the SourceCode classifier entry can set
@@ -34,7 +35,11 @@ module Coradoc
34
35
 
35
36
  # Example
36
37
  rule(example: sequence(:example)) do
37
- Model::Block::Example.new(title: '', lines: example)
38
+ Model::Block::Example.new(
39
+ title: '',
40
+ lines: example,
41
+ source_line: SourceLineExtractor.extract(example)
42
+ )
38
43
  end
39
44
 
40
45
  # Admonition. Canonicalise the type to uppercase so round-trips
@@ -45,7 +50,11 @@ module Coradoc
45
50
  content: sequence(:content)
46
51
  ) do
47
52
  canonical = Coradoc::AsciiDoc::Transform::ElementTransformers::AdmonitionStyles.canonicalize(admonition_type.to_s)
48
- Model::Admonition.new(content: content, type: canonical)
53
+ Model::Admonition.new(
54
+ content: content,
55
+ type: canonical,
56
+ source_line: SourceLineExtractor.extract(admonition_type)
57
+ )
49
58
  end
50
59
 
51
60
  # Block image
@@ -54,12 +63,17 @@ module Coradoc
54
63
  title = block_image[:title]
55
64
  path = block_image[:path]
56
65
  attrs = AttributeListNormalizer.coerce(block_image[:attribute_list_macro])
66
+ promoted, residual = Model::Image::AttributeExtractor.call(
67
+ attrs, Model::Image::BlockImage
68
+ )
57
69
  Model::Image::BlockImage.new(
58
70
  title: title,
59
71
  id: id,
60
72
  src: path,
61
- attributes: attrs,
62
- line_break: "\n"
73
+ attributes: residual,
74
+ line_break: "\n",
75
+ source_line: SourceLineExtractor.extract(block_image),
76
+ **promoted
63
77
  )
64
78
  end
65
79
  end
@@ -21,7 +21,10 @@ module Coradoc
21
21
  id = id.to_s unless id.nil?
22
22
  id = nil if id && id.empty?
23
23
 
24
- Model::Header.new(id:, title:, author:, revision:)
24
+ Model::Header.new(
25
+ id:, title:, author:, revision:,
26
+ source_line: SourceLineExtractor.extract(header)
27
+ )
25
28
  end
26
29
 
27
30
  rule(header: simple(:header)) do
@@ -34,7 +37,10 @@ module Coradoc
34
37
  last_name: simple(:last_name),
35
38
  email: simple(:email)
36
39
  ) do
37
- Model::Author.new(first_name:, last_name:, email:, middle_name: nil)
40
+ Model::Author.new(
41
+ first_name:, last_name:, email:, middle_name: nil,
42
+ source_line: SourceLineExtractor.extract(first_name)
43
+ )
38
44
  end
39
45
 
40
46
  # Revision
@@ -43,7 +49,10 @@ module Coradoc
43
49
  date: simple(:date),
44
50
  remark: simple(:remark)
45
51
  ) do
46
- Model::Revision.new(number:, date:, remark:)
52
+ Model::Revision.new(
53
+ number:, date:, remark:,
54
+ source_line: SourceLineExtractor.extract(number)
55
+ )
47
56
  end
48
57
  end
49
58
  end
@@ -22,7 +22,8 @@ module Coradoc
22
22
  rule(link: subtree(:link)) do
23
23
  Model::Inline::Link.new(
24
24
  path: link[:path].to_s,
25
- name: link[:text]&.to_s
25
+ name: link[:text]&.to_s,
26
+ source_line: SourceLineExtractor.extract(link)
26
27
  )
27
28
  end
28
29
 
@@ -31,15 +32,23 @@ module Coradoc
31
32
  href = xref[:href].to_s
32
33
  text = xref[:text]
33
34
  args = text ? [text.to_s] : []
34
- Model::Inline::CrossReference.new(href:, args:)
35
+ Model::Inline::CrossReference.new(
36
+ href:, args:,
37
+ source_line: SourceLineExtractor.extract(xref)
38
+ )
35
39
  end
36
40
 
37
41
  # Inline image
38
42
  rule(inline_image: subtree(:inline_image)) do
39
43
  attrs = AttributeListNormalizer.coerce(inline_image[:attribute_list])
44
+ promoted, residual = Model::Image::AttributeExtractor.call(
45
+ attrs, Model::Image::InlineImage
46
+ )
40
47
  Model::Image::InlineImage.new(
41
48
  src: inline_image[:path],
42
- attributes: attrs
49
+ attributes: residual,
50
+ source_line: SourceLineExtractor.extract(inline_image),
51
+ **promoted
43
52
  )
44
53
  end
45
54
 
@@ -50,13 +59,17 @@ module Coradoc
50
59
  rule(inline_passthrough: subtree(:passthrough)) do
51
60
  Model::Inline::Passthrough.new(
52
61
  content: passthrough[:raw].to_s,
53
- form: 'triple'
62
+ form: 'triple',
63
+ source_line: SourceLineExtractor.extract(passthrough)
54
64
  )
55
65
  end
56
66
 
57
67
  # Attribute reference
58
68
  rule(attribute_reference: simple(:name)) do
59
- Model::Inline::AttributeReference.new(name:)
69
+ Model::Inline::AttributeReference.new(
70
+ name:,
71
+ source_line: SourceLineExtractor.extract(name)
72
+ )
60
73
  end
61
74
 
62
75
  # Hard line break (` +\n` or `\\n`). Emitted as a dedicated
@@ -64,8 +77,10 @@ module Coradoc
64
77
  # Model::LineBreak, which only represents paragraph-separator
65
78
  # blank lines. Hard breaks carry semantic meaning: HTML/Markdown
66
79
  # renderers map them to <br>.
67
- rule(hard_line_break: simple(:_)) do
68
- Model::Inline::HardLineBreak.new
80
+ rule(hard_line_break: simple(:hard_line_break)) do
81
+ Model::Inline::HardLineBreak.new(
82
+ source_line: SourceLineExtractor.extract(hard_line_break)
83
+ )
69
84
  end
70
85
 
71
86
  # Term
@@ -73,30 +88,45 @@ module Coradoc
73
88
  term_type: simple(:term_type),
74
89
  term: simple(:term)
75
90
  ) do
76
- Coradoc::AsciiDoc::Model::Term.new(term:, type: term_type, lang: :en)
91
+ Coradoc::AsciiDoc::Model::Term.new(
92
+ term:, type: term_type, lang: :en,
93
+ source_line: SourceLineExtractor.extract(term_type)
94
+ )
77
95
  end
78
96
 
79
97
  # Footnote
80
98
  rule(footnote: simple(:footnote)) do
81
99
  text_str = footnote.to_s
82
- Coradoc::AsciiDoc::Model::Inline::Footnote.new(text: text_str)
100
+ Coradoc::AsciiDoc::Model::Inline::Footnote.new(
101
+ text: text_str,
102
+ source_line: SourceLineExtractor.extract(footnote)
103
+ )
83
104
  end
84
105
 
85
106
  rule(footnote: simple(:footnote), id: simple(:id)) do
86
107
  text_str = footnote.to_s
87
- Coradoc::AsciiDoc::Model::Inline::Footnote.new(text: text_str, id: id.to_s)
108
+ Coradoc::AsciiDoc::Model::Inline::Footnote.new(
109
+ text: text_str, id: id.to_s,
110
+ source_line: SourceLineExtractor.extract(footnote)
111
+ )
88
112
  end
89
113
 
90
114
  # Footnote with empty content (reference to named footnote)
91
115
  rule(footnote: sequence(:footnote), id: simple(:id)) do
92
116
  text_str = footnote.map(&:to_s).join
93
- Coradoc::AsciiDoc::Model::Inline::Footnote.new(text: text_str, id: id.to_s)
117
+ Coradoc::AsciiDoc::Model::Inline::Footnote.new(
118
+ text: text_str, id: id.to_s,
119
+ source_line: SourceLineExtractor.extract(footnote)
120
+ )
94
121
  end
95
122
 
96
123
  # Footnote with empty content and no id
97
124
  rule(footnote: sequence(:footnote)) do
98
125
  text_str = footnote.map(&:to_s).join
99
- Coradoc::AsciiDoc::Model::Inline::Footnote.new(text: text_str)
126
+ Coradoc::AsciiDoc::Model::Inline::Footnote.new(
127
+ text: text_str,
128
+ source_line: SourceLineExtractor.extract(footnote)
129
+ )
100
130
  end
101
131
 
102
132
  # Inline formatting rules generated from a single registry.
@@ -110,11 +140,17 @@ module Coradoc
110
140
 
111
141
  rule(constrained_key => subtree(:subtree)) do
112
142
  content = Transformer.extract_inline_content(subtree)
113
- klass.new(content: content, unconstrained: false)
143
+ klass.new(
144
+ content: content, unconstrained: false,
145
+ source_line: SourceLineExtractor.extract(subtree)
146
+ )
114
147
  end
115
148
  rule(unconstrained_key => subtree(:subtree)) do
116
149
  content = Transformer.extract_inline_content(subtree)
117
- klass.new(content: content, unconstrained: true)
150
+ klass.new(
151
+ content: content, unconstrained: true,
152
+ source_line: SourceLineExtractor.extract(subtree)
153
+ )
118
154
  end
119
155
  end
120
156
 
@@ -123,7 +159,8 @@ module Coradoc
123
159
  Model::Inline::Span.new(
124
160
  text: span_constrained[:text],
125
161
  unconstrained: false,
126
- attributes: span_constrained[:attribute_list]
162
+ attributes: span_constrained[:attribute_list],
163
+ source_line: SourceLineExtractor.extract(span_constrained)
127
164
  )
128
165
  end
129
166
 
@@ -132,31 +169,42 @@ module Coradoc
132
169
  Model::Inline::Span.new(
133
170
  text: span_unconstrained[:text],
134
171
  unconstrained: true,
135
- attributes: span_unconstrained[:attribute_list]
172
+ attributes: span_unconstrained[:attribute_list],
173
+ source_line: SourceLineExtractor.extract(span_unconstrained)
136
174
  )
137
175
  end
138
176
 
139
177
  # Superscript
140
178
  rule(superscript: subtree(:superscript)) do
141
179
  content = Transformer.extract_simple_inline_content(superscript)
142
- Model::Inline::Superscript.new(content:)
180
+ Model::Inline::Superscript.new(
181
+ content:,
182
+ source_line: SourceLineExtractor.extract(superscript)
183
+ )
143
184
  end
144
185
 
145
186
  # Subscript
146
187
  rule(subscript: subtree(:subscript)) do
147
188
  content = Transformer.extract_simple_inline_content(subscript)
148
- Model::Inline::Subscript.new(content:)
189
+ Model::Inline::Subscript.new(
190
+ content:,
191
+ source_line: SourceLineExtractor.extract(subscript)
192
+ )
149
193
  end
150
194
 
151
195
  # Highlight (simple)
152
196
  rule(highlight: simple(:text)) do
153
- Model::Highlight.new(content: text)
197
+ Model::Highlight.new(
198
+ content: text,
199
+ source_line: SourceLineExtractor.extract(text)
200
+ )
154
201
  end
155
202
  # Stem
156
203
  rule(stem: subtree(:stem)) do
157
204
  Coradoc::AsciiDoc::Model::Inline::Stem.new(
158
205
  type: stem[:stem_type],
159
- content: stem[:content]
206
+ content: stem[:content],
207
+ source_line: SourceLineExtractor.extract(stem)
160
208
  )
161
209
  end
162
210
  end
@@ -74,7 +74,8 @@ module Coradoc
74
74
  end
75
75
 
76
76
  Model::List::Item.new(
77
- content: content, id:, marker:, attached:, nested:, line_break:
77
+ content: content, id:, marker:, attached:, nested:, line_break:,
78
+ source_line: SourceLineExtractor.extract(list_item)
78
79
  )
79
80
  end
80
81
 
@@ -85,26 +86,40 @@ module Coradoc
85
86
 
86
87
  # Unordered list
87
88
  rule(unordered: sequence(:list_items)) do
88
- Model::List::Unordered.new(items: list_items)
89
+ Model::List::Unordered.new(
90
+ items: list_items,
91
+ source_line: SourceLineExtractor.extract(list_items)
92
+ )
89
93
  end
90
94
 
91
95
  rule(
92
96
  attribute_list: simple(:attribute_list),
93
97
  unordered: sequence(:list_items)
94
98
  ) do
95
- Model::List::Unordered.new(items: list_items, attrs: attribute_list)
99
+ Model::List::Unordered.new(
100
+ items: list_items,
101
+ attrs: attribute_list,
102
+ source_line: SourceLineExtractor.extract(attribute_list)
103
+ )
96
104
  end
97
105
 
98
106
  # Ordered list
99
107
  rule(ordered: sequence(:list_items)) do
100
- Model::List::Ordered.new(items: list_items)
108
+ Model::List::Ordered.new(
109
+ items: list_items,
110
+ source_line: SourceLineExtractor.extract(list_items)
111
+ )
101
112
  end
102
113
 
103
114
  rule(
104
115
  attribute_list: simple(:attribute_list),
105
116
  ordered: sequence(:list_items)
106
117
  ) do
107
- Model::List::Ordered.new(items: list_items, attrs: attribute_list)
118
+ Model::List::Ordered.new(
119
+ items: list_items,
120
+ attrs: attribute_list,
121
+ source_line: SourceLineExtractor.extract(attribute_list)
122
+ )
108
123
  end
109
124
 
110
125
  # Definition list term (with optional anchor)