bitclust-core 1.2.6 → 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 +49 -5
- 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 +17 -17
- data/lib/bitclust/capi_converter.rb +20 -0
- data/lib/bitclust/classentry.rb +68 -17
- data/lib/bitclust/compat.rb +3 -2
- data/lib/bitclust/completion.rb +59 -27
- data/lib/bitclust/crossrubyutils.rb +9 -5
- data/lib/bitclust/database.rb +29 -8
- data/lib/bitclust/doc_converter.rb +63 -0
- data/lib/bitclust/docentry.rb +2 -1
- data/lib/bitclust/entity_splitter.rb +221 -0
- data/lib/bitclust/entry.rb +48 -7
- data/lib/bitclust/exception.rb +1 -0
- data/lib/bitclust/functiondatabase.rb +14 -1
- data/lib/bitclust/functionentry.rb +6 -1
- data/lib/bitclust/functionreferenceparser.rb +5 -4
- data/lib/bitclust/generators/epub.rb +11 -3
- data/lib/bitclust/htmlutils.rb +2 -1
- data/lib/bitclust/include_graph.rb +375 -0
- data/lib/bitclust/include_pruner.rb +144 -0
- data/lib/bitclust/interface.rb +2 -1
- data/lib/bitclust/libraryentry.rb +9 -7
- data/lib/bitclust/lineinput.rb +8 -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 +4 -3
- data/lib/bitclust/method_since_calculator.rb +117 -0
- data/lib/bitclust/methoddatabase.rb +125 -14
- data/lib/bitclust/methodentry.rb +130 -7
- data/lib/bitclust/methodid.rb +13 -11
- data/lib/bitclust/methodsignature.rb +11 -4
- data/lib/bitclust/nameutils.rb +42 -22
- data/lib/bitclust/parseutils.rb +1 -0
- data/lib/bitclust/preprocessor.rb +70 -32
- data/lib/bitclust/progress_bar.rb +1 -0
- data/lib/bitclust/rdcompiler.rb +197 -96
- data/lib/bitclust/refsdatabase.rb +24 -8
- data/lib/bitclust/reloadable_request_handler.rb +72 -0
- data/lib/bitclust/requesthandler.rb +22 -16
- data/lib/bitclust/ridatabase.rb +11 -4
- data/lib/bitclust/rrd_to_markdown.rb +925 -0
- data/lib/bitclust/rrdparser.rb +197 -30
- data/lib/bitclust/runner.rb +18 -2
- data/lib/bitclust/screen.rb +81 -11
- data/lib/bitclust/search_index_generator.rb +288 -0
- data/lib/bitclust/searcher.rb +72 -22
- data/lib/bitclust/server.rb +6 -1
- data/lib/bitclust/silent_progress_bar.rb +1 -0
- data/lib/bitclust/simplesearcher.rb +25 -12
- data/lib/bitclust/subcommand.rb +8 -1
- data/lib/bitclust/subcommands/ancestors_command.rb +10 -2
- data/lib/bitclust/subcommands/checklink_command.rb +54 -0
- data/lib/bitclust/subcommands/chm_command.rb +16 -7
- data/lib/bitclust/subcommands/classes_command.rb +6 -0
- data/lib/bitclust/subcommands/epub_command.rb +1 -0
- data/lib/bitclust/subcommands/extract_command.rb +6 -0
- data/lib/bitclust/subcommands/htmlfile_command.rb +22 -7
- data/lib/bitclust/subcommands/init_command.rb +5 -1
- data/lib/bitclust/subcommands/list_command.rb +13 -4
- data/lib/bitclust/subcommands/lookup_command.rb +29 -9
- data/lib/bitclust/subcommands/methods_command.rb +11 -2
- data/lib/bitclust/subcommands/methodsince_command.rb +80 -0
- data/lib/bitclust/subcommands/preproc_command.rb +6 -0
- data/lib/bitclust/subcommands/property_command.rb +3 -1
- data/lib/bitclust/subcommands/query_command.rb +1 -0
- data/lib/bitclust/subcommands/searchpage_command.rb +102 -0
- data/lib/bitclust/subcommands/server_command.rb +16 -10
- data/lib/bitclust/subcommands/setup_command.rb +25 -10
- data/lib/bitclust/subcommands/statichtml_command.rb +132 -6
- data/lib/bitclust/subcommands/update_command.rb +111 -2
- data/lib/bitclust/syntax_highlighter.rb +52 -5
- data/lib/bitclust/textutils.rb +4 -3
- data/lib/bitclust/version.rb +2 -1
- data/lib/bitclust/version_badges.rb +90 -0
- data/lib/bitclust/whole_file_gate.rb +137 -0
- data/lib/bitclust.rb +1 -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 +429 -27
- 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 +153 -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 +178 -21
- data/README +0 -32
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
require "test/unit"
|
|
2
|
+
require "bitclust"
|
|
3
|
+
require "bitclust/searcher"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
|
|
7
|
+
class TestTerminalView < Test::Unit::TestCase
|
|
8
|
+
include BitClust
|
|
9
|
+
|
|
10
|
+
def test_show_class
|
|
11
|
+
view = TerminalView.new(Plain.new, {})
|
|
12
|
+
db = Database.dummy
|
|
13
|
+
foo = ClassEntry.new(db, "Foo")
|
|
14
|
+
bar = ClassEntry.new(db, "Bar")
|
|
15
|
+
out, err = capture_output do
|
|
16
|
+
assert_nothing_raised do
|
|
17
|
+
view.show_class([foo, bar])
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
assert_equal %w[Bar Foo], out.split
|
|
21
|
+
assert_empty err
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class TestSearcherWindowsDrivePath < Test::Unit::TestCase
|
|
26
|
+
include BitClust
|
|
27
|
+
|
|
28
|
+
ENV_KEYS = %w[REFE2_SERVER BITCLUST_SERVER REFE2_DATADIR BITCLUST_DATADIR]
|
|
29
|
+
|
|
30
|
+
def setup
|
|
31
|
+
@saved_env = {}
|
|
32
|
+
ENV_KEYS.each {|key| @saved_env[key] = ENV.delete(key) }
|
|
33
|
+
@saved_dir = Dir.pwd
|
|
34
|
+
@searcher = Searcher.new
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def teardown
|
|
38
|
+
Dir.chdir(@saved_dir)
|
|
39
|
+
ENV_KEYS.each do |key|
|
|
40
|
+
if @saved_env[key]
|
|
41
|
+
ENV[key] = @saved_env[key]
|
|
42
|
+
else
|
|
43
|
+
ENV.delete(key)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_windows_drive_path_predicate
|
|
49
|
+
assert_true @searcher.send(:windows_drive_path?, "G:/Users/foo/.bitclust/db-2.2.0")
|
|
50
|
+
assert_true @searcher.send(:windows_drive_path?, 'C:\Users\foo\.bitclust\db-2.2.0')
|
|
51
|
+
assert_false @searcher.send(:windows_drive_path?, "/home/foo/.bitclust/db-2.2.0")
|
|
52
|
+
assert_false @searcher.send(:windows_drive_path?, "druby://localhost:10001")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_drive_path_uri_keeps_drive_letter
|
|
56
|
+
uri = @searcher.send(:drive_path_uri, "G:/Users/foo/.bitclust/db-2.2.0")
|
|
57
|
+
assert_equal "file", uri.scheme
|
|
58
|
+
assert_equal "G:/Users/foo/.bitclust/db-2.2.0", uri.path
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_find_dblocation_keeps_windows_drive_letter
|
|
62
|
+
with_fake_datadir("G:/Users/foo/.bitclust/db-2.2.0") do |relative_path|
|
|
63
|
+
ENV["BITCLUST_DATADIR"] = relative_path
|
|
64
|
+
location = @searcher.send(:find_dblocation)
|
|
65
|
+
assert_equal "file", location.scheme
|
|
66
|
+
assert_equal relative_path, location.path
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_find_dblocation_keeps_unix_absolute_path
|
|
71
|
+
Dir.mktmpdir do |dir|
|
|
72
|
+
datadir = File.join(dir, ".bitclust", "db-2.2.0")
|
|
73
|
+
FileUtils.mkdir_p(datadir)
|
|
74
|
+
FileUtils.touch(File.join(datadir, "properties"))
|
|
75
|
+
ENV["BITCLUST_DATADIR"] = datadir
|
|
76
|
+
location = @searcher.send(:find_dblocation)
|
|
77
|
+
assert_equal "file", location.scheme
|
|
78
|
+
assert_equal datadir, location.path
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_database_option_keeps_windows_drive_letter
|
|
83
|
+
@searcher.parser.parse!(["--database=G:/Users/foo/.bitclust/db-2.2.0"])
|
|
84
|
+
dblocation = @searcher.instance_variable_get(:@dblocation)
|
|
85
|
+
assert_equal "file", dblocation.scheme
|
|
86
|
+
assert_equal "G:/Users/foo/.bitclust/db-2.2.0", dblocation.path
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# datadir/properties を tmpdir 内に作り、tmpdir へ chdir したうえで
|
|
92
|
+
# datadir までの相対パス(ドライブレターを含む)を渡す。":" は Unix の
|
|
93
|
+
# ファイル名として有効な文字なので、Linux 上でも "G:" というディレク
|
|
94
|
+
# トリを作ってドライブレター付きパスの再現ができる
|
|
95
|
+
def with_fake_datadir(relative_path)
|
|
96
|
+
Dir.mktmpdir do |dir|
|
|
97
|
+
datadir = File.join(dir, *relative_path.split("/"))
|
|
98
|
+
FileUtils.mkdir_p(datadir)
|
|
99
|
+
FileUtils.touch(File.join(datadir, "properties"))
|
|
100
|
+
Dir.chdir(dir) do
|
|
101
|
+
yield relative_path
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# refe の module_function 表示(bitclust#297)。
|
|
108
|
+
# HTML 側(#282)と同じく、DB のバージョンが 4.0 以降なら
|
|
109
|
+
# "Foo.#baz" ではなく "Foo?.baz" と表示する。
|
|
110
|
+
class TestSearcherModuleFunctionDisplay < Test::Unit::TestCase
|
|
111
|
+
include BitClust
|
|
112
|
+
|
|
113
|
+
def record_for(version)
|
|
114
|
+
db = MethodDatabase.dummy("version" => version)
|
|
115
|
+
spec = MethodSpec.parse("Foo.#baz")
|
|
116
|
+
SearchResult::Record.new(db, spec, spec)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def test_names_folds_module_function_typemark_for_40
|
|
120
|
+
assert_equal ["Foo?.baz"], record_for("4.0").names
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def test_names_keeps_typemark_before_40
|
|
124
|
+
assert_equal ["Foo.#baz"], record_for("3.4").names
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def test_names_without_db_version_keeps_typemark
|
|
128
|
+
spec = MethodSpec.parse("Foo.#baz")
|
|
129
|
+
assert_equal ["Foo.#baz"], SearchResult::Record.new(nil, spec, spec).names
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# refe のクエリ側も 4.0 以降の表示形式 "Foo?.baz" を受け付ける(bitclust#297)
|
|
134
|
+
class TestSearcherModuleFunctionQuery < Test::Unit::TestCase
|
|
135
|
+
include BitClust
|
|
136
|
+
|
|
137
|
+
def parse(pat)
|
|
138
|
+
Searcher.new.send(:parse_method_spec_pattern, pat)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def test_question_dot_pattern
|
|
142
|
+
assert_equal ["Foo", ".#", "baz"], parse("Foo?.baz")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def test_dot_hash_pattern_still_works
|
|
146
|
+
assert_equal ["Foo", ".#", "baz"], parse("Foo.#baz")
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def test_predicate_method_names_unaffected
|
|
150
|
+
assert_equal ["Foo", "#", "empty?"], parse("Foo#empty?")
|
|
151
|
+
assert_equal ["Foo", ".", "b?"], parse("Foo.b?")
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'test/unit'
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'bitclust'
|
|
6
|
+
require 'bitclust/methoddatabase'
|
|
7
|
+
require 'bitclust/subcommands/searchpage_command'
|
|
8
|
+
|
|
9
|
+
# searchpage サブコマンド: 複数バージョンの DB から /ja/search/ 置換用の
|
|
10
|
+
# 静的検索ページ一式(index.html + 統合 search_data.js + JS/CSS アセット)を
|
|
11
|
+
# 生成する。rurema-search(サーバ常駐の全文検索)のリタイア先。
|
|
12
|
+
#
|
|
13
|
+
# テストリスト:
|
|
14
|
+
# [x] 統合 index: 全版共通エントリは versions に全版、片方だけのものはその版のみ
|
|
15
|
+
# [x] index.html に search_versions(昇順)と検索 UI の要素が埋まる
|
|
16
|
+
# [x] vendored JS + NOTICE + search_page.js + search.css がコピーされる
|
|
17
|
+
# [x] ページ専用 UI なので search_init.js(ページ内ボックス用)は含めない
|
|
18
|
+
# [x] DB を持たない引数なし呼び出しはエラー
|
|
19
|
+
# [x] グローバル --database 不要(needs_database? が false)
|
|
20
|
+
class TestSearchpageCommand < Test::Unit::TestCase
|
|
21
|
+
def setup
|
|
22
|
+
@tmpdir = "searchpage_test_tmp"
|
|
23
|
+
FileUtils.rm_rf(@tmpdir)
|
|
24
|
+
@db34 = build_db('3.4', <<~'RD')
|
|
25
|
+
description
|
|
26
|
+
|
|
27
|
+
= class Foo < Object
|
|
28
|
+
== Instance Methods
|
|
29
|
+
--- foo
|
|
30
|
+
RD
|
|
31
|
+
@db41 = build_db('4.1', <<~'RD')
|
|
32
|
+
description
|
|
33
|
+
|
|
34
|
+
= class Foo < Object
|
|
35
|
+
== Instance Methods
|
|
36
|
+
--- foo
|
|
37
|
+
--- bar
|
|
38
|
+
RD
|
|
39
|
+
@out = "#{@tmpdir}/out"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def teardown
|
|
43
|
+
FileUtils.rm_rf(@tmpdir)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def build_db(version, builtin_rd)
|
|
47
|
+
root = "#{@tmpdir}/tree-#{version}/refm/api/src"
|
|
48
|
+
FileUtils.mkdir_p("#{root}/_builtin")
|
|
49
|
+
File.write("#{root}/LIBRARIES", "_builtin\n")
|
|
50
|
+
File.write("#{root}/_builtin.rd", builtin_rd)
|
|
51
|
+
prefix = "#{@tmpdir}/db-#{version}"
|
|
52
|
+
db = BitClust::MethodDatabase.new(prefix)
|
|
53
|
+
db.init
|
|
54
|
+
db.transaction do
|
|
55
|
+
db.propset('version', version)
|
|
56
|
+
db.propset('encoding', 'utf-8')
|
|
57
|
+
end
|
|
58
|
+
db.transaction do
|
|
59
|
+
db.update_by_stdlibtree(root)
|
|
60
|
+
end
|
|
61
|
+
prefix
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def run_command(argv)
|
|
65
|
+
cmd = BitClust::Subcommands::SearchpageCommand.new
|
|
66
|
+
cmd.parse(argv)
|
|
67
|
+
cmd.exec(argv, { prefix: nil, capi: false })
|
|
68
|
+
cmd
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_needs_no_global_database_option
|
|
72
|
+
cmd = BitClust::Subcommands::SearchpageCommand.new
|
|
73
|
+
assert_false cmd.needs_database?
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_merged_index_versions
|
|
77
|
+
run_command(["--outputdir=#{@out}", @db41, @db34])
|
|
78
|
+
js = File.read("#{@out}/js/search_data.js")
|
|
79
|
+
assert_match(/\Avar search_data = \{/, js)
|
|
80
|
+
data = JSON.parse(js.sub(/\Avar search_data = /, '').sub(/;\z/, ''))
|
|
81
|
+
foo = data['index'].find { |e| e['full_name'] == 'Foo#foo' }
|
|
82
|
+
bar = data['index'].find { |e| e['full_name'] == 'Foo#bar' }
|
|
83
|
+
klass = data['index'].find { |e| e['full_name'] == 'Foo' }
|
|
84
|
+
assert_equal %w[3.4 4.1], foo['versions']
|
|
85
|
+
assert_equal %w[4.1], bar['versions']
|
|
86
|
+
assert_equal %w[3.4 4.1], klass['versions']
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_merged_index_shows_module_functions_as_question_dot_only
|
|
90
|
+
# bitclust#279 コメント対応: 版によって表示が ".#"(4.0 より前)と
|
|
91
|
+
# "?."(4.0 以降)に割れる module function は、統合ページでは "?."
|
|
92
|
+
# 表記の1エントリに合流させる(両表記が併記されるとわかりにくい)
|
|
93
|
+
db33 = build_db('3.3', <<~'RD')
|
|
94
|
+
description
|
|
95
|
+
|
|
96
|
+
= module Kernel
|
|
97
|
+
== Module Functions
|
|
98
|
+
--- at_exit{ ... } -> Proc
|
|
99
|
+
aaa
|
|
100
|
+
RD
|
|
101
|
+
db40 = build_db('4.0', <<~'RD')
|
|
102
|
+
description
|
|
103
|
+
|
|
104
|
+
= module Kernel
|
|
105
|
+
== Module Functions
|
|
106
|
+
--- at_exit{ ... } -> Proc
|
|
107
|
+
aaa
|
|
108
|
+
RD
|
|
109
|
+
run_command(["--outputdir=#{@out}", db40, db33])
|
|
110
|
+
js = File.read("#{@out}/js/search_data.js")
|
|
111
|
+
data = JSON.parse(js.sub(/\Avar search_data = /, '').sub(/;\z/, ''))
|
|
112
|
+
entries = data['index'].select { |e| e['name'] == 'at_exit' }
|
|
113
|
+
assert_equal ['Kernel?.at_exit'], entries.map { |e| e['full_name'] }
|
|
114
|
+
assert_equal %w[3.3 4.0], entries[0]['versions']
|
|
115
|
+
assert_not_match(/Kernel\.#/, js)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def test_index_html_embeds_versions_and_ui
|
|
119
|
+
run_command(["--outputdir=#{@out}", @db41, @db34])
|
|
120
|
+
html = File.read("#{@out}/index.html")
|
|
121
|
+
assert_match(/var search_versions = \["3\.4","4\.1"\];/, html)
|
|
122
|
+
assert_match(/var search_version_base = "\.\.\/";/, html)
|
|
123
|
+
assert_match(/id="search-field"/, html)
|
|
124
|
+
assert_match(/id="search-results"/, html)
|
|
125
|
+
assert_match(%r{js/search_data\.js}, html)
|
|
126
|
+
assert_match(%r{js/search_page\.js}, html)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def test_assets_are_copied
|
|
130
|
+
run_command(["--outputdir=#{@out}", @db41, @db34])
|
|
131
|
+
%w[search_navigation.js search_ranker.js search_controller.js
|
|
132
|
+
search_page.js NOTICE].each do |f|
|
|
133
|
+
assert File.exist?("#{@out}/js/#{f}"), "js/#{f} not copied"
|
|
134
|
+
end
|
|
135
|
+
assert File.exist?("#{@out}/search.css")
|
|
136
|
+
# ページ内ドロップダウン用の search_init.js はこのページでは使わない
|
|
137
|
+
assert_false File.exist?("#{@out}/js/search_init.js")
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def test_no_database_arguments_is_an_error
|
|
141
|
+
cmd = BitClust::Subcommands::SearchpageCommand.new
|
|
142
|
+
argv = ["--outputdir=#{@out}"]
|
|
143
|
+
cmd.parse(argv)
|
|
144
|
+
assert_raise(SystemExit) do
|
|
145
|
+
capture_stderr { cmd.exec(argv, { prefix: nil, capi: false }) }
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def test_missing_outputdir_is_an_error
|
|
150
|
+
cmd = BitClust::Subcommands::SearchpageCommand.new
|
|
151
|
+
argv = [@db34]
|
|
152
|
+
cmd.parse(argv)
|
|
153
|
+
assert_raise(SystemExit) do
|
|
154
|
+
capture_stderr { cmd.exec(argv, { prefix: nil, capi: false }) }
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
private
|
|
159
|
+
|
|
160
|
+
def capture_stderr
|
|
161
|
+
orig = $stderr
|
|
162
|
+
$stderr = StringIO.new
|
|
163
|
+
yield
|
|
164
|
+
ensure
|
|
165
|
+
$stderr = orig
|
|
166
|
+
end
|
|
167
|
+
end
|
data/test/test_simplesearcher.rb
CHANGED
|
@@ -15,6 +15,12 @@ class TestSearcher < Test::Unit::TestCase
|
|
|
15
15
|
== Class Methods
|
|
16
16
|
--- bar
|
|
17
17
|
= reopen Kernel
|
|
18
|
+
== Module Functions
|
|
19
|
+
--- open
|
|
20
|
+
|
|
21
|
+
== Instance Methods
|
|
22
|
+
--- puts
|
|
23
|
+
|
|
18
24
|
== Special Variables
|
|
19
25
|
--- $spespe
|
|
20
26
|
|
|
@@ -45,4 +51,26 @@ HERE
|
|
|
45
51
|
assert_equal([], search_pattern(@db, " "), 'space')
|
|
46
52
|
assert_equal([], search_pattern(@db, ""), 'blank')
|
|
47
53
|
end
|
|
54
|
+
|
|
55
|
+
# bitclust#250 follow-up: the dynamic server's /search accepts free-text
|
|
56
|
+
# queries, and a module function may be typed in either notation --
|
|
57
|
+
# "Kernel.#open" (bitclust's own internal spec-string form, still what
|
|
58
|
+
# docs for Ruby < 4.0 display) or "Kernel?.open" (what docs for Ruby >=
|
|
59
|
+
# 4.0 display since #277). Both must resolve to the same method,
|
|
60
|
+
# regardless of which notation the *query* uses -- the query parser has
|
|
61
|
+
# no idea (and shouldn't need to know) which doc version the user is
|
|
62
|
+
# thinking of.
|
|
63
|
+
def test_module_function_query_notation
|
|
64
|
+
[['Kernel?.open', 'open'], # new (>=4.0 display) notation
|
|
65
|
+
['Kernel.#open', 'open'], # existing (bitclust-internal) notation: regression check
|
|
66
|
+
['?.open', 'open'], # bare module-function marker, no class name
|
|
67
|
+
['Kernel#puts', 'puts'], # instance method: unaffected by the "?." change
|
|
68
|
+
].each{|q, expected|
|
|
69
|
+
ret = search_pattern(@db, q)
|
|
70
|
+
assert_not_equal([], ret, q)
|
|
71
|
+
assert_equal(expected, ret[0].name, q)
|
|
72
|
+
}
|
|
73
|
+
# Singleton-method dot notation ('Hoge.h' => 'hoge' in test_simple_search
|
|
74
|
+
# above) is also unaffected; kept there rather than duplicated here.
|
|
75
|
+
end
|
|
48
76
|
end
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require 'tmpdir'
|
|
3
|
+
require 'stringio'
|
|
4
|
+
require 'bitclust'
|
|
5
|
+
require 'bitclust/subcommands/statichtml_command'
|
|
6
|
+
|
|
7
|
+
class TestStatichtmlURLMapperEx < Test::Unit::TestCase
|
|
8
|
+
def setup
|
|
9
|
+
@urlmapper = BitClust::Subcommands::StatichtmlCommand::URLMapperEx.new(
|
|
10
|
+
:suffix => 'html',
|
|
11
|
+
:edit_base_url => 'https://github.com/rurema/doctree/edit/master',
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# The edit URL must not contain a line number, so that shifting source lines
|
|
16
|
+
# does not churn the generated HTML diff.
|
|
17
|
+
def test_edit_url_has_no_line_number
|
|
18
|
+
location = BitClust::Location.new('refm/api/src/_builtin/Array', 42)
|
|
19
|
+
assert_equal('https://github.com/rurema/doctree/edit/master/refm/api/src/_builtin/Array',
|
|
20
|
+
@urlmapper.edit_url(location))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# A location restored from the database has no line number (nil); the edit URL
|
|
24
|
+
# must still be a clean file link without a trailing "#L".
|
|
25
|
+
def test_edit_url_without_line_in_location
|
|
26
|
+
location = BitClust::Location.new('refm/api/src/_builtin/Array', nil)
|
|
27
|
+
assert_equal('https://github.com/rurema/doctree/edit/master/refm/api/src/_builtin/Array',
|
|
28
|
+
@urlmapper.edit_url(location))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class TestStatichtmlRunRubyWasm < Test::Unit::TestCase
|
|
33
|
+
def build_command(themedir, outputdir)
|
|
34
|
+
cmd = BitClust::Subcommands::StatichtmlCommand.new
|
|
35
|
+
cmd.instance_variable_set(:@manager_config, { :themedir => Pathname.new(themedir) })
|
|
36
|
+
cmd.instance_variable_set(:@outputdir, Pathname.new(outputdir))
|
|
37
|
+
cmd.instance_variable_set(:@verbose, false)
|
|
38
|
+
cmd
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_option_defaults_to_nil
|
|
42
|
+
cmd = BitClust::Subcommands::StatichtmlCommand.new
|
|
43
|
+
assert_nil(cmd.instance_variable_get(:@run_ruby_wasm))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def test_option_is_parsed
|
|
47
|
+
url = 'https://cdn.jsdelivr.net/npm/@ruby/3.4-wasm-wasi@2.9.3-2.9.4/dist/ruby+stdlib.wasm'
|
|
48
|
+
cmd = BitClust::Subcommands::StatichtmlCommand.new
|
|
49
|
+
cmd.parse(["--run-ruby-wasm=#{url}"])
|
|
50
|
+
assert_equal(url, cmd.instance_variable_get(:@run_ruby_wasm))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_run_js_is_copied
|
|
54
|
+
Dir.mktmpdir do |dir|
|
|
55
|
+
themedir = File.join(dir, 'theme')
|
|
56
|
+
outputdir = File.join(dir, 'out')
|
|
57
|
+
FileUtils.mkdir_p(File.join(themedir, 'js'))
|
|
58
|
+
FileUtils.mkdir_p(outputdir)
|
|
59
|
+
File.write(File.join(themedir, 'js', 'run.js'), "// run\n")
|
|
60
|
+
File.write(File.join(themedir, 'js', 'run-worker.js'), "// worker\n")
|
|
61
|
+
cmd = build_command(themedir, outputdir)
|
|
62
|
+
cmd.send(:copy_run_ruby_wasm_script)
|
|
63
|
+
assert_true(File.file?(File.join(outputdir, 'js', 'run.js')))
|
|
64
|
+
assert_true(File.file?(File.join(outputdir, 'js', 'run-worker.js')))
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_theme_without_run_js_is_tolerated
|
|
69
|
+
Dir.mktmpdir do |dir|
|
|
70
|
+
themedir = File.join(dir, 'theme')
|
|
71
|
+
outputdir = File.join(dir, 'out')
|
|
72
|
+
FileUtils.mkdir_p(themedir)
|
|
73
|
+
FileUtils.mkdir_p(outputdir)
|
|
74
|
+
cmd = build_command(themedir, outputdir)
|
|
75
|
+
orig_stderr, $stderr = $stderr, StringIO.new
|
|
76
|
+
begin
|
|
77
|
+
assert_nothing_raised { cmd.send(:copy_run_ruby_wasm_script) }
|
|
78
|
+
assert_match(/run\.js not found/, $stderr.string)
|
|
79
|
+
assert_match(/run-worker\.js not found/, $stderr.string)
|
|
80
|
+
ensure
|
|
81
|
+
$stderr = orig_stderr
|
|
82
|
+
end
|
|
83
|
+
assert_false(File.exist?(File.join(outputdir, 'js', 'run.js')))
|
|
84
|
+
assert_false(File.exist?(File.join(outputdir, 'js', 'run-worker.js')))
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# A themedir with run.js but not yet upgraded with run-worker.js (or vice
|
|
89
|
+
# versa) should still copy whichever file it does have, rather than
|
|
90
|
+
# aborting or silently skipping both.
|
|
91
|
+
def test_partial_theme_copies_what_it_has
|
|
92
|
+
Dir.mktmpdir do |dir|
|
|
93
|
+
themedir = File.join(dir, 'theme')
|
|
94
|
+
outputdir = File.join(dir, 'out')
|
|
95
|
+
FileUtils.mkdir_p(File.join(themedir, 'js'))
|
|
96
|
+
FileUtils.mkdir_p(outputdir)
|
|
97
|
+
File.write(File.join(themedir, 'js', 'run.js'), "// run\n")
|
|
98
|
+
cmd = build_command(themedir, outputdir)
|
|
99
|
+
orig_stderr, $stderr = $stderr, StringIO.new
|
|
100
|
+
begin
|
|
101
|
+
cmd.send(:copy_run_ruby_wasm_script)
|
|
102
|
+
assert_match(/run-worker\.js not found/, $stderr.string)
|
|
103
|
+
ensure
|
|
104
|
+
$stderr = orig_stderr
|
|
105
|
+
end
|
|
106
|
+
assert_true(File.file?(File.join(outputdir, 'js', 'run.js')))
|
|
107
|
+
assert_false(File.exist?(File.join(outputdir, 'js', 'run-worker.js')))
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
class TestStatichtmlSitemap < Test::Unit::TestCase
|
|
113
|
+
def build_command(outputdir)
|
|
114
|
+
cmd = BitClust::Subcommands::StatichtmlCommand.new
|
|
115
|
+
cmd.instance_variable_set(:@outputdir, Pathname.new(outputdir))
|
|
116
|
+
cmd.instance_variable_set(:@verbose, false)
|
|
117
|
+
cmd
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def test_option_defaults_to_nil
|
|
121
|
+
cmd = BitClust::Subcommands::StatichtmlCommand.new
|
|
122
|
+
assert_nil(cmd.instance_variable_get(:@sitemap_baseurl))
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def test_option_is_parsed
|
|
126
|
+
cmd = BitClust::Subcommands::StatichtmlCommand.new
|
|
127
|
+
cmd.parse(['--sitemap-baseurl=https://docs.ruby-lang.org/ja/3.4/'])
|
|
128
|
+
assert_equal('https://docs.ruby-lang.org/ja/3.4/',
|
|
129
|
+
cmd.instance_variable_get(:@sitemap_baseurl))
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def test_record_sitemap_path_is_noop_without_baseurl
|
|
133
|
+
Dir.mktmpdir do |dir|
|
|
134
|
+
cmd = build_command(dir)
|
|
135
|
+
cmd.send(:record_sitemap_path, Pathname.new(dir) + 'class' + 'String.html')
|
|
136
|
+
assert_empty(cmd.instance_variable_get(:@sitemap_paths))
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def test_record_sitemap_path_keeps_relative_path
|
|
141
|
+
Dir.mktmpdir do |dir|
|
|
142
|
+
cmd = build_command(dir)
|
|
143
|
+
cmd.instance_variable_set(:@sitemap_baseurl, 'https://docs.ruby-lang.org/ja/3.4/')
|
|
144
|
+
cmd.send(:record_sitemap_path, Pathname.new(dir) + 'class' + 'String.html')
|
|
145
|
+
assert_equal(%w[class/String.html], cmd.instance_variable_get(:@sitemap_paths))
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def test_create_sitemap_writes_loc_urls
|
|
150
|
+
Dir.mktmpdir do |dir|
|
|
151
|
+
cmd = build_command(dir)
|
|
152
|
+
cmd.instance_variable_set(:@sitemap_paths,
|
|
153
|
+
['class/String.html', 'method/String/i/upcase.html'])
|
|
154
|
+
cmd.send(:create_sitemap, Pathname.new(dir), 'https://docs.ruby-lang.org/ja/3.4/')
|
|
155
|
+
xml = File.read(File.join(dir, 'sitemap.xml'))
|
|
156
|
+
assert_match(%r{\A<\?xml version="1\.0" encoding="UTF-8"\?>\n}, xml)
|
|
157
|
+
assert_match(%r{<urlset xmlns="http://www\.sitemaps\.org/schemas/sitemap/0\.9">}, xml)
|
|
158
|
+
assert_match(%r{<url><loc>https://docs\.ruby-lang\.org/ja/3\.4/class/String\.html</loc></url>}, xml)
|
|
159
|
+
assert_match(%r{<url><loc>https://docs\.ruby-lang\.org/ja/3\.4/method/String/i/upcase\.html</loc></url>}, xml)
|
|
160
|
+
assert_match(%r{</urlset>\n\z}, xml)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def test_create_sitemap_adds_trailing_slash_to_baseurl
|
|
165
|
+
Dir.mktmpdir do |dir|
|
|
166
|
+
cmd = build_command(dir)
|
|
167
|
+
cmd.instance_variable_set(:@sitemap_paths, ['class/String.html'])
|
|
168
|
+
cmd.send(:create_sitemap, Pathname.new(dir), 'https://docs.ruby-lang.org/ja/3.4')
|
|
169
|
+
xml = File.read(File.join(dir, 'sitemap.xml'))
|
|
170
|
+
assert_match(%r{<loc>https://docs\.ruby-lang\.org/ja/3\.4/class/String\.html</loc>}, xml)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def test_create_sitemap_escapes_special_characters
|
|
175
|
+
Dir.mktmpdir do |dir|
|
|
176
|
+
cmd = build_command(dir)
|
|
177
|
+
cmd.instance_variable_set(:@sitemap_paths, ['method/String/i/=3d=3d.html'])
|
|
178
|
+
cmd.send(:create_sitemap, Pathname.new(dir), 'https://example.com/a&b/')
|
|
179
|
+
xml = File.read(File.join(dir, 'sitemap.xml'))
|
|
180
|
+
assert_match(%r{<loc>https://example\.com/a&b/method/String/i/=3d=3d\.html</loc>}, xml)
|
|
181
|
+
assert_not_match(/a&b/, xml)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def test_create_sitemap_warns_and_truncates_beyond_limit
|
|
186
|
+
Dir.mktmpdir do |dir|
|
|
187
|
+
cmd = build_command(dir)
|
|
188
|
+
limit = BitClust::Subcommands::StatichtmlCommand::MAX_SITEMAP_URLS
|
|
189
|
+
paths = (1..(limit + 1)).map {|i| "class/C#{i}.html" }
|
|
190
|
+
cmd.instance_variable_set(:@sitemap_paths, paths)
|
|
191
|
+
orig_stderr, $stderr = $stderr, StringIO.new
|
|
192
|
+
begin
|
|
193
|
+
cmd.send(:create_sitemap, Pathname.new(dir), 'https://example.com/')
|
|
194
|
+
assert_match(/warning:.*#{limit}/, $stderr.string)
|
|
195
|
+
ensure
|
|
196
|
+
$stderr = orig_stderr
|
|
197
|
+
end
|
|
198
|
+
xml = File.read(File.join(dir, 'sitemap.xml'))
|
|
199
|
+
assert_equal(limit, xml.scan('<url>').size)
|
|
200
|
+
assert_match(%r{<loc>https://example\.com/class/C1\.html</loc>}, xml)
|
|
201
|
+
assert_not_match(/C#{limit + 1}\.html/, xml)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
@@ -82,4 +82,64 @@ class TestSyntaxHighlighter < Test::Unit::TestCase
|
|
|
82
82
|
END
|
|
83
83
|
assert_equal(expected, highlight(source))
|
|
84
84
|
end
|
|
85
|
+
|
|
86
|
+
test 'symbol const' do
|
|
87
|
+
source = <<~END
|
|
88
|
+
class Foo
|
|
89
|
+
def to_hash
|
|
90
|
+
{:Bar => 'a',
|
|
91
|
+
:Baz => 'b'
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
END
|
|
96
|
+
expected = <<~END
|
|
97
|
+
<span class="k">class</span> <span class="nn"></span><span class="o"></span><span class="nc">Foo</span>
|
|
98
|
+
<span class="k">def</span> <span class="nf">to_hash</span>
|
|
99
|
+
<span class="p">{</span><span class="ss">:Bar</span> <span class="o">=></span> <span class="s1">'a'</span>,
|
|
100
|
+
<span class="ss">:Baz</span> <span class="o">=></span> <span class="s1">'b'</span>
|
|
101
|
+
<span class="p">}</span>
|
|
102
|
+
<span class="k">end</span>
|
|
103
|
+
<span class="k">end</span>
|
|
104
|
+
END
|
|
105
|
+
assert_equal(expected, highlight(source))
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
test 'symbol list' do
|
|
109
|
+
source = <<~END
|
|
110
|
+
%i[hellow world].each {|s| p s }
|
|
111
|
+
END
|
|
112
|
+
expected = <<~END
|
|
113
|
+
<span class="ss">%i[hellow world]</span><span class="p">.</span><span class="nf">each</span> <span class="p">{</span><span class="o">|</span>s<span class="o">|</span> <span class="nb">p</span> s <span class="p">}</span>
|
|
114
|
+
END
|
|
115
|
+
assert_equal(expected, highlight(source))
|
|
116
|
+
|
|
117
|
+
source = <<~END
|
|
118
|
+
%I[hellow world].each {|s| p s }
|
|
119
|
+
END
|
|
120
|
+
expected = <<~END
|
|
121
|
+
<span class="ss">%I[hellow world]</span><span class="p">.</span><span class="nf">each</span> <span class="p">{</span><span class="o">|</span>s<span class="o">|</span> <span class="nb">p</span> s <span class="p">}</span>
|
|
122
|
+
END
|
|
123
|
+
assert_equal(expected, highlight(source))
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
test 'one liner module' do
|
|
127
|
+
source = <<~END
|
|
128
|
+
module Foo; end
|
|
129
|
+
END
|
|
130
|
+
expected = <<~END
|
|
131
|
+
<span class="k">module</span> <span class="nn">Foo</span>; <span class="k">end</span>
|
|
132
|
+
END
|
|
133
|
+
assert_equal(expected, highlight(source))
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
test 'one liner class' do
|
|
137
|
+
source = <<~END
|
|
138
|
+
class Foo; end
|
|
139
|
+
END
|
|
140
|
+
expected = <<~END
|
|
141
|
+
<span class="k">class</span> <span class="nn"></span><span class="o"></span><span class="nc">Foo</span>; <span class="k">end</span>
|
|
142
|
+
END
|
|
143
|
+
assert_equal(expected, highlight(source))
|
|
144
|
+
end
|
|
85
145
|
end
|