bitclust-core 1.6.0 → 1.7.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/Rakefile +1 -0
- data/data/bitclust/catalog/ja_JP.UTF-8 +4 -2
- data/data/bitclust/template/function-index +1 -0
- data/data/bitclust/template/layout +1 -0
- data/data/bitclust/template.offline/function-index +1 -0
- data/data/bitclust/template.offline/layout +3 -0
- data/lib/bitclust/classentry.rb +1 -1
- data/lib/bitclust/docentry.rb +1 -1
- data/lib/bitclust/functionentry.rb +1 -1
- data/lib/bitclust/irb.rb +68 -0
- data/lib/bitclust/markdown_exporter.rb +311 -0
- data/lib/bitclust/mdcompiler.rb +15 -1
- data/lib/bitclust/mdparser.rb +42 -1
- data/lib/bitclust/methoddatabase.rb +4 -0
- data/lib/bitclust/methodentry.rb +24 -0
- data/lib/bitclust/methodsignature.rb +1 -1
- data/lib/bitclust/preprocessor.rb +4 -1
- data/lib/bitclust/rbs_overload_matcher.rb +179 -0
- data/lib/bitclust/rbs_sig_importer.rb +276 -0
- data/lib/bitclust/rbs_signatures.rb +125 -0
- data/lib/bitclust/rdcompiler.rb +12 -1
- data/lib/bitclust/runner.rb +2 -0
- data/lib/bitclust/screen.rb +4 -0
- data/lib/bitclust/search_index_generator.rb +28 -10
- data/lib/bitclust/searcher.rb +12 -2
- data/lib/bitclust/subcommands/rbssig_command.rb +99 -0
- data/lib/bitclust/subcommands/setup_command.rb +4 -1
- data/lib/bitclust/subcommands/statichtml_command.rb +59 -0
- data/lib/bitclust/version.rb +1 -1
- data/lib/bitclust-irb.rb +2 -0
- data/test/test_irb_plugin.rb +118 -0
- data/test/test_link_checker.rb +1 -1
- data/test/test_markdown_exporter.rb +260 -0
- data/test/test_mdcompiler.rb +47 -5
- data/test/test_mdparser.rb +219 -4
- data/test/test_methodentry.rb +111 -0
- data/test/test_methodsignature.rb +3 -1
- data/test/test_preprocessor.rb +26 -0
- data/test/test_rbs_overload_matcher.rb +170 -0
- data/test/test_rbs_sig_importer.rb +237 -0
- data/test/test_rbssig_command.rb +186 -0
- data/test/test_rdcompiler.rb +99 -1
- data/test/test_search_index_generator.rb +81 -0
- data/test/test_setup_command.rb +21 -0
- data/test/test_statichtml_command.rb +137 -0
- data/theme/default/js/run.js +14 -0
- data/theme/default/js/search_init.js +3 -1
- data/theme/default/js/search_page.js +7 -1
- data/theme/default/js/version_switcher.js +147 -0
- data/theme/default/search.css +21 -0
- data/theme/default/style.css +83 -7
- data/theme/lillia/style.css +26 -0
- metadata +35 -1
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test/unit'
|
|
4
|
+
require 'stringio'
|
|
5
|
+
require 'bitclust'
|
|
6
|
+
require 'bitclust/markdown_exporter'
|
|
7
|
+
require 'bitclust/mdparser'
|
|
8
|
+
require 'bitclust/methoddatabase'
|
|
9
|
+
require 'bitclust/functiondatabase'
|
|
10
|
+
require 'bitclust/subcommands/statichtml_command'
|
|
11
|
+
|
|
12
|
+
# MarkdownExporter: statichtml --markdown-output 用に、HTML の各ページと
|
|
13
|
+
# 対になる Markdown を前処理済みソースから組み立てる。
|
|
14
|
+
class TestMarkdownExporter < Test::Unit::TestCase
|
|
15
|
+
PARAMS = { "version" => "3.4" }
|
|
16
|
+
|
|
17
|
+
def setup
|
|
18
|
+
@urlmapper = BitClust::Subcommands::StatichtmlCommand::URLMapperEx.new(
|
|
19
|
+
:suffix => '.html',
|
|
20
|
+
:fs_casesensitive => true
|
|
21
|
+
)
|
|
22
|
+
@urlmapper.bitclust_html_base = '..'
|
|
23
|
+
@exporter = BitClust::MarkdownExporter.new(@urlmapper, '.html')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def parse_md(md, libname = "_builtin")
|
|
27
|
+
db = BitClust::MethodDatabase.dummy(PARAMS)
|
|
28
|
+
lib = BitClust::MDParser.new(db).parse(StringIO.new(md), libname, PARAMS)
|
|
29
|
+
[db, lib]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
sub_test_case("convert_text") do
|
|
33
|
+
def test_bare_method_ref
|
|
34
|
+
assert_equal "[Array#index](../method/Array/i/index.md) を参照",
|
|
35
|
+
@exporter.convert_text("[m:Array#index] を参照")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_bare_class_ref
|
|
39
|
+
assert_equal "[String](../class/String.md)",
|
|
40
|
+
@exporter.convert_text("[c:String]")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def test_bare_library_ref
|
|
44
|
+
assert_equal "[csv](../library/csv.md)",
|
|
45
|
+
@exporter.convert_text("[lib:csv]")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_bare_doc_ref
|
|
49
|
+
assert_equal "[spec/literal](../doc/spec=2fliteral.md)",
|
|
50
|
+
@exporter.convert_text("[d:spec/literal]")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_bare_function_ref
|
|
54
|
+
assert_equal "[rb_ary_new](../function/rb_ary_new.md)",
|
|
55
|
+
@exporter.convert_text("[f:rb_ary_new]")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_module_function_ref
|
|
59
|
+
# md 表記の ?. は内部表記 .# に正規化して URL を引く(ラベルは書かれたまま)
|
|
60
|
+
assert_equal "[Kernel?.puts](../method/Kernel/m/puts.md)",
|
|
61
|
+
@exporter.convert_text("[m:Kernel?.puts]")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def test_method_ref_with_escaped_brackets
|
|
65
|
+
# [m:Array#\[\]] のようにエスケープされた参照。URL はエスケープを解いた
|
|
66
|
+
# メソッド名から引き、ラベルは書かれたまま(md として正しい形)を保つ
|
|
67
|
+
assert_equal "[Array#\\[\\]](../method/Array/i/=5b=5d.md)",
|
|
68
|
+
@exporter.convert_text("[m:Array#\\[\\]]")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_labeled_ref
|
|
72
|
+
assert_equal "[個数](../method/Array/i/size.md)",
|
|
73
|
+
@exporter.convert_text("[個数](m:Array#size)")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_labeled_ref_with_code_span_label
|
|
77
|
+
assert_equal "[`Array#size`](../method/Array/i/size.md)",
|
|
78
|
+
@exporter.convert_text("[`Array#size`](m:Array#size)")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def test_markdown_link_kept
|
|
82
|
+
assert_equal "[Ruby](https://www.ruby-lang.org/)",
|
|
83
|
+
@exporter.convert_text("[Ruby](https://www.ruby-lang.org/)")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def test_anchor_link_kept
|
|
87
|
+
assert_equal "[前述の例](#example)",
|
|
88
|
+
@exporter.convert_text("[前述の例](#example)")
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def test_url_ref
|
|
92
|
+
assert_equal "[https://www.ruby-lang.org/](https://www.ruby-lang.org/)",
|
|
93
|
+
@exporter.convert_text("[url:https://www.ruby-lang.org/]")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_ref_in_page
|
|
97
|
+
assert_equal "[overview](#overview)",
|
|
98
|
+
@exporter.convert_text("[ref:overview]")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def test_ref_cross_page
|
|
102
|
+
assert_equal "[spec/literal#num](../doc/spec=2fliteral.md#num)",
|
|
103
|
+
@exporter.convert_text("[ref:d:spec/literal#num]")
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def test_code_span_is_not_converted
|
|
107
|
+
assert_equal "`[m:Array#index]`",
|
|
108
|
+
@exporter.convert_text("`[m:Array#index]`")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def test_code_fence_is_not_converted
|
|
112
|
+
src = "```ruby\n# [m:Array#index]\n```\n[m:Array#index]\n"
|
|
113
|
+
expected = "```ruby\n# [m:Array#index]\n```\n[Array#index](../method/Array/i/index.md)\n"
|
|
114
|
+
assert_equal expected, @exporter.convert_text(src)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def test_unknown_ref_type_is_kept
|
|
118
|
+
assert_equal "[man:printf(3)]", @exporter.convert_text("[man:printf(3)]")
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def test_unresolvable_method_spec_is_kept
|
|
122
|
+
# クラス名とメソッド名に分割できない指定はそのまま残す
|
|
123
|
+
assert_equal "[m:broken spec]", @exporter.convert_text("[m:broken spec]")
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def test_escaped_bracket_is_kept
|
|
127
|
+
assert_equal "\\[m:Array#index]", @exporter.convert_text("\\[m:Array#index]")
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
sub_test_case("pages") do
|
|
132
|
+
CLASS_MD = <<~MD
|
|
133
|
+
---
|
|
134
|
+
library: _builtin
|
|
135
|
+
---
|
|
136
|
+
# class Array < Object
|
|
137
|
+
|
|
138
|
+
配列クラス。[c:Enumerable] も参照。
|
|
139
|
+
|
|
140
|
+
## Class Methods
|
|
141
|
+
|
|
142
|
+
### def self.new(size) -> Array
|
|
143
|
+
|
|
144
|
+
生成。
|
|
145
|
+
|
|
146
|
+
## Instance Methods
|
|
147
|
+
|
|
148
|
+
### def index(val) -> Integer
|
|
149
|
+
### def find_index(val) -> Integer
|
|
150
|
+
|
|
151
|
+
位置を返します。
|
|
152
|
+
MD
|
|
153
|
+
|
|
154
|
+
def test_class_page
|
|
155
|
+
_, lib = parse_md(CLASS_MD)
|
|
156
|
+
md = @exporter.class_page(lib.classes.first)
|
|
157
|
+
assert_equal <<~MD, md
|
|
158
|
+
# class Array < Object
|
|
159
|
+
|
|
160
|
+
配列クラス。[Enumerable](../class/Enumerable.md) も参照。
|
|
161
|
+
|
|
162
|
+
## Class Methods
|
|
163
|
+
|
|
164
|
+
- [new](../method/Array/s/new.md)
|
|
165
|
+
|
|
166
|
+
## Instance Methods
|
|
167
|
+
|
|
168
|
+
- [find_index](../method/Array/i/find_index.md)
|
|
169
|
+
- [index](../method/Array/i/index.md)
|
|
170
|
+
MD
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def test_module_page_heading
|
|
174
|
+
md = <<~MD
|
|
175
|
+
---
|
|
176
|
+
library: _builtin
|
|
177
|
+
---
|
|
178
|
+
# module Comparable
|
|
179
|
+
|
|
180
|
+
比較。
|
|
181
|
+
MD
|
|
182
|
+
_, lib = parse_md(md)
|
|
183
|
+
page = @exporter.class_page(lib.classes.first)
|
|
184
|
+
assert page.start_with?("# module Comparable\n"), page
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def test_method_page
|
|
188
|
+
_, lib = parse_md(CLASS_MD)
|
|
189
|
+
entry = lib.classes.first.entries.find { |e| e.names.include?("index") }
|
|
190
|
+
@urlmapper.bitclust_html_base = '../../..'
|
|
191
|
+
md = @exporter.method_page("Array#index", [entry])
|
|
192
|
+
assert_equal <<~MD, md
|
|
193
|
+
# Array#index
|
|
194
|
+
|
|
195
|
+
### def index(val) -> Integer
|
|
196
|
+
### def find_index(val) -> Integer
|
|
197
|
+
|
|
198
|
+
位置を返します。
|
|
199
|
+
MD
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def test_method_page_normalizes_module_function_name
|
|
203
|
+
_, lib = parse_md(CLASS_MD)
|
|
204
|
+
entry = lib.classes.first.entries.first
|
|
205
|
+
md = @exporter.method_page("Kernel.#puts", [entry])
|
|
206
|
+
assert md.start_with?("# Kernel?.puts\n"), md
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def test_library_page
|
|
210
|
+
md = <<~MD
|
|
211
|
+
---
|
|
212
|
+
type: library
|
|
213
|
+
category: FileFormat
|
|
214
|
+
---
|
|
215
|
+
CSV を扱うライブラリです。
|
|
216
|
+
MD
|
|
217
|
+
_, lib = parse_md(md, "csv")
|
|
218
|
+
page = @exporter.library_page(lib)
|
|
219
|
+
assert_equal <<~MD, page
|
|
220
|
+
# library csv
|
|
221
|
+
|
|
222
|
+
CSV を扱うライブラリです。
|
|
223
|
+
MD
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def test_doc_page
|
|
227
|
+
db = BitClust::MethodDatabase.dummy(PARAMS)
|
|
228
|
+
entry = BitClust::DocEntry.new(db, "help")
|
|
229
|
+
entry.title = "ヘルプ"
|
|
230
|
+
entry.source = "\n[c:String] を参照。\n"
|
|
231
|
+
page = @exporter.doc_page(entry)
|
|
232
|
+
assert_equal <<~MD, page
|
|
233
|
+
# ヘルプ
|
|
234
|
+
|
|
235
|
+
[String](../class/String.md) を参照。
|
|
236
|
+
MD
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def test_function_page
|
|
240
|
+
require 'bitclust/functiondatabase'
|
|
241
|
+
md = <<~MD
|
|
242
|
+
### VALUE rb_ary_new()
|
|
243
|
+
|
|
244
|
+
空の配列を返します。
|
|
245
|
+
MD
|
|
246
|
+
db = BitClust::FunctionDatabase.dummy(PARAMS)
|
|
247
|
+
io = StringIO.new(md)
|
|
248
|
+
def io.path = "array.c.md"
|
|
249
|
+
BitClust::MDFunctionParser.new(db).parse(io, "array.c", PARAMS)
|
|
250
|
+
page = @exporter.function_page(db.functions.first)
|
|
251
|
+
assert_equal <<~MD, page
|
|
252
|
+
# rb_ary_new
|
|
253
|
+
|
|
254
|
+
### VALUE rb_ary_new()
|
|
255
|
+
|
|
256
|
+
空の配列を返します。
|
|
257
|
+
MD
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
end
|
data/test/test_mdcompiler.rb
CHANGED
|
@@ -23,7 +23,7 @@ class TestMDCompiler < Test::Unit::TestCase
|
|
|
23
23
|
@rd = BitClust::RDCompiler.new(@u, 1, { :database => @db })
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
def compile_method(compiler, src)
|
|
26
|
+
def compile_method(compiler, src, rbs_signature_overloads: nil)
|
|
27
27
|
method_entry = Object.new
|
|
28
28
|
mock(method_entry).source { src }
|
|
29
29
|
mock(method_entry).index_id.any_times { "dummy" }
|
|
@@ -32,6 +32,7 @@ class TestMDCompiler < Test::Unit::TestCase
|
|
|
32
32
|
mock(method_entry).names.any_times { [] }
|
|
33
33
|
mock(method_entry).since_map.any_times { {} }
|
|
34
34
|
mock(method_entry).until_map.any_times { {} }
|
|
35
|
+
mock(method_entry).rbs_signature_overloads.any_times { rbs_signature_overloads }
|
|
35
36
|
compiler.compile_method(method_entry)
|
|
36
37
|
end
|
|
37
38
|
|
|
@@ -58,6 +59,47 @@ class TestMDCompiler < Test::Unit::TestCase
|
|
|
58
59
|
|
|
59
60
|
# ---- メソッドエントリ ----
|
|
60
61
|
|
|
62
|
+
# RBS シグネチャ表示: MDCompiler も entry_chunk をオーバーライドしている
|
|
63
|
+
# ので、rd 経路と同じ位置(説明 <dd> の直前)に同じ <dt> が出ること
|
|
64
|
+
def test_rbs_signatures_dt_matches_rd_output
|
|
65
|
+
rd_src = "--- index(val) -> Integer\n\n説明\n"
|
|
66
|
+
overloads = [{'segments' => [['t', '() -> void']]}]
|
|
67
|
+
md_src = BitClust::RRDToMarkdown.convert(rd_src)
|
|
68
|
+
rd_html = compile_method(@rd, rd_src, rbs_signature_overloads: overloads)
|
|
69
|
+
md_html = compile_method(@md, md_src, rbs_signature_overloads: overloads)
|
|
70
|
+
assert_include md_html,
|
|
71
|
+
'<dt class="rbs-signature"><code>() → void</code></dt>'
|
|
72
|
+
assert_equal rd_html, md_html
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# チャンク振り分けは md 側のシグネチャ行(### def ...)でも rd と同じに
|
|
76
|
+
# 動くこと(MDCompiler は行を "--- " に正規化してから照合する)
|
|
77
|
+
def test_rbs_signatures_chunk_assignment_matches_rd_output
|
|
78
|
+
rd_src = <<~RD
|
|
79
|
+
--- index(sub) -> Integer | nil
|
|
80
|
+
|
|
81
|
+
文字列で探す。
|
|
82
|
+
|
|
83
|
+
--- index(pattern) {|s| ... } -> Integer | nil
|
|
84
|
+
|
|
85
|
+
ブロックで探す。
|
|
86
|
+
RD
|
|
87
|
+
overloads = [
|
|
88
|
+
{'segments' => [['t', '(String sub) -> Integer?']],
|
|
89
|
+
'params' => ['sub'], 'arity' => [1, 1]},
|
|
90
|
+
{'segments' => [['t', '(Regexp pattern) { (String) -> bool } -> Integer?']],
|
|
91
|
+
'params' => ['pattern'], 'arity' => [1, 1], 'block' => 'req'},
|
|
92
|
+
]
|
|
93
|
+
md_src = BitClust::RRDToMarkdown.convert(rd_src)
|
|
94
|
+
rd_html = compile_method(@rd, rd_src, rbs_signature_overloads: overloads)
|
|
95
|
+
md_html = compile_method(@md, md_src, rbs_signature_overloads: overloads)
|
|
96
|
+
assert_match(
|
|
97
|
+
%r!<code>index\(pattern\).*?</dt>\n<dt class="rbs-signature"><code>\(Regexp pattern\) \{ \(String\) → bool \} → Integer\?</code></dt>\n<dd!m,
|
|
98
|
+
md_html)
|
|
99
|
+
assert_equal 2, md_html.scan('rbs-signature').size
|
|
100
|
+
assert_equal rd_html, md_html
|
|
101
|
+
end
|
|
102
|
+
|
|
61
103
|
def test_method_signature_and_paragraph
|
|
62
104
|
assert_equivalent_method <<~RD
|
|
63
105
|
--- index(val) -> Integer
|
|
@@ -752,10 +794,10 @@ class TestMDCompiler < Test::Unit::TestCase
|
|
|
752
794
|
def test_gfm_table_alignment
|
|
753
795
|
md = "# T\n\n| a | b | c |\n|:--|:-:|--:|\n| 1 | 2 | 3 |\n"
|
|
754
796
|
html = gfm_compiler.compile(md)
|
|
755
|
-
assert_include html, '<th
|
|
756
|
-
assert_include html, '<th
|
|
757
|
-
assert_include html, '<th
|
|
758
|
-
assert_include html, '<td
|
|
797
|
+
assert_include html, '<th style="text-align:left">a</th>'
|
|
798
|
+
assert_include html, '<th style="text-align:center">b</th>'
|
|
799
|
+
assert_include html, '<th style="text-align:right">c</th>'
|
|
800
|
+
assert_include html, '<td style="text-align:right">3</td>'
|
|
759
801
|
end
|
|
760
802
|
|
|
761
803
|
def test_gfm_pipe_line_without_delimiter_is_paragraph
|
data/test/test_mdparser.rb
CHANGED
|
@@ -82,7 +82,7 @@ class TestMDParser < Test::Unit::TestCase
|
|
|
82
82
|
|
|
83
83
|
## Class Methods
|
|
84
84
|
|
|
85
|
-
### def new -> Comparable
|
|
85
|
+
### def Comparable.new -> Comparable
|
|
86
86
|
|
|
87
87
|
生成。
|
|
88
88
|
MD
|
|
@@ -181,7 +181,7 @@ class TestMDParser < Test::Unit::TestCase
|
|
|
181
181
|
end
|
|
182
182
|
|
|
183
183
|
def test_unsupported_signature_forms_are_rejected
|
|
184
|
-
# RBS 形式(MARKUP_SPEC §3.2
|
|
184
|
+
# RBS 形式(MARKUP_SPEC §3.2 で構想)は描画側
|
|
185
185
|
# (MethodSignature.parse)が受理できないため、パース時に拒否する
|
|
186
186
|
base = <<~MD
|
|
187
187
|
---
|
|
@@ -197,12 +197,83 @@ class TestMDParser < Test::Unit::TestCase
|
|
|
197
197
|
|
|
198
198
|
説明。
|
|
199
199
|
MD
|
|
200
|
-
['### def each: () { (untyped) -> void } -> self'
|
|
201
|
-
'### def self.new(size = 0) -> Foo'].each do |sig|
|
|
200
|
+
['### def each: () { (untyped) -> void } -> self'].each do |sig|
|
|
202
201
|
assert_raise(BitClust::ParseError, sig) { parse_md(base % sig) }
|
|
203
202
|
end
|
|
204
203
|
end
|
|
205
204
|
|
|
205
|
+
def test_self_and_class_name_prefixed_signatures
|
|
206
|
+
# クラスメソッドは `def self.name` / `def Klass.name` 形式でも書ける。
|
|
207
|
+
# どちらも singleton method として登録され、名前にプレフィクスは含まない
|
|
208
|
+
md = <<~MD
|
|
209
|
+
---
|
|
210
|
+
library: _builtin
|
|
211
|
+
---
|
|
212
|
+
# class Foo < Object
|
|
213
|
+
|
|
214
|
+
テスト。
|
|
215
|
+
|
|
216
|
+
## Class Methods
|
|
217
|
+
|
|
218
|
+
### def self.build(x) -> Foo
|
|
219
|
+
|
|
220
|
+
self. 形式。
|
|
221
|
+
|
|
222
|
+
### def Foo.create(x) -> Foo
|
|
223
|
+
|
|
224
|
+
Klass. 形式。
|
|
225
|
+
MD
|
|
226
|
+
_, lib = parse_md(md)
|
|
227
|
+
entries = lib.classes.first.entries
|
|
228
|
+
assert_equal [["build"], ["create"]], entries.map(&:names)
|
|
229
|
+
assert_equal [:singleton_method, :singleton_method], entries.map(&:type)
|
|
230
|
+
# source には書かれたままの md が入る
|
|
231
|
+
assert_include entries[0].source, "### def self.build(x) -> Foo"
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def test_main_prefixed_signature_on_object_page
|
|
235
|
+
# object main のページでは main. プレフィクスで特異メソッドを書ける
|
|
236
|
+
# (main は fatal・ARGF.class と同様に文法上クラス名として扱われる)
|
|
237
|
+
md = <<~MD
|
|
238
|
+
---
|
|
239
|
+
library: _builtin
|
|
240
|
+
---
|
|
241
|
+
# object main
|
|
242
|
+
|
|
243
|
+
テスト。
|
|
244
|
+
|
|
245
|
+
## Class Methods
|
|
246
|
+
|
|
247
|
+
### def main.include(*modules) -> self
|
|
248
|
+
|
|
249
|
+
main. 形式。
|
|
250
|
+
MD
|
|
251
|
+
_, lib = parse_md(md)
|
|
252
|
+
entries = lib.classes.first.entries
|
|
253
|
+
assert_equal [["include"]], entries.map(&:names)
|
|
254
|
+
assert_equal [:singleton_method], entries.map(&:type)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def test_self_prefixed_signature_in_instance_methods_section_is_rejected
|
|
258
|
+
# self. は singleton 確定なので Instance Methods セクションとは矛盾する
|
|
259
|
+
md = <<~MD
|
|
260
|
+
---
|
|
261
|
+
library: _builtin
|
|
262
|
+
---
|
|
263
|
+
# class Foo < Object
|
|
264
|
+
|
|
265
|
+
テスト。
|
|
266
|
+
|
|
267
|
+
## Instance Methods
|
|
268
|
+
|
|
269
|
+
### def self.bar -> nil
|
|
270
|
+
|
|
271
|
+
矛盾。
|
|
272
|
+
MD
|
|
273
|
+
error = assert_raise(BitClust::ParseError) { parse_md(md) }
|
|
274
|
+
assert_include error.message, "signature crash"
|
|
275
|
+
end
|
|
276
|
+
|
|
206
277
|
def test_library_file_with_category
|
|
207
278
|
md = <<~MD
|
|
208
279
|
---
|
|
@@ -341,6 +412,41 @@ class TestMDParser < Test::Unit::TestCase
|
|
|
341
412
|
end
|
|
342
413
|
end
|
|
343
414
|
|
|
415
|
+
def test_update_by_markdowntree_parse_error_suggests_gem_update
|
|
416
|
+
# manual/ が新しい bitclust を必要とする記法を含むとき、古い gem では
|
|
417
|
+
# パースエラーになる。原因へたどり着けるよう gem 更新の案内を添える
|
|
418
|
+
require 'tmpdir'
|
|
419
|
+
Dir.mktmpdir do |root|
|
|
420
|
+
write = ->(rel, s) {
|
|
421
|
+
path = File.join(root, rel)
|
|
422
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
423
|
+
File.write(path, s)
|
|
424
|
+
}
|
|
425
|
+
write.call('mylib.md', "---\ntype: library\n---\nmylib。\n")
|
|
426
|
+
write.call('mylib/Foo.md', <<~MD)
|
|
427
|
+
---
|
|
428
|
+
library: mylib
|
|
429
|
+
---
|
|
430
|
+
# class Foo < Object
|
|
431
|
+
|
|
432
|
+
クラス。
|
|
433
|
+
|
|
434
|
+
## Instance Methods
|
|
435
|
+
|
|
436
|
+
### def bar: (Integer) -> String
|
|
437
|
+
|
|
438
|
+
RBS 形式のシグネチャ(未対応)。
|
|
439
|
+
MD
|
|
440
|
+
|
|
441
|
+
db = BitClust::MethodDatabase.dummy(PARAMS)
|
|
442
|
+
error = assert_raise(BitClust::ParseError) do
|
|
443
|
+
db.update_by_markdowntree(root)
|
|
444
|
+
end
|
|
445
|
+
assert_include error.message, "unsupported method signature"
|
|
446
|
+
assert_include error.message, "bitclust の gem を更新すると解決するかもしれません"
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
344
450
|
def test_copy_doc_md_keeps_relative_location
|
|
345
451
|
# Windows CI 対応: doc エントリの source_location は md_root と同じ形
|
|
346
452
|
# (相対で渡せば相対 manual/doc/...)で格納する。絶対パス化すると
|
|
@@ -535,4 +641,113 @@ class TestMDParser < Test::Unit::TestCase
|
|
|
535
641
|
assert_equal('2.0.0', entry.since_of('fuga'))
|
|
536
642
|
assert_equal('3.0', entry.since_of('fuga2'))
|
|
537
643
|
end
|
|
644
|
+
|
|
645
|
+
def test_entry_style_bare_def_in_singleton_section_is_rejected
|
|
646
|
+
# MARKUP_SPEC §3.1: 特異メソッドはプレフィクス必須(def 行だけで
|
|
647
|
+
# メソッド種別が分かるようにする)
|
|
648
|
+
md = <<~MD
|
|
649
|
+
---
|
|
650
|
+
library: _builtin
|
|
651
|
+
---
|
|
652
|
+
# class Foo < Object
|
|
653
|
+
|
|
654
|
+
テスト。
|
|
655
|
+
|
|
656
|
+
## Class Methods
|
|
657
|
+
|
|
658
|
+
### def build(x) -> Foo
|
|
659
|
+
|
|
660
|
+
プレフィクスなし。
|
|
661
|
+
MD
|
|
662
|
+
error = assert_raise(BitClust::ParseError) { parse_md(md) }
|
|
663
|
+
assert_include error.message, "singleton method entry must be written as `def Foo.name' or `def self.name'"
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
def test_entry_style_keyword_section_mismatch_is_rejected
|
|
667
|
+
# rurema/doctree#3291 型の分類間違い: キーワードとセクションの不一致
|
|
668
|
+
base = <<~MD
|
|
669
|
+
---
|
|
670
|
+
library: _builtin
|
|
671
|
+
---
|
|
672
|
+
# class Foo < Object
|
|
673
|
+
|
|
674
|
+
テスト。
|
|
675
|
+
|
|
676
|
+
## %s
|
|
677
|
+
|
|
678
|
+
### %s
|
|
679
|
+
|
|
680
|
+
不一致。
|
|
681
|
+
MD
|
|
682
|
+
[
|
|
683
|
+
# Class Methods 配下の const(#3291 の実例)
|
|
684
|
+
['Class Methods', 'const CURRENT -> Foo', "entry keyword `const' does not match this section (expected `def')"],
|
|
685
|
+
# Constants 配下の gvar
|
|
686
|
+
['Constants', 'gvar $foo -> Foo', "entry keyword `gvar' does not match this section (expected `const')"],
|
|
687
|
+
# Instance Methods 配下の const
|
|
688
|
+
['Instance Methods', 'const BAR -> Integer', "entry keyword `const' does not match this section (expected `def')"],
|
|
689
|
+
].each do |section, sig, message|
|
|
690
|
+
error = assert_raise(BitClust::ParseError, "#{section} / #{sig}") do
|
|
691
|
+
parse_md(base % [section, sig])
|
|
692
|
+
end
|
|
693
|
+
assert_include error.message, message
|
|
694
|
+
end
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
def test_entry_style_module_functions_require_keyword
|
|
698
|
+
md = <<~MD
|
|
699
|
+
---
|
|
700
|
+
library: _builtin
|
|
701
|
+
---
|
|
702
|
+
# module Foo
|
|
703
|
+
|
|
704
|
+
テスト。
|
|
705
|
+
|
|
706
|
+
## Module Functions
|
|
707
|
+
|
|
708
|
+
### def bar -> nil
|
|
709
|
+
|
|
710
|
+
module_function キーワードなし。
|
|
711
|
+
MD
|
|
712
|
+
error = assert_raise(BitClust::ParseError) { parse_md(md) }
|
|
713
|
+
assert_include error.message, "entry keyword `def' does not match this section (expected `module_function def')"
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
def test_entry_style_accepts_consistent_forms
|
|
717
|
+
md = <<~MD
|
|
718
|
+
---
|
|
719
|
+
library: _builtin
|
|
720
|
+
---
|
|
721
|
+
# class Foo < Object
|
|
722
|
+
|
|
723
|
+
テスト。
|
|
724
|
+
|
|
725
|
+
## Class Methods
|
|
726
|
+
|
|
727
|
+
### def Foo.build(x) -> Foo
|
|
728
|
+
|
|
729
|
+
Klass 形式。
|
|
730
|
+
|
|
731
|
+
### def self.create(x) -> Foo
|
|
732
|
+
|
|
733
|
+
self 形式。
|
|
734
|
+
|
|
735
|
+
## Instance Methods
|
|
736
|
+
|
|
737
|
+
### def each {|x| ... } -> self
|
|
738
|
+
|
|
739
|
+
素の def。
|
|
740
|
+
|
|
741
|
+
## Constants
|
|
742
|
+
|
|
743
|
+
### const BAR -> Integer
|
|
744
|
+
|
|
745
|
+
定数。
|
|
746
|
+
MD
|
|
747
|
+
_, lib = parse_md(md)
|
|
748
|
+
entries = lib.classes.first.entries
|
|
749
|
+
assert_equal [["build"], ["create"], ["each"], ["BAR"]], entries.map(&:names)
|
|
750
|
+
assert_equal [:singleton_method, :singleton_method, :instance_method, :constant],
|
|
751
|
+
entries.map(&:type)
|
|
752
|
+
end
|
|
538
753
|
end
|