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
@@ -10,7 +10,7 @@
10
10
 
11
11
  module BitClust
12
12
 
13
- # Provides completion search methods like _search_classes, _search_methods.
13
+ # Provides completion search methods like _search_classes, _search_methods.
14
14
  # Included by MethodDatabase and FunctionDatabase.
15
15
  module Completion
16
16
 
@@ -21,18 +21,20 @@ module BitClust
21
21
  #
22
22
 
23
23
  def _search_classes(pattern)
24
+ # @type self: MethodDatabase
24
25
  expand_ic(classes(), pattern)
25
26
  end
26
27
 
27
28
  def _search_methods(pattern)
29
+ # @type self: MethodDatabase
28
30
  case
29
31
  when pattern.empty?
30
32
  recordclass = SearchResult::Record
31
33
  SearchResult.new(self, pattern, classes(),
32
- methods().map {|m| s = m.spec; recordclass.new(s, s, m) })
34
+ methods().map {|m| s = m.spec; recordclass.new(self, s, s, m) })
33
35
  when pattern.special_variable?
34
36
  c = fetch_class('Kernel')
35
- SearchResult.new(self, pattern, [c], search_svar(c, pattern.method))
37
+ SearchResult.new(self, pattern, [c], search_svar(c, pattern.method || raise))
36
38
  when pattern.class?
37
39
  search_methods_from_cname(pattern)
38
40
  else
@@ -54,22 +56,25 @@ GC.enable; GC.start; x
54
56
  end
55
57
 
56
58
  def _search_functions(pattern)
59
+ # @type self: FunctionDatabase
57
60
  expand_ic(functions(), pattern)
58
61
  end
59
62
 
60
63
  def search_svar(c, pattern)
64
+ # @type self: MethodDatabase
61
65
  expand(c.special_variables, pattern)\
62
66
  .map {|m| SearchResult::Record.new(self, m.spec, m.spec, m) }
63
67
  end
64
68
 
65
69
  def search_methods_from_cname(pattern)
66
- cs = expand_ic(classes(), pattern.klass)
70
+ # @type self: MethodDatabase
71
+ cs = expand_ic(classes(), pattern.klass || raise)
67
72
  return SearchResult.new(self, pattern, [], []) if cs.empty?
68
73
  recs = cs.map {|c|
69
74
  c.entries.map {|m|
70
75
  if not pattern.type or m.typemark == pattern.type
71
76
  s = m.spec
72
- SearchResult::Record.new(s, s, m)
77
+ SearchResult::Record.new(self, s, s, m)
73
78
  else
74
79
  nil
75
80
  end
@@ -79,13 +84,16 @@ GC.enable; GC.start; x
79
84
  end
80
85
 
81
86
  def mspec_from_cref_mname(cref, name)
87
+ # @type self: MethodDatabase
82
88
  m = /\A(#{NameUtils::CLASS_PATH_RE})(#{NameUtils::TYPEMARK_RE})\Z/.match(cref)
83
- MethodSpec.new(m[1], m[2], name)
89
+ m || raise
90
+ MethodSpec.new(m[1] || raise, _ = m[2], name)
84
91
  end
85
92
 
86
93
  def search_methods_from_mname(pattern)
94
+ # @type self: MethodDatabase
87
95
  #timer_init
88
- names = expand_name_narrow(method_names(), pattern.method)
96
+ names = expand_name_narrow(method_names(), pattern.method || raise)
89
97
  #split_time "m expandN (#{names.size})"
90
98
  records = names.map {|name|
91
99
  crefs = mname2crefs_narrow(name)
@@ -99,14 +107,16 @@ GC.enable; GC.start; x
99
107
  end
100
108
 
101
109
  def search_methods_from_cname_mname(pattern)
110
+ # @type self: MethodDatabase
102
111
  #timer_init
103
112
  recs = try(typechars(pattern.type, pattern.method)) {|ts|
104
- expand_method_name(pattern.klass, ts, pattern.method)
113
+ expand_method_name(pattern.klass || raise, ts, pattern.method || raise)
105
114
  }
106
- SearchResult.new(self, pattern, recs.map {|rec| rec.class_name }, recs)
115
+ SearchResult.new(self, pattern, recs.map {|rec| rec.class_name }, recs) # steep:ignore
107
116
  end
108
117
 
109
118
  def expand_method_name(c, ts, m)
119
+ # @type self: MethodDatabase
110
120
  names_w = expand_name_wide(method_names(), m)
111
121
  return [] if names_w.empty?
112
122
  #split_time "m expandW (#{names_w.size})"
@@ -142,18 +152,20 @@ GC.enable; GC.start; x
142
152
  end
143
153
 
144
154
  def count_class(recs)
155
+ # @type self: MethodDatabase
145
156
  recs.map {|rec| rec.class_name }.uniq.size
146
157
  end
147
158
 
148
159
  def make_cm_combination(cpat, ts, mnames)
149
- result = []
160
+ # @type self: MethodDatabase
161
+ result = [] #: Array[SearchResult::Record]
150
162
  $cm_comb_m = 0
151
163
  mnames.each do |m|
152
164
  crefs = expand_name_narrow(mname2crefs_wide(m), cpat, ts)
153
165
  next if crefs.empty?
154
166
  $cm_comb_m += 1
155
167
  crefs.each do |ref|
156
- spec = MethodSpec.new(classid2name(ref.chop), ref[-1,1], m)
168
+ spec = MethodSpec.new(classid2name(ref.chop), _ = ref[-1,1], m)
157
169
  result.push SearchResult::Record.new(self, spec)
158
170
  end
159
171
  end
@@ -176,7 +188,7 @@ $cm_comb_m += 1
176
188
  end
177
189
 
178
190
  def unify_entries(ents)
179
- h = {}
191
+ h = {} #: Hash[SearchResult::Record, SearchResult::Record]
180
192
  ents.each do |ent|
181
193
  if ent0 = h[ent]
182
194
  ent0.merge ent
@@ -189,6 +201,7 @@ $cm_comb_m += 1
189
201
 
190
202
  # Case-insensitive search. Optimized for constant search.
191
203
  def expand_ic(xs, pattern)
204
+ # @type self: MethodDatabase | FunctionDatabase
192
205
  re1 = /\A#{Regexp.quote(pattern)}/i
193
206
  result1 = xs.select {|x| x.name_match?(re1) }
194
207
  return [] if result1.empty?
@@ -201,6 +214,7 @@ $cm_comb_m += 1
201
214
  end
202
215
 
203
216
  def expand(xs, pattern)
217
+ # @type self: MethodDatabase
204
218
  re1 = /\A#{Regexp.quote(pattern)}/i
205
219
  result1 = xs.select {|x| x.name_match?(re1) }
206
220
  return [] if result1.empty?
@@ -217,12 +231,14 @@ $cm_comb_m += 1
217
231
 
218
232
  # list up all matched items (without squeezing)
219
233
  def expand_name_wide(names, pattern)
234
+ # @type self: MethodDatabase
220
235
  re1 = /\A#{Regexp.quote(pattern)}/i
221
236
  names.grep(re1)
222
237
  end
223
238
 
224
239
  # list up matched items (with squeezing)
225
240
  def expand_name_narrow(names, pattern, suffixes = nil)
241
+ # @type self: MethodDatabase
226
242
  re1 = /\A#{Regexp.quote(pattern)}/i
227
243
  result1 = names.grep(re1)
228
244
  return [] if result1.empty?
@@ -232,6 +248,7 @@ $cm_comb_m += 1
232
248
 
233
249
  # squeeze result of #expand_name_wide
234
250
  def squeeze_names(result1, pattern, suffixes = nil)
251
+ # @type self: MethodDatabase
235
252
  regexps =
236
253
  [
237
254
  /\A#{Regexp.quote(pattern)}.*#{suffix_pattern(suffixes)}\z/i,
@@ -258,6 +275,7 @@ $cm_comb_m += 1
258
275
  #
259
276
 
260
277
  def save_completion_index
278
+ # @type self: MethodDatabase
261
279
  save_class_index
262
280
  save_method_index
263
281
  save_method_index_narrow
@@ -268,9 +286,9 @@ $cm_comb_m += 1
268
286
  end
269
287
 
270
288
  def intern_table
271
- @intern_table ||=
289
+ @intern_table ||=
272
290
  begin
273
- h = {}
291
+ h = {} #: Hash[String, String]
274
292
  classnametable().each do |id, names|
275
293
  names.each do |n|
276
294
  h[n] = id
@@ -281,6 +299,7 @@ $cm_comb_m += 1
281
299
  end
282
300
 
283
301
  def save_class_index
302
+ # @type self: MethodDatabase
284
303
  atomic_write_open('class/=index') {|f|
285
304
  classes().each do |c|
286
305
  #f.puts "#{c.id}\t#{c.names.join(' ')}" # FIXME: support class alias
@@ -290,11 +309,13 @@ $cm_comb_m += 1
290
309
  end
291
310
 
292
311
  def classnametable
312
+ # @type self: MethodDatabase
293
313
  @classnametable ||=
294
314
  begin
295
- h = {}
315
+ h = {} #: Hash[String, Array[String]]
296
316
  foreach_line('class/=index') do |line|
297
317
  id, *names = line.split
318
+ id || raise
298
319
  h[id] = names
299
320
  end
300
321
  h
@@ -304,9 +325,10 @@ $cm_comb_m += 1
304
325
  end
305
326
 
306
327
  def save_method_index_narrow
328
+ # @type self: MethodDatabase
307
329
  index =
308
330
  begin
309
- h = {}
331
+ h = {} #: Hash[String, Array[String]]
310
332
  classes().each do |c|
311
333
  c.entries.each do |m|
312
334
  ref = c.id + m.typemark
@@ -325,11 +347,13 @@ $cm_comb_m += 1
325
347
  end
326
348
 
327
349
  def method_index_small
350
+ # @type self: MethodDatabase
328
351
  @method_index_small ||=
329
352
  begin
330
- h = {}
353
+ h = {} #: Hash[String, Array[String]]
331
354
  foreach_line('method/=sindex') do |line|
332
355
  name, *crefs = line.split(nil)
356
+ name || raise
333
357
  h[name] = crefs
334
358
  end
335
359
  h
@@ -342,13 +366,16 @@ $cm_comb_m += 1
342
366
  end
343
367
 
344
368
  def save_method_index
369
+ # @type self: MethodDatabase
345
370
  index =
346
371
  begin
347
- h = {}
372
+ h = {} #: Hash[String, Array[String]]
348
373
  classes().each do |c|
349
374
  [ ['#', c._imap.keys],
350
375
  ['.', c._smap.keys],
351
376
  [':', c._cmap.keys] ].each do |t, names|
377
+ # @type var t: String
378
+ # @type var names: Array[String]
352
379
  ref = c.id + t
353
380
  names.each do |name|
354
381
  (h[name] ||= []).push ref
@@ -371,24 +398,27 @@ $cm_comb_m += 1
371
398
  # includes class aliases, includes inherited methods
372
399
  def mname2crefs_wide(name)
373
400
  tbl = classnametable()
374
- mname2crefs_0(name).map {|ref|
375
- tbl[ref.chop].map {|c| c + ref[-1,1] }
401
+ (mname2crefs_0(name) || raise).map {|ref|
402
+ tbl[ref.chop].map {|c| c + ref[-1,1] } # steep:ignore
376
403
  }.flatten
377
404
  end
378
405
 
379
406
  def mname2crefs_0(name)
380
- crefs = (@method_index ||= {})[name]
407
+ crefs = (@method_index ||= {})[name] # steep:ignore
381
408
  return crefs if crefs
382
409
  crefsstr = method_index_0()[name] or return nil
383
410
  @method_index[name] = crefsstr.split(nil)
384
411
  end
385
412
 
386
413
  def method_index_0
414
+ # @type self: MethodDatabase
387
415
  @method_index_0 ||=
388
416
  begin
389
- h = {}
417
+ h = {} #: Hash[String, String]
390
418
  foreach_line('method/=index') do |line|
391
419
  name, cnames = line.split(nil, 2)
420
+ name || raise
421
+ cnames.is_a?(String) || raise
392
422
  h[name] = cnames
393
423
  end
394
424
  h
@@ -467,7 +497,7 @@ $cm_comb_m += 1
467
497
  begin
468
498
  spec = @specs.first
469
499
  c = @db.fetch_class(spec.klass)
470
- MethodSpec.parse(c.match_entry(spec.type, spec.method))
500
+ MethodSpec.parse(c.match_entry(spec.type, spec.method) || raise)
471
501
  end
472
502
  end
473
503
 
@@ -476,7 +506,7 @@ $cm_comb_m += 1
476
506
  end
477
507
 
478
508
  def entry
479
- @entry ||= @db.get_method(origin())
509
+ @entry ||= @db.get_method(origin()) || raise
480
510
  end
481
511
 
482
512
  def name
@@ -484,7 +514,8 @@ $cm_comb_m += 1
484
514
  end
485
515
 
486
516
  def names
487
- @specs.map {|spec| spec.display_name }
517
+ version = @db && @db.properties["version"]
518
+ @specs.map {|spec| spec.display_name(version) }
488
519
  end
489
520
 
490
521
  def class_name
@@ -522,9 +553,9 @@ $cm_comb_m += 1
522
553
  def owned_method?
523
554
  @specs.any? {|spec| spec.klass == origin().klass }
524
555
  end
525
-
556
+
526
557
  def method_of_alias_class?
527
- @entry.klass.aliases.any?{|aliasclass| aliasclass.name?(class_name())}
558
+ entry.klass.aliases.any?{|aliasclass| aliasclass.name?(class_name())}
528
559
  end
529
560
 
530
561
  def inherited_method?
@@ -14,7 +14,8 @@ module BitClust
14
14
  private
15
15
 
16
16
  def print_crossruby_table(&block)
17
- print_table(*build_crossruby_table(&block))
17
+ vers, table = build_crossruby_table(&block)
18
+ print_table(vers, table)
18
19
  end
19
20
 
20
21
  def print_table(vers, table)
@@ -44,7 +45,7 @@ module BitClust
44
45
  end
45
46
 
46
47
  def get_ruby(version)
47
- forall_ruby(ENV['PATH']) do |ruby, |
48
+ forall_ruby(ENV.fetch('PATH')) do |ruby, |
48
49
  v = `#{ruby} -e 'print RUBY_VERSION'`
49
50
  patch = `#{ruby} -e 'print RUBY_PATCHLEVEL if defined? RUBY_PATCHLEVEL'`
50
51
  if version == v or ( version == v.succ and patch == '5000')
@@ -58,9 +59,9 @@ module BitClust
58
59
  ENV.delete 'RUBYOPT'
59
60
  ENV.delete 'RUBYLIB'
60
61
  ENV.delete 'GEM_HOME'
61
- vers = []
62
- table = {}
63
- forall_ruby(ENV['PATH']) do |ruby, ver|
62
+ vers = [] #: Array[String]
63
+ table = {} #: table_type
64
+ forall_ruby(ENV.fetch('PATH')) do |ruby, ver|
64
65
  puts "#{version_id(ver)}: #{ver}" if @verbose
65
66
  vers.push ver
66
67
  yield(ruby).each do |c|
@@ -71,11 +72,13 @@ module BitClust
71
72
  end
72
73
 
73
74
  def forall_ruby(path, &block)
75
+ # steep:ignore:start
74
76
  rubys(path)\
75
77
  .map {|ruby| [ruby, `#{ruby} --version`] }\
76
78
  .reject {|ruby, verstr| `which #{ruby}`.include?('@') }\
77
79
  .sort_by {|ruby, verstr| verstr }\
78
80
  .each(&block)
81
+ # steep:ignore:end
79
82
  end
80
83
 
81
84
  def rubys(path)
@@ -28,9 +28,10 @@ module BitClust
28
28
  def Database.connect(uri)
29
29
  case uri.scheme
30
30
  when 'file'
31
- new(uri.path)
31
+ new(uri.path || raise)
32
32
  when 'druby'
33
- DRbObject.new_with_uri(uri.to_s)
33
+ require 'drb'
34
+ _ = DRbObject.new_with_uri(uri.to_s) #: ::BitClust::Database
34
35
  else
35
36
  raise InvalidScheme, "unknown database scheme: #{uri.scheme}"
36
37
  end
@@ -62,14 +63,32 @@ module BitClust
62
63
  yield
63
64
  return if dummy?
64
65
  if @properties_dirty
65
- save_properties 'properties', @properties
66
+ save_properties 'properties', (@properties || raise)
66
67
  @properties_dirty = false
67
68
  end
68
- commit if dirty?
69
+ if dirty?
70
+ commit
71
+ # 更新完了の合図として properties の mtime を進める(bitclust#275)。
72
+ # server の自動リロード(ReloadableRequestHandler)は properties の
73
+ # mtime で新鮮さを判定するため、init 直後〜update 完了前の途中状態を
74
+ # 読んでしまったプロセスも、ここで mtime が進むことで次のリクエスト
75
+ # から完成した DB を読み直して収束できる
76
+ touch_properties
77
+ end
69
78
  ensure
70
79
  @in_transaction = false
71
80
  end
72
81
 
82
+ # properties の内容は変えず mtime だけ現在時刻に進める。
83
+ # ファイルが無い場合(異常系)は何もしない
84
+ def touch_properties
85
+ path = realpath('properties')
86
+ now = Time.now
87
+ File.utime(now, now, path)
88
+ rescue Errno::ENOENT
89
+ end
90
+ private :touch_properties
91
+
73
92
  # abstract dirty?
74
93
  # abstract clear_dirty
75
94
  # abstract commit
@@ -100,12 +119,12 @@ module BitClust
100
119
  end
101
120
 
102
121
  def propget(key)
103
- properties()[key]
122
+ properties()[key] # steep:ignore
104
123
  end
105
124
 
106
125
  def propset(key, value)
107
126
  check_transaction
108
- properties()[key] = value
127
+ properties()[key] = value # steep:ignore
109
128
  @properties_dirty = true
110
129
  end
111
130
 
@@ -126,6 +145,7 @@ module BitClust
126
145
  Dir.entries(realpath(rel))\
127
146
  .reject {|ent| /\A[\.=]/ =~ ent }\
128
147
  .map {|ent| decodeid(ent) }
148
+ .sort
129
149
  rescue Errno::ENOENT
130
150
  return []
131
151
  end
@@ -135,12 +155,12 @@ module BitClust
135
155
  end
136
156
 
137
157
  def load_properties(rel)
138
- h = {}
158
+ h = {} #: Hash[String, String]
139
159
  fopen(realpath(rel), 'r:UTF-8') {|f|
140
160
  while line = f.gets
141
161
  k, v = line.strip.split('=', 2)
142
162
  break unless k
143
- h[k] = v
163
+ h[k] = v || raise
144
164
  end
145
165
  h['source'] = f.read
146
166
  }
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bitclust/rrd_to_markdown'
4
+
5
+ module BitClust
6
+ # refm/doc(散文ページ)の Markdown 変換。
7
+ #
8
+ # doc ページにはクロスファイル情報(library 所属等)が無いため、
9
+ # オーケストレータは不要で、reduce(正規化)+ 単一ファイル変換のみ。
10
+ # タイトルは H1 が担い、front matter は使わない。
11
+ #
12
+ # reduce は意味を変えない表記ゆれを md→rd の再生成形に合わせる正規化と、
13
+ # クロスツリー include(api 断片の transclude)の manual レイアウトへの
14
+ # 書き換えを行う。md→rd 変換は reduce の結果を復元する(検証の期待値)。
15
+ module DocConverter
16
+ module_function
17
+
18
+ def reduce(rrd)
19
+ RRDToMarkdown.normalize_dlist_colon_spacing(rrd.lines.map { |line|
20
+ case line
21
+ when /\A\#@samplecode[ \t]+\n\z/
22
+ "\#@samplecode\n" # ラベル無しの末尾スペース
23
+ when %r{\A//\}[ \t]+\n\z}
24
+ "//}\n"
25
+ when /\A:\s{2,}(.*)/m
26
+ ": #{$1}" # 定義リスト term の余分なスペース
27
+ when /\A(={1,4}\[a:[^\]]+\])\s{2,}(.*)/m
28
+ "#{$1} #{$2}" # アンカー見出しの余分なスペース
29
+ when /\A\t+/
30
+ line.sub(/\A\t+/) { ' ' * ($& || raise).length } # 行頭タブ(doc の散文1行のみ)
31
+ when /\A\#@include\((?:\.\.\/)+api\/src\//
32
+ # 旧レイアウト ../api/src/X → 新レイアウト ../api/X
33
+ # (manual/ 配下では src 階層が無い。ブリッジが逆変換する)
34
+ line.sub(%r{((?:\.\./)+)api/src/}, '\1api/')
35
+ else
36
+ line
37
+ end
38
+ }.join)
39
+ end
40
+
41
+ def convert(rrd)
42
+ RRDToMarkdown.convert(reduce(rrd))
43
+ end
44
+
45
+ # 変換対象ファイルの一覧(doc ルート相対)。
46
+ # 旧パイプライン(copy_doc)は **/*.rd だけをページとして読むため、
47
+ # .rd 以外は doc 内 include から参照される断片のみを対象にし、
48
+ # 参照されない死にファイル(news/1.8.0.rd-2 等)は凍結側に残す
49
+ def files(doc_root)
50
+ all = Dir.glob('**/*', base: doc_root)
51
+ .select { |f| File.file?(File.join(doc_root, f)) }.sort
52
+ pages = all.select { |f| f.end_with?('.rd') }
53
+ referenced = pages.flat_map { |f|
54
+ base = File.dirname(f)
55
+ File.read(File.join(doc_root, f))
56
+ .scan(/^\#@include\((?!(?:\.\.\/)+api\/)(.*?)\)/)
57
+ .map { |t| File.expand_path(base == '.' ? (t[0] || raise) : File.join(base, t[0] || raise), '/')
58
+ .delete_prefix('/') }
59
+ }
60
+ (pages + (all & referenced)).sort
61
+ end
62
+ end
63
+ end
@@ -84,7 +84,7 @@ module BitClust
84
84
  end
85
85
 
86
86
  def description
87
- source.split(/\n\n/, 2)[0].strip
87
+ description_text(source.split(/\n\n/, 2)[0].strip)
88
88
  end
89
89
  end
90
90
  end