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
|
@@ -511,3 +511,84 @@ HERE
|
|
|
511
511
|
end
|
|
512
512
|
end
|
|
513
513
|
end
|
|
514
|
+
|
|
515
|
+
class TestSearchIndexSnippet < Test::Unit::TestCase
|
|
516
|
+
# 検索結果の説明文(snippet)。meta description と同じ entry.description
|
|
517
|
+
# (最初の説明段落のプレーンテキスト)を、空でないときだけ持たせる。
|
|
518
|
+
#
|
|
519
|
+
# テストリスト:
|
|
520
|
+
# [x] class/method/special variable の各エントリが snippet を持つ
|
|
521
|
+
# [x] 説明が空のエントリは snippet キー自体を持たない
|
|
522
|
+
# [x] merge は複数版で同一エントリのとき最新版の snippet を採用する
|
|
523
|
+
# [x] 最新版に無いページは収録されている最終版の snippet を保つ
|
|
524
|
+
def setup
|
|
525
|
+
@prefix = 'db_snip'
|
|
526
|
+
@base = 'tree_snip'
|
|
527
|
+
@root = "#{@base}/refm/api/src"
|
|
528
|
+
FileUtils.mkdir_p("#{@root}")
|
|
529
|
+
File.open("#{@root}/LIBRARIES", 'w+') { |f| f.puts '_builtin' }
|
|
530
|
+
File.open("#{@root}/_builtin.rd", 'w+') do |f|
|
|
531
|
+
f.puts <<'HERE'
|
|
532
|
+
description
|
|
533
|
+
|
|
534
|
+
= class Foo < Object
|
|
535
|
+
クラスの説明。
|
|
536
|
+
== Instance Methods
|
|
537
|
+
--- foo
|
|
538
|
+
|
|
539
|
+
メソッドの説明。
|
|
540
|
+
--- undocumented
|
|
541
|
+
= module Kernel
|
|
542
|
+
== Special Variables
|
|
543
|
+
--- $; -> String | nil
|
|
544
|
+
|
|
545
|
+
区切り文字。
|
|
546
|
+
HERE
|
|
547
|
+
end
|
|
548
|
+
@db = BitClust::MethodDatabase.new(@prefix)
|
|
549
|
+
@db.init
|
|
550
|
+
@db.transaction do
|
|
551
|
+
[%w[version 3.4], %w[encoding utf-8]].each { |k, v| @db.propset(k, v) }
|
|
552
|
+
end
|
|
553
|
+
@db.transaction { @db.update_by_stdlibtree(@root) }
|
|
554
|
+
@gen = BitClust::SearchIndexGenerator.new
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
def teardown
|
|
558
|
+
FileUtils.rm_r([@prefix, @base], :force => true)
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
def find_entry(index, full_name)
|
|
562
|
+
index.find { |e| e[:full_name] == full_name }
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
def test_snippet_carries_entry_description
|
|
566
|
+
index = @gen.build_index(@db)
|
|
567
|
+
assert_equal 'クラスの説明。', find_entry(index, 'Foo')[:snippet]
|
|
568
|
+
assert_equal 'メソッドの説明。', find_entry(index, 'Foo#foo')[:snippet]
|
|
569
|
+
assert_equal '区切り文字。', find_entry(index, '$;')[:snippet]
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
def test_snippet_is_omitted_when_description_is_empty
|
|
573
|
+
index = @gen.build_index(@db)
|
|
574
|
+
e = find_entry(index, 'Foo#undocumented')
|
|
575
|
+
assert_not_nil e
|
|
576
|
+
assert_false e.key?(:snippet)
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
def test_merge_takes_snippet_from_newest_version
|
|
580
|
+
old_e = { name: 'X', full_name: 'X', type: 'class', path: 'class/-x.html', snippet: '古い説明' }
|
|
581
|
+
new_e = { name: 'X', full_name: 'X', type: 'class', path: 'class/-x.html', snippet: '新しい説明' }
|
|
582
|
+
merged = BitClust::SearchIndexGenerator.merge([['3.4', [new_e]], ['3.3', [old_e]]])
|
|
583
|
+
assert_equal 1, merged.size
|
|
584
|
+
assert_equal '新しい説明', merged.first[:snippet]
|
|
585
|
+
assert_equal %w[3.3 3.4], merged.first[:versions]
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def test_merge_keeps_snippet_of_last_version_having_the_page
|
|
589
|
+
only_old = { name: 'Y', full_name: 'Y', type: 'class', path: 'class/-y.html', snippet: '最終収録版の説明' }
|
|
590
|
+
merged = BitClust::SearchIndexGenerator.merge([['3.4', []], ['3.3', [only_old]]])
|
|
591
|
+
assert_equal '最終収録版の説明', merged.first[:snippet]
|
|
592
|
+
assert_equal %w[3.3], merged.first[:versions]
|
|
593
|
+
end
|
|
594
|
+
end
|
data/test/test_searcher.rb
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
require "test/unit"
|
|
2
2
|
require "bitclust"
|
|
3
3
|
require "bitclust/searcher"
|
|
4
|
+
require "bitclust/user_dirs"
|
|
4
5
|
require "fileutils"
|
|
5
6
|
require "tmpdir"
|
|
7
|
+
require "yaml"
|
|
6
8
|
|
|
7
9
|
class TestTerminalView < Test::Unit::TestCase
|
|
8
10
|
include BitClust
|
|
@@ -151,3 +153,64 @@ class TestSearcherModuleFunctionQuery < Test::Unit::TestCase
|
|
|
151
153
|
assert_equal ["Foo", ".", "b?"], parse("Foo.b?")
|
|
152
154
|
end
|
|
153
155
|
end
|
|
156
|
+
|
|
157
|
+
# refe/bitclust search が、環境変数指定が無いとき設定ファイル(UserDirs)
|
|
158
|
+
# 経由で DB を見つけること。XDG の場所→従来の ~/.bitclust の順
|
|
159
|
+
class TestSearcherDefaultDbpath < Test::Unit::TestCase
|
|
160
|
+
include BitClust
|
|
161
|
+
|
|
162
|
+
ENV_KEYS = %w[HOME XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME
|
|
163
|
+
REFE2_SERVER BITCLUST_SERVER REFE2_DATADIR BITCLUST_DATADIR].freeze
|
|
164
|
+
|
|
165
|
+
def setup
|
|
166
|
+
@saved = ENV_KEYS.to_h {|k| [k, ENV[k]] }
|
|
167
|
+
@home = Dir.mktmpdir
|
|
168
|
+
ENV['HOME'] = @home
|
|
169
|
+
(ENV_KEYS - %w[HOME]).each {|k| ENV.delete(k) }
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def teardown
|
|
173
|
+
@saved.each {|k, v| v ? ENV[k] = v : ENV.delete(k) }
|
|
174
|
+
FileUtils.rm_rf(@home)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def write_config(path, prefix)
|
|
178
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
179
|
+
File.write(path, { :default_version => '9.9', :database_prefix => prefix }.to_yaml)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def test_default_dbpath_via_xdg_config
|
|
183
|
+
prefix = File.join(@home, 'data', 'bitclust', 'db')
|
|
184
|
+
MethodDatabase.new("#{prefix}-9.9").init
|
|
185
|
+
write_config(File.join(@home, '.config', 'bitclust', 'config'), prefix)
|
|
186
|
+
searcher = Searcher.new
|
|
187
|
+
assert_equal("#{prefix}-9.9", searcher.send(:default_dbpath))
|
|
188
|
+
assert_true(searcher.local_database?)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def test_default_dbpath_via_legacy_config
|
|
192
|
+
prefix = File.join(@home, '.bitclust', 'db')
|
|
193
|
+
MethodDatabase.new("#{prefix}-9.9").init
|
|
194
|
+
write_config(File.join(@home, '.bitclust', 'config'), prefix)
|
|
195
|
+
searcher = Searcher.new
|
|
196
|
+
assert_equal("#{prefix}-9.9", searcher.send(:default_dbpath))
|
|
197
|
+
assert_true(searcher.local_database?)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def test_default_dbpath_prefers_xdg_over_legacy
|
|
201
|
+
legacy_prefix = File.join(@home, '.bitclust', 'db')
|
|
202
|
+
MethodDatabase.new("#{legacy_prefix}-9.9").init
|
|
203
|
+
write_config(File.join(@home, '.bitclust', 'config'), legacy_prefix)
|
|
204
|
+
xdg_prefix = File.join(@home, 'data', 'bitclust', 'db')
|
|
205
|
+
MethodDatabase.new("#{xdg_prefix}-9.9").init
|
|
206
|
+
write_config(File.join(@home, '.config', 'bitclust', 'config'), xdg_prefix)
|
|
207
|
+
searcher = Searcher.new
|
|
208
|
+
assert_equal("#{xdg_prefix}-9.9", searcher.send(:default_dbpath))
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def test_default_dbpath_nil_without_config
|
|
212
|
+
searcher = Searcher.new
|
|
213
|
+
assert_nil(searcher.send(:default_dbpath))
|
|
214
|
+
assert_false(searcher.local_database?)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require 'stringio'
|
|
3
|
+
require 'bitclust'
|
|
4
|
+
require 'bitclust/subcommands/server_command'
|
|
5
|
+
|
|
6
|
+
class TestServerCommand < Test::Unit::TestCase
|
|
7
|
+
def build(argv)
|
|
8
|
+
cmd = BitClust::Subcommands::ServerCommand.new
|
|
9
|
+
cmd.parse(argv)
|
|
10
|
+
cmd
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_does_not_require_global_database
|
|
14
|
+
assert_false BitClust::Subcommands::ServerCommand.new.needs_database?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_parse_accepts_short_option
|
|
18
|
+
cmd = build(['--baseurl=', '-d', '/tmp/db-x'])
|
|
19
|
+
assert_equal '/tmp/db-x', cmd.__send__(:resolve_dbpath, {:prefix => nil, :capi => false})
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_parse_accepts_long_option
|
|
23
|
+
cmd = build(['--baseurl=', '--database=/tmp/db-y'])
|
|
24
|
+
assert_equal '/tmp/db-y', cmd.__send__(:resolve_dbpath, {:prefix => '/tmp/global', :capi => false})
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_global_database_is_used_as_fallback
|
|
28
|
+
cmd = build(['--baseurl='])
|
|
29
|
+
assert_equal '/tmp/global', cmd.__send__(:resolve_dbpath, {:prefix => '/tmp/global', :capi => false})
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_missing_database_aborts_with_guidance
|
|
33
|
+
cmd = build(['--baseurl='])
|
|
34
|
+
err = StringIO.new
|
|
35
|
+
orig, $stderr = $stderr, err
|
|
36
|
+
assert_raise(SystemExit) { cmd.__send__(:resolve_dbpath, {:prefix => nil, :capi => false}) }
|
|
37
|
+
assert_match(/--database/, err.string)
|
|
38
|
+
ensure
|
|
39
|
+
$stderr = orig
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'test/unit'
|
|
3
|
+
require 'bitclust'
|
|
4
|
+
require 'bitclust/subcommands/setup_command'
|
|
5
|
+
require 'bitclust/user_dirs'
|
|
6
|
+
require 'tmpdir'
|
|
7
|
+
require 'fileutils'
|
|
8
|
+
require 'yaml'
|
|
9
|
+
|
|
10
|
+
# setup サブコマンドの既定バージョン。
|
|
11
|
+
#
|
|
12
|
+
# テストリスト:
|
|
13
|
+
# [x] 既定の対象バージョンに teeny が付いていない
|
|
14
|
+
# (#%if (version == "V") や #%version V の等値ゲートは文字列一致なので、
|
|
15
|
+
# teeny 付きの版名で DB を作ると doctree CI・生成済みドキュメントと
|
|
16
|
+
# ゲートの判定がずれる)
|
|
17
|
+
class TestSetupCommand < Test::Unit::TestCase
|
|
18
|
+
def test_default_versions_have_no_teeny
|
|
19
|
+
versions = BitClust::Subcommands::SetupCommand.new.instance_variable_get(:@versions)
|
|
20
|
+
assert_false(versions.empty?)
|
|
21
|
+
versions.each do |version|
|
|
22
|
+
assert_match(/\A\d+\.\d+\z/, version)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# setup サブコマンドが選ぶ、設定・DB・doctree checkout の置き場所。
|
|
28
|
+
#
|
|
29
|
+
# テストリスト:
|
|
30
|
+
# [x] 何も無ければ XDG の場所(~/.config/bitclust, ~/.local/share/bitclust)を使う
|
|
31
|
+
# [x] 従来の ~/.bitclust/config だけあれば、そのレイアウトを維持する
|
|
32
|
+
# [x] 両方あれば XDG を使う
|
|
33
|
+
class TestSetupCommandLayout < Test::Unit::TestCase
|
|
34
|
+
ENV_KEYS = %w[HOME XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME].freeze
|
|
35
|
+
|
|
36
|
+
def setup
|
|
37
|
+
@saved = ENV_KEYS.to_h {|k| [k, ENV[k]] }
|
|
38
|
+
@home = Dir.mktmpdir
|
|
39
|
+
ENV['HOME'] = @home
|
|
40
|
+
(ENV_KEYS - %w[HOME]).each {|k| ENV.delete(k) }
|
|
41
|
+
@cmd = BitClust::Subcommands::SetupCommand.new
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def teardown
|
|
45
|
+
@saved.each {|k, v| v ? ENV[k] = v : ENV.delete(k) }
|
|
46
|
+
FileUtils.rm_rf(@home)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def touch_config(path)
|
|
50
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
51
|
+
FileUtils.touch(path)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_layout_uses_xdg_when_nothing_exists
|
|
55
|
+
config_path, database_prefix, rubydoc_dir = @cmd.send(:layout)
|
|
56
|
+
assert_equal(File.join(@home, '.config', 'bitclust', 'config'), config_path.to_s)
|
|
57
|
+
assert_equal(File.join(@home, '.local', 'share', 'bitclust', 'db'), database_prefix)
|
|
58
|
+
assert_equal(File.join(@home, '.local', 'share', 'bitclust', 'rubydoc'), rubydoc_dir.to_s)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_layout_keeps_legacy_when_only_legacy_config_exists
|
|
62
|
+
touch_config(File.join(@home, '.bitclust', 'config'))
|
|
63
|
+
config_path, database_prefix, rubydoc_dir = @cmd.send(:layout)
|
|
64
|
+
assert_equal(File.join(@home, '.bitclust', 'config'), config_path.to_s)
|
|
65
|
+
assert_equal(File.join(@home, '.bitclust', 'db'), database_prefix)
|
|
66
|
+
assert_equal(File.join(@home, '.bitclust', 'rubydoc'), rubydoc_dir.to_s)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_layout_uses_xdg_when_both_exist
|
|
70
|
+
touch_config(File.join(@home, '.bitclust', 'config'))
|
|
71
|
+
touch_config(File.join(@home, '.config', 'bitclust', 'config'))
|
|
72
|
+
config_path, database_prefix, _rubydoc_dir = @cmd.send(:layout)
|
|
73
|
+
assert_equal(File.join(@home, '.config', 'bitclust', 'config'), config_path.to_s)
|
|
74
|
+
assert_equal(File.join(@home, '.local', 'share', 'bitclust', 'db'), database_prefix)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# setup --prepare が layout の選択どおりに設定ファイルを書くこと
|
|
79
|
+
# (checkout はネットワークに出るのでスタブする)
|
|
80
|
+
class TestSetupCommandPrepare < Test::Unit::TestCase
|
|
81
|
+
ENV_KEYS = %w[HOME XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME].freeze
|
|
82
|
+
|
|
83
|
+
def setup
|
|
84
|
+
@saved = ENV_KEYS.to_h {|k| [k, ENV[k]] }
|
|
85
|
+
@home = Dir.mktmpdir
|
|
86
|
+
ENV['HOME'] = @home
|
|
87
|
+
(ENV_KEYS - %w[HOME]).each {|k| ENV.delete(k) }
|
|
88
|
+
@cmd = BitClust::Subcommands::SetupCommand.new
|
|
89
|
+
stub(@cmd).checkout {|_dir| nil }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def teardown
|
|
93
|
+
@saved.each {|k, v| v ? ENV[k] = v : ENV.delete(k) }
|
|
94
|
+
FileUtils.rm_rf(@home)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def test_prepare_writes_config_under_xdg_config_home
|
|
98
|
+
@cmd.send(:prepare)
|
|
99
|
+
config_path = File.join(@home, '.config', 'bitclust', 'config')
|
|
100
|
+
assert_true(File.exist?(config_path))
|
|
101
|
+
config = YAML.load_file(config_path)
|
|
102
|
+
assert_equal(File.join(@home, '.local', 'share', 'bitclust', 'db'), config[:database_prefix])
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def test_prepare_keeps_legacy_layout_when_legacy_config_exists
|
|
106
|
+
legacy_config = File.join(@home, '.bitclust', 'config')
|
|
107
|
+
FileUtils.mkdir_p(File.dirname(legacy_config))
|
|
108
|
+
versions = @cmd.instance_variable_get(:@versions)
|
|
109
|
+
File.write(legacy_config, {
|
|
110
|
+
:database_prefix => File.join(@home, '.bitclust', 'db'),
|
|
111
|
+
:encoding => 'utf-8',
|
|
112
|
+
:versions => versions,
|
|
113
|
+
:default_version => versions.max,
|
|
114
|
+
:mdtree => File.join(@home, '.bitclust', 'rubydoc', 'manual', 'api'),
|
|
115
|
+
:capi_mdtree => File.join(@home, '.bitclust', 'rubydoc', 'manual', 'capi'),
|
|
116
|
+
:baseurl => 'http://localhost:10080',
|
|
117
|
+
:port => '10080',
|
|
118
|
+
:pid_file => '/tmp/bitclust.pid',
|
|
119
|
+
}.to_yaml)
|
|
120
|
+
@cmd.send(:prepare)
|
|
121
|
+
assert_false(File.exist?(File.join(@home, '.config', 'bitclust', 'config')))
|
|
122
|
+
config = YAML.load_file(legacy_config)
|
|
123
|
+
assert_equal(File.join(@home, '.bitclust', 'db'), config[:database_prefix])
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# setup --purge が新旧すべての置き場所を消すこと
|
|
128
|
+
class TestSetupCommandPurge < Test::Unit::TestCase
|
|
129
|
+
ENV_KEYS = %w[HOME XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME].freeze
|
|
130
|
+
|
|
131
|
+
def setup
|
|
132
|
+
@saved = ENV_KEYS.to_h {|k| [k, ENV[k]] }
|
|
133
|
+
@home = Dir.mktmpdir
|
|
134
|
+
ENV['HOME'] = @home
|
|
135
|
+
(ENV_KEYS - %w[HOME]).each {|k| ENV.delete(k) }
|
|
136
|
+
@cmd = BitClust::Subcommands::SetupCommand.new
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def teardown
|
|
140
|
+
@saved.each {|k, v| v ? ENV[k] = v : ENV.delete(k) }
|
|
141
|
+
FileUtils.rm_rf(@home)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def test_purge_removes_legacy_xdg_config_data_and_cache_dirs
|
|
145
|
+
dirs = [
|
|
146
|
+
File.join(@home, '.bitclust'),
|
|
147
|
+
File.join(@home, '.config', 'bitclust'),
|
|
148
|
+
File.join(@home, '.local', 'share', 'bitclust'),
|
|
149
|
+
File.join(@home, '.cache', 'bitclust'),
|
|
150
|
+
]
|
|
151
|
+
dirs.each {|dir| FileUtils.mkdir_p(dir) }
|
|
152
|
+
out, _err = capture_output do
|
|
153
|
+
assert_raise(SystemExit) { @cmd.send(:purge) }
|
|
154
|
+
end
|
|
155
|
+
dirs.each do |dir|
|
|
156
|
+
assert_false(File.exist?(dir), dir)
|
|
157
|
+
assert_include(out, dir)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def test_purge_is_fine_when_nothing_exists
|
|
162
|
+
assert_raise(SystemExit) { capture_output { @cmd.send(:purge) } }
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -202,3 +202,140 @@ class TestStatichtmlSitemap < Test::Unit::TestCase
|
|
|
202
202
|
end
|
|
203
203
|
end
|
|
204
204
|
end
|
|
205
|
+
|
|
206
|
+
class TestStatichtmlMarkdownOutput < Test::Unit::TestCase
|
|
207
|
+
def build_command(markdown_output: true)
|
|
208
|
+
cmd = BitClust::Subcommands::StatichtmlCommand.new
|
|
209
|
+
argv = ["--fs-casesensitive"]
|
|
210
|
+
argv.unshift("--markdown-output") if markdown_output
|
|
211
|
+
cmd.parse(argv)
|
|
212
|
+
cmd.instance_variable_set(:@verbose, false)
|
|
213
|
+
cmd.send(:create_manager_config)
|
|
214
|
+
urlmapper = cmd.instance_variable_get(:@urlmapper)
|
|
215
|
+
urlmapper.bitclust_html_base = '..'
|
|
216
|
+
cmd.instance_variable_set(:@markdown_exporter,
|
|
217
|
+
BitClust::MarkdownExporter.new(urlmapper, '.html'))
|
|
218
|
+
cmd
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def markdown_db
|
|
222
|
+
db = BitClust::MethodDatabase.dummy({ "version" => "3.4" })
|
|
223
|
+
def db.properties = { 'source_format' => 'markdown' }
|
|
224
|
+
db
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def test_option_defaults_to_false
|
|
228
|
+
cmd = BitClust::Subcommands::StatichtmlCommand.new
|
|
229
|
+
assert_false(cmd.instance_variable_get(:@markdown_output))
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def test_option_is_parsed
|
|
233
|
+
cmd = BitClust::Subcommands::StatichtmlCommand.new
|
|
234
|
+
cmd.parse(["--markdown-output"])
|
|
235
|
+
assert_true(cmd.instance_variable_get(:@markdown_output))
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def test_markdown_path_replaces_html_suffix
|
|
239
|
+
cmd = BitClust::Subcommands::StatichtmlCommand.new
|
|
240
|
+
assert_equal("out/class/ARGF.class.md",
|
|
241
|
+
cmd.send(:markdown_path, Pathname.new("out/class/ARGF.class.html")).to_s)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def test_writes_md_next_to_html_for_markdown_db
|
|
245
|
+
Dir.mktmpdir do |dir|
|
|
246
|
+
cmd = build_command
|
|
247
|
+
db = markdown_db
|
|
248
|
+
entry = BitClust::DocEntry.new(db, "help")
|
|
249
|
+
entry.title = "ヘルプ"
|
|
250
|
+
entry.source = "\n[c:String] を参照。\n"
|
|
251
|
+
path = Pathname.new(dir) + "doc/help.html"
|
|
252
|
+
FileUtils.mkdir_p(path.dirname)
|
|
253
|
+
cmd.send(:create_markdown_file_p, entry, path, db)
|
|
254
|
+
md = File.read(File.join(dir, "doc", "help.md"))
|
|
255
|
+
assert_equal("# ヘルプ\n\n[String](../class/String.md) を参照。\n", md)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def test_skips_md_for_non_markdown_db
|
|
260
|
+
Dir.mktmpdir do |dir|
|
|
261
|
+
cmd = build_command
|
|
262
|
+
db = BitClust::MethodDatabase.dummy({ "version" => "3.4" })
|
|
263
|
+
def db.properties = {}
|
|
264
|
+
entry = BitClust::DocEntry.new(db, "help")
|
|
265
|
+
entry.title = "ヘルプ"
|
|
266
|
+
entry.source = "\n本文\n"
|
|
267
|
+
path = Pathname.new(dir) + "doc/help.html"
|
|
268
|
+
FileUtils.mkdir_p(path.dirname)
|
|
269
|
+
cmd.send(:create_markdown_file_p, entry, path, db)
|
|
270
|
+
assert_false(File.exist?(File.join(dir, "doc", "help.md")))
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def test_warns_when_db_is_not_markdown
|
|
275
|
+
cmd = build_command
|
|
276
|
+
db = BitClust::MethodDatabase.dummy({ "version" => "3.4" })
|
|
277
|
+
def db.properties = {}
|
|
278
|
+
orig_stderr, $stderr = $stderr, StringIO.new
|
|
279
|
+
begin
|
|
280
|
+
cmd.send(:warn_markdown_output_skipped, db, "the method database")
|
|
281
|
+
assert_match(/--markdown-output is ignored/, $stderr.string)
|
|
282
|
+
ensure
|
|
283
|
+
$stderr = orig_stderr
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def test_no_warning_without_option
|
|
288
|
+
cmd = build_command(markdown_output: false)
|
|
289
|
+
db = BitClust::MethodDatabase.dummy({ "version" => "3.4" })
|
|
290
|
+
def db.properties = {}
|
|
291
|
+
orig_stderr, $stderr = $stderr, StringIO.new
|
|
292
|
+
begin
|
|
293
|
+
cmd.send(:warn_markdown_output_skipped, db, "the method database")
|
|
294
|
+
assert_equal("", $stderr.string)
|
|
295
|
+
ensure
|
|
296
|
+
$stderr = orig_stderr
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
class TestStatichtmlVersionSwitcher < Test::Unit::TestCase
|
|
302
|
+
def build_command(themedir, outputdir)
|
|
303
|
+
cmd = BitClust::Subcommands::StatichtmlCommand.new
|
|
304
|
+
cmd.instance_variable_set(:@manager_config, { :themedir => Pathname.new(themedir) })
|
|
305
|
+
cmd.instance_variable_set(:@outputdir, Pathname.new(outputdir))
|
|
306
|
+
cmd.instance_variable_set(:@verbose, false)
|
|
307
|
+
cmd
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def test_version_switcher_js_is_copied
|
|
311
|
+
Dir.mktmpdir do |dir|
|
|
312
|
+
themedir = File.join(dir, 'theme')
|
|
313
|
+
outputdir = File.join(dir, 'out')
|
|
314
|
+
FileUtils.mkdir_p(File.join(themedir, 'js'))
|
|
315
|
+
FileUtils.mkdir_p(outputdir)
|
|
316
|
+
File.write(File.join(themedir, 'js', 'version_switcher.js'), "// vs\n")
|
|
317
|
+
cmd = build_command(themedir, outputdir)
|
|
318
|
+
cmd.send(:copy_version_switcher_script)
|
|
319
|
+
assert_true(File.file?(File.join(outputdir, 'js', 'version_switcher.js')))
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# An older themedir without the file must not abort the whole build.
|
|
324
|
+
def test_theme_without_version_switcher_js_is_tolerated
|
|
325
|
+
Dir.mktmpdir do |dir|
|
|
326
|
+
themedir = File.join(dir, 'theme')
|
|
327
|
+
outputdir = File.join(dir, 'out')
|
|
328
|
+
FileUtils.mkdir_p(themedir)
|
|
329
|
+
FileUtils.mkdir_p(outputdir)
|
|
330
|
+
cmd = build_command(themedir, outputdir)
|
|
331
|
+
orig_stderr, $stderr = $stderr, StringIO.new
|
|
332
|
+
begin
|
|
333
|
+
assert_nothing_raised { cmd.send(:copy_version_switcher_script) }
|
|
334
|
+
assert_match(/version_switcher\.js not found/, $stderr.string)
|
|
335
|
+
ensure
|
|
336
|
+
$stderr = orig_stderr
|
|
337
|
+
end
|
|
338
|
+
assert_false(File.exist?(File.join(outputdir, 'js', 'version_switcher.js')))
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
@@ -123,6 +123,98 @@ class TestSyntaxHighlighter < Test::Unit::TestCase
|
|
|
123
123
|
assert_equal(expected, highlight(source))
|
|
124
124
|
end
|
|
125
125
|
|
|
126
|
+
sub_test_case "symbol span" do
|
|
127
|
+
test 'ivar symbol' do
|
|
128
|
+
source = "x = :@records\n"
|
|
129
|
+
expected = "x <span class=\"o\">=</span> <span class=\"ss\">:@records</span>\n"
|
|
130
|
+
assert_equal(expected, highlight(source))
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
test 'cvar symbol' do
|
|
134
|
+
source = "x = :@@cv\n"
|
|
135
|
+
expected = "x <span class=\"o\">=</span> <span class=\"ss\">:@@cv</span>\n"
|
|
136
|
+
assert_equal(expected, highlight(source))
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
test 'gvar symbol' do
|
|
140
|
+
source = "x = :$stdout\n"
|
|
141
|
+
expected = "x <span class=\"o\">=</span> <span class=\"ss\">:$stdout</span>\n"
|
|
142
|
+
assert_equal(expected, highlight(source))
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
test 'operator symbols' do
|
|
146
|
+
source = <<~END
|
|
147
|
+
a = :+
|
|
148
|
+
b = :<<
|
|
149
|
+
c = :[]
|
|
150
|
+
d = :[]=
|
|
151
|
+
e = :<=>
|
|
152
|
+
f = :!
|
|
153
|
+
END
|
|
154
|
+
expected = <<~END
|
|
155
|
+
a <span class="o">=</span> <span class="ss">:+</span>
|
|
156
|
+
b <span class="o">=</span> <span class="ss">:<<</span>
|
|
157
|
+
c <span class="o">=</span> <span class="ss">:[]</span>
|
|
158
|
+
d <span class="o">=</span> <span class="ss">:[]=</span>
|
|
159
|
+
e <span class="o">=</span> <span class="ss">:<=></span>
|
|
160
|
+
f <span class="o">=</span> <span class="ss">:!</span>
|
|
161
|
+
END
|
|
162
|
+
assert_equal(expected, highlight(source))
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
test '%s dynamic symbol literal' do
|
|
166
|
+
source = "x = %s!sym!\n"
|
|
167
|
+
expected = "x <span class=\"o\">=</span> <span class=\"ss\">%s!sym!</span>\n"
|
|
168
|
+
assert_equal(expected, highlight(source))
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
test 'double-quoted dynamic symbol' do
|
|
172
|
+
source = "x = :\"dyn\"\n"
|
|
173
|
+
expected = "x <span class=\"o\">=</span> <span class=\"ss\">:"dyn"</span>\n"
|
|
174
|
+
assert_equal(expected, highlight(source))
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
test 'well-formedness of mixed symbol spans' do
|
|
178
|
+
source = <<~END
|
|
179
|
+
x = :@records
|
|
180
|
+
y = :@@cv
|
|
181
|
+
z = :$stdout
|
|
182
|
+
a = :+
|
|
183
|
+
b = :<<
|
|
184
|
+
c = :[]
|
|
185
|
+
s = %s!sym!
|
|
186
|
+
d = :"dyn"
|
|
187
|
+
END
|
|
188
|
+
result = highlight(source)
|
|
189
|
+
assert_equal(result.scan('<span').size, result.scan('</span>').size)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
test 'symbol does not leak the stack into later tokens' do
|
|
193
|
+
source = <<~END
|
|
194
|
+
x = :@records
|
|
195
|
+
def foo
|
|
196
|
+
end
|
|
197
|
+
END
|
|
198
|
+
baseline = highlight("def foo\nend\n")
|
|
199
|
+
result = highlight(source)
|
|
200
|
+
assert(
|
|
201
|
+
result.end_with?(baseline),
|
|
202
|
+
"expected result to end with unaffected `def foo/end` rendering:\n" \
|
|
203
|
+
" baseline: #{baseline.inspect}\n" \
|
|
204
|
+
" result: #{result.inspect}"
|
|
205
|
+
)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
test 'label is not treated as a symbol' do
|
|
209
|
+
source = "h = { a: 1 }\nfoo(:x, k: 2)\n"
|
|
210
|
+
result = highlight(source)
|
|
211
|
+
assert_equal(result.scan('<span').size, result.scan('</span>').size)
|
|
212
|
+
assert_match(%r{<span class="ss">a:</span>}, result)
|
|
213
|
+
assert_match(%r{<span class="ss">k:</span>}, result)
|
|
214
|
+
assert_match(%r{<span class="ss">:x</span>}, result)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
126
218
|
test 'one liner module' do
|
|
127
219
|
source = <<~END
|
|
128
220
|
module Foo; end
|
|
@@ -142,4 +234,36 @@ class TestSyntaxHighlighter < Test::Unit::TestCase
|
|
|
142
234
|
END
|
|
143
235
|
assert_equal(expected, highlight(source))
|
|
144
236
|
end
|
|
237
|
+
|
|
238
|
+
sub_test_case "heredoc argument and quoted label" do
|
|
239
|
+
def assert_balanced(html)
|
|
240
|
+
assert_equal html.scan(/<span\b/).size, html.scan(%r{</span>}).size, html
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
test 'identifier after a heredoc argument is rendered normally' do
|
|
244
|
+
html = highlight("method(arg1, <<LABEL, arg2)\n x\nLABEL\n")
|
|
245
|
+
assert_include(html, ', arg2<span class="p">)</span>')
|
|
246
|
+
assert_balanced(html)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
test 'block argument after a heredoc argument' do
|
|
250
|
+
html = highlight("ERB.new(<<~'END_PRODUCT', eoutvar: \"@product\").result b\n x\nEND_PRODUCT\n")
|
|
251
|
+
assert_balanced(html)
|
|
252
|
+
assert_not_include(html, '<span class="no">b')
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
test 'quoted symbol keys close the string span' do
|
|
256
|
+
html = highlight(%q({ "a":"A", 'b':"B" }) + "\n")
|
|
257
|
+
assert_balanced(html)
|
|
258
|
+
assert_include(html, %q(<span class="s1">'b':</span>))
|
|
259
|
+
assert_include(html, %q(<span class="s2">a</span><span class="s2">":</span>))
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
test 'quoted symbol key does not leak the stack' do
|
|
263
|
+
with = highlight(%q({ "a":"A" }) + "\ndef foo\nend\n")
|
|
264
|
+
without = highlight("def foo\nend\n")
|
|
265
|
+
assert_include(with, without.lines[0].chomp)
|
|
266
|
+
assert_balanced(with)
|
|
267
|
+
end
|
|
268
|
+
end
|
|
145
269
|
end
|