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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +28 -1
- data/README.md +1 -1
- data/lib/yard/fence/kramdown_gfm_document.rb +47 -4
- data/lib/yard/fence/version.rb +1 -1
- data/lib/yard/fence.rb +44 -7
- data.tar.gz.sig +0 -0
- metadata +4 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dedaaa231c20409b1d4aedb72ac7b12c0716076eedea5375ed2a232999b2ea7b
|
|
4
|
+
data.tar.gz: f6b65e208d3fc7c9a0b19939949309e6020ad1808cb5378372ac0a6ef2511715
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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.
|
|
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.
|
data/lib/yard/fence/version.rb
CHANGED
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
|
|
88
|
-
line.gsub(INLINE_TICK_FENCE)
|
|
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
|
-
|
|
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
|
+
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.
|
|
320
|
-
changelog_uri: https://github.com/galtzo-floss/yard-fence/blob/v0.
|
|
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.
|
|
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
|