bitclust-core 1.3.0 → 1.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
- data/Gemfile +3 -0
- data/README.md +65 -0
- data/Rakefile +32 -0
- data/data/bitclust/catalog/ja_JP.UTF-8 +19 -1
- data/data/bitclust/searchpage/index.html +90 -0
- data/data/bitclust/template/class +69 -8
- data/data/bitclust/template/function +2 -2
- data/data/bitclust/template/layout +11 -3
- data/data/bitclust/template/method +2 -2
- data/data/bitclust/template/search +1 -1
- data/data/bitclust/template.epub/class +22 -1
- data/data/bitclust/template.epub/function +2 -2
- data/data/bitclust/template.epub/layout +1 -3
- data/data/bitclust/template.epub/method +2 -2
- data/data/bitclust/template.lillia/class +26 -5
- data/data/bitclust/template.lillia/layout +1 -3
- data/data/bitclust/template.lillia/method +2 -2
- data/data/bitclust/template.offline/class +48 -4
- data/data/bitclust/template.offline/function +2 -2
- data/data/bitclust/template.offline/layout +27 -3
- data/data/bitclust/template.offline/method +2 -2
- data/lib/bitclust/app.rb +16 -17
- data/lib/bitclust/capi_converter.rb +20 -0
- data/lib/bitclust/classentry.rb +67 -17
- data/lib/bitclust/compat.rb +2 -2
- data/lib/bitclust/completion.rb +58 -27
- data/lib/bitclust/crossrubyutils.rb +8 -5
- data/lib/bitclust/database.rb +28 -8
- data/lib/bitclust/doc_converter.rb +63 -0
- data/lib/bitclust/docentry.rb +1 -1
- data/lib/bitclust/entity_splitter.rb +221 -0
- data/lib/bitclust/entry.rb +47 -7
- data/lib/bitclust/functiondatabase.rb +13 -1
- data/lib/bitclust/functionentry.rb +5 -1
- data/lib/bitclust/functionreferenceparser.rb +4 -4
- data/lib/bitclust/generators/epub.rb +10 -3
- data/lib/bitclust/htmlutils.rb +1 -1
- data/lib/bitclust/include_graph.rb +375 -0
- data/lib/bitclust/include_pruner.rb +144 -0
- data/lib/bitclust/interface.rb +1 -1
- data/lib/bitclust/libraryentry.rb +8 -7
- data/lib/bitclust/lineinput.rb +7 -5
- data/lib/bitclust/link_checker.rb +179 -0
- data/lib/bitclust/markdown_bridge.rb +186 -0
- data/lib/bitclust/markdown_orchestrator.rb +267 -0
- data/lib/bitclust/markdown_to_rrd.rb +824 -0
- data/lib/bitclust/markdown_tree.rb +217 -0
- data/lib/bitclust/mdcompiler.rb +883 -0
- data/lib/bitclust/mdparser.rb +347 -0
- data/lib/bitclust/messagecatalog.rb +3 -3
- data/lib/bitclust/method_since_calculator.rb +117 -0
- data/lib/bitclust/methoddatabase.rb +124 -14
- data/lib/bitclust/methodentry.rb +125 -7
- data/lib/bitclust/methodid.rb +12 -11
- data/lib/bitclust/methodsignature.rb +10 -4
- data/lib/bitclust/nameutils.rb +41 -22
- data/lib/bitclust/preprocessor.rb +66 -29
- data/lib/bitclust/rdcompiler.rb +195 -95
- data/lib/bitclust/refsdatabase.rb +23 -8
- data/lib/bitclust/reloadable_request_handler.rb +72 -0
- data/lib/bitclust/requesthandler.rb +21 -16
- data/lib/bitclust/ridatabase.rb +10 -4
- data/lib/bitclust/rrd_to_markdown.rb +925 -0
- data/lib/bitclust/rrdparser.rb +196 -30
- data/lib/bitclust/runner.rb +17 -2
- data/lib/bitclust/screen.rb +80 -11
- data/lib/bitclust/search_index_generator.rb +288 -0
- data/lib/bitclust/searcher.rb +69 -20
- data/lib/bitclust/simplesearcher.rb +24 -12
- data/lib/bitclust/subcommand.rb +7 -1
- data/lib/bitclust/subcommands/ancestors_command.rb +9 -2
- data/lib/bitclust/subcommands/checklink_command.rb +54 -0
- data/lib/bitclust/subcommands/chm_command.rb +15 -7
- data/lib/bitclust/subcommands/classes_command.rb +5 -0
- data/lib/bitclust/subcommands/extract_command.rb +5 -0
- data/lib/bitclust/subcommands/htmlfile_command.rb +21 -7
- data/lib/bitclust/subcommands/init_command.rb +4 -1
- data/lib/bitclust/subcommands/list_command.rb +12 -4
- data/lib/bitclust/subcommands/lookup_command.rb +28 -9
- data/lib/bitclust/subcommands/methods_command.rb +10 -2
- data/lib/bitclust/subcommands/methodsince_command.rb +80 -0
- data/lib/bitclust/subcommands/preproc_command.rb +5 -0
- data/lib/bitclust/subcommands/property_command.rb +2 -1
- data/lib/bitclust/subcommands/searchpage_command.rb +102 -0
- data/lib/bitclust/subcommands/server_command.rb +10 -8
- data/lib/bitclust/subcommands/setup_command.rb +24 -10
- data/lib/bitclust/subcommands/statichtml_command.rb +129 -6
- data/lib/bitclust/subcommands/update_command.rb +110 -2
- data/lib/bitclust/syntax_highlighter.rb +48 -4
- data/lib/bitclust/textutils.rb +3 -3
- data/lib/bitclust/version.rb +1 -1
- data/lib/bitclust/version_badges.rb +90 -0
- data/lib/bitclust/whole_file_gate.rb +137 -0
- data/test/test_app.rb +269 -0
- data/test/test_bitclust.rb +2 -0
- data/test/test_capi_converter.rb +91 -0
- data/test/test_class_screen.rb +413 -0
- data/test/test_copy_doc_md.rb +53 -0
- data/test/test_doc_converter.rb +78 -0
- data/test/test_entity_splitter.rb +231 -0
- data/test/test_entry.rb +207 -2
- data/test/test_eol_warning.rb +43 -0
- data/test/test_functionreferenceparser.rb +11 -0
- data/test/test_include_graph.rb +640 -0
- data/test/test_include_pruner.rb +133 -0
- data/test/test_link_checker.rb +114 -0
- data/test/test_lookup_command.rb +135 -0
- data/test/test_markdown_bridge.rb +217 -0
- data/test/test_markdown_orchestrator.rb +478 -0
- data/test/test_markdown_to_rrd.rb +757 -0
- data/test/test_markdown_tree.rb +369 -0
- data/test/test_mdcompiler.rb +1276 -0
- data/test/test_mdparser.rb +538 -0
- data/test/test_method_screen.rb +166 -0
- data/test/test_method_since_calculator.rb +290 -0
- data/test/test_methoddatabase.rb +16 -0
- data/test/test_methodentry.rb +328 -0
- data/test/test_methodsignature.rb +3 -1
- data/test/test_methodsince_command.rb +168 -0
- data/test/test_nameutils.rb +22 -0
- data/test/test_preprocessor.rb +215 -0
- data/test/test_rdcompiler.rb +401 -30
- data/test/test_refsdatabase.rb +32 -0
- data/test/test_rrd_to_markdown.rb +968 -0
- data/test/test_rrdparser.rb +295 -2
- data/test/test_run_ruby_wasm.rb +54 -0
- data/test/test_run_worker_prelude.rb +83 -0
- data/test/test_runner.rb +51 -0
- data/test/test_search_index_generator.rb +513 -0
- data/test/test_search_screen.rb +48 -0
- data/test/test_searcher.rb +132 -0
- data/test/test_searchpage_command.rb +167 -0
- data/test/test_simplesearcher.rb +28 -0
- data/test/test_statichtml_command.rb +204 -0
- data/test/test_syntax_highlighter.rb +60 -0
- data/test/test_whole_file_gate.rb +175 -0
- data/theme/default/js/NOTICE +42 -0
- data/theme/default/js/run-worker.js +111 -0
- data/theme/default/js/run.js +350 -0
- data/theme/default/js/search_controller.js +134 -0
- data/theme/default/js/search_init.js +176 -0
- data/theme/default/js/search_navigation.js +110 -0
- data/theme/default/js/search_page.js +202 -0
- data/theme/default/js/search_ranker.js +244 -0
- data/theme/default/script.js +91 -18
- data/theme/default/search.css +149 -0
- data/theme/default/style.css +193 -21
- data/theme/default/syntax-highlight.css +8 -0
- data/theme/lillia/style.css +45 -1
- metadata +161 -20
- data/README +0 -32
|
@@ -0,0 +1,968 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test/unit'
|
|
4
|
+
|
|
5
|
+
require 'bitclust/rrd_to_markdown'
|
|
6
|
+
require 'bitclust/markdown_to_rrd'
|
|
7
|
+
|
|
8
|
+
class TestRRDToMarkdown < Test::Unit::TestCase
|
|
9
|
+
def convert(rrd)
|
|
10
|
+
BitClust::RRDToMarkdown.convert(rrd)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# M2: 本文バッククォートの GFM 忠実化。
|
|
14
|
+
# GNU 風引用 `token'(rd に適切なマークアップがなかった代替記法)は
|
|
15
|
+
# インラインコードスパン `token` に変換し、それ以外の生バッククォートは
|
|
16
|
+
# \` にエスケープする(GFM で偶発スパンにならないように)
|
|
17
|
+
|
|
18
|
+
def test_gnu_quote_becomes_code_span
|
|
19
|
+
assert_equal "長いオプションは `-` の代わりに `--` で始まります。\n",
|
|
20
|
+
convert("長いオプションは `-' の代わりに `--' で始まります。\n")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_gnu_quote_with_symbols
|
|
24
|
+
assert_equal "`{}` や `**/` は使用できません。\n",
|
|
25
|
+
convert("`{}' や `**/' は使用できません。\n")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_lone_backtick_is_escaped
|
|
29
|
+
# 対にならない・GNU 引用でもない生バッククォートはエスケープ
|
|
30
|
+
assert_equal "コード中の \\` は特別です。\n",
|
|
31
|
+
convert("コード中の ` は特別です。\n")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_backtick_pair_with_space_is_escaped
|
|
35
|
+
# 空白を含む `...' は GNU 引用とみなさない(誤検出防止)
|
|
36
|
+
assert_equal "\\`wget url' のように書きます。\n",
|
|
37
|
+
convert("`wget url' のように書きます。\n")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_no_escape_inside_emlist
|
|
41
|
+
rrd = "//emlist{\nout = `wget url`\n//}\n"
|
|
42
|
+
assert_equal "```\nout = `wget url`\n```\n", convert(rrd)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_auto_code_span_not_escaped
|
|
46
|
+
# __WORD__ の自動コードスパンのバッククォートはエスケープしない
|
|
47
|
+
assert_equal "`__FILE__` を返します。\n",
|
|
48
|
+
convert("__FILE__ を返します。\n")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def test_no_auto_span_inside_md_ref
|
|
52
|
+
# Delegator 型: 参照ターゲット内の __WORD__ はスパン化しない
|
|
53
|
+
# (保護対象は変換後の一重括弧 [m:...]。二重括弧ではない)
|
|
54
|
+
assert_equal "`__FILE__` と [m:Delegator#__getobj__] を参照。\n",
|
|
55
|
+
convert("__FILE__ と [[m:Delegator#__getobj__]] を参照。\n")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_backtick_adjacent_to_auto_span
|
|
59
|
+
# 生バッククォートが __WORD__ に隣接しても双方向で壊れない
|
|
60
|
+
rrd = "`__FILE__ を参照。\n"
|
|
61
|
+
md = convert(rrd)
|
|
62
|
+
assert_equal "\\``__FILE__` を参照。\n", md
|
|
63
|
+
assert_equal rrd, BitClust::MarkdownToRRD.convert(md)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_backtick_in_anchored_heading
|
|
67
|
+
# spec/pattern_matching 型: 見出し内の対バッククォートはエスケープして温存
|
|
68
|
+
rrd = "===[a:x] 付記B: `未定義` の振る舞いの例\n"
|
|
69
|
+
md = convert(rrd)
|
|
70
|
+
assert_equal "### 付記B: \\`未定義\\` の振る舞いの例 {#x}\n", md
|
|
71
|
+
assert_equal rrd, BitClust::MarkdownToRRD.convert(md)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def test_lone_backtick_heading
|
|
75
|
+
# symref 型: 見出しがバッククォート1文字
|
|
76
|
+
rrd = "===[a:ac] `\n"
|
|
77
|
+
md = convert(rrd)
|
|
78
|
+
assert_equal "### \\` {#ac}\n", md
|
|
79
|
+
assert_equal rrd, BitClust::MarkdownToRRD.convert(md)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_gnu_quote_with_backslash_not_spanned
|
|
83
|
+
# DOSISH 型: `\' をスパン化すると `\` が \` エスケープと曖昧になるため除外
|
|
84
|
+
rrd = "2 byte 目が 0x5c(`\\') である文字。\n"
|
|
85
|
+
md = convert(rrd)
|
|
86
|
+
assert_equal "2 byte 目が 0x5c(\\`\\') である文字。\n", md
|
|
87
|
+
assert_equal rrd, BitClust::MarkdownToRRD.convert(md)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def test_dlist_term_with_backslash_not_spanned
|
|
91
|
+
# symref 型: バックスラッシュを含む term はコードスパン化しない
|
|
92
|
+
rrd = ": xxx \\\n 説明。\n"
|
|
93
|
+
md = convert(rrd)
|
|
94
|
+
assert_equal "- **xxx \\**:\n 説明。\n", md
|
|
95
|
+
assert_equal rrd, BitClust::MarkdownToRRD.convert(md)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def test_tex_double_quote_not_spanned
|
|
99
|
+
# rdoc.rd 型: TeX 風二重引用 ``text'' はスパン化せずエスケープで温存
|
|
100
|
+
rrd = "二重引用符: \"text\" もしくは ``text''\n"
|
|
101
|
+
md = convert(rrd)
|
|
102
|
+
assert_equal "二重引用符: \"text\" もしくは \\`\\`text''\n", md
|
|
103
|
+
assert_equal rrd, BitClust::MarkdownToRRD.convert(md)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def test_dlist_description_across_directives
|
|
107
|
+
# pack-template 型: dd の説明は #@ 指令行を透過して続く
|
|
108
|
+
# (ゲート行で打ち切ると後続インデント行がコード扱いになり、
|
|
109
|
+
# 版解決後の描画が RDCompiler と食い違う)
|
|
110
|
+
rrd = ": term\n 説明。\n\#@since 2.0\n 追加説明。\n\#@end\n\n本文。\n"
|
|
111
|
+
md = convert(rrd)
|
|
112
|
+
assert_equal "- **`term`**:\n 説明。\n\#@since 2.0\n 追加説明。\n\#@end\n\n本文。\n", md
|
|
113
|
+
assert_equal rrd, BitClust::MarkdownToRRD.convert(md)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def test_dlist_description_across_samplecode
|
|
117
|
+
# pack-template 型: dd 内の #@samplecode 後の字下げ行も dd 段落継続。
|
|
118
|
+
# 空行スキャンが #@samplecode を透過するとコード本体の行(カラム0)を見て
|
|
119
|
+
# dd を打ち切り、後続段落がトップレベル pre と誤分類される。
|
|
120
|
+
# フェンスは CommonMark で dd にネストするよう内容カラム(2桁)へインデント
|
|
121
|
+
# される(新 MDCompiler の INDENTED_FENCE_RE が取り込む対象)
|
|
122
|
+
rrd = ": w\n\n 説明。\n\n\#@samplecode\n[0].pack(\"w\")\n\#@end\n\n なお、後続の説明。\n"
|
|
123
|
+
md = convert(rrd)
|
|
124
|
+
assert_equal "- **`w`**:\n\n 説明。\n\n ```ruby\n [0].pack(\"w\")\n ```\n\n なお、後続の説明。\n", md
|
|
125
|
+
assert_equal rrd, BitClust::MarkdownToRRD.convert(md)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def test_ulist_followed_by_samplecode
|
|
129
|
+
# news/2_5_0 型: リスト項目直後のカラム0 #@samplecode は項目の外
|
|
130
|
+
# (RDCompiler は ul を閉じて独立ブロックとして描画する)。
|
|
131
|
+
# 項目収集が生のまま素通しすると本体の #=> 行が \# エスケープされ、
|
|
132
|
+
# 版解決後の //emlist 内にエスケープが残って描画が壊れる
|
|
133
|
+
rrd = " * 項目\n\#@samplecode\nCoverage.result\n#=> [1]\n\#@end\n * 次の項目\n"
|
|
134
|
+
md = convert(rrd)
|
|
135
|
+
assert_equal " - 項目\n```ruby\nCoverage.result\n#=> [1]\n```\n - 次の項目\n", md
|
|
136
|
+
assert_equal rrd, BitClust::MarkdownToRRD.convert(md)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def test_param_continuation_after_gate_end
|
|
140
|
+
# fileutils 型: ゲートで分岐した @param の継続行が #@end の直後に来る。
|
|
141
|
+
# nest 0 の #@end(dd より前に開いたゲートの閉じ)も透過しないと
|
|
142
|
+
# 継続行がコードフェンス化され、版解決後の描画が dd 継続にならない
|
|
143
|
+
rrd = "\#@since 2.4.0\n@param options :a が指定できます。\n\#@else\n@param options :b が指定できます。\n\#@end\n [[ref:c:FileUtils#options]]\n"
|
|
144
|
+
md = convert(rrd)
|
|
145
|
+
assert_equal "\#@since 2.4.0\n- **param** `options` -- :a が指定できます。\n\#@else\n- **param** `options` -- :b が指定できます。\n\#@end\n [ref:c:FileUtils#options]\n", md
|
|
146
|
+
assert_equal rrd, BitClust::MarkdownToRRD.convert(md)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def test_gnu_quote_roundtrip
|
|
150
|
+
rrd = "= module M\n\n`PERMUTE', `RETURN_IN_ORDER' という順序形式と ` の話。\n"
|
|
151
|
+
md = convert(rrd)
|
|
152
|
+
assert_equal "# module M\n\n`PERMUTE`, `RETURN_IN_ORDER` という順序形式と \\` の話。\n", md
|
|
153
|
+
assert_equal rrd, BitClust::MarkdownToRRD.convert(md)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Step 1: H1 ヘッダとパススルー
|
|
157
|
+
|
|
158
|
+
def test_module_header
|
|
159
|
+
assert_equal "# module Comparable\n",
|
|
160
|
+
convert("= module Comparable\n")
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def test_class_header_with_superclass
|
|
164
|
+
assert_equal "# class Array < Object\n",
|
|
165
|
+
convert("= class Array < Object\n")
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def test_reopen_header
|
|
169
|
+
assert_equal "# reopen Kernel\n",
|
|
170
|
+
convert("= reopen Kernel\n")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def test_text_passthrough
|
|
174
|
+
rrd = "= module Comparable\n\n説明文。\n"
|
|
175
|
+
expected = "# module Comparable\n\n説明文。\n"
|
|
176
|
+
assert_equal expected, convert(rrd)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def test_directive_passthrough
|
|
180
|
+
rrd = "= module Comparable\n\n\#@since 2.4.0\ntext\n\#@end\n"
|
|
181
|
+
expected = "# module Comparable\n\n\#@since 2.4.0\ntext\n\#@end\n"
|
|
182
|
+
assert_equal expected, convert(rrd)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def test_comment_passthrough
|
|
186
|
+
assert_equal "\#@# これはコメント\n",
|
|
187
|
+
convert("\#@# これはコメント\n")
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def test_include_passthrough
|
|
191
|
+
assert_equal "\#@include(io/buffer.md)\n",
|
|
192
|
+
convert("\#@include(io/buffer.md)\n")
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Step 2: H2 セクションヘッダ
|
|
196
|
+
|
|
197
|
+
def test_h2_instance_methods
|
|
198
|
+
assert_equal "## Instance Methods\n",
|
|
199
|
+
convert("== Instance Methods\n")
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def test_h2_class_methods
|
|
203
|
+
assert_equal "## Class Methods\n",
|
|
204
|
+
convert("== Class Methods\n")
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def test_h2_constants
|
|
208
|
+
assert_equal "## Constants\n",
|
|
209
|
+
convert("== Constants\n")
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Step 3: メソッドシグネチャ + コードブロック
|
|
213
|
+
|
|
214
|
+
def test_instance_method_signature
|
|
215
|
+
# B1: 空白を保持(正規化しない)
|
|
216
|
+
assert_equal "### def ==(other) -> bool\n",
|
|
217
|
+
convert("--- ==(other) -> bool\n")
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def test_method_signature_whitespace_preserved
|
|
221
|
+
assert_equal "### def [](nth) -> object | nil\n",
|
|
222
|
+
convert("--- [](nth) -> object | nil\n")
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def test_method_signature_with_block
|
|
226
|
+
assert_equal "### def new(size) {|index| ... } -> Array\n",
|
|
227
|
+
convert("--- new(size) {|index| ... } -> Array\n")
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def test_multiple_signatures
|
|
231
|
+
rrd = "--- [](nth) -> object | nil\n--- at(nth) -> object | nil\n"
|
|
232
|
+
expected = "### def [](nth) -> object | nil\n### def at(nth) -> object | nil\n"
|
|
233
|
+
assert_equal expected, convert(rrd)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def test_code_block_with_label
|
|
237
|
+
rrd = "\#@samplecode 例\n1 == 1 # => true\n\#@end\n"
|
|
238
|
+
expected = "```ruby title=\"例\"\n1 == 1 # => true\n```\n"
|
|
239
|
+
assert_equal expected, convert(rrd)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def test_code_block_without_label
|
|
243
|
+
rrd = "\#@samplecode\n1 == 1\n\#@end\n"
|
|
244
|
+
expected = "```ruby\n1 == 1\n```\n"
|
|
245
|
+
assert_equal expected, convert(rrd)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def test_samplecode_label_with_backslash
|
|
249
|
+
rrd = "\#@samplecode \"\\n\" を含む例\n1\n\#@end\n"
|
|
250
|
+
expected = "```ruby title=\"\\\"\\\\n\\\" を含む例\"\n1\n```\n"
|
|
251
|
+
assert_equal expected, convert(rrd)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def test_emlist_with_caption_and_lang
|
|
255
|
+
rrd = "//emlist[例][ruby]{\nputs 'hello'\n//}\n"
|
|
256
|
+
expected = "```ruby title=\"例\"\nputs 'hello'\n```\n"
|
|
257
|
+
assert_equal expected, convert(rrd)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def test_emlist_without_caption_with_lang
|
|
261
|
+
rrd = "//emlist[][sh]{\necho hello\n//}\n"
|
|
262
|
+
expected = "```sh\necho hello\n```\n"
|
|
263
|
+
assert_equal expected, convert(rrd)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def test_emlist_without_caption_without_lang
|
|
267
|
+
rrd = "//emlist{\nplain text\n//}\n"
|
|
268
|
+
expected = "```\nplain text\n```\n"
|
|
269
|
+
assert_equal expected, convert(rrd)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def test_emlist_caption_with_double_quote
|
|
273
|
+
rrd = "//emlist[He said \"hello\"][ruby]{\ncode\n//}\n"
|
|
274
|
+
expected = "```ruby title=\"He said \\\"hello\\\"\"\ncode\n```\n"
|
|
275
|
+
assert_equal expected, convert(rrd)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Step 4: ClassName. → self. 変換
|
|
279
|
+
|
|
280
|
+
def test_class_method_keeps_classname
|
|
281
|
+
rrd = "= class Array < Object\n\n--- Array.try_convert(obj) -> Array | nil\n"
|
|
282
|
+
result = convert(rrd)
|
|
283
|
+
assert_match(/### def Array\.try_convert\(obj\)/, result)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def test_module_method_keeps_classname
|
|
287
|
+
rrd = "= module Benchmark\n\n--- Benchmark.measure(label) -> Benchmark::Tms\n"
|
|
288
|
+
result = convert(rrd)
|
|
289
|
+
assert_match(/### def Benchmark\.measure\(label\)/, result)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Step 5: 定数・グローバル変数
|
|
293
|
+
|
|
294
|
+
def test_constant_signature
|
|
295
|
+
assert_equal "### const CR -> String\n",
|
|
296
|
+
convert("--- CR -> String\n")
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def test_constant_without_arrow
|
|
300
|
+
assert_equal "### const START\n",
|
|
301
|
+
convert("--- START\n")
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def test_global_variable_signature
|
|
305
|
+
assert_equal "### gvar $DEBUG -> bool\n",
|
|
306
|
+
convert("--- $DEBUG -> bool\n")
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# module_function
|
|
310
|
+
|
|
311
|
+
def test_module_function_signature
|
|
312
|
+
rrd = "== Module Functions\n\n--- measure(label) -> Benchmark::Tms\n"
|
|
313
|
+
result = convert(rrd)
|
|
314
|
+
assert_match(/### module_function def measure\(label\)/, result)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# ClassName.method はそのまま保持(返り値なしパターン)
|
|
318
|
+
|
|
319
|
+
def test_class_method_keeps_classname_without_nil
|
|
320
|
+
rrd = "= class Array < Object\n\n--- Array.try_convert(obj) -> Array\n"
|
|
321
|
+
result = convert(rrd)
|
|
322
|
+
assert_match(/### def Array\.try_convert\(obj\)/, result)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# 大文字始まりメソッド(( がある)
|
|
326
|
+
|
|
327
|
+
def test_uppercase_method_with_parens
|
|
328
|
+
assert_equal "### def DEBUG=(val)\n",
|
|
329
|
+
convert("--- DEBUG=(val)\n")
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# .# → ? 参照変換
|
|
333
|
+
|
|
334
|
+
def test_module_function_ref
|
|
335
|
+
assert_equal "テスト [m:Kernel?.open]\n",
|
|
336
|
+
convert("テスト [[m:Kernel.#open]]\n")
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# Step 6: @param / @return / @raise
|
|
340
|
+
|
|
341
|
+
def test_param
|
|
342
|
+
assert_equal "- **param** `other` -- 自身と比較したいオブジェクトを指定します。\n",
|
|
343
|
+
convert("@param other 自身と比較したいオブジェクトを指定します。\n")
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def test_raise
|
|
347
|
+
assert_equal "- **raise** `ArgumentError` -- <=> が nil を返したときに発生します。\n",
|
|
348
|
+
convert("@raise ArgumentError <=> が nil を返したときに発生します。\n")
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def test_return
|
|
352
|
+
assert_equal "- **return** -- 説明\n",
|
|
353
|
+
convert("@return 説明\n")
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def test_param_with_inline_ref
|
|
357
|
+
assert_equal "- **param** `obj` -- [c:Range] オブジェクト\n",
|
|
358
|
+
convert("@param obj [[c:Range]] オブジェクト\n")
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def test_raise_with_inline_ref
|
|
362
|
+
assert_equal "- **raise** `TypeError` -- [c:String] 以外\n",
|
|
363
|
+
convert("@raise TypeError [[c:String]] 以外\n")
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def test_return_with_inline_ref
|
|
367
|
+
assert_equal "- **return** -- [c:Array] を返す\n",
|
|
368
|
+
convert("@return [[c:Array]] を返す\n")
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def test_return_alignment_spaces_preserved
|
|
372
|
+
# B2: @return の整列空白を保持
|
|
373
|
+
assert_equal "- **return** -- 説明\n",
|
|
374
|
+
convert("@return 説明\n")
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def test_metadata_continuation_line
|
|
378
|
+
rrd = "@raise ArgumentError self <=> min か、self <=> max が nil を返\n したときに発生します。\n"
|
|
379
|
+
expected = "- **raise** `ArgumentError` -- self <=> min か、self <=> max が nil を返\n したときに発生します。\n"
|
|
380
|
+
assert_equal expected, convert(rrd)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
# Step 7: @see
|
|
384
|
+
|
|
385
|
+
def test_see_single
|
|
386
|
+
# B3: @see は [[→[ 置換のみ、区切りは保持
|
|
387
|
+
assert_equal "- **SEE** [m:String#-@]\n",
|
|
388
|
+
convert("@see [[m:String#-@]]\n")
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def test_see_multiple_with_comma_space
|
|
392
|
+
assert_equal "- **SEE** [m:A], [m:B]\n",
|
|
393
|
+
convert("@see [[m:A]], [[m:B]]\n")
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def test_see_multiple_without_comma_space
|
|
397
|
+
# B3: カンマ後スペースなしも保持
|
|
398
|
+
assert_equal "- **SEE** [m:A],[m:B]\n",
|
|
399
|
+
convert("@see [[m:A]],[[m:B]]\n")
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def test_see_class_ref
|
|
403
|
+
assert_equal "- **SEE** [c:Array]\n",
|
|
404
|
+
convert("@see [[c:Array]]\n")
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def test_see_doc_ref
|
|
408
|
+
assert_equal "- **SEE** [d:spec/m17n]\n",
|
|
409
|
+
convert("@see [[d:spec/m17n]]\n")
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Step 8: YAML front matter
|
|
413
|
+
|
|
414
|
+
def test_include_to_front_matter
|
|
415
|
+
rrd = "= class Array < Object\ninclude Enumerable\n\n説明\n"
|
|
416
|
+
expected = "---\ninclude:\n - Enumerable\n---\n# class Array < Object\n\n説明\n"
|
|
417
|
+
assert_equal expected, convert(rrd)
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def test_multiple_includes_to_front_matter
|
|
421
|
+
rrd = "= class Array < Object\ninclude Enumerable\ninclude Comparable\n\n説明\n"
|
|
422
|
+
expected = "---\ninclude:\n - Enumerable\n - Comparable\n---\n# class Array < Object\n\n説明\n"
|
|
423
|
+
assert_equal expected, convert(rrd)
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def test_extend_to_front_matter
|
|
427
|
+
rrd = "= class Foo < Object\nextend Forwardable\n"
|
|
428
|
+
expected = "---\nextend:\n - Forwardable\n---\n# class Foo < Object\n"
|
|
429
|
+
assert_equal expected, convert(rrd)
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def test_alias_to_front_matter
|
|
433
|
+
rrd = "= class Integer < Numeric\nalias Fixnum\n"
|
|
434
|
+
expected = "---\nalias:\n - Fixnum\n---\n# class Integer < Numeric\n"
|
|
435
|
+
assert_equal expected, convert(rrd)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def test_multi_entity_file_keeps_relations_in_body
|
|
439
|
+
# 複数エンティティを含むファイルは帰属が曖昧なため front matter 化しない(分割前の安全策)
|
|
440
|
+
rrd = "= object ARGF < ARGF.class\n\n説明1\n\n= class ARGF.class < Object\ninclude Enumerable\n\n説明2\n"
|
|
441
|
+
result = convert(rrd)
|
|
442
|
+
refute_match(/\A---/, result)
|
|
443
|
+
assert_match(/# class ARGF\.class < Object\ninclude Enumerable/, result)
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def test_versioned_alias_to_front_matter
|
|
447
|
+
# 版条件(#@)で囲まれたヘッダ関係は front matter 内に #@ を挟んで表現する(§1.6)
|
|
448
|
+
rrd = "= class Integer < Numeric\n\n\#@until 3.2\nalias Fixnum\nalias Bignum\n\#@end\n\n説明\n"
|
|
449
|
+
expected = "---\nalias:\n\#@until 3.2\n - Fixnum\n - Bignum\n\#@end\n---\n# class Integer < Numeric\n\n説明\n"
|
|
450
|
+
assert_equal expected, convert(rrd)
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
# Step 8.3: doc ツリー由来のリテラルエスケープ(refm/doc 対応)
|
|
454
|
+
# 旧 news 等には「参照や見出しに見えるリテラル本文」があり、
|
|
455
|
+
# md 側でエスケープしないと md→rd の復元で誤変換される。
|
|
456
|
+
|
|
457
|
+
def test_literal_bare_ref_lookalike_is_escaped
|
|
458
|
+
# news/1_9_0: [ruby-talk:198440] はリテラル本文(参照ではない)
|
|
459
|
+
rrd = "= class A < Object\n\n無名モジュールの変更\n\\[ruby-talk:198440] を参照。\n"
|
|
460
|
+
md = convert("= class A < Object\n\n無名モジュールの変更\n[ruby-talk:198440] を参照。\n")
|
|
461
|
+
assert_match(/\\\[ruby-talk:198440\]/, md)
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def test_real_refs_are_not_escaped
|
|
465
|
+
md = convert("= class A < Object\n\n[[m:Array#each]] と [[c:String]] を参照。\n")
|
|
466
|
+
assert_match(/\[m:Array#each\] と \[c:String\]/, md)
|
|
467
|
+
refute_match(/\\\[/, md)
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
def test_leading_hash_line_is_escaped
|
|
471
|
+
# news/1.6.0: 行頭 # のリテラル本文(コメントアウトされた変更履歴)
|
|
472
|
+
md = convert("= class A < Object\n\n\# : 2002-08-01 IO#read\n\# 本文。\n")
|
|
473
|
+
assert_match(/^\\# : 2002-08-01 IO#read$/, md)
|
|
474
|
+
assert_match(/^\\# 本文。$/, md)
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def test_anchored_h1
|
|
478
|
+
# spec/rubycmd.rd: =[a:ruby] Rubyの起動
|
|
479
|
+
assert_equal "# Rubyの起動 {#ruby}\n", convert("=[a:ruby] Rubyの起動\n")
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
def test_heading_trailing_space_is_preserved
|
|
483
|
+
# news/1.8.2: 「=== 2004-12-06 」(末尾スペース)
|
|
484
|
+
assert_equal "### 2004-12-06 \n", convert("=== 2004-12-06 \n")
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
# Step 7.5: @param 継続の貪欲さ(RDCompiler の dd_without_p と同規則)
|
|
488
|
+
# 空白を含む行(" \n" や字下げの例示ブロック)は @param 説明の継続。
|
|
489
|
+
# 完全な空行("\n")でのみ停止する(CGI::HtmlExtension#popup_menu 型)。
|
|
490
|
+
|
|
491
|
+
def test_param_continuation_spans_whitespace_only_lines
|
|
492
|
+
rrd = "= class A < Object\n== Instance Methods\n--- m(v) -> String\n\n" \
|
|
493
|
+
"@param v 説明。\n \n 例:\n code\n\n本文。\n"
|
|
494
|
+
md = convert(rrd)
|
|
495
|
+
assert_match(/-- 説明。\n \n 例:\n code\n\n本文。/, md)
|
|
496
|
+
refute_match(/```/, md) # 例示ブロックはフェンス化せず継続のまま
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# Step 7.6: RDCompiler の文脈解釈との整合(描画等価のための変換制約)
|
|
500
|
+
# RDCompiler は (a) dlist の説明を空行を跨いでインデントが続く限り dd とし
|
|
501
|
+
# (リスト風の行もテキスト)、(b) 段落中の「: 」行を段落の継続とし、
|
|
502
|
+
# (c) リスト項目直後のインデント行を深さ不問で項目の継続とする。
|
|
503
|
+
# 変換器はこの解釈を保存する(構造を変えると描画が変わるため)。
|
|
504
|
+
|
|
505
|
+
def test_dlist_description_spans_blank_lines_keeping_list_like_text
|
|
506
|
+
rrd = ": term\n 説明。\n\n * 項目風テキスト1\n * 項目風テキスト2\n\n本文。\n"
|
|
507
|
+
md = convert(rrd)
|
|
508
|
+
assert_match(/- \*\*`term`\*\*:\n 説明。\n\n \* 項目風テキスト1\n \* 項目風テキスト2\n\n本文。/, md)
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
def test_colon_line_inside_paragraph_stays_text
|
|
512
|
+
# news/1.8.5 型: テキスト行直後の「: 」行は RDCompiler では段落の継続
|
|
513
|
+
rrd = "以下のメソッドが追加されました。\n: TCPServer#accept_nonblock [new]\n\n本文。\n"
|
|
514
|
+
md = convert(rrd)
|
|
515
|
+
assert_match(/^: TCPServer#accept_nonblock \[new\]$/, md)
|
|
516
|
+
refute_match(/\*\*/, md)
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
def test_list_continuation_shallower_than_content_indent
|
|
520
|
+
# news/2_6_0 型: 項目より浅いインデントの行も RDCompiler では項目の継続
|
|
521
|
+
rrd = " * 項目の一行目が長くて\n 折り返した継続行。\n\n本文。\n"
|
|
522
|
+
md = convert(rrd)
|
|
523
|
+
assert_match(/ - 項目の一行目が長くて\n 折り返した継続行。\n\n本文。/, md)
|
|
524
|
+
refute_match(/`/, md)
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
# Step 8.35: メタデータ領域の #@# コメント(irb.rd 対応)
|
|
528
|
+
# メタ行の間に挟まる #@# は front matter 内に保持する(YAML コメントになる)。
|
|
529
|
+
# メタ領域の後・本文の前の #@#(rss.rd 型)は従来どおり body に残す。
|
|
530
|
+
|
|
531
|
+
def test_leading_comment_in_metadata_to_front_matter
|
|
532
|
+
# irb.rd: ファイル先頭の #@# Author コメント → category の直前に保持
|
|
533
|
+
rrd = "\#@# Author: Keiju\n\ncategory Development\n\n本文。\n"
|
|
534
|
+
expected = "---\n\#@# Author: Keiju\ncategory: Development\n---\n本文。\n"
|
|
535
|
+
assert_equal expected, convert(rrd)
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
def test_comment_inside_require_block_to_front_matter
|
|
539
|
+
# irb.rd: require 群の途中の #@# 注記 → require ブロック行として保持
|
|
540
|
+
rrd = "category Development\n\n\#@until 2.7.0\nrequire a\n\#@end\nrequire b\n" \
|
|
541
|
+
"\#@# note\nrequire c\n\n本文。\n"
|
|
542
|
+
expected = "---\ncategory: Development\nrequire:\n\#@until 2.7.0\n - a\n\#@end\n" \
|
|
543
|
+
" - b\n\#@# note\n - c\n---\n本文。\n"
|
|
544
|
+
assert_equal expected, convert(rrd)
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
def test_comment_directly_above_require_joins_the_block
|
|
548
|
+
# irb/workspace.rd: 先頭 #@# の直後に空行なしで require が続く
|
|
549
|
+
# (コメントはその require の説明)→ require ブロックの先頭行として保持
|
|
550
|
+
rrd = "\#@# CONTEXT_MODE 2 の場合のみ。\nrequire irb/ws-for-case-2\n\n本文。\n"
|
|
551
|
+
expected = "---\nrequire:\n\#@# CONTEXT_MODE 2 の場合のみ。\n - irb/ws-for-case-2\n---\n本文。\n"
|
|
552
|
+
assert_equal expected, convert(rrd)
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
def test_trailing_comment_before_body_stays_in_body
|
|
556
|
+
# rss.rd 型: メタ確定後の #@# は本文の前置き → body 据え置き(既存動作の維持)
|
|
557
|
+
rrd = "category FileFormat\n\n\#@# = rss\n\n説明。\n"
|
|
558
|
+
expected = "---\ncategory: FileFormat\n---\n\#@# = rss\n\n説明。\n"
|
|
559
|
+
assert_equal expected, convert(rrd)
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
# Step 8.4: メタデータ領域の部分コミット
|
|
563
|
+
# メタデータ(category/require/sublibrary)の後に版分岐つき散文や #@# コメントが
|
|
564
|
+
# 続く場合、nest==0 の空行チェックポイントまでをメタデータとして確定し、
|
|
565
|
+
# 残りは body に渡す(set.rd / thread.rd / rss.rd パターン)。
|
|
566
|
+
|
|
567
|
+
def test_category_lifts_before_versioned_prose
|
|
568
|
+
# set.rd: category Math + 空行 + 版分岐つき散文
|
|
569
|
+
rrd = "category Math\n\n\#@since 3.0\n新しい説明\n\#@else\n古い説明\n\#@end\n\n本文。\n"
|
|
570
|
+
expected = "---\ncategory: Math\n---\n" \
|
|
571
|
+
"\#@since 3.0\n新しい説明\n\#@else\n古い説明\n\#@end\n\n本文。\n"
|
|
572
|
+
assert_equal expected, convert(rrd)
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
def test_category_lifts_before_preprocessor_comment
|
|
576
|
+
# rss.rd: category FileFormat + 空行 + #@# コメント
|
|
577
|
+
rrd = "category FileFormat\n\n\#@# = rss\n\n説明。\n"
|
|
578
|
+
expected = "---\ncategory: FileFormat\n---\n\#@# = rss\n\n説明。\n"
|
|
579
|
+
assert_equal expected, convert(rrd)
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
def test_metadata_without_blank_checkpoint_stays_in_body
|
|
583
|
+
# 空行チェックポイントが無い(category 直後にゲート)場合は従来どおり据え置き
|
|
584
|
+
# (md→rd の再生成が category の後に空行を足すため、コミットすると一致しない)
|
|
585
|
+
rrd = "category Math\n\#@since 3.0\n説明\n\#@end\n"
|
|
586
|
+
assert_equal "category Math\n\#@since 3.0\n説明\n\#@end\n", convert(rrd)
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
# Step 8.5: extra front matter 注入(クロスファイル・オーケストレータ用)
|
|
590
|
+
# library 所属・構造 since/until はファイル単体からは分からないため、
|
|
591
|
+
# include グラフを解析したオーケストレータが注入する。
|
|
592
|
+
|
|
593
|
+
def test_extra_front_matter_library
|
|
594
|
+
rrd = "= class Bar < Object\n\n説明\n"
|
|
595
|
+
expected = "---\nlibrary: foo\n---\n# class Bar < Object\n\n説明\n"
|
|
596
|
+
assert_equal expected,
|
|
597
|
+
BitClust::RRDToMarkdown.convert(rrd, extra_front_matter: { "library" => "foo" })
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
def test_extra_front_matter_ordering_with_collected_relations
|
|
601
|
+
# §1.7 の順序: library → include/extend/alias → since/until
|
|
602
|
+
rrd = "= class Bar < Object\ninclude Enumerable\n\n説明\n"
|
|
603
|
+
expected = "---\nlibrary: foo\ninclude:\n - Enumerable\nsince: \"3.2\"\n---\n" \
|
|
604
|
+
"# class Bar < Object\n\n説明\n"
|
|
605
|
+
assert_equal expected,
|
|
606
|
+
BitClust::RRDToMarkdown.convert(rrd,
|
|
607
|
+
extra_front_matter: { "library" => "foo", "since" => "3.2" })
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
def test_extra_front_matter_accepts_symbol_keys
|
|
611
|
+
# IncludeGraph::Scope#gate は {since:, until:} のシンボルキーを返す
|
|
612
|
+
rrd = "= class Bar < Object\n"
|
|
613
|
+
expected = "---\nlibrary: foo\nuntil: \"4.0\"\n---\n# class Bar < Object\n"
|
|
614
|
+
assert_equal expected,
|
|
615
|
+
BitClust::RRDToMarkdown.convert(rrd,
|
|
616
|
+
extra_front_matter: { library: "foo", until: "4.0" })
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
def test_extra_front_matter_gated_library_list
|
|
620
|
+
# 多重所属(IncludeGraph#front_matter_map のリスト形式)は
|
|
621
|
+
# ゲート付きリストとして emit する(MARKUP_SPEC §1.2/§1.6)
|
|
622
|
+
rrd = "= class Mutex < Object\n\n説明\n"
|
|
623
|
+
expected = "---\n" \
|
|
624
|
+
"library:\n" \
|
|
625
|
+
" - _builtin\n" \
|
|
626
|
+
"\#@until 1.9.1\n" \
|
|
627
|
+
" - thread\n" \
|
|
628
|
+
"\#@end\n" \
|
|
629
|
+
"---\n" \
|
|
630
|
+
"# class Mutex < Object\n\n説明\n"
|
|
631
|
+
assert_equal expected,
|
|
632
|
+
BitClust::RRDToMarkdown.convert(rrd, extra_front_matter: {
|
|
633
|
+
"library" => [
|
|
634
|
+
{ "name" => "_builtin" },
|
|
635
|
+
{ "name" => "thread", "until" => "1.9.1" }
|
|
636
|
+
] })
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
def test_extra_front_matter_gated_library_list_with_since_and_both_bounds
|
|
640
|
+
rrd = "= class Foo < Object\n"
|
|
641
|
+
expected = "---\n" \
|
|
642
|
+
"library:\n" \
|
|
643
|
+
"\#@since 2.3.0\n" \
|
|
644
|
+
" - _builtin\n" \
|
|
645
|
+
"\#@end\n" \
|
|
646
|
+
"\#@since 1.9.1\n" \
|
|
647
|
+
"\#@until 2.3.0\n" \
|
|
648
|
+
" - thread\n" \
|
|
649
|
+
"\#@end\n" \
|
|
650
|
+
"\#@end\n" \
|
|
651
|
+
"---\n" \
|
|
652
|
+
"# class Foo < Object\n"
|
|
653
|
+
assert_equal expected,
|
|
654
|
+
BitClust::RRDToMarkdown.convert(rrd, extra_front_matter: {
|
|
655
|
+
"library" => [
|
|
656
|
+
{ "name" => "_builtin", "since" => "2.3.0" },
|
|
657
|
+
{ "name" => "thread", "since" => "1.9.1", "until" => "2.3.0" }
|
|
658
|
+
] })
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
def test_gated_metadata_blocks_of_different_kinds
|
|
662
|
+
# regate_metadata の出力形: require と sublibrary が別々の単一種ゲート
|
|
663
|
+
# ブロックに分かれている(rubygems.rd の据え置きゲート)。
|
|
664
|
+
# それぞれの front matter キーへゲートごと振り分ける
|
|
665
|
+
rrd = "\#@since 1.9.1\nrequire a\nrequire b\n\#@end\n\n" \
|
|
666
|
+
"\#@since 1.9.1\nsublibrary s\n\#@end\n\n" \
|
|
667
|
+
"\#@since 1.9.1\n本文。\n\#@end\n"
|
|
668
|
+
expected = "---\n" \
|
|
669
|
+
"require:\n" \
|
|
670
|
+
"\#@since 1.9.1\n" \
|
|
671
|
+
" - a\n" \
|
|
672
|
+
" - b\n" \
|
|
673
|
+
"\#@end\n" \
|
|
674
|
+
"sublibrary:\n" \
|
|
675
|
+
"\#@since 1.9.1\n" \
|
|
676
|
+
" - s\n" \
|
|
677
|
+
"\#@end\n" \
|
|
678
|
+
"---\n" \
|
|
679
|
+
"\#@since 1.9.1\n本文。\n\#@end\n"
|
|
680
|
+
assert_equal expected, BitClust::RRDToMarkdown.convert(rrd)
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
def test_gated_category_becomes_gated_scalar
|
|
684
|
+
# cmath.rd の据え置きゲート: category はスカラのままゲート行で挟む
|
|
685
|
+
# (値の差し替えではなく単一値の存在ゲートなので生 YAML でも重複キーにならない)
|
|
686
|
+
rrd = "\#@since 1.9.1\ncategory Math\n\#@end\n\n" \
|
|
687
|
+
"\#@since 1.9.1\n本文。\n\#@end\n"
|
|
688
|
+
expected = "---\n" \
|
|
689
|
+
"\#@since 1.9.1\n" \
|
|
690
|
+
"category: Math\n" \
|
|
691
|
+
"\#@end\n" \
|
|
692
|
+
"---\n" \
|
|
693
|
+
"\#@since 1.9.1\n本文。\n\#@end\n"
|
|
694
|
+
assert_equal expected, BitClust::RRDToMarkdown.convert(rrd)
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
def test_extra_front_matter_on_multi_entity_file
|
|
698
|
+
# library はファイル単位の情報なので、マルチエンティティでも曖昧なく注入できる
|
|
699
|
+
# (ファイル内の全エンティティは同一ライブラリ所属。ヘッダ関係は body 据え置きのまま)
|
|
700
|
+
rrd = "= object ARGF < ARGF.class\n\n説明1\n\n= class ARGF.class < Object\ninclude Enumerable\n\n説明2\n"
|
|
701
|
+
result = BitClust::RRDToMarkdown.convert(rrd,
|
|
702
|
+
extra_front_matter: { "library" => "_builtin" })
|
|
703
|
+
assert result.start_with?("---\nlibrary: _builtin\n---\n"), result[0, 60].inspect
|
|
704
|
+
assert_match(/include Enumerable/, result)
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
def test_extra_front_matter_rejects_unknown_keys
|
|
708
|
+
assert_raise(ArgumentError) do
|
|
709
|
+
BitClust::RRDToMarkdown.convert("= class Bar < Object\n",
|
|
710
|
+
extra_front_matter: { "libary" => "typo" })
|
|
711
|
+
end
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
def test_extra_front_matter_roundtrip_drops_injected_keys
|
|
715
|
+
# 注入キーはオーケストレータ由来の横断情報なので、md→rd では body に現れず
|
|
716
|
+
# 元の RD がそのまま復元される
|
|
717
|
+
require 'bitclust/markdown_to_rrd'
|
|
718
|
+
rrd = "= class Bar < Object\ninclude Enumerable\n\n説明\n"
|
|
719
|
+
md = BitClust::RRDToMarkdown.convert(rrd,
|
|
720
|
+
extra_front_matter: { "library" => "foo", "since" => "3.2" })
|
|
721
|
+
assert_equal rrd, BitClust::MarkdownToRRD.convert(md)
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
def test_extra_front_matter_name_emitted_after_type
|
|
725
|
+
# ファイル名の大文字小文字衝突回避で改名されたライブラリファイル
|
|
726
|
+
# (rdoc/rdoc.lib.md)の名前保持用。順序は type → name(§1.7)
|
|
727
|
+
rrd = "x/x の説明。\n"
|
|
728
|
+
expected = "---\ntype: library\nname: x/x\n---\nx/x の説明。\n"
|
|
729
|
+
assert_equal expected,
|
|
730
|
+
BitClust::RRDToMarkdown.convert(rrd,
|
|
731
|
+
extra_front_matter: { "type" => "library", "name" => "x/x" })
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
def test_mixed_gated_alias_and_plain_include_to_front_matter
|
|
735
|
+
# net/Net__HTTPServerException(分割後): 版条件つき alias と素の include の混在。
|
|
736
|
+
# #@ ブロックごとに単一種なら front matter 化できる
|
|
737
|
+
rrd = "= class A < B\n\#@since 2.6.0\nalias C\n\#@end\ninclude D\n\n説明\n"
|
|
738
|
+
expected = "---\ninclude:\n - D\nalias:\n\#@since 2.6.0\n - C\n\#@end\n---\n" \
|
|
739
|
+
"# class A < B\n\n説明\n"
|
|
740
|
+
assert_equal expected, convert(rrd)
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
def test_mixed_kinds_within_one_gate_block_stay_in_body
|
|
744
|
+
# 1つの #@ ブロック内に複数種はデータ上存在しないので据え置きのまま
|
|
745
|
+
rrd = "= class A < B\n\#@since 2.6.0\nalias C\ninclude D\n\#@end\n\n説明\n"
|
|
746
|
+
assert_equal "# class A < B\n\#@since 2.6.0\nalias C\ninclude D\n\#@end\n\n説明\n",
|
|
747
|
+
convert(rrd)
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
def test_versioned_include_if_to_front_matter
|
|
751
|
+
rrd = "= class File < IO\n\#@if (version < \"1.8.0\")\ninclude File::Constants\n\#@end\n\n説明\n"
|
|
752
|
+
expected = "---\ninclude:\n\#@if (version < \"1.8.0\")\n - File::Constants\n\#@end\n---\n# class File < IO\n\n説明\n"
|
|
753
|
+
assert_equal expected, convert(rrd)
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
def test_versioned_prose_not_treated_as_header_relation
|
|
757
|
+
# #@ が本文(関係でない)を包む場合はヘッダ関係扱いしない
|
|
758
|
+
rrd = "= module Foo\n\n\#@since 3.0\n本文\n\#@end\n"
|
|
759
|
+
expected = "# module Foo\n\n\#@since 3.0\n本文\n\#@end\n"
|
|
760
|
+
assert_equal expected, convert(rrd)
|
|
761
|
+
end
|
|
762
|
+
|
|
763
|
+
def test_front_matter_category
|
|
764
|
+
rrd = "category Network\n\nライブラリの説明\n"
|
|
765
|
+
result = convert(rrd)
|
|
766
|
+
assert_match(/\A---\ncategory: Network\n---\n/, result)
|
|
767
|
+
assert_match(/ライブラリの説明/, result)
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
def test_front_matter_require
|
|
771
|
+
rrd = "require cgi/core\nrequire cgi/cookie\n\nライブラリの説明\n"
|
|
772
|
+
result = convert(rrd)
|
|
773
|
+
assert_match(/require:\n - cgi\/core\n - cgi\/cookie\n/, result)
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
def test_front_matter_sublibrary
|
|
777
|
+
rrd = "sublibrary json/ext\n\nライブラリの説明\n"
|
|
778
|
+
result = convert(rrd)
|
|
779
|
+
assert_match(/sublibrary:\n - json\/ext\n/, result)
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
def test_front_matter_category_and_require
|
|
783
|
+
rrd = "category Network\n\nrequire socket\n\nライブラリの説明\n"
|
|
784
|
+
result = convert(rrd)
|
|
785
|
+
assert_match(/\A---\ncategory: Network\nrequire:\n - socket\n---\n/, result)
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
def test_versioned_require_to_front_matter
|
|
789
|
+
rrd = "category Network\n\n\#@since 1.9.1\nrequire cgi/core\nrequire cgi/cookie\n\#@end\n\n説明\n"
|
|
790
|
+
expected = "---\ncategory: Network\nrequire:\n\#@since 1.9.1\n - cgi/core\n - cgi/cookie\n\#@end\n---\n説明\n"
|
|
791
|
+
assert_equal expected, convert(rrd)
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
def test_file_spanning_version_gate_metadata_stays_in_body
|
|
795
|
+
# ファイル全体を包む #@(構造的ゲート)はメタを front matter にしない(項目1で対応)
|
|
796
|
+
rrd = "\#@since 1.9.1\n\ncategory Math\n\n説明\n\#@end\n"
|
|
797
|
+
result = convert(rrd)
|
|
798
|
+
refute_match(/\A---/, result)
|
|
799
|
+
assert_match(/\A\#@since 1.9.1\n/, result)
|
|
800
|
+
end
|
|
801
|
+
|
|
802
|
+
def test_no_front_matter_when_no_metadata
|
|
803
|
+
rrd = "= module Comparable\n\n説明\n"
|
|
804
|
+
result = convert(rrd)
|
|
805
|
+
assert_match(/\A# module Comparable\n/, result)
|
|
806
|
+
refute_match(/\A---/, result)
|
|
807
|
+
end
|
|
808
|
+
|
|
809
|
+
def test_include_after_blank_line_to_front_matter
|
|
810
|
+
rrd = "= class String < Object\n\ninclude Comparable\n\n説明\n"
|
|
811
|
+
expected = "---\ninclude:\n - Comparable\n---\n# class String < Object\n\n説明\n"
|
|
812
|
+
assert_equal expected, convert(rrd)
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
# Step 9: アンカー付き見出し・H4
|
|
816
|
+
|
|
817
|
+
def test_h3_with_anchor
|
|
818
|
+
assert_equal "### 破壊的な変更 {#mutable}\n",
|
|
819
|
+
convert("===[a:mutable] 破壊的な変更\n")
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
def test_h4_heading
|
|
823
|
+
assert_equal "#### 小見出し\n",
|
|
824
|
+
convert("==== 小見出し\n")
|
|
825
|
+
end
|
|
826
|
+
|
|
827
|
+
def test_h3_without_anchor
|
|
828
|
+
assert_equal "### 読み込み\n",
|
|
829
|
+
convert("=== 読み込み\n")
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
# Step 10: クロスリファレンス(インライン)
|
|
833
|
+
|
|
834
|
+
def test_inline_class_ref
|
|
835
|
+
assert_equal "[c:String] を参照。\n",
|
|
836
|
+
convert("[[c:String]] を参照。\n")
|
|
837
|
+
end
|
|
838
|
+
|
|
839
|
+
def test_inline_method_ref
|
|
840
|
+
assert_equal "[m:Array#each] と同じ。\n",
|
|
841
|
+
convert("[[m:Array#each]] と同じ。\n")
|
|
842
|
+
end
|
|
843
|
+
|
|
844
|
+
def test_inline_multiple_refs
|
|
845
|
+
assert_equal "[c:Array] と [c:Hash] を使う。\n",
|
|
846
|
+
convert("[[c:Array]] と [[c:Hash]] を使う。\n")
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
def test_inline_bracket_method_ref
|
|
850
|
+
# A2: [[m:Hash#[] ]] → [m:Hash#\[\]]
|
|
851
|
+
assert_equal "- **SEE** [m:Hash#\\[\\]]\n",
|
|
852
|
+
convert("@see [[m:Hash#[] ]]\n")
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
def test_inline_ref_with_trailing_bracket
|
|
856
|
+
# [[m:Math::PI]]] → [m:Math::PI]]
|
|
857
|
+
assert_equal "ら [-self, [m:Math::PI]] を返します。\n",
|
|
858
|
+
convert("ら [-self, [[m:Math::PI]]] を返します。\n")
|
|
859
|
+
end
|
|
860
|
+
|
|
861
|
+
def test_inline_ref_not_in_code_block
|
|
862
|
+
rrd = "\#@samplecode\n[[c:String]]\n\#@end\n"
|
|
863
|
+
expected = "```ruby\n[[c:String]]\n```\n"
|
|
864
|
+
assert_equal expected, convert(rrd)
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
# Step 11: リスト変換
|
|
868
|
+
|
|
869
|
+
def test_unordered_list
|
|
870
|
+
# B4: 元のインデント幅を保持
|
|
871
|
+
rrd = " * item1\n * item2\n"
|
|
872
|
+
expected = " - item1\n - item2\n"
|
|
873
|
+
assert_equal expected, convert(rrd)
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
def test_unordered_list_2space_indent
|
|
877
|
+
rrd = " * item1\n * item2\n"
|
|
878
|
+
expected = " - item1\n - item2\n"
|
|
879
|
+
assert_equal expected, convert(rrd)
|
|
880
|
+
end
|
|
881
|
+
|
|
882
|
+
def test_ordered_list
|
|
883
|
+
rrd = " (1) 最初\n (2) 次\n (3) 最後\n"
|
|
884
|
+
expected = " 1. 最初\n 2. 次\n 3. 最後\n"
|
|
885
|
+
assert_equal expected, convert(rrd)
|
|
886
|
+
end
|
|
887
|
+
|
|
888
|
+
def test_text_number_to_bold
|
|
889
|
+
rrd = "1. テキスト\n"
|
|
890
|
+
expected = "**1.** テキスト\n"
|
|
891
|
+
assert_equal expected, convert(rrd)
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
# C1: インデントコードブロック +3
|
|
895
|
+
|
|
896
|
+
def test_indented_code_block
|
|
897
|
+
rrd = "説明\n code line 1\n code line 2\n\n次の段落\n"
|
|
898
|
+
expected = "説明\n`````\ncode line 1\ncode line 2\n`````\n\n次の段落\n"
|
|
899
|
+
assert_equal expected, convert(rrd)
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
def test_indented_code_block_8spaces
|
|
903
|
+
rrd = "説明\n code\n\n"
|
|
904
|
+
expected = "説明\n```````````\ncode\n```````````\n\n"
|
|
905
|
+
assert_equal expected, convert(rrd)
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
# Step 12: 定義リスト
|
|
909
|
+
|
|
910
|
+
def test_definition_list
|
|
911
|
+
rrd = ": type\n Content-Type header\n"
|
|
912
|
+
expected = "- **`type`**:\n Content-Type header\n"
|
|
913
|
+
assert_equal expected, convert(rrd)
|
|
914
|
+
end
|
|
915
|
+
|
|
916
|
+
def test_definition_list_multiple
|
|
917
|
+
rrd = ": type\n Content-Type header\n: charset\n Character set\n"
|
|
918
|
+
expected = "- **`type`**:\n Content-Type header\n- **`charset`**:\n Character set\n"
|
|
919
|
+
assert_equal expected, convert(rrd)
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
def test_definition_list_multiline
|
|
923
|
+
rrd = ": [ ]\n 鈎括弧内のいずれかの文字と一致します。- でつな\n がれた文字は範囲を表します。\n"
|
|
924
|
+
expected = "- **`[ ]`**:\n 鈎括弧内のいずれかの文字と一致します。- でつな\n がれた文字は範囲を表します。\n"
|
|
925
|
+
assert_equal expected, convert(rrd)
|
|
926
|
+
end
|
|
927
|
+
|
|
928
|
+
# __WORD__ コードスパン変換
|
|
929
|
+
|
|
930
|
+
def test_add_code_spans_basic
|
|
931
|
+
assert_match(/`__END__`/, convert("__END__ を使う\n"))
|
|
932
|
+
end
|
|
933
|
+
|
|
934
|
+
# Step 13: 統合テスト — Comparable ラウンドトリップ
|
|
935
|
+
|
|
936
|
+
def test_comparable_roundtrip
|
|
937
|
+
rrd_path = File.expand_path('../../doctree/refm/api/src/_builtin/Comparable', __dir__)
|
|
938
|
+
omit 'doctree not found' unless File.exist?(rrd_path)
|
|
939
|
+
|
|
940
|
+
require 'bitclust/markdown_to_rrd'
|
|
941
|
+
require 'bitclust/methoddatabase'
|
|
942
|
+
require 'bitclust/rrdparser'
|
|
943
|
+
|
|
944
|
+
params = {"version" => "3.4"}
|
|
945
|
+
|
|
946
|
+
# Original → parse
|
|
947
|
+
db1 = BitClust::MethodDatabase.dummy(params)
|
|
948
|
+
lib1 = BitClust::RRDParser.new(db1).parse_file(rrd_path, "_builtin", params)
|
|
949
|
+
|
|
950
|
+
# RD → MD → RD → parse
|
|
951
|
+
rrd = File.read(rrd_path)
|
|
952
|
+
md = BitClust::RRDToMarkdown.convert(rrd)
|
|
953
|
+
rrd2 = BitClust::MarkdownToRRD.convert(md)
|
|
954
|
+
|
|
955
|
+
require 'tempfile'
|
|
956
|
+
Tempfile.create(['comparable', '.rrd']) do |f|
|
|
957
|
+
f.write(rrd2)
|
|
958
|
+
f.flush
|
|
959
|
+
db2 = BitClust::MethodDatabase.dummy(params)
|
|
960
|
+
lib2 = BitClust::RRDParser.new(db2).parse_file(f.path, "_builtin", params)
|
|
961
|
+
|
|
962
|
+
# 同じクラス/メソッド構造であることを検証
|
|
963
|
+
orig_methods = lib1.classes.first.entries.map(&:names)
|
|
964
|
+
roundtrip_methods = lib2.classes.first.entries.map(&:names)
|
|
965
|
+
assert_equal orig_methods, roundtrip_methods
|
|
966
|
+
end
|
|
967
|
+
end
|
|
968
|
+
end
|