coradoc-markdown 1.0.2 → 1.0.4

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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/lib/coradoc/markdown/model/admonition.rb +41 -0
  3. data/lib/coradoc/markdown/model/comment.rb +14 -0
  4. data/lib/coradoc/markdown/model/definition_term.rb +16 -6
  5. data/lib/coradoc/markdown/model/document.rb +9 -0
  6. data/lib/coradoc/markdown/model/example_block.rb +26 -0
  7. data/lib/coradoc/markdown/model/hard_line_break.rb +21 -0
  8. data/lib/coradoc/markdown/model/horizontal_rule.rb +1 -6
  9. data/lib/coradoc/markdown/model/list_item.rb +1 -1
  10. data/lib/coradoc/markdown/model/literal.rb +21 -0
  11. data/lib/coradoc/markdown/model/open_block.rb +22 -0
  12. data/lib/coradoc/markdown/model/paragraph.rb +2 -2
  13. data/lib/coradoc/markdown/model/pass.rb +21 -0
  14. data/lib/coradoc/markdown/model/sidebar.rb +22 -0
  15. data/lib/coradoc/markdown/model/verse.rb +27 -0
  16. data/lib/coradoc/markdown/parser/block_parser.rb +1 -1
  17. data/lib/coradoc/markdown/parser/frontmatter_parser.rb +23 -0
  18. data/lib/coradoc/markdown/serializer/builder.rb +57 -0
  19. data/lib/coradoc/markdown/serializer/config.rb +77 -0
  20. data/lib/coradoc/markdown/serializer/context.rb +66 -0
  21. data/lib/coradoc/markdown/serializer/element_serializer.rb +46 -0
  22. data/lib/coradoc/markdown/serializer/flavor.rb +82 -0
  23. data/lib/coradoc/markdown/serializer/registrations.rb +111 -0
  24. data/lib/coradoc/markdown/serializer/registry.rb +62 -0
  25. data/lib/coradoc/markdown/serializer/runner.rb +84 -0
  26. data/lib/coradoc/markdown/serializer/serializers/abbreviation.rb +19 -0
  27. data/lib/coradoc/markdown/serializer/serializers/admonition.rb +20 -0
  28. data/lib/coradoc/markdown/serializer/serializers/attribute_list.rb +25 -0
  29. data/lib/coradoc/markdown/serializer/serializers/blockquote.rb +19 -0
  30. data/lib/coradoc/markdown/serializer/serializers/code.rb +19 -0
  31. data/lib/coradoc/markdown/serializer/serializers/code_block.rb +19 -0
  32. data/lib/coradoc/markdown/serializer/serializers/comment.rb +31 -0
  33. data/lib/coradoc/markdown/serializer/serializers/cross_reference.rb +19 -0
  34. data/lib/coradoc/markdown/serializer/serializers/definition_list.rb +20 -0
  35. data/lib/coradoc/markdown/serializer/serializers/document.rb +22 -0
  36. data/lib/coradoc/markdown/serializer/serializers/emphasis.rb +19 -0
  37. data/lib/coradoc/markdown/serializer/serializers/example_block.rb +40 -0
  38. data/lib/coradoc/markdown/serializer/serializers/extension.rb +30 -0
  39. data/lib/coradoc/markdown/serializer/serializers/footnote.rb +20 -0
  40. data/lib/coradoc/markdown/serializer/serializers/footnote_reference.rb +19 -0
  41. data/lib/coradoc/markdown/serializer/serializers/hard_line_break.rb +22 -0
  42. data/lib/coradoc/markdown/serializer/serializers/heading.rb +19 -0
  43. data/lib/coradoc/markdown/serializer/serializers/highlight.rb +19 -0
  44. data/lib/coradoc/markdown/serializer/serializers/horizontal_rule.rb +19 -0
  45. data/lib/coradoc/markdown/serializer/serializers/image.rb +19 -0
  46. data/lib/coradoc/markdown/serializer/serializers/link.rb +29 -0
  47. data/lib/coradoc/markdown/serializer/serializers/list.rb +45 -0
  48. data/lib/coradoc/markdown/serializer/serializers/literal.rb +21 -0
  49. data/lib/coradoc/markdown/serializer/serializers/math.rb +23 -0
  50. data/lib/coradoc/markdown/serializer/serializers/open_block.rb +38 -0
  51. data/lib/coradoc/markdown/serializer/serializers/paragraph.rb +23 -0
  52. data/lib/coradoc/markdown/serializer/serializers/pass.rb +21 -0
  53. data/lib/coradoc/markdown/serializer/serializers/sidebar.rb +20 -0
  54. data/lib/coradoc/markdown/serializer/serializers/strikethrough.rb +19 -0
  55. data/lib/coradoc/markdown/serializer/serializers/strong.rb +19 -0
  56. data/lib/coradoc/markdown/serializer/serializers/subscript.rb +19 -0
  57. data/lib/coradoc/markdown/serializer/serializers/superscript.rb +19 -0
  58. data/lib/coradoc/markdown/serializer/serializers/table.rb +37 -0
  59. data/lib/coradoc/markdown/serializer/serializers/underline.rb +19 -0
  60. data/lib/coradoc/markdown/serializer/serializers/verse.rb +31 -0
  61. data/lib/coradoc/markdown/serializer/strategies/admonition/base.rb +41 -0
  62. data/lib/coradoc/markdown/serializer/strategies/admonition/container.rb +34 -0
  63. data/lib/coradoc/markdown/serializer/strategies/admonition/gfm_alert.rb +28 -0
  64. data/lib/coradoc/markdown/serializer/strategies/admonition/github.rb +27 -0
  65. data/lib/coradoc/markdown/serializer/strategies/admonition/html.rb +25 -0
  66. data/lib/coradoc/markdown/serializer/strategies/admonition/registry.rb +50 -0
  67. data/lib/coradoc/markdown/serializer/strategies/autolink/angle.rb +35 -0
  68. data/lib/coradoc/markdown/serializer/strategies/autolink/bare.rb +23 -0
  69. data/lib/coradoc/markdown/serializer/strategies/autolink/base.rb +33 -0
  70. data/lib/coradoc/markdown/serializer/strategies/autolink/none.rb +27 -0
  71. data/lib/coradoc/markdown/serializer/strategies/autolink/registry.rb +58 -0
  72. data/lib/coradoc/markdown/serializer/strategies/definition_list/base.rb +37 -0
  73. data/lib/coradoc/markdown/serializer/strategies/definition_list/flat.rb +48 -0
  74. data/lib/coradoc/markdown/serializer/strategies/definition_list/nested_html.rb +54 -0
  75. data/lib/coradoc/markdown/serializer/strategies/definition_list/registry.rb +37 -0
  76. data/lib/coradoc/markdown/serializer.rb +29 -243
  77. data/lib/coradoc/markdown/toc_generator.rb +2 -2
  78. data/lib/coradoc/markdown/transform/block_transformer.rb +163 -0
  79. data/lib/coradoc/markdown/transform/from_core_model.rb +195 -28
  80. data/lib/coradoc/markdown/transform/image_transformer.rb +20 -0
  81. data/lib/coradoc/markdown/transform/inline_transformer.rb +74 -0
  82. data/lib/coradoc/markdown/transform/list_transformer.rb +52 -0
  83. data/lib/coradoc/markdown/transform/structural_transformer.rb +94 -0
  84. data/lib/coradoc/markdown/transform/table_transformer.rb +40 -0
  85. data/lib/coradoc/markdown/transform/to_core_model.rb +24 -3
  86. data/lib/coradoc/markdown/transformer.rb +87 -2
  87. data/lib/coradoc/markdown/version.rb +1 -1
  88. data/lib/coradoc/markdown.rb +16 -2
  89. metadata +75 -1
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Markdown
5
+ class Serializer
6
+ # Named Markdown flavor profiles.
7
+ #
8
+ # Each flavor bundles sensible defaults for the 5 spec options.
9
+ # Callers can override any option via `Serializer.build`.
10
+ #
11
+ # Adding a new flavor = adding one entry here. No serializer code
12
+ # needs to change — Open/Closed.
13
+ module Flavor
14
+ PROFILES = {
15
+ commonmark: {
16
+ markdown_flavor: :commonmark,
17
+ admonition_style: :html,
18
+ definition_list_nested: :html,
19
+ suppress_comments: true,
20
+ autolinks: true
21
+ },
22
+ gfm: {
23
+ markdown_flavor: :gfm,
24
+ admonition_style: :github,
25
+ definition_list_nested: :html,
26
+ suppress_comments: true,
27
+ autolinks: true
28
+ },
29
+ kramdown: {
30
+ markdown_flavor: :kramdown,
31
+ admonition_style: :html,
32
+ definition_list_nested: :html,
33
+ suppress_comments: true,
34
+ autolinks: true
35
+ },
36
+ pandoc: {
37
+ markdown_flavor: :pandoc,
38
+ admonition_style: :html,
39
+ definition_list_nested: :html,
40
+ suppress_comments: true,
41
+ autolinks: true
42
+ },
43
+ vitepress: {
44
+ markdown_flavor: :vitepress,
45
+ admonition_style: :container,
46
+ definition_list_nested: :html,
47
+ suppress_comments: true,
48
+ autolinks: true
49
+ },
50
+ php_markdown_extra: {
51
+ markdown_flavor: :php_markdown_extra,
52
+ admonition_style: :html,
53
+ definition_list_nested: :html,
54
+ suppress_comments: true,
55
+ autolinks: true
56
+ }
57
+ }.freeze
58
+
59
+ DEFAULT_FLAVOR = :gfm
60
+
61
+ class << self
62
+ def names
63
+ PROFILES.keys
64
+ end
65
+
66
+ def known?(name)
67
+ PROFILES.key?(name.to_sym)
68
+ end
69
+
70
+ def resolve(name)
71
+ profile = PROFILES[name.to_sym] || PROFILES[DEFAULT_FLAVOR]
72
+ profile.dup
73
+ end
74
+
75
+ def default
76
+ PROFILES[DEFAULT_FLAVOR]
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'registry'
4
+ require_relative 'serializers/document'
5
+ require_relative 'serializers/heading'
6
+ require_relative 'serializers/paragraph'
7
+ require_relative 'serializers/list'
8
+ require_relative 'serializers/code_block'
9
+ require_relative 'serializers/blockquote'
10
+ require_relative 'serializers/link'
11
+ require_relative 'serializers/image'
12
+ require_relative 'serializers/horizontal_rule'
13
+ require_relative 'serializers/table'
14
+ require_relative 'serializers/emphasis'
15
+ require_relative 'serializers/strong'
16
+ require_relative 'serializers/code'
17
+ require_relative 'serializers/strikethrough'
18
+ require_relative 'serializers/highlight'
19
+ require_relative 'serializers/subscript'
20
+ require_relative 'serializers/superscript'
21
+ require_relative 'serializers/underline'
22
+ require_relative 'serializers/cross_reference'
23
+ require_relative 'serializers/attribute_list'
24
+ require_relative 'serializers/math'
25
+ require_relative 'serializers/extension'
26
+ require_relative 'serializers/definition_list'
27
+ require_relative 'serializers/footnote'
28
+ require_relative 'serializers/footnote_reference'
29
+ require_relative 'serializers/abbreviation'
30
+ require_relative 'serializers/comment'
31
+ require_relative 'serializers/admonition'
32
+ require_relative 'serializers/example_block'
33
+ require_relative 'serializers/open_block'
34
+ require_relative 'serializers/sidebar'
35
+ require_relative 'serializers/verse'
36
+ require_relative 'serializers/pass'
37
+ require_relative 'serializers/literal'
38
+ require_relative 'serializers/hard_line_break'
39
+
40
+ module Coradoc
41
+ module Markdown
42
+ class Serializer
43
+ # Auto-registers all built-in element serializers into a fresh
44
+ # Registry. Called once per Runner (cached via `default_registry`).
45
+ #
46
+ # Adding a new element serializer = appending one entry here. No
47
+ # lookup code changes — Open/Closed.
48
+ module Registrations
49
+ SERIALIZEABLE = [
50
+ Serializers::Document,
51
+ Serializers::Heading,
52
+ Serializers::Paragraph,
53
+ Serializers::List,
54
+ Serializers::CodeBlock,
55
+ Serializers::Blockquote,
56
+ Serializers::Link,
57
+ Serializers::Image,
58
+ Serializers::HorizontalRule,
59
+ Serializers::Table,
60
+ Serializers::Emphasis,
61
+ Serializers::Strong,
62
+ Serializers::Code,
63
+ Serializers::Strikethrough,
64
+ Serializers::Highlight,
65
+ Serializers::Subscript,
66
+ Serializers::Superscript,
67
+ Serializers::Underline,
68
+ Serializers::CrossReference,
69
+ Serializers::AttributeList,
70
+ Serializers::Math,
71
+ Serializers::Extension,
72
+ Serializers::DefinitionList,
73
+ Serializers::Footnote,
74
+ Serializers::FootnoteReference,
75
+ Serializers::Abbreviation,
76
+ Serializers::Comment,
77
+ Serializers::Admonition,
78
+ Serializers::ExampleBlock,
79
+ Serializers::OpenBlock,
80
+ Serializers::Sidebar,
81
+ Serializers::Verse,
82
+ Serializers::Pass,
83
+ Serializers::Literal,
84
+ Serializers::HardLineBreak
85
+ ].freeze
86
+
87
+ @mutex = Mutex.new
88
+ @default = nil
89
+
90
+ class << self
91
+ def default_registry
92
+ @mutex.synchronize do
93
+ @default ||= register_all(Registry.new)
94
+ end
95
+ end
96
+
97
+ def fresh_registry
98
+ register_all(Registry.new)
99
+ end
100
+
101
+ private
102
+
103
+ def register_all(registry)
104
+ SERIALIZEABLE.each { |klass| registry.register(klass.new) }
105
+ registry
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Markdown
5
+ class Serializer
6
+ # Type-keyed dispatch table for element serializers.
7
+ #
8
+ # Each entry is a tuple of (serializer_instance, priority). Dispatch
9
+ # resolves the highest-priority entry whose declared `handles?` predicate
10
+ # accepts the element. This lets specialized serializers (e.g. one that
11
+ # targets a specific CoreModel subclass) override generic ones without
12
+ # modifying the registry lookup logic — Open/Closed.
13
+ class Registry
14
+ class Entry < Struct.new(:serializer, :priority)
15
+ include Comparable
16
+
17
+ def <=>(other)
18
+ priority <=> other.priority
19
+ end
20
+ end
21
+
22
+ def initialize
23
+ @entries = Hash.new { |h, k| h[k] = [] }
24
+ end
25
+
26
+ def register(serializer, priority: 0)
27
+ klass = serializer.handles_type
28
+ raise ArgumentError, "Serializer #{serializer.class} declares no handles_type" unless klass
29
+
30
+ @entries[klass] << Entry.new(serializer, priority)
31
+ @entries[klass].sort!
32
+ serializer
33
+ end
34
+
35
+ def lookup(element)
36
+ each_candidate(element).first&.serializer
37
+ end
38
+
39
+ def lookup!(element)
40
+ lookup(element) || raise(ArgumentError,
41
+ "Unknown element type for serialization: #{element.class}. " \
42
+ 'Expected a known Markdown model type.')
43
+ end
44
+
45
+ private
46
+
47
+ def each_candidate(element)
48
+ return enum_for(:each_candidate, element) unless block_given?
49
+
50
+ walked = element.class.ancestors
51
+ walked.each do |klass|
52
+ @entries[klass].sort_by { |e| -e.priority }.each do |entry|
53
+ next unless entry.serializer.handles?(element)
54
+
55
+ yield entry
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'context'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ # Stateful runner: holds a frozen Config + Registry, exposes `#call`.
9
+ # Each top-level `call` creates a fresh Context (per-document mutable
10
+ # state) so the same runner can serialize multiple documents without
11
+ # cross-document leakage.
12
+ class Runner
13
+ attr_reader :config, :registry
14
+
15
+ def initialize(config:, registry:)
16
+ @config = config
17
+ @registry = registry
18
+ end
19
+
20
+ def call(element)
21
+ ctx = Context.new(config: config, registry: registry, runner: self)
22
+ result = serialize(element, ctx)
23
+ result = append_link_refs(result, ctx)
24
+ append_footnote_defs(result, ctx).to_s
25
+ end
26
+
27
+ def serialize(element, ctx = nil)
28
+ ctx ||= Context.new(config: config, registry: registry, runner: self)
29
+ case element
30
+ when String
31
+ element
32
+ when nil
33
+ ''
34
+ when Array
35
+ element.map { |e| serialize(e, ctx) }.join
36
+ else
37
+ serializer = registry.lookup(element)
38
+ if serializer
39
+ serializer.call(element, ctx)
40
+ else
41
+ raise ArgumentError,
42
+ "Unknown element type for serialization: #{element.class}. " \
43
+ 'Expected a known Markdown model type.'
44
+ end
45
+ end
46
+ end
47
+
48
+ def serialize_inline(element, ctx)
49
+ case element
50
+ when String
51
+ element
52
+ when nil
53
+ ''
54
+ when ::Coradoc::Markdown::Base, ::Coradoc::Markdown::Document
55
+ serialize(element, ctx)
56
+ when Array
57
+ element.map { |e| serialize_inline(e, ctx) }.join
58
+ else
59
+ raise ArgumentError,
60
+ "Cannot serialize inline content of type #{element.class}. " \
61
+ 'Expected String, known inline model, or Base subclass.'
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def append_link_refs(result, ctx)
68
+ return result if ctx.link_refs.empty?
69
+
70
+ refs = ctx.link_refs.map do |ref|
71
+ "[#{ref.id}]: #{ref.url}#{ref.title ? " \"#{ref.title}\"" : ''}"
72
+ end.join("\n")
73
+ "#{result}\n\n#{refs}"
74
+ end
75
+
76
+ def append_footnote_defs(result, ctx)
77
+ return result if ctx.footnote_defs.empty?
78
+
79
+ "#{result}\n\n#{ctx.footnote_defs.join("\n")}"
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../element_serializer'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Serializers
9
+ class Abbreviation < ElementSerializer
10
+ handles_type ::Coradoc::Markdown::Abbreviation
11
+
12
+ def call(element, _ctx)
13
+ "*[#{element.term}]: #{element.definition}"
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../element_serializer'
4
+ require_relative '../strategies/admonition/registry'
5
+
6
+ module Coradoc
7
+ module Markdown
8
+ class Serializer
9
+ module Serializers
10
+ class Admonition < ElementSerializer
11
+ handles_type ::Coradoc::Markdown::Admonition
12
+
13
+ def call(element, ctx)
14
+ Strategies::Admonition::Registry.render(element, ctx: ctx)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../element_serializer'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Serializers
9
+ class AttributeList < ElementSerializer
10
+ handles_type ::Coradoc::Markdown::AttributeList
11
+
12
+ def call(element, _ctx)
13
+ return '' if element.empty?
14
+
15
+ parts = []
16
+ parts << "##{element.id}" if element.id
17
+ parts += element.classes.map { |c| ".#{c}" }
18
+ parts += element.attributes.map { |nv| %(#{nv.name}="#{nv.value}") }
19
+ "{:#{parts.join(' ')}}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../element_serializer'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Serializers
9
+ class Blockquote < ElementSerializer
10
+ handles_type ::Coradoc::Markdown::Blockquote
11
+
12
+ def call(element, _ctx)
13
+ element.content.to_s.lines.map { |line| "> #{line}" }.join
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../element_serializer'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Serializers
9
+ class Code < ElementSerializer
10
+ handles_type ::Coradoc::Markdown::Code
11
+
12
+ def call(element, _ctx)
13
+ "`#{element.text}`"
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../element_serializer'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Serializers
9
+ class CodeBlock < ElementSerializer
10
+ handles_type ::Coradoc::Markdown::CodeBlock
11
+
12
+ def call(element, _ctx)
13
+ "```#{element.language}\n#{element.code}\n```"
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../element_serializer'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Serializers
9
+ # Comments are editorial annotations, not document content. The
10
+ # Markdown Serialization Spec (§Comments) requires that comments
11
+ # be suppressed by default.
12
+ #
13
+ # Behavior:
14
+ # suppress_comments == true → '' (no output)
15
+ # suppress_comments == false → '<!-- text -->'
16
+ class Comment < ElementSerializer
17
+ handles_type ::Coradoc::Markdown::Comment
18
+
19
+ def call(element, ctx)
20
+ return '' if ctx.config.suppress_comments
21
+
22
+ text = element.text.to_s.strip
23
+ return '<!---->' if text.empty?
24
+
25
+ "<!-- #{text} -->"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../element_serializer'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Serializers
9
+ class CrossReference < ElementSerializer
10
+ handles_type ::Coradoc::Markdown::CrossReference
11
+
12
+ def call(element, _ctx)
13
+ "[#{element.text}](##{element.target})"
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../element_serializer'
4
+ require_relative '../strategies/definition_list/registry'
5
+
6
+ module Coradoc
7
+ module Markdown
8
+ class Serializer
9
+ module Serializers
10
+ class DefinitionList < ElementSerializer
11
+ handles_type ::Coradoc::Markdown::DefinitionList
12
+
13
+ def call(element, ctx)
14
+ Strategies::DefinitionList::Registry.render(element, ctx)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../element_serializer'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Serializers
9
+ class Document < ElementSerializer
10
+ handles_type ::Coradoc::Markdown::Document
11
+
12
+ def call(element, ctx)
13
+ body = element.blocks.map { |block| ctx.serialize(block) }.join("\n\n")
14
+ return body unless element.frontmatter && !element.frontmatter.empty?
15
+
16
+ "---\n#{element.frontmatter}---\n\n#{body}".strip
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../element_serializer'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Serializers
9
+ class Emphasis < ElementSerializer
10
+ handles_type ::Coradoc::Markdown::Emphasis
11
+
12
+ def call(element, _ctx)
13
+ "*#{element.text}*"
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../element_serializer'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Serializers
9
+ # Example blocks emit an HTML fallback that preserves the caption
10
+ # as a heading inside `<div class="example">`.
11
+ #
12
+ # When `admonition_style == :container` (VitePress), emits
13
+ # `:::details Caption\n... \n:::` instead.
14
+ class ExampleBlock < ElementSerializer
15
+ handles_type ::Coradoc::Markdown::ExampleBlock
16
+
17
+ def call(element, ctx)
18
+ if ctx.config.admonition_style == :container
19
+ render_container(element)
20
+ else
21
+ render_html(element)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def render_html(element)
28
+ caption_html = element.caption ? %(<h4>Example: #{element.caption}</h4>\n) : ''
29
+ %(<div class="example">\n#{caption_html}<p>#{element.content.to_s}</p>\n</div>)
30
+ end
31
+
32
+ def render_container(element)
33
+ title = element.caption ? " Example: #{element.caption}" : ''
34
+ ":::details#{title}\n#{element.content.to_s}\n:::"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../element_serializer'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Serializers
9
+ class Extension < ElementSerializer
10
+ handles_type ::Coradoc::Markdown::Extension
11
+
12
+ def call(element, _ctx)
13
+ opts = element.options.empty? ? '' : " #{extension_options_to_s(element.options)}"
14
+ if element.self_closing?
15
+ "{::#{element.name}#{opts} /}"
16
+ else
17
+ "{::#{element.name}#{opts}}#{element.content}{:/}"
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def extension_options_to_s(options)
24
+ options.map { |nv| %(#{nv.name}="#{nv.value}") }.join(' ')
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../element_serializer'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Serializers
9
+ class Footnote < ElementSerializer
10
+ handles_type ::Coradoc::Markdown::Footnote
11
+
12
+ def call(element, _ctx)
13
+ content = element.content.to_s
14
+ "[^#{element.id}]: #{content}"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end