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,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Strategies
9
+ module Admonition
10
+ # HTML fallback: a div with `class="<type>"` and optional
11
+ # `<div class="title">` for the title.
12
+ class Html < Base
13
+ class << self
14
+ def render(admonition, ctx)
15
+ type = admonition.admonition_type.to_s
16
+ title_html = admonition.title ? %(<div class="title">#{admonition.title}</div>\n) : ''
17
+ %(<div class="#{type}">\n#{title_html}#{render_body(admonition, ctx)}\n</div>)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative 'github'
5
+ require_relative 'gfm_alert'
6
+ require_relative 'container'
7
+ require_relative 'html'
8
+
9
+ module Coradoc
10
+ module Markdown
11
+ class Serializer
12
+ module Strategies
13
+ module Admonition
14
+ # Resolves the active admonition strategy from `config.admonition_style`.
15
+ #
16
+ # Modes:
17
+ # :github → > **NOTE:** text (broad compat — DEFAULT for :gfm)
18
+ # :gfm_alert → > [!NOTE]\n> text (GFM native since 2024)
19
+ # :container → :::note\n... \n::: (VitePress / markdown-it)
20
+ # :html → <div class="note">...</div>
21
+ module Registry
22
+ MODES = {
23
+ github: Github,
24
+ gfm_alert: GfmAlert,
25
+ container: Container,
26
+ html: Html
27
+ }.freeze
28
+
29
+ class << self
30
+ def lookup(mode)
31
+ MODES.fetch(mode.to_sym) do
32
+ raise ArgumentError, "Unknown admonition mode: #{mode.inspect}. " \
33
+ "Known: #{MODES.keys.inspect}"
34
+ end
35
+ end
36
+
37
+ def resolve(ctx:)
38
+ lookup(ctx.config.admonition_style)
39
+ end
40
+
41
+ def render(admonition, ctx:)
42
+ resolve(ctx: ctx).render(admonition, ctx)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Strategies
9
+ module Autolink
10
+ # CommonMark angle-bracket autolink: <https://example.com>
11
+ # Renders identically across all HTML-aware renderers.
12
+ class Angle < Base
13
+ class << self
14
+ def applies?(url, text, ctx)
15
+ ctx.config.autolinks && url_eql_text?(url, text)
16
+ end
17
+
18
+ def render(url, _text, _ctx)
19
+ "<#{url}>"
20
+ end
21
+
22
+ private
23
+
24
+ def url_eql_text?(url, text)
25
+ return false unless url && text
26
+
27
+ url.to_s.strip == text.to_s.strip
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'angle'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Strategies
9
+ module Autolink
10
+ # GFM bare-URL autolink: https://example.com (no brackets).
11
+ # Supported by GFM and most modern renderers; minimal noise.
12
+ class Bare < Angle
13
+ class << self
14
+ def render(url, _text, _ctx)
15
+ url.to_s
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Markdown
5
+ class Serializer
6
+ module Strategies
7
+ module Autolink
8
+ # A link whose text equals its URL is a bare URL. Each strategy
9
+ # decides how to format it. `applies?` is checked first; if no
10
+ # strategy applies, the caller falls back to standard link syntax.
11
+ #
12
+ # Adding a new autolink form = adding one file + one entry in
13
+ # Registry::MODES. No call-site changes — Open/Closed.
14
+ class Base
15
+ class << self
16
+ def applies?(_url, _text, _ctx)
17
+ raise NotImplementedError
18
+ end
19
+
20
+ def render(_url, _text, _ctx)
21
+ raise NotImplementedError
22
+ end
23
+
24
+ def mode_name
25
+ name.split('::').last.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase.to_sym
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Strategies
9
+ module Autolink
10
+ # No autolink form — emit standard [text](url). The default
11
+ # when `autolinks: false` or when text differs from url.
12
+ class None < Base
13
+ class << self
14
+ def applies?(_url, _text, _ctx)
15
+ false
16
+ end
17
+
18
+ def render(_url, _text, _ctx)
19
+ raise NotImplementedError, 'None strategy never renders'
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative 'angle'
5
+ require_relative 'bare'
6
+ require_relative 'none'
7
+
8
+ module Coradoc
9
+ module Markdown
10
+ class Serializer
11
+ module Strategies
12
+ module Autolink
13
+ # Resolves an autolink strategy from a Config + (url, text).
14
+ #
15
+ # Mode lookup:
16
+ # autolinks == true → Angle (default, CommonMark-safe)
17
+ # autolinks == false → None (use standard link syntax)
18
+ #
19
+ # The angle form is preferred over bare because angle brackets
20
+ # disambiguate the URL in flowing text and render identically
21
+ # across HTML-aware Markdown processors.
22
+ #
23
+ # Use `Bare` explicitly for GFM-targeted output where minimal
24
+ # noise is preferred.
25
+ module Registry
26
+ MODES = {
27
+ angle: Angle,
28
+ bare: Bare,
29
+ none: None
30
+ }.freeze
31
+
32
+ class << self
33
+ def lookup(mode)
34
+ MODES.fetch(mode.to_sym) do
35
+ raise ArgumentError, "Unknown autolink mode: #{mode.inspect}. " \
36
+ "Known: #{MODES.keys.inspect}"
37
+ end
38
+ end
39
+
40
+ def resolve(url:, text:, ctx:)
41
+ mode = ctx.config.autolinks ? :angle : :none
42
+ strategy = lookup(mode)
43
+ return nil unless strategy.applies?(url, text, ctx)
44
+
45
+ strategy
46
+ end
47
+
48
+ def render_or_default(url:, text:, ctx:, default:)
49
+ strategy = resolve(url: url, text: text, ctx: ctx)
50
+ strategy ? strategy.render(url, text, ctx) : default
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module Markdown
5
+ class Serializer
6
+ module Strategies
7
+ module DefinitionList
8
+ # Each strategy renders a full DefinitionList (with all items,
9
+ # including nested ones) for a specific output form.
10
+ #
11
+ # Strategy is selected by inspecting the list: flat lists with
12
+ # no nesting use Flat (PHP Markdown Extra); lists containing
13
+ # any nesting use NestedHtml (HTML fallback per the spec).
14
+ #
15
+ # `config.definition_list_nested` overrides the auto choice:
16
+ # :html → always NestedHtml when any nesting exists
17
+ # :flatten → strip nesting (information loss; opt-in)
18
+ class Base
19
+ class << self
20
+ def applies?(_list, _ctx)
21
+ raise NotImplementedError
22
+ end
23
+
24
+ def render(_list, _ctx)
25
+ raise NotImplementedError
26
+ end
27
+
28
+ def mode_name
29
+ name.split('::').last.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase.to_sym
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Coradoc
6
+ module Markdown
7
+ class Serializer
8
+ module Strategies
9
+ module DefinitionList
10
+ # PHP Markdown Extra flat syntax:
11
+ #
12
+ # term
13
+ # : definition
14
+ #
15
+ # term2
16
+ # : first
17
+ # : second
18
+ #
19
+ # Only applies when no item has a nested DefinitionList —
20
+ # the flat syntax has no nesting mechanism.
21
+ class Flat < Base
22
+ class << self
23
+ def applies?(list, _ctx)
24
+ list.items.none? { |term| term.nested }
25
+ end
26
+
27
+ def render(list, _ctx)
28
+ list.items.map do |term|
29
+ lines = [term.text.to_s]
30
+ term.definitions.each do |defn|
31
+ content_str = defn.content.to_s
32
+ content_str.lines.each_with_index do |line, i|
33
+ stripped = line.rstrip
34
+ next if i.positive? && stripped.empty?
35
+
36
+ lines << ": #{stripped}"
37
+ end
38
+ end
39
+ lines.join("\n")
40
+ end.join("\n\n")
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative 'flat'
5
+
6
+ module Coradoc
7
+ module Markdown
8
+ class Serializer
9
+ module Strategies
10
+ module DefinitionList
11
+ # HTML fallback for nested definition lists (Markdown syntax
12
+ # cannot express nesting). Renders `<dl>` with recursive
13
+ # `<dl>` inside `<dd>` for nested entries.
14
+ #
15
+ # For flat lists (no nesting), delegates to Flat to keep the
16
+ # output Markdown-native and editable.
17
+ class NestedHtml < Base
18
+ class << self
19
+ def applies?(list, _ctx)
20
+ list.items.any? { |term| term.nested }
21
+ end
22
+
23
+ def render(list, ctx)
24
+ # When the caller asked to flatten, never emit HTML.
25
+ return Flat.render(list, ctx) if ctx.config.definition_list_nested == :flatten
26
+
27
+ render_dl(list)
28
+ end
29
+
30
+ private
31
+
32
+ def render_dl(list)
33
+ items_html = list.items.map { |term| render_term(term) }.join
34
+ "<dl>\n#{items_html}</dl>"
35
+ end
36
+
37
+ def render_term(term)
38
+ dt = "<dt>#{term.text.to_s}</dt>"
39
+ dds = term.definitions.map { |d| render_dd(d) }.join
40
+ nested = term.nested ? "\n #{render_dl(term.nested)}" : ''
41
+ "#{dt}\n#{dds}#{nested unless nested.empty?}"
42
+ end
43
+
44
+ def render_dd(definition)
45
+ content_str = definition.content.to_s.strip
46
+ "<dd>\n #{content_str}\n</dd>"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative 'flat'
5
+ require_relative 'nested_html'
6
+
7
+ module Coradoc
8
+ module Markdown
9
+ class Serializer
10
+ module Strategies
11
+ module DefinitionList
12
+ # Picks the right strategy for a given list:
13
+ #
14
+ # 1. If no item has nesting → Flat (PHP Markdown Extra)
15
+ # 2. If any item has nesting → NestedHtml (HTML fallback)
16
+ # 3. If config says :flatten and list has nesting → Flat
17
+ # (drops nesting — explicit information-loss opt-in)
18
+ module Registry
19
+ STRATEGIES = [NestedHtml, Flat].freeze
20
+
21
+ class << self
22
+ def resolve(list, ctx)
23
+ return Flat if ctx.config.definition_list_nested == :flatten
24
+
25
+ STRATEGIES.find { |s| s.applies?(list, ctx) } || Flat
26
+ end
27
+
28
+ def render(list, ctx)
29
+ resolve(list, ctx).render(list, ctx)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end