coradoc 1.1.8 → 2.0.12
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.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/Rakefile +3 -12
- data/exe/coradoc +21 -2
- data/lib/coradoc/cli.rb +185 -91
- data/lib/coradoc/configurable.rb +527 -0
- data/lib/coradoc/coradoc.rb +463 -0
- data/lib/coradoc/core_model/annotation_block.rb +57 -0
- data/lib/coradoc/core_model/base.rb +172 -0
- data/lib/coradoc/core_model/bibliography.rb +41 -0
- data/lib/coradoc/core_model/bibliography_entry.rb +48 -0
- data/lib/coradoc/core_model/block.rb +63 -0
- data/lib/coradoc/core_model/children_content.rb +53 -0
- data/lib/coradoc/core_model/comment_block.rb +10 -0
- data/lib/coradoc/core_model/definition_item.rb +46 -0
- data/lib/coradoc/core_model/definition_list.rb +28 -0
- data/lib/coradoc/core_model/element_attribute.rb +26 -0
- data/lib/coradoc/core_model/example_block.rb +10 -0
- data/lib/coradoc/core_model/footnote.rb +92 -0
- data/lib/coradoc/core_model/horizontal_rule_block.rb +10 -0
- data/lib/coradoc/core_model/id_generator.rb +16 -0
- data/lib/coradoc/core_model/image.rb +66 -0
- data/lib/coradoc/core_model/inline_element.rb +140 -0
- data/lib/coradoc/core_model/list_block.rb +135 -0
- data/lib/coradoc/core_model/list_item.rb +142 -0
- data/lib/coradoc/core_model/listing_block.rb +13 -0
- data/lib/coradoc/core_model/literal_block.rb +10 -0
- data/lib/coradoc/core_model/metadata.rb +79 -0
- data/lib/coradoc/core_model/open_block.rb +10 -0
- data/lib/coradoc/core_model/paragraph_block.rb +10 -0
- data/lib/coradoc/core_model/pass_block.rb +10 -0
- data/lib/coradoc/core_model/quote_block.rb +12 -0
- data/lib/coradoc/core_model/reviewer_block.rb +10 -0
- data/lib/coradoc/core_model/sidebar_block.rb +10 -0
- data/lib/coradoc/core_model/source_block.rb +10 -0
- data/lib/coradoc/core_model/structural_element.rb +94 -0
- data/lib/coradoc/core_model/table.rb +148 -0
- data/lib/coradoc/core_model/term.rb +53 -0
- data/lib/coradoc/core_model/text_content.rb +22 -0
- data/lib/coradoc/core_model/toc.rb +105 -0
- data/lib/coradoc/core_model/toc_generator.rb +151 -0
- data/lib/coradoc/core_model/verse_block.rb +12 -0
- data/lib/coradoc/core_model.rb +77 -0
- data/lib/coradoc/document_builder.rb +184 -0
- data/lib/coradoc/document_manipulator.rb +203 -0
- data/lib/coradoc/errors.rb +312 -0
- data/lib/coradoc/format_module.rb +49 -0
- data/lib/coradoc/hooks.rb +176 -0
- data/lib/coradoc/input.rb +17 -7
- data/lib/coradoc/logger.rb +54 -0
- data/lib/coradoc/output.rb +17 -6
- data/lib/coradoc/performance_regression.rb +109 -0
- data/lib/coradoc/processor_registry.rb +50 -0
- data/lib/coradoc/query.rb +455 -0
- data/lib/coradoc/registry.rb +156 -0
- data/lib/coradoc/serializer/registry.rb +150 -0
- data/lib/coradoc/transform.rb +11 -0
- data/lib/coradoc/validation.rb +646 -0
- data/lib/coradoc/version.rb +1 -1
- data/lib/coradoc/visitor.rb +283 -0
- data/lib/coradoc.rb +40 -19
- metadata +67 -277
- data/.editorconfig +0 -15
- data/.envrc +0 -1
- data/.irbrc +0 -1
- data/.pryrc.sample +0 -1
- data/.rubocop.yml +0 -14
- data/.rubocop_todo.yml +0 -179
- data/CHANGELOG.md +0 -9
- data/CODE_OF_CONDUCT.md +0 -84
- data/Dockerfile +0 -19
- data/Gemfile +0 -16
- data/LICENSE.txt +0 -21
- data/Makefile +0 -35
- data/README.Docker.adoc +0 -57
- data/README.adoc +0 -119
- data/coradoc.gemspec +0 -40
- data/docker-compose.yml +0 -14
- data/exe/reverse_adoc +0 -81
- data/exe/w2a +0 -60
- data/flake.lock +0 -114
- data/flake.nix +0 -135
- data/lib/coradoc/converter.rb +0 -144
- data/lib/coradoc/document.rb +0 -77
- data/lib/coradoc/element/admonition.rb +0 -18
- data/lib/coradoc/element/attribute.rb +0 -36
- data/lib/coradoc/element/attribute_list.rb +0 -138
- data/lib/coradoc/element/audio.rb +0 -33
- data/lib/coradoc/element/author.rb +0 -24
- data/lib/coradoc/element/base.rb +0 -92
- data/lib/coradoc/element/bibliography.rb +0 -24
- data/lib/coradoc/element/bibliography_entry.rb +0 -24
- data/lib/coradoc/element/block/core.rb +0 -76
- data/lib/coradoc/element/block/example.rb +0 -23
- data/lib/coradoc/element/block/listing.rb +0 -21
- data/lib/coradoc/element/block/literal.rb +0 -21
- data/lib/coradoc/element/block/open.rb +0 -22
- data/lib/coradoc/element/block/pass.rb +0 -21
- data/lib/coradoc/element/block/quote.rb +0 -19
- data/lib/coradoc/element/block/reviewer_comment.rb +0 -19
- data/lib/coradoc/element/block/side.rb +0 -19
- data/lib/coradoc/element/block/sourcecode.rb +0 -21
- data/lib/coradoc/element/block.rb +0 -17
- data/lib/coradoc/element/break.rb +0 -11
- data/lib/coradoc/element/comment_block.rb +0 -22
- data/lib/coradoc/element/comment_line.rb +0 -18
- data/lib/coradoc/element/document_attributes.rb +0 -33
- data/lib/coradoc/element/header.rb +0 -22
- data/lib/coradoc/element/image/block_image.rb +0 -32
- data/lib/coradoc/element/image/core.rb +0 -58
- data/lib/coradoc/element/image/inline_image.rb +0 -12
- data/lib/coradoc/element/image.rb +0 -10
- data/lib/coradoc/element/include.rb +0 -18
- data/lib/coradoc/element/inline/anchor.rb +0 -19
- data/lib/coradoc/element/inline/attribute_reference.rb +0 -19
- data/lib/coradoc/element/inline/bold.rb +0 -25
- data/lib/coradoc/element/inline/cross_reference.rb +0 -46
- data/lib/coradoc/element/inline/footnote.rb +0 -24
- data/lib/coradoc/element/inline/hard_line_break.rb +0 -11
- data/lib/coradoc/element/inline/highlight.rb +0 -25
- data/lib/coradoc/element/inline/italic.rb +0 -25
- data/lib/coradoc/element/inline/link.rb +0 -42
- data/lib/coradoc/element/inline/monospace.rb +0 -25
- data/lib/coradoc/element/inline/quotation.rb +0 -20
- data/lib/coradoc/element/inline/small.rb +0 -19
- data/lib/coradoc/element/inline/span.rb +0 -37
- data/lib/coradoc/element/inline/subscript.rb +0 -20
- data/lib/coradoc/element/inline/superscript.rb +0 -20
- data/lib/coradoc/element/inline/underline.rb +0 -19
- data/lib/coradoc/element/inline.rb +0 -23
- data/lib/coradoc/element/list/core.rb +0 -51
- data/lib/coradoc/element/list/definition.rb +0 -29
- data/lib/coradoc/element/list/ordered.rb +0 -17
- data/lib/coradoc/element/list/unordered.rb +0 -17
- data/lib/coradoc/element/list.rb +0 -13
- data/lib/coradoc/element/list_item.rb +0 -98
- data/lib/coradoc/element/list_item_definition.rb +0 -32
- data/lib/coradoc/element/paragraph.rb +0 -37
- data/lib/coradoc/element/revision.rb +0 -27
- data/lib/coradoc/element/section.rb +0 -62
- data/lib/coradoc/element/table.rb +0 -91
- data/lib/coradoc/element/tag.rb +0 -19
- data/lib/coradoc/element/term.rb +0 -22
- data/lib/coradoc/element/text_element.rb +0 -92
- data/lib/coradoc/element/title.rb +0 -62
- data/lib/coradoc/element/video.rb +0 -50
- data/lib/coradoc/generator.rb +0 -19
- data/lib/coradoc/input/adoc.rb +0 -30
- data/lib/coradoc/input/docx.rb +0 -64
- data/lib/coradoc/input/html/LICENSE.txt +0 -25
- data/lib/coradoc/input/html/README.adoc +0 -308
- data/lib/coradoc/input/html/cleaner.rb +0 -142
- data/lib/coradoc/input/html/config.rb +0 -77
- data/lib/coradoc/input/html/converters/a.rb +0 -52
- data/lib/coradoc/input/html/converters/aside.rb +0 -16
- data/lib/coradoc/input/html/converters/audio.rb +0 -29
- data/lib/coradoc/input/html/converters/base.rb +0 -108
- data/lib/coradoc/input/html/converters/blockquote.rb +0 -22
- data/lib/coradoc/input/html/converters/br.rb +0 -15
- data/lib/coradoc/input/html/converters/bypass.rb +0 -81
- data/lib/coradoc/input/html/converters/code.rb +0 -23
- data/lib/coradoc/input/html/converters/div.rb +0 -19
- data/lib/coradoc/input/html/converters/dl.rb +0 -62
- data/lib/coradoc/input/html/converters/drop.rb +0 -26
- data/lib/coradoc/input/html/converters/em.rb +0 -21
- data/lib/coradoc/input/html/converters/figure.rb +0 -25
- data/lib/coradoc/input/html/converters/h.rb +0 -42
- data/lib/coradoc/input/html/converters/head.rb +0 -23
- data/lib/coradoc/input/html/converters/hr.rb +0 -15
- data/lib/coradoc/input/html/converters/ignore.rb +0 -20
- data/lib/coradoc/input/html/converters/img.rb +0 -110
- data/lib/coradoc/input/html/converters/li.rb +0 -17
- data/lib/coradoc/input/html/converters/mark.rb +0 -19
- data/lib/coradoc/input/html/converters/markup.rb +0 -31
- data/lib/coradoc/input/html/converters/math.rb +0 -38
- data/lib/coradoc/input/html/converters/ol.rb +0 -65
- data/lib/coradoc/input/html/converters/p.rb +0 -23
- data/lib/coradoc/input/html/converters/pass_through.rb +0 -17
- data/lib/coradoc/input/html/converters/pre.rb +0 -55
- data/lib/coradoc/input/html/converters/q.rb +0 -16
- data/lib/coradoc/input/html/converters/strong.rb +0 -20
- data/lib/coradoc/input/html/converters/sub.rb +0 -22
- data/lib/coradoc/input/html/converters/sup.rb +0 -22
- data/lib/coradoc/input/html/converters/table.rb +0 -319
- data/lib/coradoc/input/html/converters/td.rb +0 -81
- data/lib/coradoc/input/html/converters/text.rb +0 -32
- data/lib/coradoc/input/html/converters/th.rb +0 -18
- data/lib/coradoc/input/html/converters/tr.rb +0 -22
- data/lib/coradoc/input/html/converters/video.rb +0 -29
- data/lib/coradoc/input/html/converters.rb +0 -59
- data/lib/coradoc/input/html/errors.rb +0 -14
- data/lib/coradoc/input/html/html_converter.rb +0 -168
- data/lib/coradoc/input/html/plugin.rb +0 -131
- data/lib/coradoc/input/html/plugins/plateau.rb +0 -213
- data/lib/coradoc/input/html/postprocessor.rb +0 -220
- data/lib/coradoc/input/html.rb +0 -61
- data/lib/coradoc/legacy_parser.rb +0 -200
- data/lib/coradoc/oscal.rb +0 -99
- data/lib/coradoc/output/adoc.rb +0 -19
- data/lib/coradoc/output/coradoc_tree_debug.rb +0 -21
- data/lib/coradoc/parser/asciidoc/admonition.rb +0 -24
- data/lib/coradoc/parser/asciidoc/attribute_list.rb +0 -89
- data/lib/coradoc/parser/asciidoc/base.rb +0 -87
- data/lib/coradoc/parser/asciidoc/bibliography.rb +0 -29
- data/lib/coradoc/parser/asciidoc/block.rb +0 -94
- data/lib/coradoc/parser/asciidoc/citation.rb +0 -30
- data/lib/coradoc/parser/asciidoc/content.rb +0 -64
- data/lib/coradoc/parser/asciidoc/document_attributes.rb +0 -25
- data/lib/coradoc/parser/asciidoc/header.rb +0 -29
- data/lib/coradoc/parser/asciidoc/inline.rb +0 -195
- data/lib/coradoc/parser/asciidoc/list.rb +0 -115
- data/lib/coradoc/parser/asciidoc/paragraph.rb +0 -54
- data/lib/coradoc/parser/asciidoc/section.rb +0 -61
- data/lib/coradoc/parser/asciidoc/table.rb +0 -32
- data/lib/coradoc/parser/asciidoc/term.rb +0 -41
- data/lib/coradoc/parser/asciidoc/text.rb +0 -158
- data/lib/coradoc/parser/base.rb +0 -40
- data/lib/coradoc/parser.rb +0 -11
- data/lib/coradoc/reverse_adoc.rb +0 -18
- data/lib/coradoc/transformer.rb +0 -476
- data/lib/coradoc/util.rb +0 -12
- data/lib/reverse_adoc.rb +0 -20
- data/utils/inspect_asciidoc.rb +0 -29
- data/utils/parser_analyzer.rb +0 -66
- data/utils/round_trip.rb +0 -53
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module CoreModel
|
|
5
|
+
# Represents a single item within a list
|
|
6
|
+
#
|
|
7
|
+
# A list item can contain:
|
|
8
|
+
# - Simple text content
|
|
9
|
+
# - A nested list
|
|
10
|
+
# - Child elements (paragraphs, blocks, etc.)
|
|
11
|
+
#
|
|
12
|
+
# Note: The nested_list attribute type is set after ListBlock is defined
|
|
13
|
+
# to avoid circular dependency issues.
|
|
14
|
+
#
|
|
15
|
+
# @example Simple list item
|
|
16
|
+
# item = ListItem.new(
|
|
17
|
+
# marker: "*",
|
|
18
|
+
# content: "Item text"
|
|
19
|
+
# )
|
|
20
|
+
#
|
|
21
|
+
# @example List item with nested list
|
|
22
|
+
# item = ListItem.new(
|
|
23
|
+
# marker: "*",
|
|
24
|
+
# content: "Parent item",
|
|
25
|
+
# nested_list: nested_list_block
|
|
26
|
+
# )
|
|
27
|
+
class ListItem < Base
|
|
28
|
+
# @!attribute marker
|
|
29
|
+
# @return [String, nil] the marker character(s) for this item
|
|
30
|
+
# (e.g., '*', '**', '.', '..', '-')
|
|
31
|
+
attribute :marker, :string
|
|
32
|
+
|
|
33
|
+
# @!attribute content
|
|
34
|
+
# @return [String, nil] text content of the list item
|
|
35
|
+
attribute :content, :string
|
|
36
|
+
|
|
37
|
+
# @!attribute nested_list
|
|
38
|
+
# @return [ListBlock, nil] nested list within this item
|
|
39
|
+
# Note: Typed as string initially, retyped after ListBlock defined
|
|
40
|
+
attribute :nested_list, :string
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
# Attributes to compare for semantic equivalence
|
|
45
|
+
#
|
|
46
|
+
# List items are semantically equivalent if they have the same
|
|
47
|
+
# content, nested list, and children, regardless of the specific
|
|
48
|
+
# marker used.
|
|
49
|
+
#
|
|
50
|
+
# @return [Array<Symbol>] list of comparable attributes
|
|
51
|
+
def comparable_attributes
|
|
52
|
+
%i[content nested_list children]
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Represents a list block with proper nesting support
|
|
57
|
+
#
|
|
58
|
+
# Handles all list types:
|
|
59
|
+
# - Unordered lists
|
|
60
|
+
# - Ordered lists
|
|
61
|
+
# - Description lists
|
|
62
|
+
#
|
|
63
|
+
# Lists can contain nested lists at multiple levels, with each level
|
|
64
|
+
# tracked through marker_level.
|
|
65
|
+
#
|
|
66
|
+
# @example Creating an unordered list
|
|
67
|
+
# list = CoreModel::ListBlock.new(
|
|
68
|
+
# marker_type: "unordered",
|
|
69
|
+
# marker_level: 1,
|
|
70
|
+
# items: [
|
|
71
|
+
# ListItem.new(marker: "*", content: "First item"),
|
|
72
|
+
# ListItem.new(marker: "*", content: "Second item")
|
|
73
|
+
# ]
|
|
74
|
+
# )
|
|
75
|
+
#
|
|
76
|
+
# @example Creating a nested list
|
|
77
|
+
# nested = CoreModel::ListBlock.new(
|
|
78
|
+
# marker_type: "unordered",
|
|
79
|
+
# marker_level: 2,
|
|
80
|
+
# items: [ListItem.new(marker: "**", content: "Nested item")]
|
|
81
|
+
# )
|
|
82
|
+
# list = CoreModel::ListBlock.new(
|
|
83
|
+
# marker_type: "unordered",
|
|
84
|
+
# marker_level: 1,
|
|
85
|
+
# items: [
|
|
86
|
+
# ListItem.new(
|
|
87
|
+
# marker: "*",
|
|
88
|
+
# content: "Parent item",
|
|
89
|
+
# nested_list: nested
|
|
90
|
+
# )
|
|
91
|
+
# ]
|
|
92
|
+
# )
|
|
93
|
+
class ListBlock < Base
|
|
94
|
+
# @!attribute marker_type
|
|
95
|
+
# @return [String, nil] type of list marker
|
|
96
|
+
# (e.g., 'unordered', 'ordered', 'definition')
|
|
97
|
+
attribute :marker_type, :string
|
|
98
|
+
|
|
99
|
+
# @!attribute marker_level
|
|
100
|
+
# @return [Integer] nesting level of the list (default: 1)
|
|
101
|
+
attribute :marker_level, :integer, default: -> { 1 }
|
|
102
|
+
|
|
103
|
+
# @!attribute start
|
|
104
|
+
# @return [Integer, nil] starting number for ordered lists
|
|
105
|
+
attribute :start, :integer
|
|
106
|
+
|
|
107
|
+
# @!attribute items
|
|
108
|
+
# @return [Array<ListItem>] collection of list items
|
|
109
|
+
attribute :items, ListItem, collection: true
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
# Attributes to compare for semantic equivalence
|
|
114
|
+
#
|
|
115
|
+
# Lists are semantically equivalent if they have the same marker
|
|
116
|
+
# type and items, regardless of marker level (which is structural).
|
|
117
|
+
#
|
|
118
|
+
# @return [Array<Symbol>] list of comparable attributes
|
|
119
|
+
def comparable_attributes
|
|
120
|
+
super + %i[marker_type items]
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Re-open ListItem to properly type nested_list now that ListBlock
|
|
125
|
+
# is defined
|
|
126
|
+
class ListItem
|
|
127
|
+
# Remove the temporary string-typed attribute
|
|
128
|
+
remove_method :nested_list if method_defined?(:nested_list)
|
|
129
|
+
remove_method :nested_list= if method_defined?(:nested_list=)
|
|
130
|
+
|
|
131
|
+
# Re-define with proper ListBlock type
|
|
132
|
+
attribute :nested_list, ListBlock
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module CoreModel
|
|
5
|
+
# Represents a list item
|
|
6
|
+
#
|
|
7
|
+
# List items can contain text content, nested lists, and attached blocks.
|
|
8
|
+
# They support various marker types and can have different nesting levels.
|
|
9
|
+
#
|
|
10
|
+
# @example Creating a simple list item
|
|
11
|
+
# item = ListItem.new(
|
|
12
|
+
# marker: "*",
|
|
13
|
+
# content: "First item"
|
|
14
|
+
# )
|
|
15
|
+
#
|
|
16
|
+
# @example Creating a list item with nested content
|
|
17
|
+
# item = ListItem.new(
|
|
18
|
+
# marker: "*",
|
|
19
|
+
# content: "Item with nested list",
|
|
20
|
+
# nested_list: nested_list_block,
|
|
21
|
+
# children: [attached_block]
|
|
22
|
+
# )
|
|
23
|
+
class ListItem < Base
|
|
24
|
+
attribute :children, Base, collection: true
|
|
25
|
+
|
|
26
|
+
include ChildrenContent
|
|
27
|
+
|
|
28
|
+
# @!attribute marker
|
|
29
|
+
# @return [String] the list marker (*, -, 1., etc.)
|
|
30
|
+
attribute :marker, :string
|
|
31
|
+
|
|
32
|
+
# @!attribute content
|
|
33
|
+
# @return [String] the text content of the list item
|
|
34
|
+
attribute :content, :string
|
|
35
|
+
|
|
36
|
+
# @!attribute children
|
|
37
|
+
# @return [Array] array of attached blocks/elements (mixed content, via ChildrenContent)
|
|
38
|
+
|
|
39
|
+
# Initialize with optional nested structure support
|
|
40
|
+
# @param args [Hash] initialization arguments
|
|
41
|
+
def initialize(args = {})
|
|
42
|
+
# Support :nested as alias for :nested_list
|
|
43
|
+
args[:nested_list] = args.delete(:nested) if args.key?(:nested)
|
|
44
|
+
super
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Delegate nested to nested_list (lutaml attribute added by list_block.rb)
|
|
48
|
+
def nested
|
|
49
|
+
nested_list
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Set nested list
|
|
53
|
+
# @param value [ListBlock, nil] nested list block
|
|
54
|
+
def nested=(value)
|
|
55
|
+
self.nested_list = value
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Convert to hash representation
|
|
59
|
+
#
|
|
60
|
+
# @return [Hash] hash representation of the list item
|
|
61
|
+
def to_h
|
|
62
|
+
{
|
|
63
|
+
marker: marker,
|
|
64
|
+
content: content,
|
|
65
|
+
nested_list: nested&.to_h,
|
|
66
|
+
children: children&.map { |child| child.is_a?(CoreModel::TextContent) ? { text: child.text } : child.to_h }
|
|
67
|
+
}.compact
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Create from hash
|
|
71
|
+
#
|
|
72
|
+
# @param hash [Hash] hash representation
|
|
73
|
+
# @return [ListItem] new list item instance
|
|
74
|
+
def self.from_h(hash)
|
|
75
|
+
raw_children = hash[:children] || []
|
|
76
|
+
children = raw_children.map do |child|
|
|
77
|
+
if child.is_a?(Hash) && child.key?(:text)
|
|
78
|
+
CoreModel::TextContent.new(text: child[:text])
|
|
79
|
+
elsif child.is_a?(CoreModel::Base)
|
|
80
|
+
child
|
|
81
|
+
end
|
|
82
|
+
end.compact
|
|
83
|
+
|
|
84
|
+
new(
|
|
85
|
+
marker: hash[:marker],
|
|
86
|
+
content: hash[:content],
|
|
87
|
+
nested: hash[:nested_list],
|
|
88
|
+
children: children
|
|
89
|
+
)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Override semantic equivalence to handle nested structures properly
|
|
93
|
+
def semantically_equivalent?(other)
|
|
94
|
+
return false unless other.is_a?(self.class)
|
|
95
|
+
return false unless content == other.content
|
|
96
|
+
|
|
97
|
+
# Compare nested lists if present
|
|
98
|
+
if nested || other.nested
|
|
99
|
+
return false if nested.nil? != other.nested.nil?
|
|
100
|
+
return false if nested && !lists_equivalent?(nested, other.nested)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Compare children if present
|
|
104
|
+
if children || other.children
|
|
105
|
+
return false if children.nil? != other.children.nil?
|
|
106
|
+
return false if children && !arrays_equivalent?(children, other.children)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
true
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
# Compare two list blocks for equivalence
|
|
115
|
+
def lists_equivalent?(list1, list2)
|
|
116
|
+
# Both should be ListBlock objects or compatible
|
|
117
|
+
return true if list1 == list2
|
|
118
|
+
return false if list1.nil? || list2.nil?
|
|
119
|
+
|
|
120
|
+
# Check if both are ListBlock instances
|
|
121
|
+
if list1.is_a?(Coradoc::CoreModel::ListBlock) &&
|
|
122
|
+
list2.is_a?(Coradoc::CoreModel::ListBlock)
|
|
123
|
+
list1.semantically_equivalent?(list2)
|
|
124
|
+
elsif list1.instance_of?(::Coradoc::CoreModel::ListBlock) &&
|
|
125
|
+
list2.instance_of?(::Coradoc::CoreModel::ListBlock)
|
|
126
|
+
# Serialized by Lutaml - compare as objects
|
|
127
|
+
list1.marker_type == list2.marker_type &&
|
|
128
|
+
list1.items.to_a == list2.items.to_a
|
|
129
|
+
else
|
|
130
|
+
list1 == list2
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Compare two arrays for equivalence
|
|
135
|
+
def arrays_equivalent?(arr1, arr2)
|
|
136
|
+
return false unless arr1.size == arr2.size
|
|
137
|
+
|
|
138
|
+
arr1.zip(arr2).all? { |a, b| a == b }
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module CoreModel
|
|
5
|
+
# Listing block — a delimited block for listing/content without source language
|
|
6
|
+
#
|
|
7
|
+
# Distinct from SourceBlock: a listing has no language annotation.
|
|
8
|
+
# When a language is present, SourceBlock should be used instead.
|
|
9
|
+
class ListingBlock < Block
|
|
10
|
+
def self.semantic_type = :listing
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module CoreModel
|
|
5
|
+
# A single metadata entry (key-value pair)
|
|
6
|
+
class MetadataEntry < Base
|
|
7
|
+
attribute :key, :string
|
|
8
|
+
attribute :value, :string
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Represents metadata associated with a document element
|
|
12
|
+
#
|
|
13
|
+
# Stores arbitrary key-value pairs for tracking source location,
|
|
14
|
+
# processing information, and other contextual data.
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# meta = Metadata.new
|
|
18
|
+
# meta["source_line"] = 42
|
|
19
|
+
# meta["parser_version"] = "1.0.0"
|
|
20
|
+
class Metadata < Base
|
|
21
|
+
attribute :entries, MetadataEntry, collection: true
|
|
22
|
+
|
|
23
|
+
# Get a metadata value by key
|
|
24
|
+
# @param key [String] The metadata key
|
|
25
|
+
# @return [String, nil] The value or nil if not found
|
|
26
|
+
def [](key)
|
|
27
|
+
return nil if entries.nil?
|
|
28
|
+
|
|
29
|
+
find_entry(key)&.value
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Set a metadata value
|
|
33
|
+
# @param key [String] The metadata key
|
|
34
|
+
# @param value [String] The value to set
|
|
35
|
+
def []=(key, value)
|
|
36
|
+
self.entries ||= []
|
|
37
|
+
existing = find_entry(key)
|
|
38
|
+
if existing
|
|
39
|
+
existing.value = value
|
|
40
|
+
else
|
|
41
|
+
entries << MetadataEntry.new(key: key, value: value)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Check if a key exists
|
|
46
|
+
# @param key [String] The key to check
|
|
47
|
+
# @return [Boolean] True if key exists
|
|
48
|
+
def key?(key)
|
|
49
|
+
return false if entries.nil?
|
|
50
|
+
|
|
51
|
+
!find_entry(key).nil?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Get all keys
|
|
55
|
+
# @return [Array<String>] List of keys
|
|
56
|
+
def keys
|
|
57
|
+
return [] if entries.nil?
|
|
58
|
+
|
|
59
|
+
entries.map(&:key)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Convert to hash representation
|
|
63
|
+
# @return [Hash] Hash of all metadata entries
|
|
64
|
+
def to_h
|
|
65
|
+
return {} if entries.nil?
|
|
66
|
+
|
|
67
|
+
entries.each_with_object({}) { |entry, hash| hash[entry.key] = entry.value }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def find_entry(key)
|
|
73
|
+
return nil if entries.nil?
|
|
74
|
+
|
|
75
|
+
entries.find { |e| e.key == key }
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module CoreModel
|
|
5
|
+
# Base class for structural elements
|
|
6
|
+
#
|
|
7
|
+
# Represents document structure elements that organize content.
|
|
8
|
+
# Typed subclasses (SectionElement, DocumentElement, etc.) express
|
|
9
|
+
# their role via the class hierarchy — the class IS the type.
|
|
10
|
+
#
|
|
11
|
+
# Structural elements can contain other elements (blocks, lists, etc.)
|
|
12
|
+
# and can be nested hierarchically to represent document structure.
|
|
13
|
+
class StructuralElement < Base
|
|
14
|
+
# @!attribute level
|
|
15
|
+
# @return [Integer, nil] hierarchical level (1-6 for sections)
|
|
16
|
+
attribute :level, :integer
|
|
17
|
+
|
|
18
|
+
# @!attribute content
|
|
19
|
+
# @return [String, nil] text content of the element
|
|
20
|
+
attribute :content, :string
|
|
21
|
+
|
|
22
|
+
# @!attribute children
|
|
23
|
+
# @return [Array<Base>, nil] child elements (sections, blocks, etc.)
|
|
24
|
+
attribute :children, Base, collection: true
|
|
25
|
+
|
|
26
|
+
# @!attribute attributes
|
|
27
|
+
# @return [Metadata, nil] document-level attributes (typed key-value pairs)
|
|
28
|
+
attribute :attributes, Metadata
|
|
29
|
+
|
|
30
|
+
def heading_level
|
|
31
|
+
level || 1
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def section? = false
|
|
35
|
+
def document? = false
|
|
36
|
+
def preamble? = false
|
|
37
|
+
def header? = false
|
|
38
|
+
|
|
39
|
+
# Derived element_type string for backward compatibility with
|
|
40
|
+
# templates and legacy consumers. Subclasses override this.
|
|
41
|
+
def element_type
|
|
42
|
+
self.class.element_type_name
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class << self
|
|
46
|
+
def element_type_name
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def comparable_attributes
|
|
54
|
+
[:title] + %i[level children]
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Root document element
|
|
59
|
+
class DocumentElement < StructuralElement
|
|
60
|
+
def document? = true
|
|
61
|
+
|
|
62
|
+
class << self
|
|
63
|
+
def element_type_name = 'document'
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Section with a heading at a specific level
|
|
68
|
+
class SectionElement < StructuralElement
|
|
69
|
+
def section? = true
|
|
70
|
+
|
|
71
|
+
class << self
|
|
72
|
+
def element_type_name = 'section'
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Preamble content before the first section heading
|
|
77
|
+
class PreambleElement < StructuralElement
|
|
78
|
+
def preamble? = true
|
|
79
|
+
|
|
80
|
+
class << self
|
|
81
|
+
def element_type_name = 'preamble'
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Header / title block of a document
|
|
86
|
+
class HeaderElement < StructuralElement
|
|
87
|
+
def header? = true
|
|
88
|
+
|
|
89
|
+
class << self
|
|
90
|
+
def element_type_name = 'header'
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|