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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -0
  3. data/data/bitclust/catalog/ja_JP.UTF-8 +4 -2
  4. data/data/bitclust/template/class-index +0 -1
  5. data/data/bitclust/template/function-index +1 -0
  6. data/data/bitclust/template/layout +1 -0
  7. data/data/bitclust/template.epub/class +5 -5
  8. data/data/bitclust/template.epub/class-index +0 -1
  9. data/data/bitclust/template.epub/contents +7 -4
  10. data/data/bitclust/template.epub/layout +4 -4
  11. data/data/bitclust/template.offline/class-index +0 -1
  12. data/data/bitclust/template.offline/function-index +1 -0
  13. data/data/bitclust/template.offline/layout +3 -0
  14. data/lib/bitclust/classentry.rb +1 -1
  15. data/lib/bitclust/docentry.rb +1 -1
  16. data/lib/bitclust/functionentry.rb +1 -1
  17. data/lib/bitclust/generators/epub.rb +69 -5
  18. data/lib/bitclust/irb.rb +74 -0
  19. data/lib/bitclust/markdown_bridge.rb +11 -6
  20. data/lib/bitclust/markdown_exporter.rb +311 -0
  21. data/lib/bitclust/markdown_tree.rb +118 -11
  22. data/lib/bitclust/mdcompiler.rb +15 -1
  23. data/lib/bitclust/mdparser.rb +42 -1
  24. data/lib/bitclust/methoddatabase.rb +17 -4
  25. data/lib/bitclust/methodentry.rb +24 -0
  26. data/lib/bitclust/methodsignature.rb +1 -1
  27. data/lib/bitclust/nameutils.rb +13 -0
  28. data/lib/bitclust/preprocessor.rb +4 -37
  29. data/lib/bitclust/rbs_overload_matcher.rb +179 -0
  30. data/lib/bitclust/rbs_sig_importer.rb +276 -0
  31. data/lib/bitclust/rbs_signatures.rb +125 -0
  32. data/lib/bitclust/rdcompiler.rb +12 -1
  33. data/lib/bitclust/remote.rb +405 -0
  34. data/lib/bitclust/runner.rb +7 -10
  35. data/lib/bitclust/screen.rb +6 -1
  36. data/lib/bitclust/search_index_generator.rb +28 -10
  37. data/lib/bitclust/searcher.rb +54 -25
  38. data/lib/bitclust/subcommands/chm_command.rb +1 -1
  39. data/lib/bitclust/subcommands/epub_command.rb +2 -2
  40. data/lib/bitclust/subcommands/extract_command.rb +7 -0
  41. data/lib/bitclust/subcommands/htmlfile_command.rb +7 -0
  42. data/lib/bitclust/subcommands/methods_command.rb +5 -28
  43. data/lib/bitclust/subcommands/query_command.rb +1 -0
  44. data/lib/bitclust/subcommands/rbssig_command.rb +99 -0
  45. data/lib/bitclust/subcommands/server_command.rb +21 -10
  46. data/lib/bitclust/subcommands/setup_command.rb +32 -11
  47. data/lib/bitclust/subcommands/statichtml_command.rb +59 -0
  48. data/lib/bitclust/syntax_highlighter.rb +65 -4
  49. data/lib/bitclust/user_dirs.rb +62 -0
  50. data/lib/bitclust/version.rb +1 -1
  51. data/lib/bitclust-irb.rb +2 -0
  52. data/test/test_chm_command.rb +22 -0
  53. data/test/test_epub_command.rb +60 -0
  54. data/test/test_epub_generator.rb +330 -0
  55. data/test/test_extract_command.rb +45 -0
  56. data/test/test_irb_plugin.rb +212 -0
  57. data/test/test_link_checker.rb +1 -1
  58. data/test/test_markdown_bridge.rb +35 -0
  59. data/test/test_markdown_exporter.rb +260 -0
  60. data/test/test_markdown_tree.rb +144 -0
  61. data/test/test_mdcompiler.rb +47 -5
  62. data/test/test_mdparser.rb +223 -4
  63. data/test/test_methodentry.rb +111 -0
  64. data/test/test_methods_command.rb +32 -0
  65. data/test/test_methodsignature.rb +3 -1
  66. data/test/test_opensearchdescription_screen.rb +39 -0
  67. data/test/test_preprocessor.rb +26 -0
  68. data/test/test_query_command.rb +30 -0
  69. data/test/test_rbs_overload_matcher.rb +170 -0
  70. data/test/test_rbs_sig_importer.rb +237 -0
  71. data/test/test_rbssig_command.rb +186 -0
  72. data/test/test_rdcompiler.rb +99 -1
  73. data/test/test_remote.rb +374 -0
  74. data/test/test_runner.rb +61 -1
  75. data/test/test_search_index_generator.rb +81 -0
  76. data/test/test_searcher.rb +63 -0
  77. data/test/test_server_command.rb +41 -0
  78. data/test/test_setup_command.rb +164 -0
  79. data/test/test_statichtml_command.rb +137 -0
  80. data/test/test_syntax_highlighter.rb +124 -0
  81. data/test/test_user_dirs.rb +62 -0
  82. data/theme/default/js/run.js +14 -0
  83. data/theme/default/js/search_init.js +3 -1
  84. data/theme/default/js/search_page.js +7 -1
  85. data/theme/default/js/version_switcher.js +147 -0
  86. data/theme/default/search.css +21 -0
  87. data/theme/default/style.css +83 -7
  88. data/theme/lillia/style.css +26 -0
  89. metadata +71 -2
  90. data/lib/bitclust/ridatabase.rb +0 -359
@@ -9,10 +9,12 @@
9
9
  #
10
10
 
11
11
  require 'bitclust/methoddatabase'
12
+ require 'bitclust/remote'
12
13
  require 'bitclust/functiondatabase'
13
14
  require 'bitclust/nameutils'
14
15
  require 'bitclust/methodid'
15
16
  require 'bitclust/exception'
17
+ require 'bitclust/user_dirs'
16
18
  require 'uri'
17
19
  require 'rbconfig'
18
20
  require 'optparse'
@@ -36,6 +38,7 @@ module BitClust
36
38
  @target_type = nil
37
39
  @listen_url = nil
38
40
  @foreground = false
41
+ @remote = Remote.default
39
42
  @parser = OptionParser.new {|parser|
40
43
  parser.banner = "Usage: #{@name} <pattern>"
41
44
  unless cmd == 'bitclust'
@@ -91,6 +94,10 @@ module BitClust
91
94
 
92
95
  attr_reader :parser
93
96
 
97
+ # ローカル DB が無いときに使うリモート検索(BitClust::Remote)。nil なら
98
+ # DB が無いことをエラーにする
99
+ attr_accessor :remote
100
+
94
101
  def parse(argv)
95
102
  @parser.parse! argv
96
103
  if @listen_url # server mode
@@ -121,6 +128,22 @@ module BitClust
121
128
  @parser.help
122
129
  end
123
130
 
131
+ # irb プラグインなど組み込み利用向けの入口。argv のパターンを db から
132
+ # 検索し、結果を view(TerminalView)へ出力する。db が nil なら
133
+ # 既定の場所(環境変数 BITCLUST_DATADIR など・設定ファイル(UserDirs))
134
+ # から探す
135
+ def run_query(db, argv, view)
136
+ @view = view
137
+ search_pattern db, argv
138
+ end
139
+
140
+ # 既定の場所(環境変数 BITCLUST_DATADIR など・設定ファイル(UserDirs))に
141
+ # ローカル DB があるか。irb プラグインが HTTP フォールバックへ切り替える
142
+ # 判定に使う。環境変数が不正な DB を指していれば InvalidDatabase
143
+ def local_database?
144
+ !find_dblocation().nil?
145
+ end
146
+
124
147
  private
125
148
 
126
149
  def server_mode_check(argv)
@@ -243,15 +266,23 @@ module BitClust
243
266
  [ "#{datadir}/refe2", "#{datadir}/bitclust" ].each do |path|
244
267
  return path if MethodDatabase.datadir?(path)
245
268
  end
246
- config_path = Pathname(ENV.fetch('HOME')) + ".bitclust" + "config"
247
- if config_path.exist?
248
- config = YAML.load_file(config_path)
269
+ config = UserDirs.load_config
270
+ if config
249
271
  return "#{config[:database_prefix]}-#{config[:default_version]}"
250
272
  end
251
273
  nil
252
274
  end
253
275
 
254
276
  def search_pattern(db, argv)
277
+ if db.nil? && !find_dblocation
278
+ remote = @remote
279
+ unless remote
280
+ raise InvalidDatabase, "no database found: run `bitclust setup` to create one (remote search is disabled by BITCLUST_REMOTE_URL)"
281
+ end
282
+ remote.lookup(argv, @view.io, describe_all: @describe_all, line: @linep,
283
+ class_only: @target_type == :class)
284
+ return
285
+ end
255
286
  db ||= new_database()
256
287
  @view.database ||= db if @view
257
288
  # FIXME なぜか else 節にきかないのでここに書いておく
@@ -352,16 +383,6 @@ module BitClust
352
383
  find_method db, cnames.join('::'), '::', name
353
384
  end
354
385
 
355
- # 区切りは反転文字列上で探す。"?." は 4.0 以降の module function の
356
- # 表示形式(反転すると ".?")で、内部表記の ".#" に正規化する
357
- def parse_method_spec_pattern(pat)
358
- _m, _t, _c = pat.reverse.split(/([\#,]\.|\.[\#,]|\.\?|[\#\.\,])/, 2)
359
- c = (_c || raise).reverse
360
- t = (_t || raise).tr(',', '#').sub(/\#\.|\.\?/, '.#')
361
- m = (_m || raise).reverse
362
- return c, t, m
363
- end
364
-
365
386
  end
366
387
 
367
388
 
@@ -374,16 +395,34 @@ module BitClust
374
395
 
375
396
  class TerminalView
376
397
 
377
- def initialize(compiler, opts)
398
+ # io を省略すると出力先は呼び出し時点の $stdout(従来挙動)
399
+ def initialize(compiler, opts, io: nil)
378
400
  @compiler = compiler
379
401
  @describe_all = opts[:describe_all]
380
402
  @line = opts[:line]
381
403
  @encoding = opts[:encoding]
382
404
  @database = nil
405
+ @io = io
383
406
  end
384
407
 
385
408
  attr_accessor :database
386
409
 
410
+ # 出力先(io: 省略時は $stdout)
411
+ def io
412
+ @io || $stdout
413
+ end
414
+
415
+ # 名前の一覧を表示する(-l なら 1 行 1 件、それ以外は端末幅に詰める)
416
+ def print_names(names)
417
+ if @line
418
+ names.sort.each do |n|
419
+ puts n
420
+ end
421
+ else
422
+ print_packed_names names.sort
423
+ end
424
+ end
425
+
387
426
  def show_class(cs)
388
427
  if cs.size == 1
389
428
  if @line
@@ -440,16 +479,6 @@ module BitClust
440
479
 
441
480
  private
442
481
 
443
- def print_names(names)
444
- if @line
445
- names.sort.each do |n|
446
- puts n
447
- end
448
- else
449
- print_packed_names names.sort
450
- end
451
- end
452
-
453
482
  def print_packed_names(names)
454
483
  max = terminal_column()
455
484
  buf = +''
@@ -546,7 +575,7 @@ module BitClust
546
575
  end
547
576
 
548
577
  def puts(*args)
549
- super(*args.collect {|arg| convert(arg)})
578
+ (@io || $stdout).puts(*args.collect {|arg| convert(arg)})
550
579
  end
551
580
 
552
581
  def convert(string)
@@ -102,7 +102,7 @@ EOS
102
102
  end
103
103
 
104
104
  def to_html
105
- str = "<LI> <OBJECT type=\"text/sitemap\">\n"
105
+ str = +"<LI> <OBJECT type=\"text/sitemap\">\n"
106
106
  str << " <param name=\"Name\" value=\"<%=h @name%>\">\n"
107
107
  if @local
108
108
  str << " <param name=\"Local\" value=\"<%=@local%>\">\n"
@@ -12,7 +12,7 @@ module BitClust
12
12
  def initialize
13
13
  super
14
14
  @verbose = true
15
- @catalogdir = nil
15
+ @catalogdir = srcdir_root + "data/bitclust/catalog"
16
16
  @templatedir = srcdir_root + "data/bitclust/template.epub"
17
17
  @themedir = srcdir_root + "theme/default"
18
18
  @filename = "rurema-#{Date.today}.epub"
@@ -54,7 +54,7 @@ module BitClust
54
54
  generator = BitClust::Generators::EPUB.new(:prefix => options[:prefix],
55
55
  :capi => options[:capi],
56
56
  :outputdir => @outputdir,
57
- :catalog => @catalog,
57
+ :catalog => @catalogdir,
58
58
  :templatedir => @templatedir,
59
59
  :themedir => @themedir,
60
60
  :fs_casesensitive => @fs_casesensitive,
@@ -25,6 +25,7 @@ module BitClust
25
25
  def exec(argv, options)
26
26
  success = true
27
27
  argv.each do |path|
28
+ reject_markdown_source(path)
28
29
  begin
29
30
  lib = RRDParser.parse_stdlib_file(path)
30
31
  if @check_only
@@ -41,6 +42,12 @@ module BitClust
41
42
  exit success
42
43
  end
43
44
 
45
+ # 旧 RD ソース(refm)専用なので、Markdown は未対応として案内する
46
+ def reject_markdown_source(path)
47
+ return unless path.end_with?('.md')
48
+ error "#{path}: extract は旧 RD ソース(refm)専用で、Markdown(manual/ の .md)には未対応です。manual/ の内容は doctree の rake generate:X.Y と bitclust server で確認してください"
49
+ end
50
+
44
51
  def show_library(lib)
45
52
  puts "= Library #{lib.name}"
46
53
  lib.classes.each do |c|
@@ -47,6 +47,7 @@ module BitClust
47
47
  end
48
48
  @capi = options[:capi]
49
49
  target_file = argv[0]
50
+ reject_markdown_source(target_file)
50
51
  if /refm\/doc\// =~ target_file
51
52
  @rd_file = true if @rd_file.nil?
52
53
  end
@@ -88,6 +89,12 @@ module BitClust
88
89
 
89
90
  private
90
91
 
92
+ # 旧 RD ソース(refm)専用なので、Markdown は未対応として案内する
93
+ def reject_markdown_source(path)
94
+ return unless path && path.end_with?('.md')
95
+ error "#{path}: htmlfile は旧 RD ソース(refm)専用で、Markdown(manual/ の .md)には未対応です。manual/ の内容は doctree の rake generate:X.Y と bitclust server(または rake statichtml:X.Y)で確認してください"
96
+ end
97
+
91
98
  def lookup(lib, key)
92
99
  case
93
100
  when @capi && NameUtils.functionname?(key)
@@ -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)
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # bitclust/subcommands/rbssig_command.rb
4
+ #
5
+ # RBS 型シグネチャを DB のメソッドエントリに書き込む(rbs_sig property):
6
+ #
7
+ # bitclust rbssig --sig-root=RBS_CHECKOUT <dbpath>
8
+ # bitclust rbssig --sig-dir=DIR [--sig-dir=DIR ...] <dbpath>
9
+ #
10
+ # DB 構築(update)後・statichtml 前に実行する(methodsince と同じ位置)。
11
+ # --sig-root には対象 Ruby バージョンに同梱される rbs のチェックアウト
12
+ # (core/ と stdlib/ を含む)を渡す。表示は 4.0 以降のドキュメント専用
13
+ # なので、DB の version が 4.0 未満ならエラーにする。
14
+
15
+ require 'bitclust'
16
+ require 'bitclust/subcommand'
17
+ require 'bitclust/rbs_sig_importer'
18
+ require 'tmpdir'
19
+ require 'fileutils'
20
+
21
+ module BitClust
22
+ module Subcommands
23
+ class RbssigCommand < Subcommand
24
+ MINIMUM_VERSION = Gem::Version.new('4.0')
25
+
26
+ def initialize
27
+ super
28
+ @sig_root = nil
29
+ @sig_dirs = []
30
+ @dry_run = false
31
+ @parser.banner = "Usage: #{File.basename($0, '.*')} rbssig [options] <dbpath>"
32
+ @parser.on('--sig-root=PATH', 'ruby/rbs checkout containing core/ and stdlib/.') {|path|
33
+ @sig_root = path
34
+ }
35
+ @parser.on('--sig-dir=PATH', 'Directory of extra .rbs files (repeatable).') {|path|
36
+ @sig_dirs.push path
37
+ }
38
+ @parser.on('--dry-run', 'Compute and print stats without saving.') {
39
+ @dry_run = true
40
+ }
41
+ end
42
+
43
+ # DB パスは位置引数で受けるのでグローバル --database は不要
44
+ def needs_database?
45
+ false
46
+ end
47
+
48
+ def exec(argv, options)
49
+ error('no --sig-root or --sig-dir given') if @sig_root.nil? && @sig_dirs.empty?
50
+ error("wrong number of arguments (#{argv.size} for 1)") unless argv.size == 1
51
+ path = argv.first
52
+ version = check_version(path)
53
+
54
+ sig_root = @sig_root
55
+ importer = RbsSigImporter.new(
56
+ core_root: sig_root && File.join(sig_root, 'core'),
57
+ repository_root: sig_root && File.join(sig_root, 'stdlib'),
58
+ sig_dirs: @sig_dirs)
59
+ stats =
60
+ if @dry_run
61
+ apply_dry_run(importer, path)
62
+ else
63
+ importer.apply(MethodDatabase.new(path))
64
+ end
65
+ puts "#{File.basename(path)} (version #{version}): " \
66
+ "entries_updated=#{stats[:entries_updated]} sigs_matched=#{stats[:sigs_matched]} " \
67
+ "methods_missed=#{stats[:methods_missed]}"
68
+ end
69
+
70
+ private
71
+
72
+ # RBS シグネチャ表示は 4.0 以降のドキュメント専用。比較は
73
+ # display_typemark(nameutils.rb)と同じく Gem::Version で行う
74
+ # (文字列比較だと "10.0" が "4.0" より小さく見える)
75
+ def check_version(path)
76
+ version = MethodDatabase.new(path).propget('version') or
77
+ error("#{path}: no version property (not a bitclust database?)")
78
+ begin
79
+ if Gem::Version.new(version) < MINIMUM_VERSION
80
+ error("#{path}: version #{version} is before 4.0; RBS signatures are only for 4.0+ databases")
81
+ end
82
+ rescue ArgumentError
83
+ error("#{path}: malformed version property: #{version.inspect}")
84
+ end
85
+ version
86
+ end
87
+
88
+ # --dry-run: 実 DB には書き込まず、複製に apply した統計だけを見せる
89
+ # (methodsince の apply_dry_run と同じ手口)
90
+ def apply_dry_run(importer, path)
91
+ Dir.mktmpdir do |tmp|
92
+ copy = File.join(tmp, 'db')
93
+ FileUtils.cp_r(path, copy)
94
+ importer.apply(MethodDatabase.new(copy))
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -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
@@ -20,7 +21,10 @@ module BitClust
20
21
  @prepare = nil
21
22
  @cleanup = nil
22
23
  @purge = nil
23
- @versions = ["3.3.0", "3.4.0", "4.0.0"]
24
+ # teeny は付けない。#%if (version == "V") や #%version V の等値ゲートは
25
+ # 文字列一致なので、teeny 付きの版名で DB を作ると doctree CI・
26
+ # 生成済みドキュメントとゲートの判定がずれる
27
+ @versions = ["3.3", "3.4", "4.0"]
24
28
  @update = true
25
29
  @parser.banner = "Usage: #{File.basename($0, '.*')} setup [options]"
26
30
  @parser.on('--prepare', 'Prepare config file and checkout repository. Do not create database.') {
@@ -40,7 +44,9 @@ module BitClust
40
44
  }
41
45
  end
42
46
 
43
- # DB は ~/.bitclust 配下に自前で作るのでグローバル --database は不要
47
+ # DB は設定ファイル(UserDirs)が示す場所(既定は $XDG_DATA_HOME/bitclust、
48
+ # 旧 ~/.bitclust があればそちら)に自前で作るのでグローバル --database
49
+ # は不要
44
50
  def needs_database?
45
51
  false
46
52
  end
@@ -65,22 +71,37 @@ module BitClust
65
71
  private
66
72
 
67
73
  def purge
68
- home_directory = Pathname(ENV.fetch("HOME"))
69
- config_dir = home_directory + ".bitclust"
70
74
  print "Remove all generated files..."
71
- 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
72
81
  puts "done!"
73
82
  exit 0
74
83
  end
75
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
+
76
99
  def prepare
77
- home_directory = Pathname(ENV.fetch("HOME")).expand_path
78
- config_dir = home_directory + ".bitclust"
79
- config_dir.mkpath
80
- config_path = config_dir + "config"
81
- rubydoc_dir = config_dir + "rubydoc"
100
+ config_path, database_prefix, rubydoc_dir = layout
101
+ config_path.dirname.mkpath
102
+ rubydoc_dir.dirname.mkpath
82
103
  @config = {
83
- :database_prefix => (config_dir + "db").to_s,
104
+ :database_prefix => database_prefix,
84
105
  :encoding => "utf-8",
85
106
  :versions => @versions,
86
107
  :default_version => @versions.max,
@@ -13,6 +13,7 @@ require 'bitclust/subcommand'
13
13
  require 'bitclust/progress_bar'
14
14
  require 'bitclust/silent_progress_bar'
15
15
  require 'bitclust/search_index_generator'
16
+ require 'bitclust/markdown_exporter'
16
17
 
17
18
  module BitClust
18
19
  module Subcommands
@@ -125,6 +126,7 @@ module BitClust
125
126
  @run_ruby_wasm = nil
126
127
  @sitemap_baseurl = nil
127
128
  @sitemap_paths = []
129
+ @markdown_output = false
128
130
  @parser.banner = "Usage: #{File.basename($0, '.*')} statichtml [options]"
129
131
  @parser.on('-o', '--outputdir=PATH', 'Output directory') do |path|
130
132
  begin
@@ -170,6 +172,9 @@ module BitClust
170
172
  @parser.on('--sitemap-baseurl=URL', 'Generate sitemap.xml under the output directory, with <loc> built from this base URL (e.g. https://docs.ruby-lang.org/ja/3.4/). Omit to skip sitemap.xml generation (default)') do |url|
171
173
  @sitemap_baseurl = url
172
174
  end
175
+ @parser.on('--markdown-output', 'Also write each page as Markdown (.md next to the .html), assembled from the preprocessed Markdown sources. Ignored for databases not created from a Markdown tree') do
176
+ @markdown_output = true
177
+ end
173
178
  @parser.on('--no-stop-on-syntax-error', 'Do not stop on syntax error') do |boolean|
174
179
  @stop_on_syntax_error = boolean
175
180
  end
@@ -185,8 +190,10 @@ module BitClust
185
190
  db = MethodDatabase.new(prefix.to_s)
186
191
  fdb = FunctionDatabase.new(prefix.to_s)
187
192
  manager = ScreenManager.new(@manager_config)
193
+ @markdown_exporter = MarkdownExporter.new(@urlmapper, @suffix) if @markdown_output
188
194
 
189
195
  db.transaction do
196
+ warn_markdown_output_skipped(db, "the method database")
190
197
  methods = {} #: Hash[String, Array[MethodEntry]]
191
198
  db.methods.each_with_index do |entry, i|
192
199
  next if entry.undefined?
@@ -203,6 +210,7 @@ module BitClust
203
210
  end
204
211
 
205
212
  fdb.transaction do
213
+ warn_markdown_output_skipped(fdb, "the function database")
206
214
  create_html_entries("capi", fdb.functions, manager, fdb)
207
215
  end
208
216
 
@@ -234,6 +242,7 @@ module BitClust
234
242
  FileUtils.cp(@manager_config[:themedir] + "script.js",
235
243
  @outputdir.to_s, :verbose => @verbose, :preserve => true)
236
244
  copy_run_ruby_wasm_script if @run_ruby_wasm
245
+ copy_version_switcher_script
237
246
  FileUtils.cp(@manager_config[:themedir] + @manager_config[:favicon_url],
238
247
  @outputdir.to_s, :verbose => @verbose, :preserve => true)
239
248
  Dir.mktmpdir do |tmpdir|
@@ -412,6 +421,19 @@ HERE
412
421
  end
413
422
  end
414
423
 
424
+ # Copy the version-switcher script. A themedir without it is
425
+ # tolerated: warn and skip instead of aborting the whole build.
426
+ def copy_version_switcher_script
427
+ src = @manager_config[:themedir] + "js" + "version_switcher.js"
428
+ unless src.file?
429
+ $stderr.puts "warning: #{src} not found; version switcher script not copied"
430
+ return
431
+ end
432
+ jsdir = @outputdir + "js"
433
+ FileUtils.mkdir_p(jsdir) unless jsdir.directory?
434
+ FileUtils.cp(src.to_s, jsdir.to_s, :verbose => @verbose, :preserve => true)
435
+ end
436
+
415
437
  def create_html_file(entry, manager, outputdir, db)
416
438
  e = entry.is_a?(Array) ? entry.sort.first : entry
417
439
  case e.type_id
@@ -419,6 +441,7 @@ HERE
419
441
  @urlmapper.bitclust_html_base = '..'
420
442
  path = outputdir + e.type_id.to_s + html_filename(encodename_package(e.name), @suffix)
421
443
  create_html_file_p(entry, manager, path, db)
444
+ create_markdown_file_p(e, path, db)
422
445
  when :function
423
446
  create_html_function_file(entry, manager, outputdir, db)
424
447
  else
@@ -435,6 +458,11 @@ HERE
435
458
  path = outputdir + e.type_id.to_s + encodename_package(e.klass.name) +
436
459
  e.typechar + html_filename(encodename_package(name), @suffix)
437
460
  create_html_file_p(entries, manager, path, db)
461
+ if markdown_export?(db)
462
+ create_file(markdown_path(path),
463
+ @markdown_exporter.method_page(method_name, entries.sort),
464
+ :verbose => @verbose)
465
+ end
438
466
  end
439
467
 
440
468
  def create_html_function_file(entry, manager, outputdir, db)
@@ -442,6 +470,7 @@ HERE
442
470
  @urlmapper.bitclust_html_base = '..'
443
471
  path = outputdir + entry.type_id.to_s + html_filename(entry.name, @suffix)
444
472
  create_html_file_p(entry, manager, path, db)
473
+ create_markdown_file_p(entry, path, db)
445
474
  end
446
475
 
447
476
  def create_html_file_p(entry, manager, path, db)
@@ -453,6 +482,36 @@ HERE
453
482
  record_sitemap_path(path)
454
483
  end
455
484
 
485
+ # --markdown-output は Markdown ツリー由来の DB でのみ有効。
486
+ # ソースが md でない(凍結版 RD など)DB では何も出力しない
487
+ def markdown_export?(db)
488
+ @markdown_output && db.properties['source_format'] == 'markdown'
489
+ end
490
+
491
+ def warn_markdown_output_skipped(db, label)
492
+ return if !@markdown_output || db.properties['source_format'] == 'markdown'
493
+ $stderr.puts "warning: --markdown-output is ignored for #{label}: it was not created from a Markdown tree"
494
+ end
495
+
496
+ def create_markdown_file_p(entry, html_path, db)
497
+ return unless markdown_export?(db)
498
+ content =
499
+ case entry.type_id
500
+ when :library then @markdown_exporter.library_page(_ = entry)
501
+ when :class then @markdown_exporter.class_page(_ = entry)
502
+ when :doc then @markdown_exporter.doc_page(_ = entry)
503
+ when :function then @markdown_exporter.function_page(_ = entry)
504
+ else return
505
+ end
506
+ create_file(markdown_path(html_path), content, :verbose => @verbose)
507
+ end
508
+
509
+ def markdown_path(html_path)
510
+ s = html_path.to_s
511
+ s = s.end_with?(@suffix) ? s[0, s.length - @suffix.length].to_s : s
512
+ Pathname.new("#{s}.md")
513
+ end
514
+
456
515
  def create_file(path, str, options = {})
457
516
  verbose = options[:verbose]
458
517
  $stderr.print("creating #{path} ...") if verbose