bitclust-core 1.7.0 → 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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/data/bitclust/template/class-index +0 -1
  3. data/data/bitclust/template.epub/class +5 -5
  4. data/data/bitclust/template.epub/class-index +0 -1
  5. data/data/bitclust/template.epub/contents +7 -4
  6. data/data/bitclust/template.epub/layout +4 -4
  7. data/data/bitclust/template.offline/class-index +0 -1
  8. data/lib/bitclust/generators/epub.rb +69 -5
  9. data/lib/bitclust/irb.rb +14 -8
  10. data/lib/bitclust/markdown_bridge.rb +11 -6
  11. data/lib/bitclust/markdown_tree.rb +118 -11
  12. data/lib/bitclust/methoddatabase.rb +17 -4
  13. data/lib/bitclust/nameutils.rb +13 -0
  14. data/lib/bitclust/preprocessor.rb +0 -36
  15. data/lib/bitclust/remote.rb +405 -0
  16. data/lib/bitclust/runner.rb +5 -10
  17. data/lib/bitclust/screen.rb +2 -1
  18. data/lib/bitclust/searcher.rb +43 -24
  19. data/lib/bitclust/subcommands/chm_command.rb +1 -1
  20. data/lib/bitclust/subcommands/epub_command.rb +2 -2
  21. data/lib/bitclust/subcommands/extract_command.rb +7 -0
  22. data/lib/bitclust/subcommands/htmlfile_command.rb +7 -0
  23. data/lib/bitclust/subcommands/methods_command.rb +5 -28
  24. data/lib/bitclust/subcommands/query_command.rb +1 -0
  25. data/lib/bitclust/subcommands/server_command.rb +21 -10
  26. data/lib/bitclust/subcommands/setup_command.rb +28 -10
  27. data/lib/bitclust/syntax_highlighter.rb +65 -4
  28. data/lib/bitclust/user_dirs.rb +62 -0
  29. data/lib/bitclust/version.rb +1 -1
  30. data/test/test_chm_command.rb +22 -0
  31. data/test/test_epub_command.rb +60 -0
  32. data/test/test_epub_generator.rb +330 -0
  33. data/test/test_extract_command.rb +45 -0
  34. data/test/test_irb_plugin.rb +100 -6
  35. data/test/test_markdown_bridge.rb +35 -0
  36. data/test/test_markdown_tree.rb +144 -0
  37. data/test/test_mdparser.rb +39 -0
  38. data/test/test_methods_command.rb +32 -0
  39. data/test/test_opensearchdescription_screen.rb +39 -0
  40. data/test/test_query_command.rb +30 -0
  41. data/test/test_remote.rb +374 -0
  42. data/test/test_runner.rb +61 -1
  43. data/test/test_searcher.rb +63 -0
  44. data/test/test_server_command.rb +41 -0
  45. data/test/test_setup_command.rb +143 -0
  46. data/test/test_syntax_highlighter.rb +124 -0
  47. data/test/test_user_dirs.rb +62 -0
  48. metadata +37 -2
  49. data/lib/bitclust/ridatabase.rb +0 -359
@@ -29,16 +29,9 @@ module BitClust
29
29
  @mode = :diff
30
30
  @target = path
31
31
  }
32
- @parser.on('-c', '') {
33
- @content = true
34
- require 'bitclust/ridatabase'
35
- }
36
32
  @parser.on('--ruby=[VER]', "The version of Ruby interpreter"){|version|
37
33
  @version = version
38
34
  }
39
- @parser.on('--ri-database', 'The path of ri database'){|path|
40
- @ri_path = path
41
- }
42
35
  end
43
36
 
44
37
  def parse(argv)
@@ -67,27 +60,11 @@ module BitClust
67
60
  list0 = c.entries + list0
68
61
  # @type var list: Array[String]
69
62
  list = list0.map {|ent| ent.labels.map {|n| expand_mf(n) } }.flatten
70
- if @content
71
- ri = @ri_path ? RiDatabase.open(@ri_path, nil) : RiDatabase.open_system_db
72
- ri.current_class = c.name
73
- mthds = ( ri.singleton_methods + ri.instance_methods )
74
- fmt = Formatter.new
75
- (keys - list).sort.each do |name|
76
- mthd = mthds.find{|m| name == m.fullname }
77
- if mthd
78
- puts fmt.method_info(mthd.entry)
79
- else
80
- name = name.sub(/\A\w+#/, '')
81
- puts "--- #{name}\n#%todo\n\n"
82
- end
83
- end
84
- else
85
- (keys - list).sort.each do |name|
86
- puts "-#{name}"
87
- end
88
- (list - keys).sort.each do |name|
89
- puts "+#{name}"
90
- end
63
+ (keys - list).sort.each do |name|
64
+ puts "-#{name}"
65
+ end
66
+ (list - keys).sort.each do |name|
67
+ puts "+#{name}"
91
68
  end
92
69
  else
93
70
  raise "must not happen: #{@mode.inspect}"
@@ -19,6 +19,7 @@ module BitClust
19
19
  end
20
20
 
21
21
  def exec(argv, options)
22
+ super # @db を用意する(スクリプトからは @db で参照する)
22
23
  argv.each do |query|
23
24
  # pp eval(query) # FIXME: causes ArgumentError
24
25
  p eval(query)
@@ -9,6 +9,7 @@ require 'uri'
9
9
 
10
10
  require 'bitclust'
11
11
  require 'bitclust/subcommand'
12
+ require 'bitclust/user_dirs'
12
13
 
13
14
  module BitClust
14
15
  module Subcommands
@@ -50,7 +51,7 @@ module BitClust
50
51
  @parser.on('--baseurl=URL', 'The base URL to host.') {|url|
51
52
  @baseurl = url
52
53
  }
53
- @parser.on('--database=PATH', 'MethodDatabase root directory.') {|path|
54
+ @parser.on('-d', '--database=PATH', 'MethodDatabase root directory.') {|path|
54
55
  @dbpath = path
55
56
  }
56
57
  @parser.on('--srcdir=PATH', 'BitClust source directory.') {|path|
@@ -85,17 +86,20 @@ module BitClust
85
86
  }
86
87
  end
87
88
 
89
+ # DB はサブコマンド自身の -d/--database か、グローバル --database
90
+ # (options[:prefix])のどちらでも受ける。runner にはグローバル側を
91
+ # 要求させない(ancestors と同じ扱い)
92
+ def needs_database?
93
+ false
94
+ end
95
+
88
96
  def parse(argv)
89
97
  super
90
98
  load_config_file
91
99
  set_srcdir(srcdir_root) unless @srcdir
92
100
 
93
101
  unless @baseurl
94
- $stderr.puts "missing base URL. Use --baseurl or check ~/.bitclust/config"
95
- exit 1
96
- end
97
- unless @dbpath || @autop
98
- $stderr.puts "missing database path. Use --database"
102
+ $stderr.puts "missing base URL. Use --baseurl or check the config file (bitclust setup)"
99
103
  exit 1
100
104
  end
101
105
  unless @datadir
@@ -116,6 +120,7 @@ module BitClust
116
120
  end
117
121
 
118
122
  def exec(argv, options)
123
+ @dbpath = resolve_dbpath(options)
119
124
  begin
120
125
  require 'webrick'
121
126
  rescue LoadError
@@ -210,11 +215,17 @@ module BitClust
210
215
  @themedir ||= "#{@srcdir}/theme"
211
216
  end
212
217
 
218
+ # サブコマンド側の --database が無ければグローバル --database
219
+ # (runner の options[:prefix])を使う。--auto のときは不要
220
+ def resolve_dbpath(options)
221
+ dbpath = @dbpath || options[:prefix]
222
+ abort "missing database path. Use --database (-d)" unless dbpath || @autop
223
+ dbpath
224
+ end
225
+
213
226
  def load_config_file
214
- home_directory = Pathname(ENV.fetch('HOME'))
215
- config_path = home_directory + ".bitclust/config"
216
- if config_path.exist?
217
- config = YAML.load_file(config_path)
227
+ config = UserDirs.load_config
228
+ if config
218
229
  @baseurl ||= config[:baseurl]
219
230
  @dbpath ||= "#{config[:database_prefix]}-#{config[:default_version]}"
220
231
  @port ||= config[:port]
@@ -8,6 +8,7 @@ require 'yaml'
8
8
 
9
9
  require 'bitclust'
10
10
  require 'bitclust/subcommand'
11
+ require 'bitclust/user_dirs'
11
12
 
12
13
  module BitClust
13
14
  module Subcommands
@@ -43,7 +44,9 @@ module BitClust
43
44
  }
44
45
  end
45
46
 
46
- # DB は ~/.bitclust 配下に自前で作るのでグローバル --database は不要
47
+ # DB は設定ファイル(UserDirs)が示す場所(既定は $XDG_DATA_HOME/bitclust、
48
+ # 旧 ~/.bitclust があればそちら)に自前で作るのでグローバル --database
49
+ # は不要
47
50
  def needs_database?
48
51
  false
49
52
  end
@@ -68,22 +71,37 @@ module BitClust
68
71
  private
69
72
 
70
73
  def purge
71
- home_directory = Pathname(ENV.fetch("HOME"))
72
- config_dir = home_directory + ".bitclust"
73
74
  print "Remove all generated files..."
74
- FileUtils.rm_rf(config_dir.to_s)
75
+ puts
76
+ [UserDirs.legacy_dir, UserDirs.config_home, UserDirs.data_home, UserDirs.cache_home].each do |dir|
77
+ next unless dir.exist?
78
+ FileUtils.rm_rf(dir.to_s)
79
+ puts " removed #{dir}"
80
+ end
75
81
  puts "done!"
76
82
  exit 0
77
83
  end
78
84
 
85
+ # 設定ファイル・DB・doctree checkout の置き場所を選ぶ。既に旧
86
+ # ~/.bitclust/config があり、まだ XDG 側の設定ファイルが無ければ
87
+ # 互換のため旧レイアウトを維持する。それ以外は XDG レイアウトを使う
88
+ def layout
89
+ legacy_config = UserDirs.legacy_dir + "config"
90
+ xdg_config = UserDirs.config_home + "config"
91
+ if legacy_config.exist? && !xdg_config.exist?
92
+ config_dir = UserDirs.legacy_dir
93
+ [config_dir + "config", (config_dir + "db").to_s, config_dir + "rubydoc"]
94
+ else
95
+ [xdg_config, (UserDirs.data_home + "db").to_s, UserDirs.data_home + "rubydoc"]
96
+ end
97
+ end
98
+
79
99
  def prepare
80
- home_directory = Pathname(ENV.fetch("HOME")).expand_path
81
- config_dir = home_directory + ".bitclust"
82
- config_dir.mkpath
83
- config_path = config_dir + "config"
84
- rubydoc_dir = config_dir + "rubydoc"
100
+ config_path, database_prefix, rubydoc_dir = layout
101
+ config_path.dirname.mkpath
102
+ rubydoc_dir.dirname.mkpath
85
103
  @config = {
86
- :database_prefix => (config_dir + "db").to_s,
104
+ :database_prefix => database_prefix,
87
105
  :encoding => "utf-8",
88
106
  :versions => @versions,
89
107
  :default_version => @versions.max,
@@ -161,9 +161,6 @@ module BitClust
161
161
  data << "<span class=\"nf\">#{token}</span>"
162
162
  when @stack.last == :embexpr
163
163
  data << "<span class=\"n\">#{token}</span>"
164
- when @stack.last == :heredoc
165
- style = COLORS[:heredoc_beg]
166
- data << "<span class=\"#{style}\">#{token}"
167
164
  when @stack.last == :method_call
168
165
  data << "<span class=\"nf\">#{token}</span>"
169
166
  @stack.pop
@@ -192,6 +189,48 @@ module BitClust
192
189
  data
193
190
  end
194
191
 
192
+ # on_symbeg が開いた <span class="ss"> を、シンボルの末尾トークン
193
+ # (HTML エスケープ済み)を添えて閉じ、@stack の :symbol を外す。
194
+ # on_ident/on_const/on_kw は自前で閉じるので、それ以外の
195
+ # 末尾になりうるトークン(ivar/cvar/gvar/backtick/op/文字列終端)から呼ぶ
196
+ def close_symbol_span(token, data)
197
+ data << "#{escape_html(token)}</span>"
198
+ @stack.pop
199
+ data
200
+ end
201
+
202
+ def on_ivar(token, data)
203
+ if @stack.last == :symbol
204
+ close_symbol_span(token, data)
205
+ else
206
+ on_default(:on_ivar, token, data)
207
+ end
208
+ end
209
+
210
+ def on_cvar(token, data)
211
+ if @stack.last == :symbol
212
+ close_symbol_span(token, data)
213
+ else
214
+ on_default(:on_cvar, token, data)
215
+ end
216
+ end
217
+
218
+ def on_gvar(token, data)
219
+ if @stack.last == :symbol
220
+ close_symbol_span(token, data)
221
+ else
222
+ on_default(:on_gvar, token, data)
223
+ end
224
+ end
225
+
226
+ def on_backtick(token, data)
227
+ if @stack.last == :symbol
228
+ close_symbol_span(token, data)
229
+ else
230
+ on_default(:on_backtick, token, data)
231
+ end
232
+ end
233
+
195
234
  def on_kw(token, data)
196
235
  case
197
236
  when @stack.last == :symbol
@@ -221,6 +260,8 @@ module BitClust
221
260
 
222
261
  def on_op(token, data)
223
262
  case
263
+ when @stack.last == :symbol
264
+ close_symbol_span(token, data)
224
265
  when token == "::" && [:class, :module].include?(_ = @stack.last)
225
266
  @name_buffer << token
226
267
  when token == "<<" && @stack.last == :class
@@ -312,7 +353,7 @@ module BitClust
312
353
 
313
354
  def on_symbeg(token, data)
314
355
  style = COLORS[:symbeg]
315
- data << "<span class=\"#{style}\">#{token}"
356
+ data << "<span class=\"#{style}\">#{escape_html(token)}"
316
357
  @stack << :symbol
317
358
  data
318
359
  end
@@ -336,14 +377,34 @@ module BitClust
336
377
  data << escape_html(token)
337
378
  when @stack.last == :string2
338
379
  data << "<span class=\"s2\">#{escape_html(token)}</span>"
380
+ when @stack.last == :symbol
381
+ data << escape_html(token)
339
382
  else
340
383
  on_default(:on_tstring_content, token, data)
341
384
  end
342
385
  data
343
386
  end
344
387
 
388
+ # "a": / 'b': 形式のシンボルキー。開いている文字列の span を
389
+ # 文字列終端と同じ流儀で閉じる
390
+ def on_label_end(token, data)
391
+ case @stack.last
392
+ when :string1
393
+ data << "#{escape_html(token)}</span>"
394
+ @stack.pop
395
+ when :string2
396
+ data << "<span class=\"s2\">#{escape_html(token)}</span>"
397
+ @stack.pop
398
+ else
399
+ on_default(:on_label_end, token, data)
400
+ end
401
+ data
402
+ end
403
+
345
404
  def on_tstring_end(token, data)
346
405
  case
406
+ when @stack.last == :symbol
407
+ data << "#{escape_html(token)}</span>"
347
408
  when token == "'"
348
409
  data << "#{token}</span>"
349
410
  when %i[qwords words qsymbols symbols].include?(_ = @stack.last)
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # ユーザーごとの設定ファイル・DB・キャッシュの置き場所。
4
+ # XDG Base Directory(XDG_CONFIG_HOME / XDG_DATA_HOME / XDG_CACHE_HOME、
5
+ # 未設定なら ~/.config ~/.local/share ~/.cache)配下の bitclust/ を使い、
6
+ # 従来の ~/.bitclust は互換のため読み取りの fallback として残す。
7
+
8
+ require 'pathname'
9
+ require 'yaml'
10
+
11
+ module BitClust
12
+ module UserDirs
13
+ module_function
14
+
15
+ def home
16
+ Pathname(ENV.fetch('HOME')).expand_path
17
+ end
18
+
19
+ # 従来の置き場所(bitclust setup が 1.7 まで作っていた場所)
20
+ def legacy_dir
21
+ home + '.bitclust'
22
+ end
23
+
24
+ def config_home
25
+ xdg_dir('XDG_CONFIG_HOME', '.config')
26
+ end
27
+
28
+ def data_home
29
+ xdg_dir('XDG_DATA_HOME', File.join('.local', 'share'))
30
+ end
31
+
32
+ def cache_home
33
+ xdg_dir('XDG_CACHE_HOME', '.cache')
34
+ end
35
+
36
+ # 設定ファイルの候補(優先順)
37
+ def config_candidates
38
+ [config_home + 'config', legacy_dir + 'config']
39
+ end
40
+
41
+ # 存在する設定ファイル。XDG の場所を先に見て、無ければ従来の
42
+ # ~/.bitclust/config。どちらも無ければ nil
43
+ def config_file
44
+ config_candidates.find(&:exist?)
45
+ end
46
+
47
+ # 設定ファイルを読む。無ければ nil
48
+ def load_config
49
+ path = config_file
50
+ path ? YAML.load_file(path) : nil
51
+ end
52
+
53
+ # 環境変数が絶対パスならそれを、無ければ HOME 直下の既定を使う
54
+ # (XDG の仕様では相対パスの指定は無視する)
55
+ def xdg_dir(env_key, default_subdir)
56
+ value = ENV[env_key]
57
+ base = (value && Pathname(value).absolute?) ? Pathname(value) : home + default_subdir
58
+ base + 'bitclust'
59
+ end
60
+ private_class_method :xdg_dir
61
+ end
62
+ end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module BitClust
3
- VERSION = "1.7.0"
3
+ VERSION = "1.8.0"
4
4
  end
@@ -0,0 +1,22 @@
1
+ require 'test/unit'
2
+ require 'bitclust'
3
+ require 'bitclust/subcommands/chm_command'
4
+
5
+ class TestChmSitemap < Test::Unit::TestCase
6
+ def test_to_html_builds_nested_sitemap
7
+ root = BitClust::Subcommands::ChmCommand::Sitemap.new('Ruby', 'doc/index.html')
8
+ child = BitClust::Subcommands::ChmCommand::Sitemap::Content.new('Array <=> & "q"', 'class/-array.html')
9
+ root << child
10
+ html = root.to_html
11
+ assert_match(%r{<param name="Name" value="Ruby">}, html)
12
+ assert_match(%r{<param name="Local" value="doc/index.html">}, html)
13
+ assert_match(%r{<UL>.*Array &lt;=&gt; &amp; &quot;q&quot;.*</UL>}m, html)
14
+ end
15
+
16
+ def test_to_html_without_children_has_no_ul
17
+ leaf = BitClust::Subcommands::ChmCommand::Sitemap::Content.new('Leaf')
18
+ html = leaf.to_html
19
+ assert_not_match(/<UL>/, html)
20
+ assert_not_match(/Local/, html)
21
+ end
22
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+ require 'test/unit'
3
+ require 'test/unit/rr'
4
+ require 'tmpdir'
5
+ require 'bitclust'
6
+ require 'bitclust/subcommands/epub_command'
7
+ require 'bitclust/generators/epub'
8
+
9
+ # `bitclust epub` は常に失敗していた:
10
+ #
11
+ # 1) --catalog=PATH は @catalogdir に保存されるが、exec は(定義されていない
12
+ # ため常に nil の)@catalog をジェネレータへ渡していた。ジェネレータは
13
+ # それを statichtml へ --catalog=(空文字列)として渡し、
14
+ # Pathname.new("").realpath が Errno::ENOENT で失敗する。
15
+ # 2) @catalogdir の既定値が nil だったので、--catalog を指定しない限り
16
+ # `bitclust epub -o DIR` は必ず失敗していた
17
+ # (chm_command / statichtml_command と同じく、既定は同梱の
18
+ # data/bitclust/catalog にすべき)。
19
+ class TestEPUBCommandCatalog < Test::Unit::TestCase
20
+ def test_default_catalogdir_is_the_bundled_catalog
21
+ cmd = BitClust::Subcommands::EPUBCommand.new
22
+ cmd.parse([])
23
+ expected = cmd.srcdir_root + "data/bitclust/catalog"
24
+ assert_equal(expected, cmd.instance_variable_get(:@catalogdir))
25
+ assert_true(expected.directory?, "#{expected} must exist so --catalog can be omitted")
26
+ end
27
+
28
+ def test_catalog_option_overrides_the_default
29
+ Dir.mktmpdir do |dir|
30
+ cmd = BitClust::Subcommands::EPUBCommand.new
31
+ cmd.parse(["--catalog=#{dir}"])
32
+ assert_equal(Pathname.new(dir).realpath, cmd.instance_variable_get(:@catalogdir))
33
+ end
34
+ end
35
+
36
+ def capture_generator_options(cmd)
37
+ captured = nil
38
+ fake_generator = Object.new
39
+ stub(fake_generator).generate { nil }
40
+ stub(BitClust::Generators::EPUB).new {|opts| captured = opts; fake_generator }
41
+ cmd.exec([], { :prefix => nil, :capi => false })
42
+ captured
43
+ end
44
+
45
+ def test_exec_passes_the_default_catalogdir_to_the_generator
46
+ cmd = BitClust::Subcommands::EPUBCommand.new
47
+ cmd.parse([])
48
+ opts = capture_generator_options(cmd)
49
+ assert_equal(cmd.instance_variable_get(:@catalogdir), opts[:catalog])
50
+ end
51
+
52
+ def test_exec_passes_the_overridden_catalogdir_to_the_generator
53
+ Dir.mktmpdir do |dir|
54
+ cmd = BitClust::Subcommands::EPUBCommand.new
55
+ cmd.parse(["--catalog=#{dir}"])
56
+ opts = capture_generator_options(cmd)
57
+ assert_equal(Pathname.new(dir).realpath, opts[:catalog])
58
+ end
59
+ end
60
+ end