bitclust-core 1.6.1 → 1.8.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/class-index +0 -1
- data/data/bitclust/template/function-index +1 -0
- data/data/bitclust/template/layout +1 -0
- data/data/bitclust/template.epub/class +5 -5
- data/data/bitclust/template.epub/class-index +0 -1
- data/data/bitclust/template.epub/contents +7 -4
- data/data/bitclust/template.epub/layout +4 -4
- data/data/bitclust/template.offline/class-index +0 -1
- 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/generators/epub.rb +69 -5
- data/lib/bitclust/irb.rb +74 -0
- data/lib/bitclust/markdown_bridge.rb +11 -6
- data/lib/bitclust/markdown_exporter.rb +311 -0
- data/lib/bitclust/markdown_tree.rb +118 -11
- data/lib/bitclust/mdcompiler.rb +15 -1
- data/lib/bitclust/mdparser.rb +42 -1
- data/lib/bitclust/methoddatabase.rb +17 -4
- data/lib/bitclust/methodentry.rb +24 -0
- data/lib/bitclust/methodsignature.rb +1 -1
- data/lib/bitclust/nameutils.rb +13 -0
- data/lib/bitclust/preprocessor.rb +4 -37
- 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/remote.rb +405 -0
- data/lib/bitclust/runner.rb +7 -10
- data/lib/bitclust/screen.rb +6 -1
- data/lib/bitclust/search_index_generator.rb +28 -10
- data/lib/bitclust/searcher.rb +54 -25
- data/lib/bitclust/subcommands/chm_command.rb +1 -1
- data/lib/bitclust/subcommands/epub_command.rb +2 -2
- data/lib/bitclust/subcommands/extract_command.rb +7 -0
- data/lib/bitclust/subcommands/htmlfile_command.rb +7 -0
- data/lib/bitclust/subcommands/methods_command.rb +5 -28
- data/lib/bitclust/subcommands/query_command.rb +1 -0
- data/lib/bitclust/subcommands/rbssig_command.rb +99 -0
- data/lib/bitclust/subcommands/server_command.rb +21 -10
- data/lib/bitclust/subcommands/setup_command.rb +32 -11
- data/lib/bitclust/subcommands/statichtml_command.rb +59 -0
- data/lib/bitclust/syntax_highlighter.rb +65 -4
- data/lib/bitclust/user_dirs.rb +62 -0
- data/lib/bitclust/version.rb +1 -1
- data/lib/bitclust-irb.rb +2 -0
- data/test/test_chm_command.rb +22 -0
- data/test/test_epub_command.rb +60 -0
- data/test/test_epub_generator.rb +330 -0
- data/test/test_extract_command.rb +45 -0
- data/test/test_irb_plugin.rb +212 -0
- data/test/test_link_checker.rb +1 -1
- data/test/test_markdown_bridge.rb +35 -0
- data/test/test_markdown_exporter.rb +260 -0
- data/test/test_markdown_tree.rb +144 -0
- data/test/test_mdcompiler.rb +47 -5
- data/test/test_mdparser.rb +223 -4
- data/test/test_methodentry.rb +111 -0
- data/test/test_methods_command.rb +32 -0
- data/test/test_methodsignature.rb +3 -1
- data/test/test_opensearchdescription_screen.rb +39 -0
- data/test/test_preprocessor.rb +26 -0
- data/test/test_query_command.rb +30 -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_remote.rb +374 -0
- data/test/test_runner.rb +61 -1
- data/test/test_search_index_generator.rb +81 -0
- data/test/test_searcher.rb +63 -0
- data/test/test_server_command.rb +41 -0
- data/test/test_setup_command.rb +164 -0
- data/test/test_statichtml_command.rb +137 -0
- data/test/test_syntax_highlighter.rb +124 -0
- data/test/test_user_dirs.rb +62 -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 +71 -2
- data/lib/bitclust/ridatabase.rb +0 -359
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'test/unit'
|
|
3
|
+
require 'tmpdir'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'stringio'
|
|
6
|
+
require 'json'
|
|
7
|
+
require 'bitclust'
|
|
8
|
+
require 'bitclust/methoddatabase'
|
|
9
|
+
require 'bitclust/searcher'
|
|
10
|
+
require 'bitclust/irb'
|
|
11
|
+
|
|
12
|
+
# bitclust-irb gem の irb コマンド(refe)。リモート検索そのものは
|
|
13
|
+
# test_remote.rb で検証する。
|
|
14
|
+
#
|
|
15
|
+
# テストリスト:
|
|
16
|
+
# [x] TerminalView が io: に出力する(既定は従来どおり $stdout)
|
|
17
|
+
# [x] Irb.lookup がメソッドを検索して整形結果を io へ書く
|
|
18
|
+
# [x] Irb.lookup がクラスも引ける
|
|
19
|
+
# [x] 空のパターンは使い方を表示して例外にしない
|
|
20
|
+
# [x] 見つからないパターンはメッセージを io へ書いて例外にしない
|
|
21
|
+
# [x] require で IRB::Command::Base のサブクラスとして登録される
|
|
22
|
+
# [x] Searcher#local_database? は既定の場所に DB が無ければ false・あれば true
|
|
23
|
+
# [x] Irb.lookup: db が与えられていれば remote を使わない
|
|
24
|
+
# [x] Irb.lookup: ローカル DB が無ければ remote で検索する
|
|
25
|
+
# [x] Irb.lookup: remote が nil なら bitclust setup を案内する
|
|
26
|
+
# [x] refe の CLI(Searcher#exec)も DB が無ければ remote で検索する
|
|
27
|
+
class TestBitClustIrbPlugin < Test::Unit::TestCase
|
|
28
|
+
FOO_MD = <<~'MD'
|
|
29
|
+
---
|
|
30
|
+
library: foo
|
|
31
|
+
---
|
|
32
|
+
# class Foo < Object
|
|
33
|
+
|
|
34
|
+
クラスの説明。
|
|
35
|
+
|
|
36
|
+
## Instance Methods
|
|
37
|
+
|
|
38
|
+
### def bar -> nil
|
|
39
|
+
|
|
40
|
+
bar の説明。
|
|
41
|
+
MD
|
|
42
|
+
|
|
43
|
+
BASE_URL = 'https://docs.ruby-lang.org/ja/latest/'
|
|
44
|
+
INDEX_JS = "var search_data = #{JSON.generate(index: [
|
|
45
|
+
{ name: 'String', full_name: 'String', type: 'class', path: 'class/String.html' },
|
|
46
|
+
{ name: 'gsub', full_name: 'String#gsub', type: 'instance_method', path: 'method/String/i/gsub.html' },
|
|
47
|
+
])};\n"
|
|
48
|
+
PAGES = {
|
|
49
|
+
'js/search_data.js' => INDEX_JS,
|
|
50
|
+
'method/String/i/gsub.md' => "# String#gsub\n\n置き換えます。\n",
|
|
51
|
+
}.freeze
|
|
52
|
+
|
|
53
|
+
def build_markdown_db(dir)
|
|
54
|
+
api = File.join(dir, 'manual', 'api')
|
|
55
|
+
FileUtils.mkdir_p(File.join(api, 'foo'))
|
|
56
|
+
File.write(File.join(api, 'foo.md'), "---\ntype: library\n---\nfoo ライブラリ。\n")
|
|
57
|
+
File.write(File.join(api, 'foo', 'Foo.md'), FOO_MD)
|
|
58
|
+
prefix = File.join(dir, 'db')
|
|
59
|
+
db = BitClust::MethodDatabase.new(prefix)
|
|
60
|
+
db.init
|
|
61
|
+
db.transaction do
|
|
62
|
+
db.propset('version', '3.4')
|
|
63
|
+
db.propset('encoding', 'utf-8')
|
|
64
|
+
end
|
|
65
|
+
db.transaction do
|
|
66
|
+
db.update_by_markdowntree(api)
|
|
67
|
+
end
|
|
68
|
+
BitClust::MethodDatabase.new(prefix)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def with_db(&block)
|
|
72
|
+
Dir.mktmpdir do |dir|
|
|
73
|
+
yield build_markdown_db(dir)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# 既定の DB 探索(環境変数・設定ファイル)がどれも当たらない状態
|
|
78
|
+
DB_ENV_KEYS = %w[REFE2_SERVER BITCLUST_SERVER REFE2_DATADIR BITCLUST_DATADIR
|
|
79
|
+
HOME XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME].freeze
|
|
80
|
+
|
|
81
|
+
def without_local_db
|
|
82
|
+
saved = DB_ENV_KEYS.to_h {|k| [k, ENV[k]] }
|
|
83
|
+
Dir.mktmpdir do |home|
|
|
84
|
+
DB_ENV_KEYS.each {|k| ENV.delete(k) }
|
|
85
|
+
ENV['HOME'] = home
|
|
86
|
+
yield home
|
|
87
|
+
end
|
|
88
|
+
ensure
|
|
89
|
+
saved.each {|k, v| v ? ENV[k] = v : ENV.delete(k) }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def fake_remote(dir, pages = PAGES)
|
|
93
|
+
fetcher = lambda do |uri, _headers|
|
|
94
|
+
rel = uri.to_s.delete_prefix(BASE_URL)
|
|
95
|
+
pages.key?(rel) ? [200, pages[rel].dup] : [404, '']
|
|
96
|
+
end
|
|
97
|
+
BitClust::Remote.new(base_url: BASE_URL, fetcher: fetcher, cache_dir: File.join(dir, 'cache'))
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def never_remote(dir)
|
|
101
|
+
BitClust::Remote.new(base_url: BASE_URL, cache_dir: File.join(dir, 'cache'),
|
|
102
|
+
fetcher: ->(_uri, _headers) { flunk 'remote must not be used' })
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def test_terminal_view_writes_to_given_io
|
|
106
|
+
out = StringIO.new
|
|
107
|
+
view = BitClust::TerminalView.new(BitClust::Plain.new, {}, io: out)
|
|
108
|
+
view.send(:puts, 'hello')
|
|
109
|
+
assert_equal("hello\n", out.string)
|
|
110
|
+
assert_same(out, view.io)
|
|
111
|
+
assert_same($stdout, BitClust::TerminalView.new(BitClust::Plain.new, {}).io)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def test_lookup_method
|
|
115
|
+
with_db do |db|
|
|
116
|
+
out = StringIO.new
|
|
117
|
+
BitClust::Irb.lookup('Foo#bar', db: db, io: out)
|
|
118
|
+
assert_match(/Foo#bar/, out.string)
|
|
119
|
+
assert_match(/bar の説明。/, out.string)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def test_lookup_class
|
|
124
|
+
with_db do |db|
|
|
125
|
+
out = StringIO.new
|
|
126
|
+
BitClust::Irb.lookup('Foo', db: db, io: out)
|
|
127
|
+
assert_match(/class Foo < Object/, out.string)
|
|
128
|
+
assert_match(/クラスの説明。/, out.string)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def test_lookup_empty_pattern_shows_usage
|
|
133
|
+
with_db do |db|
|
|
134
|
+
out = StringIO.new
|
|
135
|
+
assert_nothing_raised do
|
|
136
|
+
BitClust::Irb.lookup('', db: db, io: out)
|
|
137
|
+
end
|
|
138
|
+
assert_match(/refe/, out.string)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def test_lookup_not_found_reports_instead_of_raising
|
|
143
|
+
with_db do |db|
|
|
144
|
+
out = StringIO.new
|
|
145
|
+
assert_nothing_raised do
|
|
146
|
+
BitClust::Irb.lookup('NoSuchClass#no_such_method', db: db, io: out)
|
|
147
|
+
end
|
|
148
|
+
assert_match(/no such method/, out.string)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def test_command_is_registered_for_irb
|
|
153
|
+
begin
|
|
154
|
+
require 'irb/command'
|
|
155
|
+
rescue LoadError
|
|
156
|
+
omit 'irb >= 1.13 is not available'
|
|
157
|
+
end
|
|
158
|
+
assert(BitClust::Irb::RefeCommand < ::IRB::Command::Base)
|
|
159
|
+
assert_match(/るりま|リファレンス/, BitClust::Irb::RefeCommand.description)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def test_searcher_local_database_p
|
|
163
|
+
without_local_db do
|
|
164
|
+
assert_false(BitClust::Searcher.new.local_database?)
|
|
165
|
+
with_db do |db|
|
|
166
|
+
ENV['BITCLUST_DATADIR'] = db.instance_variable_get(:@prefix)
|
|
167
|
+
assert_true(BitClust::Searcher.new.local_database?)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def test_lookup_uses_local_db_when_given
|
|
173
|
+
with_db do |db|
|
|
174
|
+
Dir.mktmpdir do |dir|
|
|
175
|
+
out = StringIO.new
|
|
176
|
+
BitClust::Irb.lookup('Foo#bar', db: db, io: out, remote: never_remote(dir))
|
|
177
|
+
assert_match(/bar の説明。/, out.string)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def test_lookup_falls_back_to_remote_without_local_db
|
|
183
|
+
without_local_db do |home|
|
|
184
|
+
out = StringIO.new
|
|
185
|
+
BitClust::Irb.lookup('String#gsub', io: out, remote: fake_remote(home))
|
|
186
|
+
assert_match(/置き換えます。/, out.string)
|
|
187
|
+
assert_match(%r{method/String/i/gsub\.html}, out.string)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def test_lookup_without_remote_and_local_db_shows_setup_hint
|
|
192
|
+
without_local_db do
|
|
193
|
+
out = StringIO.new
|
|
194
|
+
assert_nothing_raised do
|
|
195
|
+
BitClust::Irb.lookup('String#gsub', io: out, remote: nil)
|
|
196
|
+
end
|
|
197
|
+
assert_match(/bitclust setup/, out.string)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def test_refe_cli_falls_back_to_remote_without_local_db
|
|
202
|
+
without_local_db do |home|
|
|
203
|
+
out = StringIO.new
|
|
204
|
+
searcher = BitClust::Searcher.new
|
|
205
|
+
searcher.remote = fake_remote(home)
|
|
206
|
+
searcher.parse(%w[String#gsub])
|
|
207
|
+
view = BitClust::TerminalView.new(BitClust::Plain.new, {}, io: out)
|
|
208
|
+
searcher.run_query(nil, %w[String#gsub], view)
|
|
209
|
+
assert_match(/置き換えます。/, out.string)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
data/test/test_link_checker.rb
CHANGED
|
@@ -91,6 +91,41 @@ class TestMarkdownBridge < Test::Unit::TestCase
|
|
|
91
91
|
out["thread.rd"]
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
+
def test_membership_version_and_else_wrap_include_sites
|
|
95
|
+
# bitclust#331: #@version V は #@if (version == "V")、その #@else は
|
|
96
|
+
# #@if (version != "V") で再具現化する。範囲の #@else は 2 つの
|
|
97
|
+
# membership(A 未満 / B 以上)になるので include サイトも 2 つ
|
|
98
|
+
out = build_from(
|
|
99
|
+
"builtin.md" => "---\ntype: library\n---\nbuiltin 概要。\n",
|
|
100
|
+
"onlylib.md" => "---\ntype: library\n---\nonlylib 概要。\n",
|
|
101
|
+
"range.md" => "---\ntype: library\n---\nrange 概要。\n",
|
|
102
|
+
"onlylib/Shared.md" =>
|
|
103
|
+
"---\n" \
|
|
104
|
+
"library:\n" \
|
|
105
|
+
"\#@version 3.2\n" \
|
|
106
|
+
" - onlylib\n" \
|
|
107
|
+
"\#@else\n" \
|
|
108
|
+
" - builtin\n" \
|
|
109
|
+
"\#@end\n" \
|
|
110
|
+
"\#@version 3.1...3.3\n" \
|
|
111
|
+
" - range\n" \
|
|
112
|
+
"\#@else\n" \
|
|
113
|
+
" - range\n" \
|
|
114
|
+
"\#@end\n" \
|
|
115
|
+
"---\n" \
|
|
116
|
+
"# class Shared < Object\nS。\n"
|
|
117
|
+
)
|
|
118
|
+
assert_equal "onlylib 概要。\n\n\#@if (version == \"3.2\")\n\#@include(onlylib/Shared)\n\#@end\n",
|
|
119
|
+
out["onlylib.rd"]
|
|
120
|
+
assert_equal "builtin 概要。\n\n\#@if (version != \"3.2\")\n\#@include(onlylib/Shared)\n\#@end\n",
|
|
121
|
+
out["builtin.rd"]
|
|
122
|
+
assert_equal "range 概要。\n\n" \
|
|
123
|
+
"\#@until 3.3\n\#@since 3.1\n\#@include(onlylib/Shared)\n\#@end\n\#@end\n" \
|
|
124
|
+
"\#@until 3.1\n\#@include(onlylib/Shared)\n\#@end\n" \
|
|
125
|
+
"\#@since 3.3\n\#@include(onlylib/Shared)\n\#@end\n",
|
|
126
|
+
out["range.rd"]
|
|
127
|
+
end
|
|
128
|
+
|
|
94
129
|
def test_libraries_manifest
|
|
95
130
|
out = build
|
|
96
131
|
assert_equal "bar/baz\ndual\nfoo\n\#@until 3.1\ngated\n\#@end\nsub/sub\n", out["LIBRARIES"]
|
|
@@ -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_markdown_tree.rb
CHANGED
|
@@ -366,4 +366,148 @@ class TestMarkdownTree < Test::Unit::TestCase
|
|
|
366
366
|
"version-range-gated H1 relations must not be flagged"
|
|
367
367
|
assert_equal [], tree.warnings
|
|
368
368
|
end
|
|
369
|
+
|
|
370
|
+
# --- front matter ゲート付きリストのディレクティブ(bitclust#331) ---
|
|
371
|
+
# テストリスト:
|
|
372
|
+
# [x] #%else は直前ゲートの反転(until → since / since → until)
|
|
373
|
+
# [x] #%version A...B / A... / ...B / V(単一版)
|
|
374
|
+
# [x] #%version A...B の #%else は「A 未満」と「B 以上」の 2 membership
|
|
375
|
+
# [x] #%version V の #%else は except: [V]
|
|
376
|
+
# [x] 入れ子のゲートは交差(since は max・until は min)
|
|
377
|
+
# [x] 矛盾する入れ子(単一版 ≠ 単一版)は membership を作らない
|
|
378
|
+
# [x] #%# コメント行は無視
|
|
379
|
+
# [x] 未知の #% 行・#%if・対応のない #%else/#%end・二重 #%else・
|
|
380
|
+
# リスト終端で閉じていないゲートは ParseError(無音の打ち切りをやめる)
|
|
381
|
+
|
|
382
|
+
def membership_scan(list)
|
|
383
|
+
scan(
|
|
384
|
+
"a.md" => LIB, "b.md" => LIB, "c.md" => LIB,
|
|
385
|
+
"a/X.md" => "---\nlibrary:\n#{list}---\n# class X < Object\n"
|
|
386
|
+
)
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def memberships_of(list)
|
|
390
|
+
tree = membership_scan(list)
|
|
391
|
+
assert_equal [], tree.warnings
|
|
392
|
+
tree.entities["a/X.md"][:memberships]
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
def test_else_inverts_until
|
|
396
|
+
assert_equal [
|
|
397
|
+
{ library: "a", until: "3.4" },
|
|
398
|
+
{ library: "b", since: "3.4" },
|
|
399
|
+
], memberships_of("#%until 3.4\n - a\n#%else\n - b\n#%end\n")
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def test_else_inverts_since
|
|
403
|
+
assert_equal [
|
|
404
|
+
{ library: "a", since: "3.4" },
|
|
405
|
+
{ library: "b", until: "3.4" },
|
|
406
|
+
], memberships_of("#%since 3.4\n - a\n#%else\n - b\n#%end\n")
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def test_version_range_forms
|
|
410
|
+
assert_equal [
|
|
411
|
+
{ library: "a", since: "3.1", until: "3.3" },
|
|
412
|
+
{ library: "b", since: "3.1" },
|
|
413
|
+
{ library: "c", until: "3.3" },
|
|
414
|
+
], memberships_of(
|
|
415
|
+
"#%version 3.1...3.3\n - a\n#%end\n" \
|
|
416
|
+
"#%version 3.1...\n - b\n#%end\n" \
|
|
417
|
+
"#%version ...3.3\n - c\n#%end\n"
|
|
418
|
+
)
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def test_single_version
|
|
422
|
+
assert_equal [{ library: "a", version: "3.1" }],
|
|
423
|
+
memberships_of("#%version 3.1\n - a\n#%end\n")
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def test_else_of_version_range_splits_into_two_memberships
|
|
427
|
+
assert_equal [
|
|
428
|
+
{ library: "a", since: "3.1", until: "3.3" },
|
|
429
|
+
{ library: "b", until: "3.1" },
|
|
430
|
+
{ library: "b", since: "3.3" },
|
|
431
|
+
], memberships_of("#%version 3.1...3.3\n - a\n#%else\n - b\n#%end\n")
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def test_else_of_single_version_is_except
|
|
435
|
+
assert_equal [
|
|
436
|
+
{ library: "a", version: "3.1" },
|
|
437
|
+
{ library: "b", except: ["3.1"] },
|
|
438
|
+
], memberships_of("#%version 3.1\n - a\n#%else\n - b\n#%end\n")
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
def test_nested_gates_intersect
|
|
442
|
+
assert_equal [
|
|
443
|
+
{ library: "a", since: "3.2", until: "3.4" },
|
|
444
|
+
{ library: "b", since: "3.0", until: "3.2" },
|
|
445
|
+
], memberships_of(
|
|
446
|
+
"#%since 3.0\n#%until 3.4\n#%since 3.2\n - a\n#%else\n - b\n#%end\n#%end\n#%end\n"
|
|
447
|
+
)
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def test_contradicting_nested_single_versions_yield_no_membership
|
|
451
|
+
assert_equal [{ library: "b", version: "3.1" }],
|
|
452
|
+
memberships_of("#%version 3.1\n#%version 3.2\n - a\n#%end\n - b\n#%end\n")
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
def test_directive_comment_lines_are_ignored
|
|
456
|
+
assert_equal [{ library: "a" }, { library: "b", until: "3.4" }],
|
|
457
|
+
memberships_of("#%# comment\n - a\n#%until 3.4\n#%# another\n - b\n#%end\n")
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def test_at_prefix_else_and_version
|
|
461
|
+
assert_equal [
|
|
462
|
+
{ library: "a", since: "3.1", until: "3.3" },
|
|
463
|
+
{ library: "b", until: "3.1" },
|
|
464
|
+
{ library: "b", since: "3.3" },
|
|
465
|
+
], memberships_of("\#@version 3.1...3.3\n - a\n\#@else\n - b\n\#@end\n")
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def assert_list_parse_error(list, pattern)
|
|
469
|
+
e = assert_raise(BitClust::ParseError) { membership_scan(list) }
|
|
470
|
+
assert_match pattern, e.message
|
|
471
|
+
assert_match(/a\/X\.md:\d+/, e.message, "message must locate the file and line")
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
def test_unknown_directive_in_list_raises
|
|
475
|
+
assert_list_parse_error(" - a\n#%sinse 3.4\n - b\n#%end\n", /unknown preprocessor directive/)
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
def test_if_in_list_is_unsupported
|
|
479
|
+
assert_list_parse_error("#%if (version >= \"3.4\")\n - a\n#%end\n", /#%if is not supported/)
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
def test_else_without_gate_raises
|
|
483
|
+
assert_list_parse_error(" - a\n#%else\n - b\n#%end\n", /no matching/)
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def test_end_without_gate_raises
|
|
487
|
+
assert_list_parse_error(" - a\n#%end\n", /no matching/)
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
def test_duplicate_else_raises
|
|
491
|
+
assert_list_parse_error("#%since 3.4\n - a\n#%else\n - b\n#%else\n - c\n#%end\n", /duplicate/)
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def test_unterminated_gate_at_list_end_raises
|
|
495
|
+
assert_list_parse_error("#%since 3.4\n - a\n", /unterminated/)
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
def test_indented_directive_in_list_raises
|
|
499
|
+
# 列 0 でない #% 行は Preprocessor でも指令にならない(YAML コメント扱い)。
|
|
500
|
+
# リスト内で無音にゲートが無視されたりリストが打ち切られたりしないよう、
|
|
501
|
+
# 指令風のインデント行はエラーにする
|
|
502
|
+
assert_list_parse_error(" - a\n #%since 3.0\n - b\n#%end\n", /must start at column 0/)
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
def test_yaml_comment_in_list_is_ignored
|
|
506
|
+
assert_equal [{ library: "a" }, { library: "b", since: "3.0" }],
|
|
507
|
+
memberships_of(" # plain yaml comment\n - a\n#%since 3.0\n # another\n - b\n#%end\n")
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
def test_wrong_version_range_raises
|
|
511
|
+
assert_list_parse_error("#%version 3.1..3.3\n - a\n#%end\n", /wrong version range/)
|
|
512
|
+
end
|
|
369
513
|
end
|