bitclust-core 1.3.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/README.md +65 -0
  4. data/Rakefile +32 -0
  5. data/data/bitclust/catalog/ja_JP.UTF-8 +19 -1
  6. data/data/bitclust/searchpage/index.html +90 -0
  7. data/data/bitclust/template/class +69 -8
  8. data/data/bitclust/template/function +2 -2
  9. data/data/bitclust/template/layout +11 -3
  10. data/data/bitclust/template/method +2 -2
  11. data/data/bitclust/template/search +1 -1
  12. data/data/bitclust/template.epub/class +22 -1
  13. data/data/bitclust/template.epub/function +2 -2
  14. data/data/bitclust/template.epub/layout +1 -3
  15. data/data/bitclust/template.epub/method +2 -2
  16. data/data/bitclust/template.lillia/class +26 -5
  17. data/data/bitclust/template.lillia/layout +1 -3
  18. data/data/bitclust/template.lillia/method +2 -2
  19. data/data/bitclust/template.offline/class +48 -4
  20. data/data/bitclust/template.offline/function +2 -2
  21. data/data/bitclust/template.offline/layout +27 -3
  22. data/data/bitclust/template.offline/method +2 -2
  23. data/lib/bitclust/app.rb +16 -17
  24. data/lib/bitclust/capi_converter.rb +20 -0
  25. data/lib/bitclust/classentry.rb +67 -17
  26. data/lib/bitclust/compat.rb +2 -2
  27. data/lib/bitclust/completion.rb +58 -27
  28. data/lib/bitclust/crossrubyutils.rb +8 -5
  29. data/lib/bitclust/database.rb +28 -8
  30. data/lib/bitclust/doc_converter.rb +63 -0
  31. data/lib/bitclust/docentry.rb +1 -1
  32. data/lib/bitclust/entity_splitter.rb +221 -0
  33. data/lib/bitclust/entry.rb +47 -7
  34. data/lib/bitclust/functiondatabase.rb +13 -1
  35. data/lib/bitclust/functionentry.rb +5 -1
  36. data/lib/bitclust/functionreferenceparser.rb +4 -4
  37. data/lib/bitclust/generators/epub.rb +10 -3
  38. data/lib/bitclust/htmlutils.rb +1 -1
  39. data/lib/bitclust/include_graph.rb +375 -0
  40. data/lib/bitclust/include_pruner.rb +144 -0
  41. data/lib/bitclust/interface.rb +1 -1
  42. data/lib/bitclust/libraryentry.rb +8 -7
  43. data/lib/bitclust/lineinput.rb +7 -5
  44. data/lib/bitclust/link_checker.rb +179 -0
  45. data/lib/bitclust/markdown_bridge.rb +186 -0
  46. data/lib/bitclust/markdown_orchestrator.rb +267 -0
  47. data/lib/bitclust/markdown_to_rrd.rb +824 -0
  48. data/lib/bitclust/markdown_tree.rb +217 -0
  49. data/lib/bitclust/mdcompiler.rb +883 -0
  50. data/lib/bitclust/mdparser.rb +347 -0
  51. data/lib/bitclust/messagecatalog.rb +3 -3
  52. data/lib/bitclust/method_since_calculator.rb +117 -0
  53. data/lib/bitclust/methoddatabase.rb +128 -14
  54. data/lib/bitclust/methodentry.rb +125 -7
  55. data/lib/bitclust/methodid.rb +12 -11
  56. data/lib/bitclust/methodsignature.rb +10 -4
  57. data/lib/bitclust/nameutils.rb +41 -22
  58. data/lib/bitclust/preprocessor.rb +66 -29
  59. data/lib/bitclust/rdcompiler.rb +195 -95
  60. data/lib/bitclust/refsdatabase.rb +23 -8
  61. data/lib/bitclust/reloadable_request_handler.rb +72 -0
  62. data/lib/bitclust/requesthandler.rb +21 -16
  63. data/lib/bitclust/ridatabase.rb +10 -4
  64. data/lib/bitclust/rrd_to_markdown.rb +925 -0
  65. data/lib/bitclust/rrdparser.rb +196 -30
  66. data/lib/bitclust/runner.rb +17 -2
  67. data/lib/bitclust/screen.rb +80 -11
  68. data/lib/bitclust/search_index_generator.rb +288 -0
  69. data/lib/bitclust/searcher.rb +69 -20
  70. data/lib/bitclust/simplesearcher.rb +24 -12
  71. data/lib/bitclust/subcommand.rb +7 -1
  72. data/lib/bitclust/subcommands/ancestors_command.rb +9 -2
  73. data/lib/bitclust/subcommands/checklink_command.rb +54 -0
  74. data/lib/bitclust/subcommands/chm_command.rb +15 -7
  75. data/lib/bitclust/subcommands/classes_command.rb +5 -0
  76. data/lib/bitclust/subcommands/extract_command.rb +5 -0
  77. data/lib/bitclust/subcommands/htmlfile_command.rb +21 -7
  78. data/lib/bitclust/subcommands/init_command.rb +4 -1
  79. data/lib/bitclust/subcommands/list_command.rb +12 -4
  80. data/lib/bitclust/subcommands/lookup_command.rb +28 -9
  81. data/lib/bitclust/subcommands/methods_command.rb +10 -2
  82. data/lib/bitclust/subcommands/methodsince_command.rb +80 -0
  83. data/lib/bitclust/subcommands/preproc_command.rb +5 -0
  84. data/lib/bitclust/subcommands/property_command.rb +2 -1
  85. data/lib/bitclust/subcommands/searchpage_command.rb +102 -0
  86. data/lib/bitclust/subcommands/server_command.rb +10 -8
  87. data/lib/bitclust/subcommands/setup_command.rb +24 -10
  88. data/lib/bitclust/subcommands/statichtml_command.rb +129 -6
  89. data/lib/bitclust/subcommands/update_command.rb +110 -2
  90. data/lib/bitclust/syntax_highlighter.rb +48 -4
  91. data/lib/bitclust/textutils.rb +3 -3
  92. data/lib/bitclust/version.rb +1 -1
  93. data/lib/bitclust/version_badges.rb +90 -0
  94. data/lib/bitclust/whole_file_gate.rb +137 -0
  95. data/test/test_app.rb +269 -0
  96. data/test/test_bitclust.rb +2 -0
  97. data/test/test_capi_converter.rb +91 -0
  98. data/test/test_class_screen.rb +413 -0
  99. data/test/test_copy_doc_md.rb +53 -0
  100. data/test/test_doc_converter.rb +78 -0
  101. data/test/test_entity_splitter.rb +231 -0
  102. data/test/test_entry.rb +207 -2
  103. data/test/test_eol_warning.rb +43 -0
  104. data/test/test_functionreferenceparser.rb +11 -0
  105. data/test/test_include_graph.rb +640 -0
  106. data/test/test_include_pruner.rb +133 -0
  107. data/test/test_link_checker.rb +114 -0
  108. data/test/test_lookup_command.rb +135 -0
  109. data/test/test_markdown_bridge.rb +217 -0
  110. data/test/test_markdown_orchestrator.rb +478 -0
  111. data/test/test_markdown_to_rrd.rb +757 -0
  112. data/test/test_markdown_tree.rb +369 -0
  113. data/test/test_mdcompiler.rb +1276 -0
  114. data/test/test_mdparser.rb +573 -0
  115. data/test/test_method_screen.rb +166 -0
  116. data/test/test_method_since_calculator.rb +290 -0
  117. data/test/test_methoddatabase.rb +16 -0
  118. data/test/test_methodentry.rb +328 -0
  119. data/test/test_methodsignature.rb +3 -1
  120. data/test/test_methodsince_command.rb +168 -0
  121. data/test/test_nameutils.rb +22 -0
  122. data/test/test_preprocessor.rb +215 -0
  123. data/test/test_rdcompiler.rb +401 -30
  124. data/test/test_refsdatabase.rb +32 -0
  125. data/test/test_rrd_to_markdown.rb +968 -0
  126. data/test/test_rrdparser.rb +295 -2
  127. data/test/test_run_ruby_wasm.rb +54 -0
  128. data/test/test_run_worker_prelude.rb +83 -0
  129. data/test/test_runner.rb +51 -0
  130. data/test/test_search_index_generator.rb +513 -0
  131. data/test/test_search_screen.rb +48 -0
  132. data/test/test_searcher.rb +132 -0
  133. data/test/test_searchpage_command.rb +167 -0
  134. data/test/test_simplesearcher.rb +28 -0
  135. data/test/test_statichtml_command.rb +204 -0
  136. data/test/test_syntax_highlighter.rb +60 -0
  137. data/test/test_whole_file_gate.rb +175 -0
  138. data/theme/default/js/NOTICE +42 -0
  139. data/theme/default/js/run-worker.js +111 -0
  140. data/theme/default/js/run.js +350 -0
  141. data/theme/default/js/search_controller.js +134 -0
  142. data/theme/default/js/search_init.js +176 -0
  143. data/theme/default/js/search_navigation.js +110 -0
  144. data/theme/default/js/search_page.js +202 -0
  145. data/theme/default/js/search_ranker.js +244 -0
  146. data/theme/default/script.js +91 -18
  147. data/theme/default/search.css +149 -0
  148. data/theme/default/style.css +193 -21
  149. data/theme/default/syntax-highlight.css +8 -0
  150. data/theme/lillia/style.css +45 -1
  151. metadata +161 -20
  152. data/README +0 -32
@@ -0,0 +1,573 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test/unit'
4
+ require 'stringio'
5
+ require 'fileutils'
6
+ require 'bitclust/mdparser'
7
+ require 'bitclust/rrdparser'
8
+ require 'bitclust/methoddatabase'
9
+
10
+ # MDParser: Markdown ソースを直接パースして DB エントリを作る(フェーズ3 M3)。
11
+ # 構造(クラス・エントリ・関係)は同等 rd の RRDParser と一致し、
12
+ # source には md 断片がそのまま入る。
13
+ class TestMDParser < Test::Unit::TestCase
14
+ PARAMS = { "version" => "3.4" }
15
+
16
+ def parse_md(md, libname = "_builtin")
17
+ db = BitClust::MethodDatabase.dummy(PARAMS)
18
+ lib = BitClust::MDParser.new(db).parse(StringIO.new(md), libname, PARAMS)
19
+ [db, lib]
20
+ end
21
+
22
+ def parse_rd(rd, libname = "_builtin")
23
+ db = BitClust::MethodDatabase.dummy(PARAMS)
24
+ lib = BitClust::RRDParser.new(db).parse(StringIO.new(rd), libname, PARAMS)
25
+ [db, lib]
26
+ end
27
+
28
+ def test_class_with_include_and_method
29
+ md = <<~MD
30
+ ---
31
+ library: _builtin
32
+ include:
33
+ - Enumerable
34
+ ---
35
+ # class Array < Object
36
+
37
+ 配列クラス。
38
+
39
+ ## Instance Methods
40
+
41
+ ### def index(val) -> Integer
42
+
43
+ 説明。
44
+ MD
45
+ rd = <<~RD
46
+ = class Array < Object
47
+ include Enumerable
48
+
49
+ 配列クラス。
50
+
51
+ == Instance Methods
52
+
53
+ --- index(val) -> Integer
54
+
55
+ 説明。
56
+ RD
57
+ _, mlib = parse_md(md)
58
+ _, rlib = parse_rd(rd)
59
+
60
+ mc = mlib.classes.first
61
+ rc = rlib.classes.first
62
+ assert_equal rc.name, mc.name
63
+ assert_equal rc.type, mc.type
64
+ assert_equal rc.superclass&.name, mc.superclass&.name
65
+ assert_equal rc.included.map(&:name), mc.included.map(&:name)
66
+ assert_equal rc.entries.map(&:names), mc.entries.map(&:names)
67
+ assert_equal rc.entries.first.type, mc.entries.first.type
68
+
69
+ # source は md のまま
70
+ assert_equal "配列クラス。", mc.source
71
+ assert_equal "### def index(val) -> Integer\n\n説明。\n", mc.entries.first.source
72
+ end
73
+
74
+ def test_module_and_class_methods_section
75
+ md = <<~MD
76
+ ---
77
+ library: _builtin
78
+ ---
79
+ # module Comparable
80
+
81
+ 比較演算モジュール。
82
+
83
+ ## Class Methods
84
+
85
+ ### def new -> Comparable
86
+
87
+ 生成。
88
+ MD
89
+ _, lib = parse_md(md)
90
+ c = lib.classes.first
91
+ assert_equal "Comparable", c.name
92
+ assert_equal :module, c.type
93
+ assert_equal :singleton_method, c.entries.first.type
94
+ end
95
+
96
+ def test_constants_section_with_const_keyword
97
+ md = <<~MD
98
+ ---
99
+ library: _builtin
100
+ ---
101
+ # class Float < Numeric
102
+
103
+ 浮動小数点数。
104
+
105
+ ## Constants
106
+
107
+ ### const DIG -> Integer
108
+
109
+ 桁数。
110
+ MD
111
+ _, lib = parse_md(md)
112
+ e = lib.classes.first.entries.first
113
+ assert_equal :constant, e.type
114
+ assert_equal ["DIG"], e.names
115
+ assert_equal "### const DIG -> Integer\n\n桁数。\n", e.source
116
+ end
117
+
118
+ def test_reopen_with_dynamic_include_across_files
119
+ # ライブラリはファイル単位で組み立てる(module 定義とreopen が別ファイル、
120
+ # 同じ libname で順にパースすると同一 Library に合流する)
121
+ module_md = <<~MD
122
+ ---
123
+ library: mylib
124
+ ---
125
+ # module MyModule
126
+
127
+ モジュール。
128
+ MD
129
+ reopen_md = <<~MD
130
+ ---
131
+ library: mylib
132
+ include:
133
+ - MyModule
134
+ ---
135
+ # reopen Object
136
+ MD
137
+ db = BitClust::MethodDatabase.dummy(PARAMS)
138
+ BitClust::MDParser.new(db).parse(StringIO.new(module_md), "mylib", PARAMS)
139
+ BitClust::MDParser.new(db).parse(StringIO.new(reopen_md), "mylib", PARAMS)
140
+ assert_equal ["MyModule"], db.get_class("Object").dynamically_included.map(&:name)
141
+ end
142
+
143
+ def test_gvar_signature
144
+ md = <<~MD
145
+ ---
146
+ library: _builtin
147
+ ---
148
+ # module Kernel
149
+
150
+ カーネル。
151
+
152
+ ## Special Variables
153
+
154
+ ### gvar $stdin -> IO
155
+
156
+ 標準入力。
157
+ MD
158
+ _, lib = parse_md(md)
159
+ e = lib.classes.first.entries.first
160
+ assert_equal :special_variable, e.type
161
+ assert_equal "### gvar $stdin -> IO\n\n標準入力。\n", e.source
162
+ end
163
+
164
+ def test_relations_require_single_entity
165
+ # 案B: 関係(include 等)を持つファイルは単一エンティティでなければならない
166
+ md = <<~MD
167
+ ---
168
+ library: _builtin
169
+ include:
170
+ - Enumerable
171
+ ---
172
+ # class Foo < Object
173
+
174
+ ふー。
175
+
176
+ # class Bar < Object
177
+
178
+ ばー。
179
+ MD
180
+ assert_raise(BitClust::ParseError) { parse_md(md) }
181
+ end
182
+
183
+ def test_unsupported_signature_forms_are_rejected
184
+ # RBS 形式(MARKUP_SPEC §3.2 で構想)と self. プレフィクスは描画側
185
+ # (MethodSignature.parse)が受理できないため、パース時に拒否する
186
+ base = <<~MD
187
+ ---
188
+ library: _builtin
189
+ ---
190
+ # class Foo < Object
191
+
192
+ テスト。
193
+
194
+ ## Instance Methods
195
+
196
+ %s
197
+
198
+ 説明。
199
+ MD
200
+ ['### def each: () { (untyped) -> void } -> self',
201
+ '### def self.new(size = 0) -> Foo'].each do |sig|
202
+ assert_raise(BitClust::ParseError, sig) { parse_md(base % sig) }
203
+ end
204
+ end
205
+
206
+ def test_library_file_with_category
207
+ md = <<~MD
208
+ ---
209
+ type: library
210
+ category: FileFormat
211
+ ---
212
+ CSV を扱うライブラリです。
213
+ MD
214
+ _, lib = parse_md(md, "csv")
215
+ assert_equal "FileFormat", lib.category
216
+ assert_equal "CSV を扱うライブラリです。", lib.source
217
+ end
218
+
219
+ def test_since_directive_in_body
220
+ md = <<~MD
221
+ ---
222
+ library: _builtin
223
+ ---
224
+ # class Array < Object
225
+
226
+ 配列。
227
+
228
+ ## Instance Methods
229
+
230
+ \#@since 3.4
231
+ ### def newmethod -> nil
232
+
233
+ 新しい。
234
+ \#@end
235
+ MD
236
+ _, lib = parse_md(md)
237
+ assert_equal [["newmethod"]], lib.classes.first.entries.map(&:names)
238
+ end
239
+
240
+ def test_update_by_markdowntree
241
+ # MarkdownTree 駆動の組み立て: lib ファイル → メンバー(reopen 後置)、
242
+ # 版ゲート外のライブラリ/メンバーはスキップ
243
+ require 'tmpdir'
244
+ Dir.mktmpdir do |root|
245
+ write = ->(rel, s) {
246
+ path = File.join(root, rel)
247
+ FileUtils.mkdir_p(File.dirname(path))
248
+ File.write(path, s)
249
+ }
250
+ write.call('mylib.md', <<~MD)
251
+ ---
252
+ type: library
253
+ category: Text
254
+ ---
255
+ mylib の説明。
256
+ MD
257
+ write.call('mylib/MyModule.md', <<~MD)
258
+ ---
259
+ library: mylib
260
+ ---
261
+ # module MyModule
262
+
263
+ モジュール。
264
+ MD
265
+ # 辞書順で module より先に来る reopen ファイル(後置されることを確認)
266
+ write.call('mylib/Kernel.md', <<~MD)
267
+ ---
268
+ library: mylib
269
+ include:
270
+ - MyModule
271
+ ---
272
+ # reopen Object
273
+ MD
274
+ write.call('mylib/Gone.md', <<~MD)
275
+ ---
276
+ library: mylib
277
+ until: "3.0"
278
+ ---
279
+ # class Gone < Object
280
+
281
+ 3.0 で消えた。
282
+ MD
283
+ write.call('oldlib.md', <<~MD)
284
+ ---
285
+ type: library
286
+ until: "2.0"
287
+ ---
288
+ 古いライブラリ。
289
+ MD
290
+
291
+ db = BitClust::MethodDatabase.dummy(PARAMS)
292
+ db.update_by_markdowntree(root)
293
+
294
+ assert_equal ["mylib"], db.libraries.map(&:name)
295
+ lib = db.fetch_library("mylib")
296
+ assert_equal "Text", lib.category
297
+ assert_equal "mylib の説明。", lib.source
298
+ # reopen はメソッド定義が無い限り lib.classes に載らない(rd と同じ)
299
+ assert_equal %w[MyModule], lib.classes.map(&:name).sort
300
+ assert_equal ["MyModule"], db.get_class("Object").dynamically_included.map(&:name)
301
+ # source_location は md の実パス
302
+ assert_equal "#{root}/mylib.md", lib.source_location.file
303
+ assert_equal "#{root}/mylib/MyModule.md", db.get_class("MyModule").source_location.file
304
+ end
305
+ end
306
+
307
+ def test_update_by_markdowntree_gated_memberships
308
+ # 多重所属(ゲート付き library リスト): 版によって所属ライブラリが変わる。
309
+ # 3.4 では builtin のみ、3.0 では builtin と oldlib の両方に属する
310
+ require 'tmpdir'
311
+ Dir.mktmpdir do |root|
312
+ write = ->(rel, s) {
313
+ path = File.join(root, rel)
314
+ FileUtils.mkdir_p(File.dirname(path))
315
+ File.write(path, s)
316
+ }
317
+ write.call('builtin.md', "---\ntype: library\n---\nbuiltin。\n")
318
+ write.call('oldlib.md', "---\ntype: library\n---\noldlib。\n")
319
+ write.call('oldlib/Shared.md', <<~'MD')
320
+ ---
321
+ library:
322
+ - builtin
323
+ #@until 3.2
324
+ - oldlib
325
+ #@end
326
+ ---
327
+ # class Shared < Object
328
+
329
+ 共有クラス。
330
+ MD
331
+
332
+ db34 = BitClust::MethodDatabase.dummy("version" => "3.4")
333
+ db34.update_by_markdowntree(root)
334
+ assert_equal %w[Shared], db34.fetch_library("builtin").classes.map(&:name)
335
+ assert_equal [], db34.fetch_library("oldlib").classes.map(&:name)
336
+
337
+ db30 = BitClust::MethodDatabase.dummy("version" => "3.0")
338
+ db30.update_by_markdowntree(root)
339
+ assert_equal %w[Shared], db30.fetch_library("builtin").classes.map(&:name)
340
+ assert_equal %w[Shared], db30.fetch_library("oldlib").classes.map(&:name)
341
+ end
342
+ end
343
+
344
+ def test_update_by_markdowntree_parse_error_suggests_gem_update
345
+ # manual/ が新しい bitclust を必要とする記法を含むとき、古い gem では
346
+ # パースエラーになる。原因へたどり着けるよう gem 更新の案内を添える
347
+ require 'tmpdir'
348
+ Dir.mktmpdir do |root|
349
+ write = ->(rel, s) {
350
+ path = File.join(root, rel)
351
+ FileUtils.mkdir_p(File.dirname(path))
352
+ File.write(path, s)
353
+ }
354
+ write.call('mylib.md', "---\ntype: library\n---\nmylib。\n")
355
+ write.call('mylib/Foo.md', <<~MD)
356
+ ---
357
+ library: mylib
358
+ ---
359
+ # class Foo < Object
360
+
361
+ クラス。
362
+
363
+ ## Instance Methods
364
+
365
+ ### def bar: (Integer) -> String
366
+
367
+ RBS 形式のシグネチャ(未対応)。
368
+ MD
369
+
370
+ db = BitClust::MethodDatabase.dummy(PARAMS)
371
+ error = assert_raise(BitClust::ParseError) do
372
+ db.update_by_markdowntree(root)
373
+ end
374
+ assert_include error.message, "unsupported method signature"
375
+ assert_include error.message, "bitclust の gem を更新すると解決するかもしれません"
376
+ end
377
+ end
378
+
379
+ def test_copy_doc_md_keeps_relative_location
380
+ # Windows CI 対応: doc エントリの source_location は md_root と同じ形
381
+ # (相対で渡せば相対 manual/doc/...)で格納する。絶対パス化すると
382
+ # Windows のドライブレター(D:)のコロンで Location の
383
+ # デシリアライズ(file:line 分割)が壊れる
384
+ require 'tmpdir'
385
+ Dir.mktmpdir do |root|
386
+ write = ->(rel, s) {
387
+ path = File.join(root, rel)
388
+ FileUtils.mkdir_p(File.dirname(path))
389
+ File.write(path, s)
390
+ }
391
+ write.call('manual/api/mylib.md', <<~MD)
392
+ ---
393
+ type: library
394
+ category: Text
395
+ ---
396
+ mylib の説明。
397
+ MD
398
+ write.call('manual/doc/help.md', "= ヘルプ\n\n本文。\n")
399
+ write.call('manual/doc/parts.md', "\#@include(included)\n")
400
+ write.call('manual/doc/included.md', "= 部品\n\n部品本文。\n")
401
+
402
+ db = BitClust::MethodDatabase.new(File.join(root, 'db'))
403
+ db.init
404
+ db.transaction {
405
+ db.propset 'version', '3.4'
406
+ db.propset 'encoding', 'utf-8'
407
+ }
408
+ Dir.chdir(root) do
409
+ db.transaction {
410
+ db.update_by_markdowntree('manual/api')
411
+ }
412
+ end
413
+ doc_ids = db.docs.map(&:name).sort
414
+ assert_equal %w[help parts], doc_ids # include 参照先はページにしない
415
+ assert_equal 'manual/doc/help.md',
416
+ db.docs.find { |d| d.name == 'help' }.source_location.file.to_s
417
+ end
418
+ end
419
+
420
+ def test_location_deserialize_drops_line
421
+ # source_location の永続化はファイルのみ(行番号は生成物の diff を安定させる
422
+ # ため落とす。master b1d81d7 のポリシー)。旧形式の file:line も
423
+ # 後方互換で file だけを読む。Windows で壊れないよう doc の格納パスは
424
+ # 相対に保つ(copy_doc_md 側の慣例)
425
+ db = BitClust::MethodDatabase.dummy(PARAMS)
426
+ e = BitClust::DocEntry.new(db, 'x')
427
+ e.__send__(:_set_properties,
428
+ { 'source_location' => 'manual/doc/help.md:12' })
429
+ loc = e.instance_variable_get(:@source_location)
430
+ assert_equal 'manual/doc/help.md', loc.file
431
+ assert_nil loc.line
432
+ end
433
+
434
+ def test_function_parser
435
+ # capi: キーワード無しの ### 全行がシグネチャ。source は本文のみ(md のまま)
436
+ require 'bitclust/functiondatabase'
437
+ md = <<~MD
438
+ ### VALUE rb_ary_new()
439
+
440
+ 空の Ruby の配列を作成し返します。
441
+
442
+ ```
443
+ VALUE ary = rb_ary_new();
444
+ ```
445
+
446
+ ### static int rb_ary_size(VALUE ary)
447
+
448
+ 内部関数です。
449
+ MD
450
+ db = BitClust::FunctionDatabase.dummy(PARAMS)
451
+ io = StringIO.new(md)
452
+ def io.path = "array.c.md"
453
+ BitClust::MDFunctionParser.new(db).parse(io, "array.c", PARAMS)
454
+ funcs = db.functions.sort_by(&:name)
455
+ assert_equal %w[rb_ary_new rb_ary_size], funcs.map(&:name)
456
+ f = funcs.first
457
+ assert_equal "array.c", f.filename
458
+ assert_equal "VALUE", f.type
459
+ # body はヘッダ直後の空行込み(レガシー FunctionReferenceParser と同じ)
460
+ assert_equal "\n空の Ruby の配列を作成し返します。\n\n```\nVALUE ary = rb_ary_new();\n```\n\n",
461
+ f.source
462
+ assert funcs.last.private
463
+ end
464
+
465
+ def test_multi_entity_bundle_without_relations
466
+ md = <<~MD
467
+ ---
468
+ library: _builtin
469
+ ---
470
+ # class Errno::EPERM < SystemCallError
471
+
472
+ EPERM。
473
+
474
+ # class Errno::ENOENT < SystemCallError
475
+
476
+ ENOENT。
477
+ MD
478
+ _, lib = parse_md(md)
479
+ assert_equal %w[Errno::EPERM Errno::ENOENT], lib.classes.map(&:name)
480
+ end
481
+ def test_method_attribute_list_sets_kind
482
+ md = <<~MD
483
+ ---
484
+ library: _builtin
485
+ ---
486
+ # class Hoge
487
+
488
+ クラスの説明。
489
+
490
+ ## Instance Methods
491
+
492
+ ### def fuga -> nil
493
+ {: nomethod}
494
+
495
+ 説明のためのエントリです。
496
+
497
+ ### def piyo -> nil
498
+ {: undef}
499
+ MD
500
+ db, = parse_md(md)
501
+ assert_equal(:nomethod, db.get_method(BitClust::MethodSpec.parse('Hoge#fuga')).kind)
502
+ assert_equal(:undefined, db.get_method(BitClust::MethodSpec.parse('Hoge#piyo')).kind)
503
+ end
504
+
505
+ def test_method_attribute_on_every_alias_signature
506
+ md = <<~MD
507
+ ---
508
+ library: _builtin
509
+ ---
510
+ # class Hoge
511
+
512
+ クラスの説明。
513
+
514
+ ## Instance Methods
515
+
516
+ ### def fuga -> nil
517
+ {: nomethod}
518
+ ### def fuga2 -> nil
519
+ {: nomethod}
520
+
521
+ 説明のためのエントリです。
522
+ MD
523
+ db, = parse_md(md)
524
+ fuga = db.get_method(BitClust::MethodSpec.parse('Hoge#fuga'))
525
+ assert_equal(:nomethod, fuga.kind)
526
+ assert_equal(['fuga', 'fuga2'], fuga.names)
527
+ end
528
+
529
+ def test_method_attribute_since_is_recorded
530
+ md = <<~MD
531
+ ---
532
+ library: _builtin
533
+ ---
534
+ # class Hoge
535
+
536
+ クラスの説明。
537
+
538
+ ## Instance Methods
539
+
540
+ ### def fuga -> nil
541
+ {: since="3.1"}
542
+
543
+ 説明のためのエントリです。
544
+ MD
545
+ db, = parse_md(md)
546
+ entry = db.get_method(BitClust::MethodSpec.parse('Hoge#fuga'))
547
+ assert_equal('3.1', entry.since_of('fuga'))
548
+ end
549
+
550
+ def test_method_attribute_since_differs_per_alias_signature
551
+ md = <<~MD
552
+ ---
553
+ library: _builtin
554
+ ---
555
+ # class Hoge
556
+
557
+ クラスの説明。
558
+
559
+ ## Instance Methods
560
+
561
+ ### def fuga -> nil
562
+ {: since="2.0.0"}
563
+ ### def fuga2 -> nil
564
+ {: since="3.0"}
565
+
566
+ 説明のためのエントリです。
567
+ MD
568
+ db, = parse_md(md)
569
+ entry = db.get_method(BitClust::MethodSpec.parse('Hoge#fuga'))
570
+ assert_equal('2.0.0', entry.since_of('fuga'))
571
+ assert_equal('3.0', entry.since_of('fuga2'))
572
+ end
573
+ end