yard-fence 0.4.0 → 0.6.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aaa318b924e8efbaddbb3666df3f71157678a5fbab11972f51550ecd48f4ddb2
4
- data.tar.gz: af4bb34d3b799695511f5b880aa1fffa7d9765dcb71a8c5c780bfbd53f5d9f35
3
+ metadata.gz: dedaaa231c20409b1d4aedb72ac7b12c0716076eedea5375ed2a232999b2ea7b
4
+ data.tar.gz: f6b65e208d3fc7c9a0b19939949309e6020ad1808cb5378372ac0a6ef2511715
5
5
  SHA512:
6
- metadata.gz: c59ecea9ee908fe804879b2197ab6047769d70a2256e69522afff4069ad73e95b48f370f0ab9a1827dc4f955e2e1f3ded8c7017da27869c0cb2b262e986f910c
7
- data.tar.gz: 4ed6d00979a897c5ced52a0c72ae108c2eb05a29be9e73b9e0dd5bbc1546e80c6f54a1d780460b4a41268e0dcac27cff10142e70fb7826755f8ba511a6715508
6
+ metadata.gz: a5f0e3c15d75462209e1a717b762b137256aab58dfc44527008cf159458ed50fa47450ddb8355f0f2b006db3cff4f18ba9258ffc6f5542177c5921a2a2d42292
7
+ data.tar.gz: 81f406fdc8c3e399fa730ec7ab3790fa62981a96a02852abaaa8c0edca52cc8070cef89933407e142bc1e741a3f1e46cd933c369ade528cbf2f593c188d02b52
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,29 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [0.6.0] - 2025-11-07
34
+
35
+ - TAG: [v0.6.0][0.6.0t]
36
+ - COVERAGE: 100.00% -- 119/119 lines in 4 files
37
+ - BRANCH COVERAGE: 100.00% -- 34/34 branches in 4 files
38
+ - 37.93% documented
39
+
40
+ ### Added
41
+
42
+ - Catch unrendered code blocks and attempt to convert to HTML
43
+
44
+ ## [0.5.0] - 2025-11-07
45
+
46
+ - TAG: [v0.5.0][0.5.0t]
47
+ - COVERAGE: 100.00% -- 98/98 lines in 4 files
48
+ - BRANCH COVERAGE: 100.00% -- 22/22 branches in 4 files
49
+ - 34.62% documented
50
+
51
+ ### Added
52
+
53
+ - Support multi-line braces
54
+ - 100% lines / 100% branches test coverage
55
+
33
56
  ## [0.4.0] - 2025-11-07
34
57
 
35
58
  - TAG: [v0.4.0][0.4.0t]
@@ -82,7 +105,11 @@ Please file a bug if you notice a violation of semantic versioning.
82
105
 
83
106
  - Initial release
84
107
 
85
- [Unreleased]: https://github.com/galtzo-floss/yard-fence/compare/v0.4.0...HEAD
108
+ [Unreleased]: https://github.com/galtzo-floss/yard-fence/compare/v0.6.0...HEAD
109
+ [0.6.0]: https://github.com/galtzo-floss/yard-fence/compare/v0.5.0...v0.6.0
110
+ [0.6.0t]: https://github.com/galtzo-floss/yard-fence/releases/tag/v0.6.0
111
+ [0.5.0]: https://github.com/galtzo-floss/yard-fence/compare/v0.4.0...v0.5.0
112
+ [0.5.0t]: https://github.com/galtzo-floss/yard-fence/releases/tag/v0.5.0
86
113
  [0.4.0]: https://github.com/galtzo-floss/yard-fence/compare/v0.3.0...v0.4.0
87
114
  [0.4.0t]: https://github.com/galtzo-floss/yard-fence/releases/tag/v0.4.0
88
115
  [0.3.0]: https://github.com/galtzo-floss/yard-fence/compare/v0.2.0...v0.3.0
data/README.md CHANGED
@@ -586,7 +586,7 @@ Thanks for RTFM. ☺️
586
586
  [📌gitmoji]: https://gitmoji.dev
587
587
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
588
588
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
589
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.084-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
589
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.119-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
590
590
  [🔐security]: SECURITY.md
591
591
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
592
592
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -12,14 +12,57 @@ require "kramdown-parser-gfm"
12
12
  module Yard
13
13
  module Fence
14
14
  class KramdownGfmDocument < Kramdown::Document
15
+ # Detects an unrendered fenced code block that slipped through HTML generation.
16
+ # Note on <details markdown="1">:
17
+ # - The classic kramdown parser honors the markdown="1" attribute on block HTML like <details>,
18
+ # and will parse contained markdown as block-level content.
19
+ # - The GFM parser generally handles many cases well, and markdown="1" may appear to work in
20
+ # most sections; however, we've observed edge cases where fenced code blocks inside a
21
+ # <details markdown="1"> are left as literal backticks (rendered as <p>``` …</p>).
22
+ # This fallback detects that situation and re-renders with the classic parser.
23
+ UNRENDERED_FENCE_PARAGRAPH = /<p>```/
24
+ DETAILS_MARKDOWN_1 = /<details[^>]*markdown=["']1["'][^>]*>/i
25
+
15
26
  def initialize(source, options = {})
16
27
  options[:input] = "GFM" unless options.key?(:input)
28
+ @__yard_fence_source = source # Keep original for potential fallback.
17
29
  super(source, options)
18
30
  end
31
+
32
+ # Override to_html to provide a smart fallback: if the GFM parse leaves literal
33
+ # fenced code markers (```), re-run the render with the classic 'kramdown' input which
34
+ # correctly evaluates markdown inside <details markdown="1"> blocks.
35
+ # Opt-out via ENV["YARD_FENCE_DISABLE_FALLBACK"] == "1".
36
+ def to_html
37
+ html = super
38
+ return html if ENV["YARD_FENCE_DISABLE_FALLBACK"] == "1"
39
+ return html unless @__yard_fence_source.include?("```")
40
+
41
+ if needs_fallback?(html)
42
+ fallback_options = @options.merge(input: "kramdown")
43
+ fb_html = Kramdown::Document.new(@__yard_fence_source, fallback_options).to_html
44
+ return fb_html if fallback_improved?(fb_html)
45
+ end
46
+ html
47
+ end
48
+
49
+ private
50
+
51
+ def needs_fallback?(html)
52
+ # Obvious failure: raw fenced code rendered as a paragraph
53
+ return true if html.match?(UNRENDERED_FENCE_PARAGRAPH)
54
+ # Edge case: details wrapper present in source but output lacks any code block
55
+ if @__yard_fence_source.match?(DETAILS_MARKDOWN_1)
56
+ has_code_block = html.include?("<pre") && html.include?("<code")
57
+ return !has_code_block
58
+ end
59
+ false
60
+ end
61
+
62
+ def fallback_improved?(fb_html)
63
+ fb_html.match?(UNRENDERED_FENCE_PARAGRAPH) == false &&
64
+ fb_html.include?("<pre") && fb_html.include?("<code")
65
+ end
19
66
  end
20
67
  end
21
68
  end
22
-
23
- # Note:
24
- # We intentionally do NOT auto-register here to avoid circular require warnings when
25
- # YARD is loading. Prefer calling Yard::Fence.use_kramdown_gfm! from your .yardopts.
@@ -3,7 +3,7 @@
3
3
  module Yard
4
4
  module Fence
5
5
  module Version
6
- VERSION = "0.4.0"
6
+ VERSION = "0.6.0"
7
7
  end
8
8
  VERSION = Version::VERSION
9
9
  end
data/lib/yard/fence.rb CHANGED
@@ -61,12 +61,19 @@ module Yard
61
61
  module Fence
62
62
  ASCII_BRACES = "{}"
63
63
  FULLWIDTH_BRACES = "{}"
64
+ # IMPORTANT: YARD expects :const to be a String.
65
+ # YARD concatenates with a String prefix (e.g., "::" + const),
66
+ # so a Symbol here will break provider resolution. Some static analyzers
67
+ # suggest making this a Symbol to match types, but that is incorrect for YARD.
68
+ # Do NOT change this to a Symbol.
64
69
  KRAMDOWN_PROVIDER = {lib: :kramdown, const: "KramdownGfmDocument"}
65
70
  GLOB_PATTERN = "*.{md,MD,txt,TXT}"
66
71
  TRIPLE_TICK_FENCE = /^\s*```/
67
72
  INLINE_TICK_FENCE = /`([^`]+)`/
68
73
  DOUBLE_BRACE_PLACEHOLDER_REGEX = /{{([^{}]+)}}/
69
74
  SINGLE_BRACE_PLACEHOLDER_REGEX = /{([A-Za-z0-9_:\-]+)}/
75
+ # Lines that are part of a classic indented code block (CommonMark: 4 spaces)
76
+ INDENTED_CODE_LINE = /^ {4,}\S/
70
77
 
71
78
  class Error < StandardError; end
72
79
  # :nocov:
@@ -84,8 +91,11 @@ module Yard
84
91
 
85
92
  # Escape braces inside inline `code` spans only.
86
93
  def sanitize_inline_code(line)
87
- # Use $1 because the block parameter (_) is the matched substring, not a MatchData object.
88
- line.gsub(INLINE_TICK_FENCE) { |_| "`#{fullwidth_braces($1)}`" }
94
+ # Use Regexp.last_match to safely access capture; to_s guards against nil
95
+ line.gsub(INLINE_TICK_FENCE) do |_|
96
+ inner = Regexp.last_match(1).to_s
97
+ "`#{fullwidth_braces(inner)}`"
98
+ end
89
99
  end
90
100
 
91
101
  # Walk the text, toggling a simple in_fence state on ``` lines.
@@ -93,13 +103,31 @@ module Yard
93
103
  # and disarm simple prose placeholders like {issuer} or {{something}}.
94
104
  def sanitize_fenced_blocks(text)
95
105
  in_fence = false
106
+ in_indented_block = false
96
107
 
97
108
  text.each_line.map do |line|
98
109
  if line.match?(TRIPLE_TICK_FENCE)
110
+ # Toggle fenced block state; leaving indented block if switching into explicit fence
99
111
  in_fence = !in_fence
112
+ in_indented_block = false if in_fence
100
113
  line
101
114
  elsif in_fence
102
115
  fullwidth_braces(line)
116
+ elsif in_indented_block
117
+ # Continue indented block until a blank line or non-indented line breaks it
118
+ if line.strip.empty? || !line.match?(INDENTED_CODE_LINE)
119
+ in_indented_block = false
120
+ # Process this line as normal prose outside block
121
+ ln = sanitize_inline_code(line)
122
+ ln = ln.gsub(DOUBLE_BRACE_PLACEHOLDER_REGEX) { |m| fullwidth_braces(m) }
123
+ ln.gsub(SINGLE_BRACE_PLACEHOLDER_REGEX) { |m| fullwidth_braces(m) }
124
+ else
125
+ fullwidth_braces(line)
126
+ end
127
+ elsif line.match?(INDENTED_CODE_LINE)
128
+ # Enter indented code block on first qualifying line
129
+ in_indented_block = true
130
+ fullwidth_braces(line)
103
131
  else
104
132
  ln = sanitize_inline_code(line)
105
133
  # IMPORTANT: handle double-brace placeholders first so we don't partially
@@ -162,6 +190,10 @@ module Yard
162
190
  end
163
191
  # :nocov:
164
192
  providers = ::YARD::Templates::Helpers::MarkupHelper::MARKUP_PROVIDERS[:markdown]
193
+ # NOTE: Intentionally using String for :const in KRAMDOWN_PROVIDER.
194
+ # YARD performs string concatenation with this value (e.g., "::" + const).
195
+ # Changing it to a Symbol to satisfy static type hints breaks runtime behavior.
196
+ # Suppress type fuzz complaints: expected Hash{Symbol->Symbol}, actual includes String by design.
165
197
  providers.unshift(KRAMDOWN_PROVIDER)
166
198
  providers.uniq! { |p| [p[:lib].to_s, p[:const].to_s] }
167
199
  true
@@ -169,14 +201,16 @@ module Yard
169
201
  warn("Yard::Fence.use_kramdown_gfm!: failed to load YARD helper: #{e.class}: #{e.message}")
170
202
  false
171
203
  end
204
+
205
+ def at_load_hook
206
+ Yard::Fence.prepare_tmp_files
207
+ rescue => e
208
+ warn("Yard::Fence: failed to prepare tmp/yard-fence files: #{e.class}: #{e.message}")
209
+ end
172
210
  end
173
211
 
174
212
  # Execute at load-time so files exist before YARD scans tmp/yard-fence/*.md
175
- begin
176
- Yard::Fence.prepare_tmp_files
177
- rescue => e
178
- warn("Yard::Fence: failed to prepare tmp/yard-fence files: #{e.class}: #{e.message}")
179
- end
213
+ Yard::Fence.at_load_hook
180
214
  end
181
215
 
182
216
  # Extend the Version with VersionGem::Basic to provide semantic version helpers.
@@ -184,6 +218,8 @@ Yard::Fence::Version.class_eval do
184
218
  extend VersionGem::Basic
185
219
  end
186
220
 
221
+ # :nocov:
222
+ # Not checking coverage of the at_exit hook, because it would require a forked process.
187
223
  unless ENV["YARD_FENCE_SKIP_AT_EXIT"] == "1"
188
224
  # After YARD completes, restore ASCII braces in generated HTML docs.
189
225
  # This guarantees the published docs (docs/*.html) show and copy normal { }.
@@ -191,3 +227,4 @@ unless ENV["YARD_FENCE_SKIP_AT_EXIT"] == "1"
191
227
  Yard::Fence.postprocess_html_docs
192
228
  end
193
229
  end
230
+ # :nocov:
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yard-fence
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -316,10 +316,10 @@ licenses:
316
316
  - MIT
317
317
  metadata:
318
318
  homepage_uri: https://yard-fence.galtzo.com/
319
- source_code_uri: https://github.com/galtzo-floss/yard-fence/tree/v0.4.0
320
- changelog_uri: https://github.com/galtzo-floss/yard-fence/blob/v0.4.0/CHANGELOG.md
319
+ source_code_uri: https://github.com/galtzo-floss/yard-fence/tree/v0.6.0
320
+ changelog_uri: https://github.com/galtzo-floss/yard-fence/blob/v0.6.0/CHANGELOG.md
321
321
  bug_tracker_uri: https://github.com/galtzo-floss/yard-fence/issues
322
- documentation_uri: https://www.rubydoc.info/gems/yard-fence/0.4.0
322
+ documentation_uri: https://www.rubydoc.info/gems/yard-fence/0.6.0
323
323
  funding_uri: https://github.com/sponsors/pboling
324
324
  wiki_uri: https://github.com/galtzo-floss/yard-fence/wiki
325
325
  news_uri: https://www.railsbling.com/tags/yard-fence
metadata.gz.sig CHANGED
Binary file