bitclust-core 1.3.0 → 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.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/README.md +65 -0
  4. data/Rakefile +32 -0
  5. data/data/bitclust/catalog/ja_JP.UTF-8 +19 -1
  6. data/data/bitclust/searchpage/index.html +90 -0
  7. data/data/bitclust/template/class +69 -8
  8. data/data/bitclust/template/function +2 -2
  9. data/data/bitclust/template/layout +11 -3
  10. data/data/bitclust/template/method +2 -2
  11. data/data/bitclust/template/search +1 -1
  12. data/data/bitclust/template.epub/class +22 -1
  13. data/data/bitclust/template.epub/function +2 -2
  14. data/data/bitclust/template.epub/layout +1 -3
  15. data/data/bitclust/template.epub/method +2 -2
  16. data/data/bitclust/template.lillia/class +26 -5
  17. data/data/bitclust/template.lillia/layout +1 -3
  18. data/data/bitclust/template.lillia/method +2 -2
  19. data/data/bitclust/template.offline/class +48 -4
  20. data/data/bitclust/template.offline/function +2 -2
  21. data/data/bitclust/template.offline/layout +27 -3
  22. data/data/bitclust/template.offline/method +2 -2
  23. data/lib/bitclust/app.rb +16 -17
  24. data/lib/bitclust/capi_converter.rb +20 -0
  25. data/lib/bitclust/classentry.rb +67 -17
  26. data/lib/bitclust/compat.rb +2 -2
  27. data/lib/bitclust/completion.rb +58 -27
  28. data/lib/bitclust/crossrubyutils.rb +8 -5
  29. data/lib/bitclust/database.rb +28 -8
  30. data/lib/bitclust/doc_converter.rb +63 -0
  31. data/lib/bitclust/docentry.rb +1 -1
  32. data/lib/bitclust/entity_splitter.rb +221 -0
  33. data/lib/bitclust/entry.rb +47 -7
  34. data/lib/bitclust/functiondatabase.rb +13 -1
  35. data/lib/bitclust/functionentry.rb +5 -1
  36. data/lib/bitclust/functionreferenceparser.rb +4 -4
  37. data/lib/bitclust/generators/epub.rb +10 -3
  38. data/lib/bitclust/htmlutils.rb +1 -1
  39. data/lib/bitclust/include_graph.rb +375 -0
  40. data/lib/bitclust/include_pruner.rb +144 -0
  41. data/lib/bitclust/interface.rb +1 -1
  42. data/lib/bitclust/libraryentry.rb +8 -7
  43. data/lib/bitclust/lineinput.rb +7 -5
  44. data/lib/bitclust/link_checker.rb +179 -0
  45. data/lib/bitclust/markdown_bridge.rb +186 -0
  46. data/lib/bitclust/markdown_orchestrator.rb +267 -0
  47. data/lib/bitclust/markdown_to_rrd.rb +824 -0
  48. data/lib/bitclust/markdown_tree.rb +217 -0
  49. data/lib/bitclust/mdcompiler.rb +883 -0
  50. data/lib/bitclust/mdparser.rb +347 -0
  51. data/lib/bitclust/messagecatalog.rb +3 -3
  52. data/lib/bitclust/method_since_calculator.rb +117 -0
  53. data/lib/bitclust/methoddatabase.rb +124 -14
  54. data/lib/bitclust/methodentry.rb +125 -7
  55. data/lib/bitclust/methodid.rb +12 -11
  56. data/lib/bitclust/methodsignature.rb +10 -4
  57. data/lib/bitclust/nameutils.rb +41 -22
  58. data/lib/bitclust/preprocessor.rb +66 -29
  59. data/lib/bitclust/rdcompiler.rb +195 -95
  60. data/lib/bitclust/refsdatabase.rb +23 -8
  61. data/lib/bitclust/reloadable_request_handler.rb +72 -0
  62. data/lib/bitclust/requesthandler.rb +21 -16
  63. data/lib/bitclust/ridatabase.rb +10 -4
  64. data/lib/bitclust/rrd_to_markdown.rb +925 -0
  65. data/lib/bitclust/rrdparser.rb +196 -30
  66. data/lib/bitclust/runner.rb +17 -2
  67. data/lib/bitclust/screen.rb +80 -11
  68. data/lib/bitclust/search_index_generator.rb +288 -0
  69. data/lib/bitclust/searcher.rb +69 -20
  70. data/lib/bitclust/simplesearcher.rb +24 -12
  71. data/lib/bitclust/subcommand.rb +7 -1
  72. data/lib/bitclust/subcommands/ancestors_command.rb +9 -2
  73. data/lib/bitclust/subcommands/checklink_command.rb +54 -0
  74. data/lib/bitclust/subcommands/chm_command.rb +15 -7
  75. data/lib/bitclust/subcommands/classes_command.rb +5 -0
  76. data/lib/bitclust/subcommands/extract_command.rb +5 -0
  77. data/lib/bitclust/subcommands/htmlfile_command.rb +21 -7
  78. data/lib/bitclust/subcommands/init_command.rb +4 -1
  79. data/lib/bitclust/subcommands/list_command.rb +12 -4
  80. data/lib/bitclust/subcommands/lookup_command.rb +28 -9
  81. data/lib/bitclust/subcommands/methods_command.rb +10 -2
  82. data/lib/bitclust/subcommands/methodsince_command.rb +80 -0
  83. data/lib/bitclust/subcommands/preproc_command.rb +5 -0
  84. data/lib/bitclust/subcommands/property_command.rb +2 -1
  85. data/lib/bitclust/subcommands/searchpage_command.rb +102 -0
  86. data/lib/bitclust/subcommands/server_command.rb +10 -8
  87. data/lib/bitclust/subcommands/setup_command.rb +24 -10
  88. data/lib/bitclust/subcommands/statichtml_command.rb +129 -6
  89. data/lib/bitclust/subcommands/update_command.rb +110 -2
  90. data/lib/bitclust/syntax_highlighter.rb +48 -4
  91. data/lib/bitclust/textutils.rb +3 -3
  92. data/lib/bitclust/version.rb +1 -1
  93. data/lib/bitclust/version_badges.rb +90 -0
  94. data/lib/bitclust/whole_file_gate.rb +137 -0
  95. data/test/test_app.rb +269 -0
  96. data/test/test_bitclust.rb +2 -0
  97. data/test/test_capi_converter.rb +91 -0
  98. data/test/test_class_screen.rb +413 -0
  99. data/test/test_copy_doc_md.rb +53 -0
  100. data/test/test_doc_converter.rb +78 -0
  101. data/test/test_entity_splitter.rb +231 -0
  102. data/test/test_entry.rb +207 -2
  103. data/test/test_eol_warning.rb +43 -0
  104. data/test/test_functionreferenceparser.rb +11 -0
  105. data/test/test_include_graph.rb +640 -0
  106. data/test/test_include_pruner.rb +133 -0
  107. data/test/test_link_checker.rb +114 -0
  108. data/test/test_lookup_command.rb +135 -0
  109. data/test/test_markdown_bridge.rb +217 -0
  110. data/test/test_markdown_orchestrator.rb +478 -0
  111. data/test/test_markdown_to_rrd.rb +757 -0
  112. data/test/test_markdown_tree.rb +369 -0
  113. data/test/test_mdcompiler.rb +1276 -0
  114. data/test/test_mdparser.rb +538 -0
  115. data/test/test_method_screen.rb +166 -0
  116. data/test/test_method_since_calculator.rb +290 -0
  117. data/test/test_methoddatabase.rb +16 -0
  118. data/test/test_methodentry.rb +328 -0
  119. data/test/test_methodsignature.rb +3 -1
  120. data/test/test_methodsince_command.rb +168 -0
  121. data/test/test_nameutils.rb +22 -0
  122. data/test/test_preprocessor.rb +215 -0
  123. data/test/test_rdcompiler.rb +401 -30
  124. data/test/test_refsdatabase.rb +32 -0
  125. data/test/test_rrd_to_markdown.rb +968 -0
  126. data/test/test_rrdparser.rb +295 -2
  127. data/test/test_run_ruby_wasm.rb +54 -0
  128. data/test/test_run_worker_prelude.rb +83 -0
  129. data/test/test_runner.rb +51 -0
  130. data/test/test_search_index_generator.rb +513 -0
  131. data/test/test_search_screen.rb +48 -0
  132. data/test/test_searcher.rb +132 -0
  133. data/test/test_searchpage_command.rb +167 -0
  134. data/test/test_simplesearcher.rb +28 -0
  135. data/test/test_statichtml_command.rb +204 -0
  136. data/test/test_syntax_highlighter.rb +60 -0
  137. data/test/test_whole_file_gate.rb +175 -0
  138. data/theme/default/js/NOTICE +42 -0
  139. data/theme/default/js/run-worker.js +111 -0
  140. data/theme/default/js/run.js +350 -0
  141. data/theme/default/js/search_controller.js +134 -0
  142. data/theme/default/js/search_init.js +176 -0
  143. data/theme/default/js/search_navigation.js +110 -0
  144. data/theme/default/js/search_page.js +202 -0
  145. data/theme/default/js/search_ranker.js +244 -0
  146. data/theme/default/script.js +91 -18
  147. data/theme/default/search.css +149 -0
  148. data/theme/default/style.css +193 -21
  149. data/theme/default/syntax-highlight.css +8 -0
  150. data/theme/lillia/style.css +45 -1
  151. metadata +161 -20
  152. data/README +0 -32
@@ -134,7 +134,7 @@ EOS
134
134
  end
135
135
 
136
136
  def method_url(spec)
137
- cname, tmark, mname = *split_method_spec(spec)
137
+ cname, tmark, mname = split_method_spec(spec)
138
138
  "/method/#{encodename_fs(cname)}/#{typemark2char(tmark)}/#{encodename_fs(mname)}.html"
139
139
  end
140
140
 
@@ -170,6 +170,7 @@ EOS
170
170
  manager = ScreenManager.new(@manager_config)
171
171
  @html_files = []
172
172
  db.transaction do
173
+ # @type var methods: Hash[String, Array[MethodEntry]]
173
174
  methods = {}
174
175
  db.methods.each do |entry|
175
176
  method_name = entry.klass.name + entry.typemark + entry.name
@@ -184,6 +185,7 @@ EOS
184
185
  e = c.is_a?(Array) ? c.sort.first : c
185
186
  case e.type_id
186
187
  when :library
188
+ # @type var e: LibraryEntry
187
189
  content = Sitemap::Content.new(e.name.to_s, filename)
188
190
  if e.name.to_s != '_builtin'
189
191
  @sitemap[:library][1] << content
@@ -191,6 +193,7 @@ EOS
191
193
  end
192
194
  @index_contents << Sitemap::Content.new(e.name.to_s, filename)
193
195
  when :class
196
+ # @type var e: ClassEntry
194
197
  content = Sitemap::Content.new(e.name.to_s, filename)
195
198
  if e.library.name.to_s == '_builtin'
196
199
  @sitemap[:library][0] << content
@@ -199,12 +202,15 @@ EOS
199
202
  end
200
203
  @index_contents << Sitemap::Content.new("#{e.name} (#{e.library.name})", filename)
201
204
  when :method
205
+ # @type var e: MethodEntry
202
206
  e.names.each do |e_name|
203
207
  name = e.typename == :special_variable ? "$#{e_name}" : e_name
204
208
  @index_contents <<
205
209
  Sitemap::Content.new("#{name} (#{e.library.name} - #{e.klass.name})", filename)
210
+ # bitclust#250: CHM の索引(.hhk)に出る表示ラベル。4.0 以降の
211
+ # ドキュメントでは module function を "?." で表示する
206
212
  @index_contents <<
207
- Sitemap::Content.new("#{e.klass.name}#{e.typemark}#{name} (#{e.library.name})", filename)
213
+ Sitemap::Content.new("#{e.klass.name}#{e.display_typemark}#{name} (#{e.library.name})", filename)
208
214
  end
209
215
  end
210
216
  pb.title = align_progress_bar_title(e.name)
@@ -219,7 +225,7 @@ EOS
219
225
  create_file(@outputdir + 'library/index.html', manager.library_index_screen(db.libraries.sort, {:database => db}).body)
220
226
  create_file(@outputdir + 'class/index.html', manager.class_index_screen(db.classes.sort, {:database => db}).body)
221
227
  FileUtils.cp(@manager_config[:themedir] + @manager_config[:css_url],
222
- @outputdir.to_s, {:verbose => true, :preserve => true})
228
+ @outputdir.to_s, verbose: true, preserve: true)
223
229
  end
224
230
 
225
231
  private
@@ -243,15 +249,17 @@ EOS
243
249
  "\u0308" => "\u2025",
244
250
  "\u2014" => "\u2015",
245
251
  "\u2212" => "\uff0d",
246
- "\u301c" => "\uff5e" }
252
+ "\u301c" => "\uff5e" }
247
253
 
248
254
  def create_html_file(entry, manager, outputdir, db)
249
255
  html = manager.entry_screen(entry, {:database => db}).body
250
256
  e = entry.is_a?(Array) ? entry.sort.first : entry
251
257
  path = case e.type_id
252
258
  when :library, :class, :doc
253
- outputdir + e.type_id.to_s + (NameUtils.encodename_fs(e.name) + '.html')
259
+ # @type var e: LibraryEntry | ClassEntry | DocEntry
260
+ outputdir + e.type_id.to_s + (NameUtils.encodename_fs(e.name) + '.html')
254
261
  when :method
262
+ # @type var e: MethodEntry
255
263
  outputdir + e.type_id.to_s + NameUtils.encodename_fs(e.klass.name) +
256
264
  e.typechar + (NameUtils.encodename_fs(e.name) + '.html')
257
265
  else
@@ -261,7 +269,7 @@ EOS
261
269
 
262
270
  begin
263
271
  new_html = html.gsub(/charset=utf-8/i, 'charset=Windows-31J').
264
- encode('windows-31j', { :fallback => FIX_UNDEF })
272
+ encode('windows-31j', fallback: FIX_UNDEF)
265
273
  mode = 'w:windows-31j'
266
274
  rescue
267
275
  new_html = html # write file as it is, utf-8
@@ -277,7 +285,7 @@ EOS
277
285
  $stderr.print("creating #{path} ...")
278
286
  str = ERB.new(skel).result(binding).
279
287
  gsub(/charset=utf-8/i, 'charset=Windows-31J').
280
- encode('windows-31j', { :fallback => FIX_UNDEF } )
288
+ encode('windows-31j', fallback: FIX_UNDEF)
281
289
  path.open('w:windows-31j') do |f|
282
290
  f.write(str)
283
291
  end
@@ -27,6 +27,11 @@ module BitClust
27
27
  option_error('wrong number of arguments') unless argv.size == 1
28
28
  end
29
29
 
30
+ # システムの ruby を調べるだけで DB を使わない
31
+ def needs_database?
32
+ false
33
+ end
34
+
30
35
  def exec(argv, options)
31
36
  lib = argv[0]
32
37
  print_crossruby_table {|ruby| defined_classes(ruby, lib, @rejects) }
@@ -17,6 +17,11 @@ module BitClust
17
17
  }
18
18
  end
19
19
 
20
+ # ソースファイルを処理するだけで DB を使わない
21
+ def needs_database?
22
+ false
23
+ end
24
+
20
25
  def exec(argv, options)
21
26
  success = true
22
27
  argv.each do |path|
@@ -16,13 +16,13 @@ module BitClust
16
16
  @target = nil
17
17
  @templatedir = srcdir_root + "data/bitclust/template.offline"
18
18
  @baseurl = "file://" + srcdir_root.to_s
19
- @version = "2.0.0"
19
+ @version = RUBY_VERSION
20
20
  @parser.banner = "Usage: #{File.basename($0, '.*')} htmlfile [options] rdfile"
21
21
  @parser.on('--target=NAME', 'Compile NAME to HTML.') {|name|
22
22
  @target = name
23
23
  }
24
- @parser.on('--force', '-f', 'Force to use rd_file template.') {|name|
25
- @rd_file = true
24
+ @parser.on('--[no-]force', '-f', 'Force to use rd_file template.') {|bool|
25
+ @rd_file = bool
26
26
  }
27
27
  @parser.on('--ruby_version=VER', '--ruby=VER', 'Set Ruby version') {|version|
28
28
  @version = version
@@ -35,6 +35,11 @@ module BitClust
35
35
  }
36
36
  end
37
37
 
38
+ # DB は与えられれば使うが必須ではない (--database は任意)
39
+ def needs_database?
40
+ false
41
+ end
42
+
38
43
  def exec(argv, options)
39
44
  db = MethodDatabase.dummy({'version' => @version})
40
45
  if options[:prefix]
@@ -42,7 +47,10 @@ module BitClust
42
47
  end
43
48
  @capi = options[:capi]
44
49
  target_file = argv[0]
45
- options = { 'version' => @version }
50
+ if /refm\/doc\// =~ target_file
51
+ @rd_file = true if @rd_file.nil?
52
+ end
53
+ options = { 'version' => @version } #: Database::database_properties
46
54
  manager = ScreenManager.new(:templatedir => @templatedir,
47
55
  :base_url => @baseurl,
48
56
  :cgi_url => @baseurl,
@@ -59,11 +67,11 @@ module BitClust
59
67
  lib = RRDParser.parse_stdlib_file(target_file, options)
60
68
  end
61
69
  entry = @target ? lookup(lib, @target) : lib
62
- puts manager.entry_screen(entry, { :database => db }).body
70
+ puts manager.entry_screen(entry, { :database => db, :target_version => @version }).body
63
71
  return
64
72
  rescue ParseError => ex
65
73
  $stderr.puts ex.message
66
- $stderr.puts ex.backtrace[0], ex.backtrace[1..-1].map{|s| "\tfrom " + s}
74
+ $stderr.puts ex.backtrace[0], ex.backtrace[1..-1]&.map{|s| "\tfrom " + s}
67
75
  end
68
76
  end
69
77
 
@@ -83,8 +91,12 @@ module BitClust
83
91
  def lookup(lib, key)
84
92
  case
85
93
  when @capi && NameUtils.functionname?(key)
86
- lib.find {|func| func.name == key}
94
+ # @type var lib: Array[FunctionEntry]
95
+ lib.is_a?(Array) or raise
96
+ lib.find {|func| func.name == key} or raise
87
97
  when NameUtils.method_spec?(key)
98
+ # @type var lib: LibraryEntry
99
+ lib.is_a?(LibraryEntry) or raise
88
100
  spec = MethodSpec.parse(key)
89
101
  if spec.constant?
90
102
  begin
@@ -96,6 +108,8 @@ module BitClust
96
108
  lib.fetch_methods(spec)
97
109
  end
98
110
  when NameUtils.classname?(key)
111
+ # @type var lib: LibraryEntry
112
+ lib.is_a?(LibraryEntry) or raise
99
113
  lib.fetch_class(key)
100
114
  else
101
115
  raise InvalidKey, "wrong search key: #{key.inspect}"
@@ -20,12 +20,15 @@ module BitClust
20
20
  STANDARD_PROPERTIES = %w( encoding version )
21
21
 
22
22
  def exec(argv, options)
23
- prefix = options[:prefix]
23
+ prefix = options[:prefix] || raise
24
24
  db = MethodDatabase.new(prefix)
25
25
  db.init
26
26
  db.transaction {
27
27
  argv.each do |kv|
28
28
  k, v = kv.split('=', 2)
29
+ if k.nil? || v.nil?
30
+ raise "argument must be KEY=VALUE, but #{kv.inspect}"
31
+ end
29
32
  db.propset k, v
30
33
  end
31
34
  }
@@ -41,21 +41,29 @@ module BitClust
41
41
  super
42
42
  case @mode
43
43
  when :library
44
- @db.libraries.map {|lib| lib.name }.sort.each do |name|
44
+ db = @db
45
+ db.is_a?(MethodDatabase) or raise "invalid database given. Use without --capi option"
46
+ db.libraries.map {|lib| lib.name }.sort.each do |name|
45
47
  puts name
46
48
  end
47
49
  when :class
48
- @db.classes.map {|c| c.name }.sort.each do |name|
50
+ db = @db
51
+ db.is_a?(MethodDatabase) or raise "invalid database given. Use without --capi option"
52
+ db.classes.map {|c| c.name }.sort.each do |name|
49
53
  puts name
50
54
  end
51
55
  when :method
52
- @db.classes.sort_by {|c| c.name }.each do |c|
56
+ db = @db
57
+ db.is_a?(MethodDatabase) or raise "invalid database given. Use without --capi option"
58
+ db.classes.sort_by {|c| c.name }.each do |c|
53
59
  c.entries.sort_by {|m| m.id }.each do |m|
54
60
  puts m.label
55
61
  end
56
62
  end
57
63
  when :function
58
- @db.functions.sort_by {|f| f.name }.each do |f|
64
+ db = @db
65
+ db.is_a?(FunctionDatabase) or raise "invalid database given. Use with --capi option"
66
+ db.functions.sort_by {|f| f.name }.each do |f|
59
67
  puts f.name
60
68
  end
61
69
  else
@@ -51,19 +51,23 @@ module BitClust
51
51
 
52
52
  def exec(argv, options)
53
53
  super
54
- entry = fetch_entry(@db, @type, @key)
55
- puts fill_template(get_template(@type, @format), entry)
54
+ entry = fetch_entry(@db, @type, @key || raise)
55
+ puts fill_template(get_template(@type || raise, @format || raise), entry)
56
56
  end
57
57
 
58
58
  def fetch_entry(db, type, key)
59
59
  case type
60
60
  when :library
61
+ # @type var db: MethodDatabase
61
62
  db.fetch_library(key)
62
63
  when :class
64
+ # @type var db: MethodDatabase
63
65
  db.fetch_class(key)
64
66
  when :method
67
+ # @type var db: MethodDatabase
65
68
  db.fetch_method(MethodSpec.parse(key))
66
69
  when :function
70
+ # @type var db: FunctionDatabase
67
71
  db.fetch_function(key)
68
72
  else
69
73
  raise "must not happen: #{type.inspect}"
@@ -96,7 +100,7 @@ module BitClust
96
100
  <dt>classes</dt><dd><%= entry.classes.map {|c| c.name }.sort.join(', ') %></dd>
97
101
  <dt>methods</dt><dd><%= entry.methods.map {|m| m.name }.sort.join(', ') %></dd>
98
102
  </dl>
99
- <%= compile_rd(entry.source) %>
103
+ <%= compile_rd(entry) %>
100
104
  End
101
105
  },
102
106
  :class => {
@@ -119,7 +123,7 @@ module BitClust
119
123
  <dt>singleton_methods</dt><dd><%= entry.singleton_methods.map {|m| m.name }.sort.join(', ') %></dd>
120
124
  <dt>instance_methods</dt><dd><%= entry.instance_methods.map {|m| m.name }.sort.join(', ') %></dd>
121
125
  </dl>
122
- <%= compile_rd(entry.source) %>
126
+ <%= compile_rd(entry) %>
123
127
  End
124
128
  },
125
129
  :method => {
@@ -142,7 +146,7 @@ module BitClust
142
146
  <dt>kind</dt><dd><%= entry.kind %></dd>
143
147
  <dt>library</dt><dd><%= entry.library.name %></dd>
144
148
  </dl>
145
- <%= compile_rd(entry.source) %>
149
+ <%= compile_rd(entry) %>
146
150
  End
147
151
  },
148
152
  :function => {
@@ -159,16 +163,31 @@ module BitClust
159
163
  <dt>header</dt><dd><%= entry.header %></dd>
160
164
  <dt>filename</dt><dd><%= entry.filename %></dd>
161
165
  </dl>
162
- <%= compile_rd(entry.source) %>
166
+ <%= compile_rd(entry) %>
163
167
  End
164
168
  }
165
169
  }
166
170
 
167
- def compile_rd(src)
171
+ def compile_rd(entry)
168
172
  umap = URLMapper.new(:base_url => 'http://example.com',
169
173
  :cgi_url => 'http://example.com/view')
170
- compiler = RDCompiler.new(umap, 2)
171
- compiler.compile(src)
174
+ if @db.properties['source_format'] == 'markdown'
175
+ # md ソースの DB は screen.rb と同じく MDCompiler(GFM)で描画する
176
+ Kernel.require 'bitclust/mdcompiler'
177
+ compiler = MDCompiler.new(umap, 2, { :gfm => true, :database => @db })
178
+ else
179
+ compiler = RDCompiler.new(umap, 2, { :database => @db })
180
+ end
181
+ # シグネチャ行の描画(permalink の id 等)はエントリ文脈を要求する
182
+ # ので、compile ではなくエントリ種別ごとの compile_* を使う
183
+ case entry
184
+ when MethodEntry
185
+ compiler.compile_method(entry)
186
+ when FunctionEntry
187
+ compiler.compile_function(entry)
188
+ else
189
+ compiler.compile(entry.source)
190
+ end
172
191
  end
173
192
  end
174
193
  end
@@ -46,6 +46,11 @@ module BitClust
46
46
  option_error("wrong number of arguments") unless argv.size == 1
47
47
  end
48
48
 
49
+ # システムの ruby とリファレンスファイルを比較するだけで DB を使わない
50
+ def needs_database?
51
+ false
52
+ end
53
+
49
54
  def exec(argv, options)
50
55
  classname = argv[0]
51
56
  case @mode
@@ -60,6 +65,7 @@ module BitClust
60
65
  c = lib.fetch_class(classname)
61
66
  list0 = lib.classes.find_all{|c0| /\A#{classname}\b/o =~ c0.name }
62
67
  list0 = c.entries + list0
68
+ # @type var list: Array[String]
63
69
  list = list0.map {|ent| ent.labels.map {|n| expand_mf(n) } }.flatten
64
70
  if @content
65
71
  ri = @ri_path ? RiDatabase.open(@ri_path, nil) : RiDatabase.open_system_db
@@ -72,7 +78,7 @@ module BitClust
72
78
  puts fmt.method_info(mthd.entry)
73
79
  else
74
80
  name = name.sub(/\A\w+#/, '')
75
- puts "--- #{name}\n\#@todo\n\n"
81
+ puts "--- #{name}\n#%todo\n\n"
76
82
  end
77
83
  end
78
84
  else
@@ -84,7 +90,7 @@ module BitClust
84
90
  end
85
91
  end
86
92
  else
87
- raise "must not happen: #{mode.inspect}"
93
+ raise "must not happen: #{@mode.inspect}"
88
94
  end
89
95
  end
90
96
 
@@ -104,6 +110,8 @@ module BitClust
104
110
 
105
111
  def m_order(m)
106
112
  m, t = *m.reverse.split(/(\#|\.|::)/, 2)
113
+ m or raise
114
+ t or raise
107
115
  [ORDER[t] || 0, m.reverse]
108
116
  end
109
117
 
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # bitclust/subcommands/methodsince_command.rb
4
+ #
5
+ # Fills per-name since/until into method entries by scanning a version
6
+ # ladder of databases:
7
+ #
8
+ # bitclust methodsince --update=db-3.4 db-1.8.7 db-1.9.3 ... db-3.3 db-3.4
9
+ #
10
+ # Every positional argument and every --update path together form the
11
+ # version ladder used to compute presence; --update paths are additionally
12
+ # written back to (see bitclust/method_since_calculator.rb, bitclust#132 P2).
13
+ #
14
+
15
+ require 'bitclust'
16
+ require 'bitclust/subcommand'
17
+ require 'bitclust/method_since_calculator'
18
+ require 'tmpdir'
19
+ require 'fileutils'
20
+
21
+ module BitClust
22
+ module Subcommands
23
+ class MethodsinceCommand < Subcommand
24
+ def initialize
25
+ super
26
+ @updates = []
27
+ @dry_run = false
28
+ @parser.banner = "Usage: #{File.basename($0, '.*')} methodsince [options] <dbpath>..."
29
+ @parser.on('--update=PATH', 'Writable target database to fill in-place (repeatable).') {|path|
30
+ @updates.push path
31
+ }
32
+ @parser.on('--dry-run', 'Compute and print stats without saving.') {
33
+ @dry_run = true
34
+ }
35
+ end
36
+
37
+ # DB パスは自前の引数(位置引数 + --update)で受けるので、
38
+ # グローバル --database は不要
39
+ def needs_database?
40
+ false
41
+ end
42
+
43
+ def exec(argv, options)
44
+ error("no --update given (nothing to write to)") if @updates.empty?
45
+
46
+ ladder_paths = (argv + @updates).uniq {|path| File.expand_path(path) }
47
+ calculator = MethodSinceCalculator.new(ladder_paths.map {|path| MethodDatabase.new(path) })
48
+ calculator.scan
49
+
50
+ @updates.each do |path|
51
+ stats, version = @dry_run ? apply_dry_run(calculator, path) : apply_and_save(calculator, path)
52
+ puts "#{File.basename(path)} (version #{version}): " \
53
+ "entries_updated=#{stats[:entries_updated]} since_filled=#{stats[:since_filled]} " \
54
+ "until_filled=#{stats[:until_filled]} floor_skipped=#{stats[:floor_skipped]}"
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def apply_and_save(calculator, path)
61
+ target = MethodDatabase.new(path)
62
+ version = target.propget('version') or
63
+ error("#{path}: no version property (not a bitclust database?)")
64
+ [calculator.apply(target), version]
65
+ end
66
+
67
+ # --dry-run: 実際のデータベースには一切書き込まず、複製に対して
68
+ # apply した結果の統計だけを見せる
69
+ def apply_dry_run(calculator, path)
70
+ version = MethodDatabase.new(path).propget('version') or
71
+ error("#{path}: no version property (not a bitclust database?)")
72
+ Dir.mktmpdir do |tmp|
73
+ copy = File.join(tmp, 'db')
74
+ FileUtils.cp_r(path, copy)
75
+ [calculator.apply(MethodDatabase.new(copy)), version]
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -18,6 +18,11 @@ module BitClust
18
18
  }
19
19
  end
20
20
 
21
+ # ソースファイルを処理するだけで DB を使わない
22
+ def needs_database?
23
+ false
24
+ end
25
+
21
26
  def exec(argv, options)
22
27
  argv.each do |path|
23
28
  File.open(path) {|file|
@@ -49,7 +49,7 @@ module BitClust
49
49
  end
50
50
 
51
51
  def exec(argv, options)
52
- prefix = options[:prefix]
52
+ prefix = options[:prefix] || raise
53
53
  db = MethodDatabase.new(prefix)
54
54
  case @mode
55
55
  when :list
@@ -62,6 +62,7 @@ module BitClust
62
62
  end
63
63
  when :set
64
64
  key, val = *argv
65
+ key || raise; val || raise
65
66
  db.transaction {
66
67
  db.propset key, val
67
68
  }
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # bitclust/subcommands/searchpage_command.rb
4
+ #
5
+ # Generates a standalone, cross-version, client-side search page from
6
+ # multiple version databases:
7
+ #
8
+ # bitclust searchpage --outputdir=DIR db-3.0 db-3.1 ... db-4.1
9
+ #
10
+ # The output (index.html + js/search_data.js + assets) is fully static and
11
+ # replaces the server-backed rurema-search app at docs.ruby-lang.org/ja/search/.
12
+ # Each database's version is read from its own properties, and the merged
13
+ # index tags every entry with the versions it appears in.
14
+ #
15
+
16
+ require 'pathname'
17
+ require 'fileutils'
18
+ require 'json'
19
+ require 'optparse'
20
+
21
+ require 'bitclust'
22
+ require 'bitclust/subcommand'
23
+ require 'bitclust/search_index_generator'
24
+
25
+ module BitClust
26
+ module Subcommands
27
+ class SearchpageCommand < Subcommand
28
+ # search_init.js is the in-page dropdown wiring used by statichtml
29
+ # layouts; this page ships its own wiring (search_page.js) instead.
30
+ VENDORED_JS_FILES = %w[
31
+ search_navigation.js search_ranker.js search_controller.js
32
+ ].freeze
33
+
34
+ def initialize
35
+ super
36
+ @outputdir = nil
37
+ @themedir = srcdir_root + "theme/default"
38
+ @templatedir = srcdir_root + "data/bitclust/searchpage"
39
+ @suffix = ".html"
40
+ @fs_casesensitive = false
41
+ @verbose = false
42
+ @parser.banner = "Usage: #{File.basename($0, '.*')} searchpage [options] <dbpath>..."
43
+ @parser.on('-o', '--outputdir=PATH', 'Output directory.') {|path|
44
+ @outputdir = Pathname.new(path)
45
+ }
46
+ @parser.on('--themedir=PATH', 'Theme directory.') {|path|
47
+ @themedir = Pathname.new(path)
48
+ }
49
+ @parser.on('--fs-casesensitive', 'Filesystem is case-sensitive.') {
50
+ @fs_casesensitive = true
51
+ }
52
+ @parser.on('--verbose', 'Show progress.') {
53
+ @verbose = true
54
+ }
55
+ end
56
+
57
+ # DB paths are taken as positional arguments (one per version), so the
58
+ # global --database option is not required.
59
+ def needs_database?
60
+ false
61
+ end
62
+
63
+ def exec(argv, options)
64
+ outputdir = @outputdir or error("no --outputdir given")
65
+ error("no database given (pass one path per version)") if argv.empty?
66
+
67
+ generator = SearchIndexGenerator.new(suffix: @suffix,
68
+ fs_casesensitive: @fs_casesensitive)
69
+ version_indexes = argv.map {|path|
70
+ db = MethodDatabase.new(path)
71
+ fdb = FunctionDatabase.new(path) if File.directory?(File.join(path, 'function'))
72
+ version = db.properties['version'] or
73
+ error("#{path}: no version property (not a bitclust database?)")
74
+ $stderr.puts "indexing #{path} (#{version})" if @verbose
75
+ [version, generator.build_index(db, fdb)] #: [String, Array[SearchIndexGenerator::entry]]
76
+ }
77
+ versions = version_indexes.map {|version, _| version }
78
+ .sort_by {|v| Gem::Version.new(v) }
79
+
80
+ jsdir = outputdir + "js"
81
+ FileUtils.mkdir_p(jsdir)
82
+ (jsdir + "search_data.js").write(SearchIndexGenerator.merged_js(version_indexes))
83
+ VENDORED_JS_FILES.each do |js|
84
+ FileUtils.cp(@themedir + "js" + js, jsdir.to_s, :preserve => true)
85
+ end
86
+ # Ship the MIT notice for the vendored Aliki files alongside them.
87
+ FileUtils.cp(@themedir + "js" + "NOTICE", jsdir.to_s, :preserve => true)
88
+ FileUtils.cp(@themedir + "js" + "search_page.js", jsdir.to_s, :preserve => true)
89
+ FileUtils.cp(@themedir + "search.css", outputdir.to_s, :preserve => true)
90
+ (outputdir + "index.html").write(render_page(versions))
91
+ $stderr.puts "generated search page for #{versions.join(', ')}" if @verbose
92
+ end
93
+
94
+ private
95
+
96
+ def render_page(versions)
97
+ template = (@templatedir + "index.html").read
98
+ template.sub('%%SEARCH_VERSIONS%%', JSON.generate(versions))
99
+ end
100
+ end
101
+ end
102
+ end
@@ -107,11 +107,11 @@ module BitClust
107
107
  exit 1
108
108
  end
109
109
  if @pid_file
110
- if File.exist?(@pid_file)
110
+ if File.exist?(@pid_file || raise)
111
111
  $stderr.puts "There is still #{@pid_file}. Is another process running?"
112
112
  exit 1
113
113
  end
114
- @pid_file = File.expand_path(@pid_file)
114
+ @pid_file = File.expand_path(@pid_file || raise)
115
115
  end
116
116
  end
117
117
 
@@ -133,7 +133,7 @@ module BitClust
133
133
  @params[:Logger] = WEBrick::Log.new($stderr, WEBrick::Log::INFO)
134
134
  @params[:AccessLog] = []
135
135
  end
136
- basepath = URI.parse(@baseurl).path
136
+ basepath = URI.parse(@baseurl || raise).path or raise
137
137
  server = WEBrick::HTTPServer.new(@params)
138
138
 
139
139
  if @autop
@@ -172,17 +172,19 @@ module BitClust
172
172
  server.mount File.join(basepath, 'theme/'), WEBrick::HTTPServlet::FileHandler, @themedir
173
173
 
174
174
  if @debugp
175
- trap(:INT) { server.shutdown }
175
+ Signal.trap(:INT) { server.shutdown }
176
176
  else
177
177
  WEBrick::Daemon.start do
178
- trap(:TERM) {
178
+ Signal.trap(:TERM) {
179
179
  server.shutdown
180
180
  begin
181
- File.unlink @pid_file if @pid_file
181
+ pid_file = @pid_file
182
+ File.unlink pid_file if pid_file
182
183
  rescue Errno::ENOENT
183
184
  end
184
185
  }
185
- File.open(@pid_file, 'w') {|f| f.write Process.pid } if @pid_file
186
+ pid_file = @pid_file
187
+ File.open(pid_file, 'w') {|f| f.write Process.pid } if pid_file
186
188
  end
187
189
  end
188
190
  exit if $".include?("exerb/mkexy.rb")
@@ -209,7 +211,7 @@ module BitClust
209
211
  end
210
212
 
211
213
  def load_config_file
212
- home_directory = Pathname(ENV['HOME'])
214
+ home_directory = Pathname(ENV.fetch('HOME'))
213
215
  config_path = home_directory + ".bitclust/config"
214
216
  if config_path.exist?
215
217
  config = YAML.load_file(config_path)