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,1276 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bitclust/mdcompiler'
|
|
4
|
+
require 'bitclust/rdcompiler'
|
|
5
|
+
require 'bitclust/rrd_to_markdown'
|
|
6
|
+
require 'bitclust/database'
|
|
7
|
+
require 'bitclust/methoddatabase'
|
|
8
|
+
require 'bitclust/screen'
|
|
9
|
+
require 'test/unit'
|
|
10
|
+
require 'test/unit/rr'
|
|
11
|
+
require 'stringio'
|
|
12
|
+
|
|
13
|
+
# MDCompiler: Markdown ソース → HTML のネイティブコンパイラ(フェーズ3 M1)。
|
|
14
|
+
# M1 の不変条件: 変換器が生成する md に対して、対応する rd を RDCompiler に
|
|
15
|
+
# かけた場合と同一の HTML を出力する(等価モード)。
|
|
16
|
+
# GFM 拡張(テーブル・コードスパンの <code> 化等)は M2 で解禁する。
|
|
17
|
+
class TestMDCompiler < Test::Unit::TestCase
|
|
18
|
+
def setup
|
|
19
|
+
@dummy = 'dummy'
|
|
20
|
+
@u = BitClust::URLMapper.new(Hash.new { @dummy })
|
|
21
|
+
@db = BitClust::MethodDatabase.dummy("version" => "2.0.0")
|
|
22
|
+
@md = BitClust::MDCompiler.new(@u, 1, { :database => @db })
|
|
23
|
+
@rd = BitClust::RDCompiler.new(@u, 1, { :database => @db })
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def compile_method(compiler, src)
|
|
27
|
+
method_entry = Object.new
|
|
28
|
+
mock(method_entry).source { src }
|
|
29
|
+
mock(method_entry).index_id.any_times { "dummy" }
|
|
30
|
+
mock(method_entry).defined?.any_times { true }
|
|
31
|
+
mock(method_entry).id.any_times { "String/i.index._builtin" }
|
|
32
|
+
mock(method_entry).names.any_times { [] }
|
|
33
|
+
mock(method_entry).since_map.any_times { {} }
|
|
34
|
+
mock(method_entry).until_map.any_times { {} }
|
|
35
|
+
compiler.compile_method(method_entry)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# rd ソースを両経路(rd 直接 / md 変換後)でコンパイルして等価を確認する
|
|
39
|
+
def assert_equivalent_method(rd_src)
|
|
40
|
+
md_src = BitClust::RRDToMarkdown.convert(rd_src)
|
|
41
|
+
assert_equal compile_method(@rd, rd_src), compile_method(@md, md_src),
|
|
42
|
+
"md source:\n#{md_src}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def assert_equivalent_doc(rd_src)
|
|
46
|
+
md_src = BitClust::RRDToMarkdown.convert(rd_src)
|
|
47
|
+
assert_equal @rd.compile(rd_src), @md.compile(md_src), "md source:\n#{md_src}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def capture_stderr
|
|
51
|
+
orig = $stderr
|
|
52
|
+
$stderr = StringIO.new
|
|
53
|
+
yield
|
|
54
|
+
$stderr.string
|
|
55
|
+
ensure
|
|
56
|
+
$stderr = orig
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# ---- メソッドエントリ ----
|
|
60
|
+
|
|
61
|
+
def test_method_signature_and_paragraph
|
|
62
|
+
assert_equivalent_method <<~RD
|
|
63
|
+
--- index(val) -> Integer
|
|
64
|
+
|
|
65
|
+
説明文。
|
|
66
|
+
2行目。
|
|
67
|
+
RD
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_multiple_signatures
|
|
71
|
+
assert_equivalent_method <<~RD
|
|
72
|
+
--- index(val) -> Integer
|
|
73
|
+
--- index {|item| ... } -> Integer
|
|
74
|
+
|
|
75
|
+
説明。
|
|
76
|
+
RD
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def test_param_return_raise
|
|
80
|
+
assert_equivalent_method <<~RD
|
|
81
|
+
--- index(val) -> Integer
|
|
82
|
+
|
|
83
|
+
説明。
|
|
84
|
+
|
|
85
|
+
@param val 探す値。
|
|
86
|
+
継続行。
|
|
87
|
+
@return 位置。
|
|
88
|
+
@raise TypeError 型が合わないとき。
|
|
89
|
+
RD
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def test_see
|
|
93
|
+
assert_equivalent_method <<~RD
|
|
94
|
+
--- index(val) -> Integer
|
|
95
|
+
|
|
96
|
+
説明。
|
|
97
|
+
|
|
98
|
+
@see [[m:Array#rindex]], [[m:Array#find_index]]
|
|
99
|
+
RD
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def test_inline_references
|
|
103
|
+
assert_equivalent_method <<~RD
|
|
104
|
+
--- index(val) -> Integer
|
|
105
|
+
|
|
106
|
+
[[m:Array#rindex]] と [[c:String]] を参照。
|
|
107
|
+
[[m:Hash#[] ]] のような括弧メソッドも。
|
|
108
|
+
RD
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def test_inline_ref_with_hyphenated_fragment
|
|
112
|
+
# [ref:d:page#frag] のフラグメントはハイフンを含められる
|
|
113
|
+
# (doctree/manual の glossary.md 用語アンカー等、rd 直接/md 変換後で等価)
|
|
114
|
+
assert_equivalent_method <<~RD
|
|
115
|
+
--- index(val) -> Integer
|
|
116
|
+
|
|
117
|
+
[[ref:d:hoge/bar#thread-safe]] を参照。
|
|
118
|
+
RD
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def test_code_block_from_emlist
|
|
122
|
+
assert_equivalent_method <<~RD
|
|
123
|
+
--- index(val) -> Integer
|
|
124
|
+
|
|
125
|
+
例:
|
|
126
|
+
|
|
127
|
+
//emlist[例][ruby]{
|
|
128
|
+
[1, 2].index(2) # => 1
|
|
129
|
+
//}
|
|
130
|
+
RD
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def test_plain_emlist_block
|
|
134
|
+
assert_equivalent_method <<~RD
|
|
135
|
+
--- index(val) -> Integer
|
|
136
|
+
|
|
137
|
+
//emlist{
|
|
138
|
+
plain text art
|
|
139
|
+
//}
|
|
140
|
+
RD
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def test_indented_code_block
|
|
144
|
+
assert_equivalent_method <<~RD
|
|
145
|
+
--- index(val) -> Integer
|
|
146
|
+
|
|
147
|
+
例:
|
|
148
|
+
|
|
149
|
+
[1, 2].index(2)
|
|
150
|
+
# => 1
|
|
151
|
+
|
|
152
|
+
続き。
|
|
153
|
+
RD
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def test_indented_code_with_tabs
|
|
157
|
+
# Kernel#mkmf 型: タブは元のカラム位置で展開される(detab はデデント前)
|
|
158
|
+
assert_equivalent_method "--- mkmf -> ()\n\n説明。\n\n -d ARGS\trun dir_config\n -h ARGS\trun have_header\n"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def test_item_list
|
|
162
|
+
assert_equivalent_method <<~RD
|
|
163
|
+
--- index(val) -> Integer
|
|
164
|
+
|
|
165
|
+
説明:
|
|
166
|
+
|
|
167
|
+
* 項目1
|
|
168
|
+
* 項目2
|
|
169
|
+
継続行。
|
|
170
|
+
RD
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def test_dlist
|
|
174
|
+
assert_equivalent_method <<~RD
|
|
175
|
+
--- index(val) -> Integer
|
|
176
|
+
|
|
177
|
+
: term1
|
|
178
|
+
説明1。
|
|
179
|
+
: term2
|
|
180
|
+
説明2。
|
|
181
|
+
RD
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def test_dlist_term_anchor
|
|
185
|
+
# 用語の後ろの {#id} は dt のアンカー id になる(用語集の各用語リンク用, rurema/doctree#2634)
|
|
186
|
+
html = @md.compile("- **アリティー**: {#arity}\n- **`arity`**:\n 仮引数の数。\n")
|
|
187
|
+
assert_include(html, '<dt id="arity">アリティー</dt>')
|
|
188
|
+
# {#id} が無い項目には id は付かない
|
|
189
|
+
assert_not_include(html, '<dt id="arity">arity')
|
|
190
|
+
# 通常の dlist は従来どおり id 無し
|
|
191
|
+
plain = @md.compile("- **foo**:\n bar\n")
|
|
192
|
+
assert_include(plain, '<dt>foo</dt>')
|
|
193
|
+
assert_not_include(plain, '<dt id')
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def test_entry_heading
|
|
197
|
+
assert_equivalent_method <<~RD
|
|
198
|
+
--- index(val) -> Integer
|
|
199
|
+
|
|
200
|
+
説明。
|
|
201
|
+
|
|
202
|
+
===[a:anchor] 深掘り
|
|
203
|
+
|
|
204
|
+
本文。
|
|
205
|
+
RD
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def test_entry_heading_with_hyphenated_anchor
|
|
209
|
+
# ハイフン入りアンカー(doctree/manual の glossary.md 用語アンカー等)でも
|
|
210
|
+
# rd 直接コンパイルと md 変換後コンパイルが等価であること
|
|
211
|
+
assert_equivalent_method <<~RD
|
|
212
|
+
--- index(val) -> Integer
|
|
213
|
+
|
|
214
|
+
説明。
|
|
215
|
+
|
|
216
|
+
===[a:thread-safe] スレッドセーフ
|
|
217
|
+
|
|
218
|
+
本文。
|
|
219
|
+
RD
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def test_const_signature
|
|
223
|
+
assert_equivalent_method <<~RD
|
|
224
|
+
--- INDEX_VERSION -> String
|
|
225
|
+
|
|
226
|
+
定数の説明。
|
|
227
|
+
RD
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def test_param_description_with_example_block
|
|
231
|
+
# CGI::HtmlExtension#popup_menu 型: 空白のみ行を挟む例示ブロックも @param の説明
|
|
232
|
+
assert_equivalent_method <<~RD
|
|
233
|
+
--- m(v) -> String
|
|
234
|
+
|
|
235
|
+
説明。
|
|
236
|
+
|
|
237
|
+
@param v 説明。
|
|
238
|
+
|
|
239
|
+
例:
|
|
240
|
+
code_like_text
|
|
241
|
+
RD
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def test_param_description_with_emlist
|
|
245
|
+
# Array#pack 型: @param の説明に //emlist が続く(rd では dd の一部)
|
|
246
|
+
assert_equivalent_method <<~RD
|
|
247
|
+
--- m(v) -> String
|
|
248
|
+
|
|
249
|
+
説明。
|
|
250
|
+
|
|
251
|
+
@param v 値。
|
|
252
|
+
|
|
253
|
+
//emlist[例][ruby]{
|
|
254
|
+
p 1
|
|
255
|
+
//}
|
|
256
|
+
|
|
257
|
+
@return 結果。
|
|
258
|
+
RD
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def test_adjacent_fences_with_blank_line_merge
|
|
262
|
+
# IO#sysseek 型: ゲート分断されたインデントブロックは版解決後に
|
|
263
|
+
# 空白のみ行を挟む隣接フェンスとして現れる。rd の pre は空白行を
|
|
264
|
+
# 跨ぐため、間の空白行ごと1つの <pre> にマージする
|
|
265
|
+
rd_src = "--- m(v) -> String\n\n説明。\n\n" \
|
|
266
|
+
" line1\n # => result\n \n\n line2\n"
|
|
267
|
+
md_src = "### def m(v) -> String\n\n説明。\n\n" \
|
|
268
|
+
"`````\nline1\n`````\n`````\n# => result\n`````\n \n\n`````\nline2\n`````\n"
|
|
269
|
+
assert_equal compile_method(@rd, rd_src), compile_method(@md, md_src),
|
|
270
|
+
"md source:\n#{md_src}"
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def test_leftover_emlist_in_doc
|
|
274
|
+
# news/2_5_0 型: リスト脈絡の #@samplecode は md に生のまま残り、
|
|
275
|
+
# 前処理で //emlist になる。RDCompiler と同じく独立ブロックとして描画する
|
|
276
|
+
rd_src = " * 項目 [[m:Coverage.start]]\n//emlist[][ruby]{\nCoverage.start\n//}\n * 次の項目\n"
|
|
277
|
+
md_src = " - 項目 [m:Coverage.start]\n//emlist[][ruby]{\nCoverage.start\n//}\n - 次の項目\n"
|
|
278
|
+
assert_equal @rd.compile(rd_src), @md.compile(md_src), "md source:\n#{md_src}"
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def test_leftover_emlist_in_method_entry
|
|
282
|
+
rd_src = "--- m(v) -> String\n\n説明。\n//emlist[][ruby]{\np 1\n//}\n"
|
|
283
|
+
md_src = "### def m(v) -> String\n\n説明。\n//emlist[][ruby]{\np 1\n//}\n"
|
|
284
|
+
assert_equal compile_method(@rd, rd_src), compile_method(@md, md_src),
|
|
285
|
+
"md source:\n#{md_src}"
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def test_doc_see_renders_see_also
|
|
289
|
+
# findings#1: doc/lib ページの @see も SEE_ALSO として解釈する
|
|
290
|
+
# (従来は RDCompiler の library_file に分岐がなく段落テキストだった)
|
|
291
|
+
rd_src = "= 見出し\n\n説明。\n\n@see [[m:Array#each]]\n"
|
|
292
|
+
md_src = BitClust::RRDToMarkdown.convert(rd_src)
|
|
293
|
+
rd_html = @rd.compile(rd_src)
|
|
294
|
+
assert_match(/\[SEE_ALSO\]/, rd_html)
|
|
295
|
+
assert_equal rd_html, @md.compile(md_src), "md source:\n#{md_src}"
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def test_entry_paragraph_with_midline_see_not_swallowed
|
|
299
|
+
# findings#3: 行頭アンカー無しの /@see/ dispatch が本文行を see() に
|
|
300
|
+
# 吸う潜在バグ。段落の先頭行に「@see」を含む文があっても段落のまま
|
|
301
|
+
rd_src = "--- m(v) -> String\n\n本文中で @see を説明する行。\n"
|
|
302
|
+
html = compile_method(@rd, rd_src)
|
|
303
|
+
assert_not_match(/SEE_ALSO/, html)
|
|
304
|
+
assert_match(/本文中で @see を説明する行。/, html)
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def test_undef_renders_dedicated_message
|
|
308
|
+
# {: undef} 属性行は専用の説明文を描画する(statichtml は undefined を
|
|
309
|
+
# skip するため露出は server 等の動的経路のみ)
|
|
310
|
+
rd_src = "--- <(other) -> bool\n{: undef}\n"
|
|
311
|
+
md_src = BitClust::RRDToMarkdown.convert(rd_src)
|
|
312
|
+
rd_html = compile_method(@rd, rd_src)
|
|
313
|
+
assert_not_match(/UNKNOWN_META_INFO/, rd_html)
|
|
314
|
+
assert_match(/このメソッドは定義されていません/, rd_html)
|
|
315
|
+
assert_equal rd_html, compile_method(@md, md_src), "md source:\n#{md_src}"
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def test_signature_without_space
|
|
319
|
+
# OpenSSL::X509::Extension 型: 「---name」(スペース無し)を RDCompiler は受理する
|
|
320
|
+
assert_equivalent_method <<~RD
|
|
321
|
+
---critical=(bool)
|
|
322
|
+
重要度を設定します。
|
|
323
|
+
|
|
324
|
+
@param bool 真偽値
|
|
325
|
+
RD
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def test_raise_without_description_trailing_space
|
|
329
|
+
# Gem::RemoteFetcher#download 型: 「@raise Ex 」(説明なし・末尾スペース)
|
|
330
|
+
# RDCompiler は dd 内に空白テキスト行を出す
|
|
331
|
+
assert_equivalent_method "--- m(v) -> String\n\n説明。\n\n@raise SomeError \n"
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def test_indented_code_across_double_blank
|
|
335
|
+
# Process#exec 型: 空行2つを挟むインデントコードは1つの <pre> に融合する
|
|
336
|
+
assert_equivalent_method <<~RD
|
|
337
|
+
--- m(v) -> String
|
|
338
|
+
|
|
339
|
+
例:
|
|
340
|
+
|
|
341
|
+
exec "echo *"
|
|
342
|
+
# never get here
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
exec "echo", "*"
|
|
346
|
+
# never get here
|
|
347
|
+
|
|
348
|
+
@param v 値。
|
|
349
|
+
RD
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def test_undef_metadata
|
|
353
|
+
# Complex#< 型: {: undef} 属性行。両経路とも「定義されていません」を描画
|
|
354
|
+
assert_equivalent_method <<~RD
|
|
355
|
+
--- <(other) -> bool
|
|
356
|
+
{: undef}
|
|
357
|
+
RD
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def test_nomethod_metadata
|
|
361
|
+
# @nomethod(説明のための未定義メソッド)はマーカーなので本文には描画しない
|
|
362
|
+
md_src = BitClust::RRDToMarkdown.convert(<<~RD)
|
|
363
|
+
--- to_a -> Array
|
|
364
|
+
{: nomethod}
|
|
365
|
+
|
|
366
|
+
説明のためここに記載しています。
|
|
367
|
+
RD
|
|
368
|
+
html = compile_method(@md, md_src)
|
|
369
|
+
assert_not_include(html, 'UNKNOWN_META_INFO')
|
|
370
|
+
assert_not_include(html, '{:')
|
|
371
|
+
assert_include(html, '説明のためここに記載しています。')
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def test_nomethod_metadata_equivalence
|
|
375
|
+
assert_equivalent_method <<~RD
|
|
376
|
+
--- to_a -> Array
|
|
377
|
+
{: nomethod}
|
|
378
|
+
|
|
379
|
+
説明のためここに記載しています。
|
|
380
|
+
RD
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def test_unknown_metadata_after_param
|
|
384
|
+
# @param に続く未知メタデータは同じ <dl> 内に UNKNOWN_META_INFO で入る
|
|
385
|
+
assert_equivalent_method <<~RD
|
|
386
|
+
--- m(v) -> bool
|
|
387
|
+
|
|
388
|
+
説明。
|
|
389
|
+
|
|
390
|
+
@param v 値。
|
|
391
|
+
@undef
|
|
392
|
+
RD
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
def test_dlist_description_with_interleaved_emlist
|
|
396
|
+
# String#% 型: dd は「インデント段落 + emlist」を交互に何個でも受ける。
|
|
397
|
+
# 1つ目の継続(プレフィックスを…)は dt の直後(空行なし)なので
|
|
398
|
+
# lazy continuation としてインデント1でも dd に入るが、2つ目
|
|
399
|
+
# (浮動小数点数…)は emlist の後の空行を挟むため、CommonMark 準拠の
|
|
400
|
+
# ためマーカー幅(2)と同じインデントにしている(#3232 対応)
|
|
401
|
+
assert_equivalent_method <<~RD
|
|
402
|
+
--- m(v) -> String
|
|
403
|
+
|
|
404
|
+
: #
|
|
405
|
+
プレフィックスを付けます。
|
|
406
|
+
|
|
407
|
+
//emlist[][ruby]{
|
|
408
|
+
p 1
|
|
409
|
+
//}
|
|
410
|
+
|
|
411
|
+
浮動小数点数に対しては必ず付けます。
|
|
412
|
+
|
|
413
|
+
//emlist[][ruby]{
|
|
414
|
+
p 2
|
|
415
|
+
//}
|
|
416
|
+
|
|
417
|
+
続き。
|
|
418
|
+
RD
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def test_dlist_with_list_like_text_in_description
|
|
422
|
+
# news/1.8.0 型: dd 内のリスト風テキストは RDCompiler ではただのテキスト
|
|
423
|
+
assert_equivalent_doc <<~RD
|
|
424
|
+
= タイトル
|
|
425
|
+
|
|
426
|
+
: term
|
|
427
|
+
説明。
|
|
428
|
+
|
|
429
|
+
* 項目風1
|
|
430
|
+
* 項目風2
|
|
431
|
+
RD
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def test_colon_line_as_paragraph_continuation
|
|
435
|
+
# news/1.8.5 型: テキスト行直後の「: 」行は段落の継続
|
|
436
|
+
assert_equivalent_doc <<~RD
|
|
437
|
+
= タイトル
|
|
438
|
+
|
|
439
|
+
以下が追加されました。
|
|
440
|
+
: TCPServer#accept_nonblock [new]
|
|
441
|
+
|
|
442
|
+
本文。
|
|
443
|
+
RD
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
def test_list_item_with_shallow_continuation
|
|
447
|
+
# news/2_6_0 型: 項目より浅い折り返しも項目の継続
|
|
448
|
+
assert_equivalent_doc <<~RD
|
|
449
|
+
= タイトル
|
|
450
|
+
|
|
451
|
+
* 項目の一行目が長くて
|
|
452
|
+
折り返した継続行。
|
|
453
|
+
|
|
454
|
+
本文。
|
|
455
|
+
RD
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
def test_h5_heading
|
|
459
|
+
# spec/safelevel 型: ===== は h3(hlevel 1 + (5-3))として描画される
|
|
460
|
+
assert_equivalent_doc <<~RD
|
|
461
|
+
= タイトル
|
|
462
|
+
|
|
463
|
+
==== レベル 0
|
|
464
|
+
|
|
465
|
+
===== 汚染されるオブジェクト
|
|
466
|
+
|
|
467
|
+
本文。
|
|
468
|
+
RD
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
def test_olist_item_continuation
|
|
472
|
+
# spec/terminate 型: (N) 項目の折り返し行は項目の継続
|
|
473
|
+
assert_equivalent_doc <<~RD
|
|
474
|
+
= タイトル
|
|
475
|
+
|
|
476
|
+
(1) すべてのスレッドを kill
|
|
477
|
+
(2) ハンドラが登録されていればそれを実
|
|
478
|
+
行する。
|
|
479
|
+
(3) 後始末。
|
|
480
|
+
|
|
481
|
+
本文。
|
|
482
|
+
RD
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
def test_see_in_doc_is_plain_text
|
|
486
|
+
# pack_template 型: doc/lib ページの @see は RDCompiler では段落テキスト
|
|
487
|
+
assert_equivalent_doc <<~RD
|
|
488
|
+
= タイトル
|
|
489
|
+
|
|
490
|
+
本文。
|
|
491
|
+
|
|
492
|
+
@see [[c:String]]
|
|
493
|
+
RD
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
def test_dlist_colon_term_without_space
|
|
497
|
+
# spec/operator 型: dlist 継続の「:term」(スペース無し)も dt
|
|
498
|
+
assert_equivalent_doc <<~RD
|
|
499
|
+
= タイトル
|
|
500
|
+
|
|
501
|
+
: 再定義できる演算子
|
|
502
|
+
いろいろ。
|
|
503
|
+
|
|
504
|
+
:再定義できない演算子
|
|
505
|
+
これらは再定義できません。
|
|
506
|
+
|
|
507
|
+
本文。
|
|
508
|
+
RD
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
def test_colon_no_space_in_paragraph_stays_text
|
|
512
|
+
# openssl/ASN1 型: 段落継続の「:SYMBOL」行は dlist 化しない
|
|
513
|
+
assert_equivalent_doc <<~RD
|
|
514
|
+
= タイトル
|
|
515
|
+
|
|
516
|
+
タグクラスを返します。
|
|
517
|
+
:IMPLICIT、:EXPLICIT、nil のいずれかを返します。
|
|
518
|
+
|
|
519
|
+
本文。
|
|
520
|
+
RD
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
def test_discrete_numbered_text
|
|
524
|
+
# lib:logger 型: 離散した「N. テキスト」は RD では段落テキスト
|
|
525
|
+
# (md では **N.** 太字。M1 では元のテキストに戻して描画する)
|
|
526
|
+
assert_equivalent_doc <<~RD
|
|
527
|
+
= タイトル
|
|
528
|
+
|
|
529
|
+
ログの出力先について。
|
|
530
|
+
|
|
531
|
+
1. STDERR/STDOUTに出力するように指定
|
|
532
|
+
|
|
533
|
+
本文が続きます。
|
|
534
|
+
|
|
535
|
+
2. ファイルに出力するように指定
|
|
536
|
+
RD
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
# ---- M2: GFM モード(:gfm オプション) ----
|
|
540
|
+
# M1 等価モード(既定)と違い、RDCompiler と同一 HTML ではなく
|
|
541
|
+
# GFM の表現(<code>/<strong>/<table>)を描画する
|
|
542
|
+
|
|
543
|
+
def gfm_compiler
|
|
544
|
+
BitClust::MDCompiler.new(@u, 1, { :database => @db, :gfm => true })
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
def test_gfm_code_span_in_paragraph
|
|
548
|
+
html = gfm_compiler.compile("# タイトル\n\nこの `code` はスパンです。\n")
|
|
549
|
+
assert_include html, "この <code>code</code> はスパンです。"
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
def test_gfm_escaped_backtick_stays_literal
|
|
553
|
+
html = gfm_compiler.compile("# タイトル\n\n\\`--version' と \\`-' の話。\n")
|
|
554
|
+
assert_include html, "`--version' と `-' の話。"
|
|
555
|
+
assert_not_include html, "<code>"
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
def test_gfm_auto_span_becomes_code
|
|
559
|
+
html = gfm_compiler.compile("# タイトル\n\n`__FILE__` を返します。\n")
|
|
560
|
+
assert_include html, "<code>__FILE__</code> を返します。"
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
def test_gfm_code_span_escapes_html
|
|
564
|
+
html = gfm_compiler.compile("# タイトル\n\n`a<b>` です。\n")
|
|
565
|
+
assert_include html, "<code>a<b></code> です。"
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
# ---- CommonMark 6.1: N 連バッククォートのコードスパン(doctree#3232) ----
|
|
569
|
+
|
|
570
|
+
def test_gfm_double_backtick_span_contains_single_backtick
|
|
571
|
+
# 開始と同じ長さ(2連)のバッククォート列で閉じ、前後の空白を1個ずつ剥ぐ
|
|
572
|
+
html = gfm_compiler.compile("# T\n\n`` foo ` bar `` です。\n")
|
|
573
|
+
assert_include html, "<code>foo ` bar</code> です。"
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
def test_gfm_triple_backtick_span
|
|
577
|
+
# 行頭 ``` はブロックレベルのフェンスと解釈されるため、文中に置く
|
|
578
|
+
html = gfm_compiler.compile("# T\n\n見よ ``` code ``` です。\n")
|
|
579
|
+
assert_include html, "<code>code</code> です。"
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
def test_gfm_unbalanced_backtick_run_stays_literal
|
|
583
|
+
# 閉じる相手のいない開始列はコードスパンにせずリテラルのまま
|
|
584
|
+
html = gfm_compiler.compile("# T\n\n``foo` です。\n")
|
|
585
|
+
assert_include html, "``foo` です。"
|
|
586
|
+
assert_not_include html, "<code>"
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
def test_gfm_escaped_backtick_inside_span_keeps_backslash
|
|
590
|
+
# コードスパン内ではバックスラッシュエスケープが無効(CommonMark 6.1)。
|
|
591
|
+
# \` はバックスラッシュごとリテラルに残る(GitHub の実描画と同一)
|
|
592
|
+
html = gfm_compiler.compile("# T\n\n`` \\` `` は変換されません。\n")
|
|
593
|
+
assert_include html, "<code>\\`</code> は変換されません。"
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
def test_gfm_bare_backtick_in_bracket_ref_still_resolves
|
|
597
|
+
# 既存の [m:$`](素のバッククォート)の後方互換
|
|
598
|
+
html = gfm_compiler.compile("# T\n\n[m:$`] を参照。\n")
|
|
599
|
+
assert_include html, ">$`</a>"
|
|
600
|
+
assert_not_include html, "[[m:$"
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
def test_gfm_escaped_backtick_in_bracket_ref_resolves
|
|
604
|
+
# znz さん提示の書き方(doctree#3232 レビュー): [m:$\`] は
|
|
605
|
+
# [m:$`](素のバッククォート)と同じ参照へ解決される
|
|
606
|
+
escaped = gfm_compiler.compile("# T\n\n[m:$\\`] を参照。\n")
|
|
607
|
+
bare = gfm_compiler.compile("# T\n\n[m:$`] を参照。\n")
|
|
608
|
+
assert_equal bare, escaped
|
|
609
|
+
assert_include escaped, ">$`</a>"
|
|
610
|
+
assert_not_include escaped, "[[m:$"
|
|
611
|
+
assert_not_include escaped, "\\`"
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
def test_gfm_znz_gsub_paragraph_golden
|
|
615
|
+
# doctree PR #3232 レビューで znz さんが提示した段落そのもの
|
|
616
|
+
# (String#gsub の説明)。GitHub 上の実描画(gh api -X POST /markdown で
|
|
617
|
+
# 確認済み)と同等の構造 — \`・\'・\+ がコードスパンに、
|
|
618
|
+
# 3つの参照がすべて解決されること — をまとめて確認する
|
|
619
|
+
md = <<~MD
|
|
620
|
+
# T
|
|
621
|
+
|
|
622
|
+
置換文字列内では `` \\` ``、`\\'`、`\\+` も使えます。
|
|
623
|
+
これらは [m:$\\`]、[m:$']、[m:$+] に対応します。
|
|
624
|
+
MD
|
|
625
|
+
html = gfm_compiler.compile(md)
|
|
626
|
+
assert_include html, "<code>\\`</code>"
|
|
627
|
+
assert_include html, "<code>\\'</code>"
|
|
628
|
+
assert_include html, "<code>\\+</code>"
|
|
629
|
+
assert_include html, ">$`</a>"
|
|
630
|
+
assert_include html, ">$'</a>"
|
|
631
|
+
assert_include html, ">$+</a>"
|
|
632
|
+
assert_not_include html, "[[m:$"
|
|
633
|
+
assert_not_include html, "[m:$"
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
def test_gfm_ref_and_span_coexist
|
|
637
|
+
html = gfm_compiler.compile("# タイトル\n\n[c:String] と `x` を参照。\n")
|
|
638
|
+
assert_include html, "</a> と <code>x</code> を参照。"
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
def test_gfm_strong_number
|
|
642
|
+
html = gfm_compiler.compile("# タイトル\n\n**1.** 最初の説明。\n")
|
|
643
|
+
assert_include html, "<strong>1.</strong> 最初の説明。"
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
def test_gfm_inline_strong
|
|
647
|
+
html = gfm_compiler.compile("# タイトル\n\nこの **重要** な点。\n")
|
|
648
|
+
assert_include html, "この <strong>重要</strong> な点。"
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
def test_gfm_inline_strong_no_space_boundary
|
|
652
|
+
# 和文は空白なしで隣接する
|
|
653
|
+
html = gfm_compiler.compile("# タイトル\n\nこれは**とても重要**です。\n")
|
|
654
|
+
assert_include html, "これは<strong>とても重要</strong>です。"
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
def test_gfm_inline_strong_multiple
|
|
658
|
+
html = gfm_compiler.compile("# タイトル\n\n**a** と **b** の話。\n")
|
|
659
|
+
assert_include html, "<strong>a</strong> と <strong>b</strong> の話。"
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
def test_gfm_inline_strong_wraps_ref
|
|
663
|
+
html = gfm_compiler.compile("# タイトル\n\n**[c:String]** を参照。\n")
|
|
664
|
+
assert_include html, "<strong><a href="
|
|
665
|
+
assert_include html, "</a></strong> を参照。"
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
def test_gfm_inline_strong_wraps_code_span
|
|
669
|
+
html = gfm_compiler.compile("# タイトル\n\n**`freeze` される**ので注意。\n")
|
|
670
|
+
assert_include html, "<strong><code>freeze</code> される</strong>ので注意。"
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
def test_gfm_inline_strong_in_code_span_stays_literal
|
|
674
|
+
html = gfm_compiler.compile("# タイトル\n\n`**not strong**` と書く。\n")
|
|
675
|
+
assert_include html, "<code>**not strong**</code> と書く。"
|
|
676
|
+
assert_not_include html, "<strong>"
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
def test_gfm_inline_strong_exponent_stays_literal
|
|
680
|
+
# 単語内の ** は冪乗演算子とみなして適用しない
|
|
681
|
+
html = gfm_compiler.compile("# タイトル\n\n値は 2**32 と 2**64 です。\n")
|
|
682
|
+
assert_not_include html, "<strong>"
|
|
683
|
+
assert_include html, "2**32 と 2**64"
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
def test_gfm_inline_strong_space_boundary_stays_literal
|
|
687
|
+
html = gfm_compiler.compile("# タイトル\n\na ** b ** c の形。\n")
|
|
688
|
+
assert_not_include html, "<strong>"
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
def test_gfm_inline_strong_spans_soft_break
|
|
692
|
+
# 段落内の改行は行結合で除去されるので、GFM と同じく soft break をまたげる
|
|
693
|
+
html = gfm_compiler.compile("# タイトル\n\n**改行を\nまたぐ**形。\n")
|
|
694
|
+
assert_include html, "<strong>改行をまたぐ</strong>形。"
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
def test_gfm_inline_strong_triple_asterisk_stays_literal
|
|
698
|
+
html = gfm_compiler.compile("# タイトル\n\n***em strong*** は対象外。\n")
|
|
699
|
+
assert_not_include html, "<strong>"
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
def test_gfm_param_name_code
|
|
703
|
+
md = "### def m(v) -> String\n\n説明。\n\n- **param** `v` -- 値。\n"
|
|
704
|
+
html = compile_method(gfm_compiler, md)
|
|
705
|
+
assert_include html, "[PARAM] <code>v</code>:"
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
def test_gfm_raise_name_code
|
|
709
|
+
md = "### def m(v) -> String\n\n説明。\n\n- **raise** `TypeError` -- 型エラー。\n"
|
|
710
|
+
html = compile_method(gfm_compiler, md)
|
|
711
|
+
assert_include html, "[EXCEPTION] <code>TypeError</code>:"
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
def test_gfm_dlist_code_term
|
|
715
|
+
html = gfm_compiler.compile("# タイトル\n\n- **`type`**: Content-Type ヘッダ。\n")
|
|
716
|
+
assert_include html, "<dt><code>type</code></dt>"
|
|
717
|
+
end
|
|
718
|
+
|
|
719
|
+
def test_gfm_dlist_ref_term_resolves_inside_code
|
|
720
|
+
# spec/eval 型: ref のコード term は <code> 内でリンク解決する
|
|
721
|
+
html = gfm_compiler.compile("# T\n\n- **`[c:String]`**: 文字列クラス。\n")
|
|
722
|
+
assert_include html, "<dt><code><a href="
|
|
723
|
+
assert_include html, "</a></code></dt>"
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
def test_gfm_dlist_term_inner_stripped
|
|
727
|
+
# news/1.8.2 型: スパン内の末尾スペースは dt に含めない(rd の strip と同じ)
|
|
728
|
+
html = gfm_compiler.compile("# T\n\n- **`CSV.open, and generate `**: 説明。\n")
|
|
729
|
+
assert_include html, "<dt><code>CSV.open, and generate</code></dt>"
|
|
730
|
+
end
|
|
731
|
+
|
|
732
|
+
def test_gfm_dlist_plain_term_stays_plain
|
|
733
|
+
html = gfm_compiler.compile("# タイトル\n\n- **用語**: 説明文。\n")
|
|
734
|
+
assert_include html, "<dt>用語</dt>"
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
def test_gfm_table_basic
|
|
738
|
+
md = <<~MD
|
|
739
|
+
# タイトル
|
|
740
|
+
|
|
741
|
+
| 文字列 | ステータス |
|
|
742
|
+
|--------|-----------|
|
|
743
|
+
| `"OK"` | 200 |
|
|
744
|
+
MD
|
|
745
|
+
html = gfm_compiler.compile(md)
|
|
746
|
+
assert_include html, "<table>"
|
|
747
|
+
assert_include html, "<th>文字列</th>"
|
|
748
|
+
assert_include html, "<td><code>"OK"</code></td>"
|
|
749
|
+
assert_include html, "<td>200</td>"
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
def test_gfm_table_alignment
|
|
753
|
+
md = "# T\n\n| a | b | c |\n|:--|:-:|--:|\n| 1 | 2 | 3 |\n"
|
|
754
|
+
html = gfm_compiler.compile(md)
|
|
755
|
+
assert_include html, '<th align="left">a</th>'
|
|
756
|
+
assert_include html, '<th align="center">b</th>'
|
|
757
|
+
assert_include html, '<th align="right">c</th>'
|
|
758
|
+
assert_include html, '<td align="right">3</td>'
|
|
759
|
+
end
|
|
760
|
+
|
|
761
|
+
def test_gfm_pipe_line_without_delimiter_is_paragraph
|
|
762
|
+
# FalseClass 型: | 演算子の散文はテーブルにしない(区切り行必須)
|
|
763
|
+
md = "# T\n\n| は再定義可能な演算子です。通常は false | other の形で使われます。\n"
|
|
764
|
+
html = gfm_compiler.compile(md)
|
|
765
|
+
assert_not_include html, "<table>"
|
|
766
|
+
assert_include html, "| は再定義可能な演算子です。"
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
def test_gfm_off_by_default
|
|
770
|
+
html = @md.compile("# タイトル\n\nこの `code' はどうか。\n")
|
|
771
|
+
assert_not_include html, "<code>code"
|
|
772
|
+
end
|
|
773
|
+
|
|
774
|
+
# ---- M2: Markdown リンク(MARKUP_SPEC §7.4/§7.5、news/1_9_0 型) ----
|
|
775
|
+
|
|
776
|
+
def test_gfm_autolink
|
|
777
|
+
html = gfm_compiler.compile("# T\n\n<https://example.com/a#b> を参照。\n")
|
|
778
|
+
assert_include html,
|
|
779
|
+
'<a class="external" href="https://example.com/a#b">https://example.com/a#b</a> を参照。'
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
def test_gfm_autolink_in_dlist_description
|
|
783
|
+
md = "# T\n\n- **`__callee__`**:\n\n <https://example.com/x>\n"
|
|
784
|
+
html = gfm_compiler.compile(md)
|
|
785
|
+
assert_include html, '<a class="external" href="https://example.com/x">https://example.com/x</a>'
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
def test_gfm_autolink_requires_url_scheme
|
|
789
|
+
html = gfm_compiler.compile("# T\n\na <b> c です。\n")
|
|
790
|
+
assert_include html, "a <b> c です。"
|
|
791
|
+
assert_not_include html, "<a "
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
def test_gfm_inline_link
|
|
795
|
+
html = gfm_compiler.compile("# T\n\n[例示ドメイン](https://example.com)のサポート\n")
|
|
796
|
+
assert_include html, '<a class="external" href="https://example.com">例示ドメイン</a>のサポート'
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
def test_gfm_inline_link_with_bracketed_text
|
|
800
|
+
# news/1_9_0 型: [[ruby-cvs:16833]](アーカイブURL) — 表示テキストに角括弧
|
|
801
|
+
html = gfm_compiler.compile("# T\n\n[[ruby-cvs:16833]](https://example.com/16833)\n")
|
|
802
|
+
assert_include html, '<a class="external" href="https://example.com/16833">[ruby-cvs:16833]</a>'
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
def test_gfm_inline_link_text_with_parens_and_url_query
|
|
806
|
+
md = "# T\n\n[patch: activity in 1.9 (2006-06)](https://example.com/hiki.rb?Changes+in+Ruby)\n"
|
|
807
|
+
html = gfm_compiler.compile(md)
|
|
808
|
+
assert_include html,
|
|
809
|
+
'<a class="external" href="https://example.com/hiki.rb?Changes+in+Ruby">patch: activity in 1.9 (2006-06)</a>'
|
|
810
|
+
end
|
|
811
|
+
|
|
812
|
+
def test_gfm_fragment_link
|
|
813
|
+
html = gfm_compiler.compile("# T\n\n[破壊的な変更](#mutable) を参照。\n")
|
|
814
|
+
assert_include html, '<a href="#mutable">破壊的な変更</a> を参照。'
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
def test_gfm_non_url_destination_stays_text
|
|
818
|
+
# 参照直後の括弧書きをリンクと誤認しない(宛先は URL/#フラグメントのみ)
|
|
819
|
+
html = gfm_compiler.compile("# T\n\n[c:String](文字列)を参照。\n")
|
|
820
|
+
assert_include html, "</a>(文字列)を参照。"
|
|
821
|
+
end
|
|
822
|
+
|
|
823
|
+
def test_gfm_link_inside_code_span_stays_code
|
|
824
|
+
html = gfm_compiler.compile("# T\n\n`[x](https://example.com)` はコード。\n")
|
|
825
|
+
assert_include html, "<code>[x](https://example.com)</code> はコード。"
|
|
826
|
+
end
|
|
827
|
+
|
|
828
|
+
def test_gfm_link_escapes_html_in_text_and_url
|
|
829
|
+
html = gfm_compiler.compile("# T\n\n[a<b>](https://example.com/?q=<x>)\n")
|
|
830
|
+
assert_include html, '<a class="external" href="https://example.com/?q=<x>">a<b></a>'
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
# ---- M2: インデントされたコードフェンス(リスト・dlist 内、news/1_9_0 型) ----
|
|
834
|
+
|
|
835
|
+
def test_gfm_indented_fence_in_dlist_description
|
|
836
|
+
md = <<~MD
|
|
837
|
+
# T
|
|
838
|
+
|
|
839
|
+
- **`Dir.glob`**:
|
|
840
|
+
|
|
841
|
+
説明文です。
|
|
842
|
+
|
|
843
|
+
```ruby
|
|
844
|
+
p Dir.glob(["f*","b*"]) # => ["foo", "bar"]
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
続きの説明。
|
|
848
|
+
MD
|
|
849
|
+
html = gfm_compiler.compile(md)
|
|
850
|
+
assert_include html, '<pre class="highlight ruby">'
|
|
851
|
+
assert_include html, 'Dir.glob'
|
|
852
|
+
assert_not_include html, '```'
|
|
853
|
+
# 内容はフェンス行のインデント分がデデントされる
|
|
854
|
+
assert_include html, "<span class=\"nb\">p</span>"
|
|
855
|
+
assert_include html, "<p>\n続きの説明。\n</p>"
|
|
856
|
+
end
|
|
857
|
+
|
|
858
|
+
def test_gfm_indented_fence_without_lang
|
|
859
|
+
md = "# T\n\n- **`proc`**:\n\n 説明。\n\n ```text\n x = {|a| p a}\n ```\n"
|
|
860
|
+
html = gfm_compiler.compile(md)
|
|
861
|
+
assert_include html, "x = {|a| p a}"
|
|
862
|
+
assert_not_include html, '```'
|
|
863
|
+
end
|
|
864
|
+
|
|
865
|
+
def test_gfm_indented_fence_in_list_item
|
|
866
|
+
md = "# T\n\n- 項目\n ```ruby\n p 1\n ```\n- 次の項目\n"
|
|
867
|
+
html = gfm_compiler.compile(md)
|
|
868
|
+
assert_include html, '<pre class="highlight ruby">'
|
|
869
|
+
assert_include html, "次の項目"
|
|
870
|
+
assert_not_include html, '```'
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
def test_gfm_indented_fence_at_top_level
|
|
874
|
+
# リスト項目と空行で切り離されたフェンス(行駆動モデルではトップレベル扱い)
|
|
875
|
+
md = "# T\n\n- 項目\n\n ```ruby\n p 1\n ```\n\n本文。\n"
|
|
876
|
+
html = gfm_compiler.compile(md)
|
|
877
|
+
assert_include html, '<pre class="highlight ruby">'
|
|
878
|
+
assert_include html, "本文。"
|
|
879
|
+
assert_not_include html, '```'
|
|
880
|
+
end
|
|
881
|
+
|
|
882
|
+
def test_gfm_indented_fence_in_method_entry
|
|
883
|
+
md = "### def m(v) -> String\n\n説明。\n\n- **`opt`**:\n\n ```ruby\n p 1\n ```\n"
|
|
884
|
+
html = compile_method(gfm_compiler, md)
|
|
885
|
+
assert_include html, '<pre class="highlight ruby">'
|
|
886
|
+
assert_not_include html, '```'
|
|
887
|
+
end
|
|
888
|
+
|
|
889
|
+
def test_gfm_indented_fence_in_param_continuation
|
|
890
|
+
md = "### def m(v) -> String\n\n説明。\n\n- **param** `v` -- 値。\n\n ```ruby\n p 1\n ```\n"
|
|
891
|
+
html = compile_method(gfm_compiler, md)
|
|
892
|
+
assert_include html, '<pre class="highlight ruby">'
|
|
893
|
+
assert_not_include html, '```'
|
|
894
|
+
end
|
|
895
|
+
|
|
896
|
+
def test_gfm_stray_indented_line_does_not_hang
|
|
897
|
+
# リストと空行で切り離された残余のインデント行はどのブロックにも
|
|
898
|
+
# 該当しない。段落として消費する(従来はディスパッチが進まずハング)
|
|
899
|
+
md = "# T\n\n- 項目\n\n 切り離された継続行。\n\n本文。\n"
|
|
900
|
+
html = gfm_compiler.compile(md)
|
|
901
|
+
assert_include html, "切り離された継続行。"
|
|
902
|
+
assert_include html, "本文。"
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
def test_gfm_stray_indented_line_in_method_entry_does_not_hang
|
|
906
|
+
md = "### def m(v) -> String\n\n- 項目\n\n 切り離された継続行。\n"
|
|
907
|
+
html = compile_method(gfm_compiler, md)
|
|
908
|
+
assert_include html, "切り離された継続行。"
|
|
909
|
+
end
|
|
910
|
+
|
|
911
|
+
def test_gfm_indented_fence_with_title
|
|
912
|
+
md = "# T\n\n- 項目\n ```ruby title=\"例\"\n p 1\n ```\n"
|
|
913
|
+
html = gfm_compiler.compile(md)
|
|
914
|
+
assert_include html, '<span class="caption">例</span>'
|
|
915
|
+
assert_include html, '<pre class="highlight ruby">'
|
|
916
|
+
end
|
|
917
|
+
|
|
918
|
+
# ---- doc / ライブラリページ ----
|
|
919
|
+
|
|
920
|
+
def test_doc_headline_and_paragraph
|
|
921
|
+
assert_equivalent_doc <<~RD
|
|
922
|
+
= タイトル
|
|
923
|
+
|
|
924
|
+
本文段落。
|
|
925
|
+
|
|
926
|
+
== 節
|
|
927
|
+
|
|
928
|
+
節の本文。
|
|
929
|
+
RD
|
|
930
|
+
end
|
|
931
|
+
|
|
932
|
+
def test_doc_lists_and_code
|
|
933
|
+
assert_equivalent_doc <<~RD
|
|
934
|
+
= タイトル
|
|
935
|
+
|
|
936
|
+
* 項目1
|
|
937
|
+
* 項目2
|
|
938
|
+
|
|
939
|
+
//emlist[例][ruby]{
|
|
940
|
+
p 1
|
|
941
|
+
//}
|
|
942
|
+
RD
|
|
943
|
+
end
|
|
944
|
+
|
|
945
|
+
def test_doc_dlist
|
|
946
|
+
assert_equivalent_doc <<~RD
|
|
947
|
+
= タイトル
|
|
948
|
+
|
|
949
|
+
: 用語
|
|
950
|
+
定義文。
|
|
951
|
+
RD
|
|
952
|
+
end
|
|
953
|
+
|
|
954
|
+
# ---- CommonMark 準拠のリスト継続インデント (doctree#3232, znz レビュー) ----
|
|
955
|
+
# https://spec.commonmark.org/0.31.2/#list-items Example 255-258, 264 相当。
|
|
956
|
+
# マーカーの内容カラム幅(「- 」なら2、dlist の dt も常に「- 」なので2)
|
|
957
|
+
# 未満のインデントは、空行を挿むと項目に属さない。ただし空行を挿まない
|
|
958
|
+
# 直接継続(lazy continuation)は CommonMark 同様、幅を問わず許容する。
|
|
959
|
+
|
|
960
|
+
def test_dlist_direct_continuation_allows_shallow_indent
|
|
961
|
+
# 空行なしの直接継続(lazy)はマーカー幅(2)未満でも dd に含まれる
|
|
962
|
+
# (symref.md の実例の大半はこの形)
|
|
963
|
+
html = @md.compile("- **term**:\n description.\n")
|
|
964
|
+
assert_match(%r{<dd>\s*<p>\s*description\.\s*</p>\s*</dd>}m, html)
|
|
965
|
+
end
|
|
966
|
+
|
|
967
|
+
def test_dlist_blank_continuation_with_marker_width_indent_stays_in_dd
|
|
968
|
+
# CommonMark Example 256 相当: マーカー幅(2)以上のインデントなら
|
|
969
|
+
# 空行を挟んだ2段落目も dd に含まれる
|
|
970
|
+
html = @md.compile("- **term**:\n one\n\n two\n")
|
|
971
|
+
assert_include(html, 'one')
|
|
972
|
+
assert_include(html, 'two')
|
|
973
|
+
assert_operator(html.index('two'), :<, html.index('</dd>'))
|
|
974
|
+
end
|
|
975
|
+
|
|
976
|
+
def test_dlist_blank_continuation_with_shallow_indent_breaks_out
|
|
977
|
+
# CommonMark Example 255 相当(symref.md の実例と同型):
|
|
978
|
+
# 空行を挟んだ1スペースだけの継続はマーカー幅(2)未満のため dd に
|
|
979
|
+
# 属さず、dl の外のトップレベル段落になる
|
|
980
|
+
html = @md.compile("- **`2 + 3`**:\n\n 足し算です。\n")
|
|
981
|
+
assert_operator(html.index('</dd>'), :<, html.index('足し算'))
|
|
982
|
+
assert_operator(html.index('</dl>'), :<, html.index('足し算'))
|
|
983
|
+
end
|
|
984
|
+
|
|
985
|
+
def test_dlist_multiple_blank_lines_between_paragraphs
|
|
986
|
+
# CommonMark Example 262 相当: 2行以上の空行を挟んでも
|
|
987
|
+
# インデントが十分なら dd の続きとして扱う
|
|
988
|
+
html = @md.compile("- **term**:\n one\n\n\n two\n")
|
|
989
|
+
assert_operator(html.index('two'), :<, html.index('</dd>'))
|
|
990
|
+
end
|
|
991
|
+
|
|
992
|
+
def test_dlist_fence_after_blank_requires_marker_width
|
|
993
|
+
# フェンスは lazy continuation できない(空行の有無に関わらず
|
|
994
|
+
# マーカー幅以上のインデントが必要)。幅未満なら dd の外の
|
|
995
|
+
# トップレベルフェンスになる
|
|
996
|
+
html = @md.compile("- **term**:\n\n ```\n code\n ```\n")
|
|
997
|
+
assert_operator(html.index('</dd>'), :<, html.index('<pre>'))
|
|
998
|
+
end
|
|
999
|
+
|
|
1000
|
+
def test_item_list_blank_continuation_with_sufficient_indent_stays_nested
|
|
1001
|
+
# CommonMark Example 256 相当・doctree news/*.md の実例型:
|
|
1002
|
+
# 現行実装は空行に当たった時点で項目の外に「脱走」していたが、
|
|
1003
|
+
# マーカー幅以上のインデントなら項目(li)の中に留まる
|
|
1004
|
+
html = @md.compile("- one\n\n two\n")
|
|
1005
|
+
assert_operator(html.index('two'), :<, html.index('</li>'))
|
|
1006
|
+
end
|
|
1007
|
+
|
|
1008
|
+
def test_item_list_blank_continuation_with_insufficient_indent_breaks_out
|
|
1009
|
+
# CommonMark Example 255 相当: マーカー幅未満なら項目の外になる
|
|
1010
|
+
html = @md.compile("- one\n\n two\n")
|
|
1011
|
+
assert_operator(html.index('</li>'), :<, html.index('two'))
|
|
1012
|
+
end
|
|
1013
|
+
|
|
1014
|
+
def test_item_list_fence_after_blank_with_sufficient_indent_stays_nested
|
|
1015
|
+
# news/2_7_0.md 等の実例型:「- 説明文\n\n ```ruby\n...\n ```」の
|
|
1016
|
+
# ようにフェンス例が項目に属したまま描画される(従来は脱走していた)
|
|
1017
|
+
html = @md.compile("- one\n\n ```ruby\n code\n ```\n")
|
|
1018
|
+
assert_operator(html.index('<pre'), :<, html.index('</li>'))
|
|
1019
|
+
end
|
|
1020
|
+
|
|
1021
|
+
def test_item_list_shallow_direct_continuation_still_works
|
|
1022
|
+
# news/2_6_0 型(既存の RD 由来コンテンツ): 空行なしの直接継続は
|
|
1023
|
+
# 項目マーカーより浅いインデントでも項目の継続のまま(回帰確認)
|
|
1024
|
+
assert_equivalent_doc <<~RD
|
|
1025
|
+
= タイトル
|
|
1026
|
+
|
|
1027
|
+
* 項目の一行目が長くて
|
|
1028
|
+
折り返した継続行。
|
|
1029
|
+
|
|
1030
|
+
本文。
|
|
1031
|
+
RD
|
|
1032
|
+
end
|
|
1033
|
+
|
|
1034
|
+
def test_ordered_item_list_blank_continuation_requires_marker_width
|
|
1035
|
+
# 番号付きリストはマーカー文字列の幅がマーカーごとに変わる
|
|
1036
|
+
# (「1. 」なら3)。幅未満は空行を挟むと項目の外になる
|
|
1037
|
+
html = @md.compile("1. one\n\n two\n")
|
|
1038
|
+
assert_operator(html.index('</li>'), :<, html.index('two'))
|
|
1039
|
+
wide = @md.compile("1. one\n\n two\n")
|
|
1040
|
+
assert_operator(wide.index('two'), :<, wide.index('</li>'))
|
|
1041
|
+
end
|
|
1042
|
+
|
|
1043
|
+
# ---- インデントコードブロックの検知・警告 (CommonMark Example 264 相当) ----
|
|
1044
|
+
# 言語指定を推奨するため実装は非対応のまま。互換性を壊さず、検知したら
|
|
1045
|
+
# stderr に警告のみ出す
|
|
1046
|
+
|
|
1047
|
+
def test_warns_on_top_level_indented_code_block
|
|
1048
|
+
err = capture_stderr { @md.compile("説明。\n\n code like text\n") }
|
|
1049
|
+
assert_match(/warning/i, err)
|
|
1050
|
+
assert_match(/:\d+/, err) # ファイル名(または識別子):行番号
|
|
1051
|
+
# 描画そのものは変更しない(非互換のまま、警告のみ)
|
|
1052
|
+
html = @md.compile("説明。\n\n code like text\n")
|
|
1053
|
+
assert_match(%r{<p>\s*code like text\s*</p>}m, html)
|
|
1054
|
+
assert_not_match(/<pre>/, html)
|
|
1055
|
+
end
|
|
1056
|
+
|
|
1057
|
+
def test_no_warning_for_shallow_indent_paragraph
|
|
1058
|
+
err = capture_stderr { @md.compile("説明。\n\n two spaces only\n") }
|
|
1059
|
+
assert_equal('', err)
|
|
1060
|
+
end
|
|
1061
|
+
|
|
1062
|
+
def test_no_warning_for_indented_content_consumed_by_list_item
|
|
1063
|
+
# リストの中に正しく取り込まれるインデントは警告対象外
|
|
1064
|
+
# (リスト外・フェンス外のみが対象)
|
|
1065
|
+
err = capture_stderr { @md.compile("- one\n\n four space continuation\n") }
|
|
1066
|
+
assert_equal('', err)
|
|
1067
|
+
end
|
|
1068
|
+
|
|
1069
|
+
def test_no_warning_for_fenced_code_block
|
|
1070
|
+
err = capture_stderr { @md.compile("```ruby\n x = 1\n```\n") }
|
|
1071
|
+
assert_equal('', err)
|
|
1072
|
+
end
|
|
1073
|
+
|
|
1074
|
+
def test_md_ref_module_function_display_typemark
|
|
1075
|
+
# bitclust#282: Markdown ソースの [m:Kernel?.at_exit] は inline 復元時に
|
|
1076
|
+
# ".#" へ正規化される(RD の MethodSpec 互換)が、表示ラベルは DB
|
|
1077
|
+
# バージョンが 4.0 以降なら "?." に戻す。3.4 以前は ".#" のまま。
|
|
1078
|
+
# どちらの表記で書かれていても、ページの見出し表記と常に一致する
|
|
1079
|
+
src = "### def m(v) -> String\n\n" \
|
|
1080
|
+
"[m:Kernel?.at_exit] と [m:Kernel.#at_exit] を参照。\n"
|
|
1081
|
+
{ '3.4' => 'Kernel.#at_exit', '4.0' => 'Kernel?.at_exit' }.each do |version, label|
|
|
1082
|
+
db = BitClust::MethodDatabase.dummy("version" => version)
|
|
1083
|
+
md = BitClust::MDCompiler.new(@u, 1, { :database => db })
|
|
1084
|
+
html = compile_method(md, src)
|
|
1085
|
+
assert_equal(2, html.scan(">#{label}</a>").size,
|
|
1086
|
+
"version=#{version}\n#{html}")
|
|
1087
|
+
end
|
|
1088
|
+
end
|
|
1089
|
+
|
|
1090
|
+
# ---- Markdown リンク記法での rurema 参照(ラベル付きリンク) ----
|
|
1091
|
+
# [ラベル](m:spec) のように、リンク宛先へ参照 scheme を書けるようにする。
|
|
1092
|
+
# 呼び出し形を表示しつつメソッドへリンクする用途:
|
|
1093
|
+
# [`OpenSSL::Random.egd_bytes(filename, 255)`](m:OpenSSL::Random?.egd_bytes)
|
|
1094
|
+
|
|
1095
|
+
sub_test_case("md link with rurema reference destination") do
|
|
1096
|
+
def test_ref_link_with_code_span_label
|
|
1097
|
+
html = gfm_compiler.compile("# t\n\n[`Foo#bar(x, y)`](m:Foo#bar) を参照。\n")
|
|
1098
|
+
assert_include html, '<code>Foo#bar(x, y)</code>'
|
|
1099
|
+
assert_include html, 'href="dummy/method/'
|
|
1100
|
+
assert_not_include html, 'class="external"'
|
|
1101
|
+
end
|
|
1102
|
+
|
|
1103
|
+
def test_ref_link_plain_label
|
|
1104
|
+
html = gfm_compiler.compile("# t\n\n[文字列クラス](c:String) を参照。\n")
|
|
1105
|
+
assert_include html, '>文字列クラス</a>'
|
|
1106
|
+
assert_include html, 'href="dummy/class/'
|
|
1107
|
+
end
|
|
1108
|
+
|
|
1109
|
+
def test_ref_link_module_function_spec
|
|
1110
|
+
html = gfm_compiler.compile("# t\n\n[`open(name)`](m:Kernel?.open)\n")
|
|
1111
|
+
assert_include html, '<code>open(name)</code>'
|
|
1112
|
+
assert_include html, 'href="dummy/method/'
|
|
1113
|
+
end
|
|
1114
|
+
|
|
1115
|
+
def test_ref_link_doc_with_fragment_via_ref_scheme
|
|
1116
|
+
html = gfm_compiler.compile("# t\n\n[仕様](ref:d:spec/m17n#anc)\n")
|
|
1117
|
+
assert_include html, '#anc"'
|
|
1118
|
+
assert_include html, '>仕様</a>'
|
|
1119
|
+
end
|
|
1120
|
+
|
|
1121
|
+
def test_ref_link_method_with_fragment_via_ref_scheme
|
|
1122
|
+
html = gfm_compiler.compile("# t\n\n[フォーマット](ref:m:Foo#bar#fmt)\n")
|
|
1123
|
+
assert_include html, '#fmt"'
|
|
1124
|
+
assert_include html, '>フォーマット</a>'
|
|
1125
|
+
assert_include html, 'href="dummy/method/'
|
|
1126
|
+
end
|
|
1127
|
+
|
|
1128
|
+
def test_ref_link_same_page_anchor
|
|
1129
|
+
html = gfm_compiler.compile("# t\n\n[別名](ref:anc)\n")
|
|
1130
|
+
assert_include html, '<a href="#anc">別名</a>'
|
|
1131
|
+
end
|
|
1132
|
+
|
|
1133
|
+
def test_ref_link_lib
|
|
1134
|
+
html = gfm_compiler.compile("# t\n\n[JSON ライブラリ](lib:json)\n")
|
|
1135
|
+
assert_include html, '>JSON ライブラリ</a>'
|
|
1136
|
+
assert_include html, 'href="dummy/library/'
|
|
1137
|
+
end
|
|
1138
|
+
|
|
1139
|
+
def test_ref_link_man
|
|
1140
|
+
html = gfm_compiler.compile("# t\n\n[statx システムコール](man:statx(2linux))\n")
|
|
1141
|
+
assert_include html, 'href="http://man7.org/linux/man-pages/man2/statx.2.html"'
|
|
1142
|
+
assert_include html, '>statx システムコール</a>'
|
|
1143
|
+
assert_include html, 'class="external"'
|
|
1144
|
+
end
|
|
1145
|
+
|
|
1146
|
+
def test_ref_link_man_unresolvable_section_falls_back_to_label
|
|
1147
|
+
html = gfm_compiler.compile("# t\n\n[謎のマニュアル](man:foo(9zzz))\n")
|
|
1148
|
+
assert_include html, '謎のマニュアル'
|
|
1149
|
+
assert_not_include html, '<a '
|
|
1150
|
+
end
|
|
1151
|
+
|
|
1152
|
+
def test_unknown_scheme_stays_plain_text
|
|
1153
|
+
html = gfm_compiler.compile("# t\n\n[x](zzz:y)\n")
|
|
1154
|
+
assert_include html, '[x](zzz:y)'
|
|
1155
|
+
assert_not_include html, '<a '
|
|
1156
|
+
end
|
|
1157
|
+
|
|
1158
|
+
def test_label_is_escaped
|
|
1159
|
+
html = gfm_compiler.compile("# t\n\n[a<b](c:String)\n")
|
|
1160
|
+
assert_include html, '>a<b</a>'
|
|
1161
|
+
end
|
|
1162
|
+
|
|
1163
|
+
def test_ref_link_fires_link_checker
|
|
1164
|
+
recorder = Class.new do
|
|
1165
|
+
attr_reader :refs
|
|
1166
|
+
def initialize; @refs = []; end
|
|
1167
|
+
def note_ref(type, arg); @refs << "#{type}:#{arg}"; end
|
|
1168
|
+
end.new
|
|
1169
|
+
md = BitClust::MDCompiler.new(@u, 1,
|
|
1170
|
+
{ :database => @db, :gfm => true, :link_checker => recorder })
|
|
1171
|
+
md.compile("# t\n\n[`bar`](m:Foo#bar) と [s](c:String) と [仕様](ref:d:spec/m17n#anc)\n")
|
|
1172
|
+
assert_include recorder.refs, "m:Foo#bar"
|
|
1173
|
+
assert_include recorder.refs, "c:String"
|
|
1174
|
+
assert_include recorder.refs, "ref:d:spec/m17n#anc"
|
|
1175
|
+
end
|
|
1176
|
+
end
|
|
1177
|
+
|
|
1178
|
+
end
|
|
1179
|
+
|
|
1180
|
+
# lang 指定付きコードブロックのハイライト(ruby は Ripper ベースの
|
|
1181
|
+
# SyntaxHighlighter、その他の言語は Rouge、未知の言語はエスケープのみ)
|
|
1182
|
+
class TestMDCompilerRougeHighlight < Test::Unit::TestCase
|
|
1183
|
+
def setup
|
|
1184
|
+
@dummy = 'dummy'
|
|
1185
|
+
@u = BitClust::URLMapper.new(Hash.new { @dummy })
|
|
1186
|
+
@db = BitClust::MethodDatabase.dummy("version" => "2.0.0")
|
|
1187
|
+
@md = BitClust::MDCompiler.new(@u, 1, { :database => @db, :gfm => true })
|
|
1188
|
+
@rd = BitClust::RDCompiler.new(@u, 1, { :database => @db })
|
|
1189
|
+
end
|
|
1190
|
+
|
|
1191
|
+
def test_c_fence_is_highlighted_with_rouge
|
|
1192
|
+
html = @md.compile("```c\nint x = 1; /* comment */\n```\n")
|
|
1193
|
+
assert_match(/<pre class="highlight c">/, html)
|
|
1194
|
+
assert_match(%r{<span class="kt">int</span>}, html)
|
|
1195
|
+
assert_match(%r{<span class="cm">/\* comment \*/</span>}, html)
|
|
1196
|
+
end
|
|
1197
|
+
|
|
1198
|
+
def test_ruby_alias_rb_uses_bitclust_highlighter
|
|
1199
|
+
ruby_html = @md.compile("```ruby\nputs 1\n```\n")
|
|
1200
|
+
rb_html = @md.compile("```rb\nputs 1\n```\n")
|
|
1201
|
+
assert_equal(ruby_html.sub('highlight ruby', 'highlight rb'), rb_html)
|
|
1202
|
+
# bitclust の SyntaxHighlighter 由来のマークアップ(Rouge の Ruby lexer ではなく)
|
|
1203
|
+
assert_match(%r{<span class="nb">puts</span>}, rb_html)
|
|
1204
|
+
end
|
|
1205
|
+
|
|
1206
|
+
def test_unknown_lang_fence_is_escaped
|
|
1207
|
+
html = @md.compile("```nosuchlang\n<b>&raw</b>\n```\n")
|
|
1208
|
+
assert_match(/<pre class="highlight nosuchlang">/, html)
|
|
1209
|
+
assert_match(/<b>&raw<\/b>/, html)
|
|
1210
|
+
assert_not_match(/<b>/, html)
|
|
1211
|
+
end
|
|
1212
|
+
|
|
1213
|
+
def test_text_fence_is_escaped_without_highlight
|
|
1214
|
+
html = @md.compile("```text\n<b>plain</b>\n```\n")
|
|
1215
|
+
assert_match(/<b>plain<\/b>/, html)
|
|
1216
|
+
assert_not_match(/<b>plain/, html)
|
|
1217
|
+
end
|
|
1218
|
+
|
|
1219
|
+
# info string パースの直接テスト。ハイライト出力経由の検証は Ruby の
|
|
1220
|
+
# パーサ版差(同じ断片でもエラー報告の有無が変わる)で不具合がマスク
|
|
1221
|
+
# されうるため、返り値そのものを固定する
|
|
1222
|
+
class BitClust::MDCompiler; public :parse_fence_info; end
|
|
1223
|
+
data("lang のみ" => ["ruby", ["ruby", nil, false]],
|
|
1224
|
+
"invalid 付き" => ["ruby invalid", ["ruby", nil, true]],
|
|
1225
|
+
"title 付き" => ['ruby title="例"', ["ruby", "例", false]],
|
|
1226
|
+
"invalid + title" => ['ruby invalid title="SyntaxError の例"',
|
|
1227
|
+
["ruby", "SyntaxError の例", true]],
|
|
1228
|
+
"title 内エスケープ + invalid" => ['ruby invalid title="a\"b"',
|
|
1229
|
+
["ruby", 'a"b', true]],
|
|
1230
|
+
"未知の余分な語" => ["ruby foo", [nil, nil, false]],
|
|
1231
|
+
"空" => ["", [nil, nil, false]])
|
|
1232
|
+
def test_parse_fence_info(data)
|
|
1233
|
+
rest, expected = data
|
|
1234
|
+
assert_equal(expected, @md.parse_fence_info(rest))
|
|
1235
|
+
end
|
|
1236
|
+
|
|
1237
|
+
# ```ruby invalid: 構文として完全でないコード(SyntaxError の例・文法断片)を
|
|
1238
|
+
# Ripper の構文チェックなしで Rouge の Ruby lexer により色付けする(issue #251)
|
|
1239
|
+
def test_ruby_invalid_fence_uses_rouge_without_syntax_check
|
|
1240
|
+
# Ripper ではパースできない断片でもビルドエラーにならない
|
|
1241
|
+
html = @md.compile("```ruby invalid\nif cond then\n```\n")
|
|
1242
|
+
assert_match(/<pre class="highlight ruby">/, html)
|
|
1243
|
+
assert_match(%r{<span class="k">if</span>}, html)
|
|
1244
|
+
end
|
|
1245
|
+
|
|
1246
|
+
def test_ruby_invalid_fence_with_title
|
|
1247
|
+
html = @md.compile("```ruby invalid title=\"SyntaxError の例\"\ndef broken(\n```\n")
|
|
1248
|
+
assert_match(%r{<span class="caption">SyntaxError の例</span>}, html)
|
|
1249
|
+
assert_match(/<pre class="highlight ruby">/, html)
|
|
1250
|
+
assert_match(%r{<span class="k">def</span>}, html)
|
|
1251
|
+
end
|
|
1252
|
+
|
|
1253
|
+
def test_invalid_flag_on_other_lang_is_harmless
|
|
1254
|
+
plain = @md.compile("```c\nint x = 1;\n```\n")
|
|
1255
|
+
flagged = @md.compile("```c invalid\nint x = 1;\n```\n")
|
|
1256
|
+
assert_equal(plain, flagged)
|
|
1257
|
+
end
|
|
1258
|
+
|
|
1259
|
+
def test_ruby_fence_without_invalid_still_checks_syntax
|
|
1260
|
+
md = BitClust::MDCompiler.new(@u, 1,
|
|
1261
|
+
{ :database => @db, :gfm => true, :stop_on_syntax_error => false })
|
|
1262
|
+
html = md.compile("```ruby\nif cond then\n```\n")
|
|
1263
|
+
# invalid なしの ruby は従来どおり構文チェックされ、エラー時は
|
|
1264
|
+
# エスケープのみのフォールバック(色付けなし)になる
|
|
1265
|
+
assert_not_match(/<span class="k">/, html)
|
|
1266
|
+
assert_match(/if cond then/, html)
|
|
1267
|
+
end
|
|
1268
|
+
|
|
1269
|
+
def test_rd_emlist_with_c_lang_is_equivalent
|
|
1270
|
+
rd_src = "//emlist[キャプション][c]{\nint x = 1;\n//}\n"
|
|
1271
|
+
md_src = BitClust::RRDToMarkdown.convert(rd_src)
|
|
1272
|
+
assert_equal(@rd.compile(rd_src), @md.compile(md_src), "md source:\n#{md_src}")
|
|
1273
|
+
assert_match(%r{<span class="kt">int</span>}, @rd.compile(rd_src))
|
|
1274
|
+
end
|
|
1275
|
+
|
|
1276
|
+
end
|