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.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/README.md +65 -0
- data/Rakefile +32 -0
- data/data/bitclust/catalog/ja_JP.UTF-8 +19 -1
- data/data/bitclust/searchpage/index.html +90 -0
- data/data/bitclust/template/class +69 -8
- data/data/bitclust/template/function +2 -2
- data/data/bitclust/template/layout +11 -3
- data/data/bitclust/template/method +2 -2
- data/data/bitclust/template/search +1 -1
- data/data/bitclust/template.epub/class +22 -1
- data/data/bitclust/template.epub/function +2 -2
- data/data/bitclust/template.epub/layout +1 -3
- data/data/bitclust/template.epub/method +2 -2
- data/data/bitclust/template.lillia/class +26 -5
- data/data/bitclust/template.lillia/layout +1 -3
- data/data/bitclust/template.lillia/method +2 -2
- data/data/bitclust/template.offline/class +48 -4
- data/data/bitclust/template.offline/function +2 -2
- data/data/bitclust/template.offline/layout +27 -3
- data/data/bitclust/template.offline/method +2 -2
- data/lib/bitclust/app.rb +16 -17
- data/lib/bitclust/capi_converter.rb +20 -0
- data/lib/bitclust/classentry.rb +67 -17
- data/lib/bitclust/compat.rb +2 -2
- data/lib/bitclust/completion.rb +58 -27
- data/lib/bitclust/crossrubyutils.rb +8 -5
- data/lib/bitclust/database.rb +28 -8
- data/lib/bitclust/doc_converter.rb +63 -0
- data/lib/bitclust/docentry.rb +1 -1
- data/lib/bitclust/entity_splitter.rb +221 -0
- data/lib/bitclust/entry.rb +47 -7
- data/lib/bitclust/functiondatabase.rb +13 -1
- data/lib/bitclust/functionentry.rb +5 -1
- data/lib/bitclust/functionreferenceparser.rb +4 -4
- data/lib/bitclust/generators/epub.rb +10 -3
- data/lib/bitclust/htmlutils.rb +1 -1
- data/lib/bitclust/include_graph.rb +375 -0
- data/lib/bitclust/include_pruner.rb +144 -0
- data/lib/bitclust/interface.rb +1 -1
- data/lib/bitclust/libraryentry.rb +8 -7
- data/lib/bitclust/lineinput.rb +7 -5
- data/lib/bitclust/link_checker.rb +179 -0
- data/lib/bitclust/markdown_bridge.rb +186 -0
- data/lib/bitclust/markdown_orchestrator.rb +267 -0
- data/lib/bitclust/markdown_to_rrd.rb +824 -0
- data/lib/bitclust/markdown_tree.rb +217 -0
- data/lib/bitclust/mdcompiler.rb +883 -0
- data/lib/bitclust/mdparser.rb +347 -0
- data/lib/bitclust/messagecatalog.rb +3 -3
- data/lib/bitclust/method_since_calculator.rb +117 -0
- data/lib/bitclust/methoddatabase.rb +124 -14
- data/lib/bitclust/methodentry.rb +125 -7
- data/lib/bitclust/methodid.rb +12 -11
- data/lib/bitclust/methodsignature.rb +10 -4
- data/lib/bitclust/nameutils.rb +41 -22
- data/lib/bitclust/preprocessor.rb +66 -29
- data/lib/bitclust/rdcompiler.rb +195 -95
- data/lib/bitclust/refsdatabase.rb +23 -8
- data/lib/bitclust/reloadable_request_handler.rb +72 -0
- data/lib/bitclust/requesthandler.rb +21 -16
- data/lib/bitclust/ridatabase.rb +10 -4
- data/lib/bitclust/rrd_to_markdown.rb +925 -0
- data/lib/bitclust/rrdparser.rb +196 -30
- data/lib/bitclust/runner.rb +17 -2
- data/lib/bitclust/screen.rb +80 -11
- data/lib/bitclust/search_index_generator.rb +288 -0
- data/lib/bitclust/searcher.rb +69 -20
- data/lib/bitclust/simplesearcher.rb +24 -12
- data/lib/bitclust/subcommand.rb +7 -1
- data/lib/bitclust/subcommands/ancestors_command.rb +9 -2
- data/lib/bitclust/subcommands/checklink_command.rb +54 -0
- data/lib/bitclust/subcommands/chm_command.rb +15 -7
- data/lib/bitclust/subcommands/classes_command.rb +5 -0
- data/lib/bitclust/subcommands/extract_command.rb +5 -0
- data/lib/bitclust/subcommands/htmlfile_command.rb +21 -7
- data/lib/bitclust/subcommands/init_command.rb +4 -1
- data/lib/bitclust/subcommands/list_command.rb +12 -4
- data/lib/bitclust/subcommands/lookup_command.rb +28 -9
- data/lib/bitclust/subcommands/methods_command.rb +10 -2
- data/lib/bitclust/subcommands/methodsince_command.rb +80 -0
- data/lib/bitclust/subcommands/preproc_command.rb +5 -0
- data/lib/bitclust/subcommands/property_command.rb +2 -1
- data/lib/bitclust/subcommands/searchpage_command.rb +102 -0
- data/lib/bitclust/subcommands/server_command.rb +10 -8
- data/lib/bitclust/subcommands/setup_command.rb +24 -10
- data/lib/bitclust/subcommands/statichtml_command.rb +129 -6
- data/lib/bitclust/subcommands/update_command.rb +110 -2
- data/lib/bitclust/syntax_highlighter.rb +48 -4
- data/lib/bitclust/textutils.rb +3 -3
- data/lib/bitclust/version.rb +1 -1
- data/lib/bitclust/version_badges.rb +90 -0
- data/lib/bitclust/whole_file_gate.rb +137 -0
- data/test/test_app.rb +269 -0
- data/test/test_bitclust.rb +2 -0
- data/test/test_capi_converter.rb +91 -0
- data/test/test_class_screen.rb +413 -0
- data/test/test_copy_doc_md.rb +53 -0
- data/test/test_doc_converter.rb +78 -0
- data/test/test_entity_splitter.rb +231 -0
- data/test/test_entry.rb +207 -2
- data/test/test_eol_warning.rb +43 -0
- data/test/test_functionreferenceparser.rb +11 -0
- data/test/test_include_graph.rb +640 -0
- data/test/test_include_pruner.rb +133 -0
- data/test/test_link_checker.rb +114 -0
- data/test/test_lookup_command.rb +135 -0
- data/test/test_markdown_bridge.rb +217 -0
- data/test/test_markdown_orchestrator.rb +478 -0
- data/test/test_markdown_to_rrd.rb +757 -0
- data/test/test_markdown_tree.rb +369 -0
- data/test/test_mdcompiler.rb +1276 -0
- data/test/test_mdparser.rb +538 -0
- data/test/test_method_screen.rb +166 -0
- data/test/test_method_since_calculator.rb +290 -0
- data/test/test_methoddatabase.rb +16 -0
- data/test/test_methodentry.rb +328 -0
- data/test/test_methodsignature.rb +3 -1
- data/test/test_methodsince_command.rb +168 -0
- data/test/test_nameutils.rb +22 -0
- data/test/test_preprocessor.rb +215 -0
- data/test/test_rdcompiler.rb +401 -30
- data/test/test_refsdatabase.rb +32 -0
- data/test/test_rrd_to_markdown.rb +968 -0
- data/test/test_rrdparser.rb +295 -2
- data/test/test_run_ruby_wasm.rb +54 -0
- data/test/test_run_worker_prelude.rb +83 -0
- data/test/test_runner.rb +51 -0
- data/test/test_search_index_generator.rb +513 -0
- data/test/test_search_screen.rb +48 -0
- data/test/test_searcher.rb +132 -0
- data/test/test_searchpage_command.rb +167 -0
- data/test/test_simplesearcher.rb +28 -0
- data/test/test_statichtml_command.rb +204 -0
- data/test/test_syntax_highlighter.rb +60 -0
- data/test/test_whole_file_gate.rb +175 -0
- data/theme/default/js/NOTICE +42 -0
- data/theme/default/js/run-worker.js +111 -0
- data/theme/default/js/run.js +350 -0
- data/theme/default/js/search_controller.js +134 -0
- data/theme/default/js/search_init.js +176 -0
- data/theme/default/js/search_navigation.js +110 -0
- data/theme/default/js/search_page.js +202 -0
- data/theme/default/js/search_ranker.js +244 -0
- data/theme/default/script.js +91 -18
- data/theme/default/search.css +149 -0
- data/theme/default/style.css +193 -21
- data/theme/default/syntax-highlight.css +8 -0
- data/theme/lillia/style.css +45 -1
- metadata +161 -20
- data/README +0 -32
data/lib/bitclust/rrdparser.rb
CHANGED
|
@@ -12,6 +12,7 @@ require 'bitclust/compat'
|
|
|
12
12
|
require 'bitclust/preprocessor'
|
|
13
13
|
require 'bitclust/methodid'
|
|
14
14
|
require 'bitclust/methoddatabase'
|
|
15
|
+
require 'bitclust/methodsignature'
|
|
15
16
|
require 'bitclust/lineinput'
|
|
16
17
|
require 'bitclust/parseutils'
|
|
17
18
|
require 'bitclust/nameutils'
|
|
@@ -33,21 +34,24 @@ module BitClust
|
|
|
33
34
|
def RRDParser.parse(s, lib, params = {"version" => "1.9.0"})
|
|
34
35
|
parser = new(MethodDatabase.dummy(params))
|
|
35
36
|
if s.respond_to?(:to_io)
|
|
37
|
+
# @type var s: File
|
|
36
38
|
io = s.to_io
|
|
37
39
|
elsif s.respond_to?(:to_str)
|
|
40
|
+
# @type var s: String
|
|
38
41
|
s1 = s.to_str
|
|
39
42
|
require 'stringio'
|
|
40
43
|
io = StringIO.new(s1)
|
|
41
44
|
else
|
|
42
45
|
io = s
|
|
43
46
|
end
|
|
47
|
+
# @type var io: File | StringIO
|
|
44
48
|
l = parser.parse(io, lib, params)
|
|
45
49
|
return l, parser.db
|
|
46
50
|
end
|
|
47
51
|
|
|
48
52
|
def RRDParser.split_doc(source)
|
|
49
53
|
if m = /^=(\[a:.*?\])?( +(.*)|([^=].*))\r?\n/.match(source)
|
|
50
|
-
title = $3 || $4
|
|
54
|
+
title = $3 || $4 || raise
|
|
51
55
|
s = m.post_match
|
|
52
56
|
return title, s
|
|
53
57
|
end
|
|
@@ -141,7 +145,7 @@ module BitClust
|
|
|
141
145
|
unless m
|
|
142
146
|
parse_error "level-1 header syntax error", line
|
|
143
147
|
end
|
|
144
|
-
return m[1], isconst(m[2], line), isconst(m[3], line)
|
|
148
|
+
return (m[1] || raise), isconst((m[2]), line), isconst((m[3]), line)
|
|
145
149
|
end
|
|
146
150
|
|
|
147
151
|
def isconst(name, line)
|
|
@@ -159,7 +163,7 @@ module BitClust
|
|
|
159
163
|
read_extends f
|
|
160
164
|
read_includes f
|
|
161
165
|
f.skip_blank_lines
|
|
162
|
-
@context.klass
|
|
166
|
+
@context.klass&.source = f.break(/\A==?[^=]|\A---/).join('').rstrip
|
|
163
167
|
read_level2_blocks f
|
|
164
168
|
end
|
|
165
169
|
|
|
@@ -177,7 +181,7 @@ module BitClust
|
|
|
177
181
|
f.skip_blank_lines
|
|
178
182
|
read_extends f
|
|
179
183
|
f.skip_blank_lines
|
|
180
|
-
@context.klass
|
|
184
|
+
@context.klass&.source = f.break(/\A==?[^=]|\A---/).join('').rstrip
|
|
181
185
|
@context.visibility = :public
|
|
182
186
|
@context.type = :singleton_method
|
|
183
187
|
read_level2_blocks f
|
|
@@ -219,9 +223,11 @@ module BitClust
|
|
|
219
223
|
f.while_match(/\A==[^=]/) do |line|
|
|
220
224
|
case line.sub(/\A==/, '').strip
|
|
221
225
|
when /\A((?:public|private|protected)\s+)?(?:(class|singleton|instance)\s+)?methods?\z/i
|
|
222
|
-
@
|
|
226
|
+
# @type var visibility: :public | :private | :protected
|
|
227
|
+
visibility = ($1 || 'public').downcase.strip.intern
|
|
228
|
+
@context.visibility = visibility
|
|
223
229
|
t = ($2 || 'instance').downcase.sub(/class/, 'singleton')
|
|
224
|
-
@context.type = "#{t}_method".intern
|
|
230
|
+
@context.type = _ = "#{t}_method".intern
|
|
225
231
|
when /\AModule\s+Functions?\z/i
|
|
226
232
|
@context.module_function
|
|
227
233
|
when /\AConstants?\z/i
|
|
@@ -243,10 +249,10 @@ module BitClust
|
|
|
243
249
|
|
|
244
250
|
def concat_aliases(chunks)
|
|
245
251
|
return [] if chunks.empty?
|
|
246
|
-
result = [chunks.shift]
|
|
252
|
+
result = [chunks.shift || raise]
|
|
247
253
|
chunks.each do |chunk|
|
|
248
|
-
if result.last
|
|
249
|
-
result.last
|
|
254
|
+
if result.last&.alias?(chunk)
|
|
255
|
+
result.last&.unify chunk
|
|
250
256
|
else
|
|
251
257
|
result.push chunk
|
|
252
258
|
end
|
|
@@ -256,7 +262,7 @@ module BitClust
|
|
|
256
262
|
|
|
257
263
|
def read_chunks(f)
|
|
258
264
|
f.skip_blank_lines
|
|
259
|
-
result = []
|
|
265
|
+
result = [] #: Array[Chunk]
|
|
260
266
|
f.while_match(/\A---/) do |line|
|
|
261
267
|
f.ungets line
|
|
262
268
|
result.push read_chunk(f)
|
|
@@ -264,14 +270,31 @@ module BitClust
|
|
|
264
270
|
result
|
|
265
271
|
end
|
|
266
272
|
|
|
273
|
+
# シグネチャ行に続くメソッド属性行({: ...}。直前のシグネチャ行に束縛)
|
|
274
|
+
METHOD_ATTRIBUTE_LINE_RE = /\A\{:.*\}[ \t]*$/
|
|
275
|
+
|
|
267
276
|
def read_chunk(f)
|
|
268
|
-
|
|
277
|
+
# シグネチャ行の直後には {: ...} 属性行を置ける。属性行を挟んでも
|
|
278
|
+
# ひとつのチャンク(別名グループ)として読む
|
|
279
|
+
header = [] #: Array[String]
|
|
280
|
+
sig_lines = [] #: Array[String]
|
|
281
|
+
while f.next?
|
|
282
|
+
if /\A---/ =~ f.peek
|
|
283
|
+
line = f.gets or raise
|
|
284
|
+
header.push line
|
|
285
|
+
sig_lines.push line
|
|
286
|
+
elsif !sig_lines.empty? && METHOD_ATTRIBUTE_LINE_RE =~ f.peek
|
|
287
|
+
header.push(f.gets || raise)
|
|
288
|
+
else
|
|
289
|
+
break
|
|
290
|
+
end
|
|
291
|
+
end
|
|
269
292
|
body = f.break(/\A(?:---|={1,2}[^=])/)
|
|
270
293
|
src = (header + body).join('')
|
|
271
|
-
src.location =
|
|
272
|
-
sigs =
|
|
273
|
-
mainsig = check_chunk_signatures(sigs,
|
|
274
|
-
names = sigs.map {|s| s.name }.uniq.sort
|
|
294
|
+
src.location = sig_lines[0].location
|
|
295
|
+
sigs = sig_lines.map {|line| method_signature(line) }
|
|
296
|
+
mainsig = check_chunk_signatures(sigs, sig_lines[0])
|
|
297
|
+
names = sigs.map {|s| s.name }.compact.uniq.sort
|
|
275
298
|
Chunk.new(mainsig, names, src)
|
|
276
299
|
end
|
|
277
300
|
|
|
@@ -313,9 +336,12 @@ module BitClust
|
|
|
313
336
|
def method_signature(line)
|
|
314
337
|
case
|
|
315
338
|
when m = SIGNATURE.match(line)
|
|
316
|
-
|
|
339
|
+
klass, typemark_, name = m.captures
|
|
340
|
+
# @type var typemark: NameUtils::typemark
|
|
341
|
+
typemark = _ = typemark_
|
|
342
|
+
Signature.new(klass, typemark, name)
|
|
317
343
|
when m = GVAR.match(line)
|
|
318
|
-
Signature.new(nil, '$', m[1][1..-1])
|
|
344
|
+
Signature.new(nil, '$', (m[1] || raise)[1..-1])
|
|
319
345
|
else
|
|
320
346
|
parse_error "wrong method signature", line
|
|
321
347
|
end
|
|
@@ -366,10 +392,13 @@ module BitClust
|
|
|
366
392
|
|
|
367
393
|
def define_object(name, singleton_object_class, location: nil)
|
|
368
394
|
singleton_object_class = @db.get_class(singleton_object_class) if singleton_object_class
|
|
395
|
+
# steep:ignore:start
|
|
369
396
|
register_class :object, name, singleton_object_class, location: location
|
|
397
|
+
# steep:ignore:end
|
|
370
398
|
end
|
|
371
399
|
|
|
372
400
|
def register_class(type, name, superclass, location: nil)
|
|
401
|
+
name or raise
|
|
373
402
|
@klass = @db.open_class(name) {|c|
|
|
374
403
|
c.type = type
|
|
375
404
|
c.superclass = superclass
|
|
@@ -401,30 +430,31 @@ module BitClust
|
|
|
401
430
|
end
|
|
402
431
|
|
|
403
432
|
def include(name)
|
|
404
|
-
@klass
|
|
433
|
+
@klass&.include @db.get_class(name)
|
|
405
434
|
end
|
|
406
435
|
|
|
407
436
|
def extend(name)
|
|
408
|
-
@klass
|
|
437
|
+
@klass&.extend @db.get_class(name)
|
|
409
438
|
end
|
|
410
439
|
|
|
411
440
|
def dynamic_include(name)
|
|
412
|
-
@klass
|
|
441
|
+
@klass&.dynamic_include(@db.get_class(name), @library)
|
|
413
442
|
end
|
|
414
443
|
|
|
415
444
|
def dynamic_extend(name)
|
|
416
|
-
@klass
|
|
445
|
+
@klass&.dynamic_extend(@db.get_class(name), @library)
|
|
417
446
|
end
|
|
418
447
|
|
|
419
448
|
# Add a alias +name+ to the alias list.
|
|
420
449
|
def alias(name)
|
|
450
|
+
klass = @klass || raise
|
|
421
451
|
@db.open_class(name) do |c|
|
|
422
|
-
c.type =
|
|
452
|
+
c.type = klass.type
|
|
423
453
|
c.library = @library
|
|
424
|
-
c.aliasof =
|
|
425
|
-
c.source = "Alias of [[c:#{
|
|
454
|
+
c.aliasof = klass
|
|
455
|
+
c.source = "Alias of [[c:#{klass.name}]]\n"
|
|
426
456
|
@library.add_class c
|
|
427
|
-
|
|
457
|
+
klass.alias c
|
|
428
458
|
end
|
|
429
459
|
end
|
|
430
460
|
|
|
@@ -437,7 +467,7 @@ module BitClust
|
|
|
437
467
|
end
|
|
438
468
|
|
|
439
469
|
def special_variable
|
|
440
|
-
unless @klass and @klass
|
|
470
|
+
unless @klass and @klass&.name == 'Kernel'
|
|
441
471
|
raise "must not happen: type=special_variable but class!=Kernel"
|
|
442
472
|
end
|
|
443
473
|
@type = :special_variable
|
|
@@ -445,17 +475,30 @@ module BitClust
|
|
|
445
475
|
|
|
446
476
|
def signature
|
|
447
477
|
return nil unless @klass
|
|
448
|
-
Signature.new(@klass
|
|
478
|
+
Signature.new(@klass&.name, @type ? typename2mark(@type || raise) : nil, nil)
|
|
449
479
|
end
|
|
450
480
|
|
|
451
481
|
def define_method(chunk)
|
|
452
482
|
id = method_id(chunk)
|
|
483
|
+
attrs = method_attributes(chunk)
|
|
453
484
|
@db.open_method(id) {|m|
|
|
454
485
|
m.names = chunk.names.sort
|
|
455
|
-
m.kind =
|
|
486
|
+
m.kind = if attrs.flags.include?('undef')
|
|
487
|
+
:undefined
|
|
488
|
+
elsif attrs.flags.include?('nomethod')
|
|
489
|
+
:nomethod
|
|
490
|
+
else
|
|
491
|
+
@kind
|
|
492
|
+
end
|
|
493
|
+
# steep:ignore:start
|
|
456
494
|
m.visibility = @visibility || :public
|
|
495
|
+
# steep:ignore:end
|
|
457
496
|
m.source = chunk.source
|
|
458
497
|
m.source_location = chunk.source.location
|
|
498
|
+
attrs.since_until.each do |name, kv|
|
|
499
|
+
m.fill_since(name, kv['since'] || raise) if kv['since']
|
|
500
|
+
m.fill_until(name, kv['until'] || raise) if kv['until']
|
|
501
|
+
end
|
|
459
502
|
case @kind
|
|
460
503
|
when :added, :redefined
|
|
461
504
|
@library.add_method m
|
|
@@ -463,10 +506,133 @@ module BitClust
|
|
|
463
506
|
}
|
|
464
507
|
end
|
|
465
508
|
|
|
509
|
+
# 現在サポートするメソッド属性({: ...} 属性行のトークン)。
|
|
510
|
+
# - nomethod/undef: 裸語。kind はエントリ単位でしか持てないので、
|
|
511
|
+
# 別名(複数シグネチャ)のエントリでは全シグネチャに同じ属性が
|
|
512
|
+
# 付いていることを要求する
|
|
513
|
+
# - since="X"/until="X"(bitclust#132 P4): シグネチャ単位で束縛され、
|
|
514
|
+
# そのシグネチャの名前だけに適用される。nomethod/undef と違い、
|
|
515
|
+
# 別名ごとに異なる値を持てる(全シグネチャ一致は要求しない)のが
|
|
516
|
+
# 本来の用途。X は "3.2" のように数字とドットのみ。
|
|
517
|
+
# 空値(since="")は「明示的に不明」= バッジ非表示の指定で、メソッド
|
|
518
|
+
# 自体は昔からあるのにドキュメント追加が遅れ、バージョンラダーからの
|
|
519
|
+
# 自動算出が誤った版を出す場合の抑止に使う(空文字が記録されるため
|
|
520
|
+
# 算出値で上書きされず、表示側も空はバッジを出さない)
|
|
521
|
+
METHOD_ATTRIBUTES = %w[nomethod undef]
|
|
522
|
+
KV_METHOD_ATTRIBUTES = %w[since until]
|
|
523
|
+
KV_METHOD_ATTRIBUTE_VALUE_RE = /\A(?:\d+(?:\.\d+)*)?\z/
|
|
524
|
+
|
|
525
|
+
# md の `### def name ...`/`### module_function def name ...`/
|
|
526
|
+
# `### const name`/`### gvar $name` シグネチャ行を rd 形式
|
|
527
|
+
# (`--- name ...`)へ正規化するための接頭辞(MDParser::SIG_RE と
|
|
528
|
+
# 同じパターン)。属性の紐付け先となるメソッド名を
|
|
529
|
+
# MethodSignature.parse で取り出すために使う
|
|
530
|
+
MD_METHOD_SIG_PREFIX_RE = /\A### (?:module_function def |def |const |gvar )/
|
|
531
|
+
|
|
532
|
+
# method_attributes の返り値。
|
|
533
|
+
# - flags: 裸語トークン(nomethod/undef)の集合。エントリ単位の属性
|
|
534
|
+
# なので全シグネチャで同一であることを method_attributes が保証済み
|
|
535
|
+
# - since_until: シグネチャ単位で束縛された since="X"/until="X" を
|
|
536
|
+
# { メソッド名 => { "since" => "X", "until" => "X" } } の形で保持する
|
|
537
|
+
MethodAttributes = Struct.new(:flags, :since_until)
|
|
538
|
+
|
|
539
|
+
# kramdown Block IAL 風の {: ...} 属性行からメタデータを集める。
|
|
540
|
+
# 属性行は「直前のシグネチャ行のみ」に束縛される(kramdown と同じ解釈)。
|
|
541
|
+
# 裸語トークン(nomethod/undef)は kind がエントリ単位でしか持てないため
|
|
542
|
+
# 全シグネチャで同じであることを要求するが、since=/until= はシグネチャ
|
|
543
|
+
# 単位でそのままの名前に適用するため、この一致要求からは除外する。
|
|
544
|
+
# 本文に入ったら探索を打ち切る(コード例中の {: ...} を誤検出しないため)
|
|
545
|
+
def method_attributes(chunk)
|
|
546
|
+
per_sig = [] #: Array[Array[String]]
|
|
547
|
+
since_until = {} #: Hash[String, Hash[String, String]]
|
|
548
|
+
current_name = nil #: String?
|
|
549
|
+
current_keys = nil #: Hash[String, bool]?
|
|
550
|
+
chunk.source.each_line do |line_|
|
|
551
|
+
line = line_.chomp
|
|
552
|
+
case line
|
|
553
|
+
when /\A---\s/, /\A\#\#\#\s/
|
|
554
|
+
per_sig.push []
|
|
555
|
+
current_name = method_attribute_target_name(line)
|
|
556
|
+
current_keys = {}
|
|
557
|
+
when /\A\{:(.*)\}[ \t]*\z/
|
|
558
|
+
break if per_sig.empty?
|
|
559
|
+
($1 || raise).strip.split(/\s+/).each do |token|
|
|
560
|
+
if METHOD_ATTRIBUTES.include?(token)
|
|
561
|
+
per_sig.last&.push token
|
|
562
|
+
else
|
|
563
|
+
key, value = parse_kv_method_attribute(token, chunk)
|
|
564
|
+
keys = current_keys or raise
|
|
565
|
+
if keys.key?(key)
|
|
566
|
+
raise ParseError,
|
|
567
|
+
"#{chunk.source.location}: duplicate method attribute #{key.inspect} on the same signature"
|
|
568
|
+
end
|
|
569
|
+
keys[key] = true
|
|
570
|
+
name = current_name or
|
|
571
|
+
raise ParseError,
|
|
572
|
+
"#{chunk.source.location}: cannot determine the signature name to bind method attribute #{token.inspect} to"
|
|
573
|
+
(since_until[name] ||= {})[key] = value
|
|
574
|
+
end
|
|
575
|
+
end
|
|
576
|
+
else
|
|
577
|
+
break
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
return MethodAttributes.new([], {}) if per_sig.empty?
|
|
581
|
+
sets = per_sig.map {|a| a.uniq.sort }
|
|
582
|
+
unless sets.uniq.size == 1
|
|
583
|
+
raise ParseError,
|
|
584
|
+
"#{chunk.source.location}: method attributes must be the same on every signature of an entry: #{sets.inspect}"
|
|
585
|
+
end
|
|
586
|
+
# 紐付け先の名前がエントリの names と食い違ったら黙って捨てずに
|
|
587
|
+
# エラーにする(名前導出の規約ずれを CI で検出するための安全網)
|
|
588
|
+
since_until.each_key do |name|
|
|
589
|
+
unless chunk.names.include?(name)
|
|
590
|
+
raise ParseError,
|
|
591
|
+
"#{chunk.source.location}: since/until attribute bound to unknown signature name #{name.inspect} (entry names: #{chunk.names.join(', ')})"
|
|
592
|
+
end
|
|
593
|
+
end
|
|
594
|
+
MethodAttributes.new(sets.first || raise, since_until)
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
# since="X"/until="X" 形式のトークンを解析して [key, value] を返す。
|
|
598
|
+
# 受理する形式以外(キーが不明・値が非引用・値が数字とドット以外を
|
|
599
|
+
# 含む等)はすべて ParseError にする
|
|
600
|
+
def parse_kv_method_attribute(token, chunk)
|
|
601
|
+
m = /\A(\w+)=(.*)\z/.match(token)
|
|
602
|
+
if m
|
|
603
|
+
key = m[1] || raise
|
|
604
|
+
raw_value = m[2] || raise
|
|
605
|
+
quoted = /\A"(.*)"\z/.match(raw_value)
|
|
606
|
+
if quoted && KV_METHOD_ATTRIBUTES.include?(key) && KV_METHOD_ATTRIBUTE_VALUE_RE.match?(quoted[1] || raise)
|
|
607
|
+
return [key, (quoted[1] || raise)]
|
|
608
|
+
end
|
|
609
|
+
end
|
|
610
|
+
raise ParseError,
|
|
611
|
+
"#{chunk.source.location}: invalid method attribute #{token.inspect} " \
|
|
612
|
+
"(supported: #{METHOD_ATTRIBUTES.join(', ')}, " \
|
|
613
|
+
"#{KV_METHOD_ATTRIBUTES.map {|k| %Q(#{k}="X") }.join('/')} where X is digits and dots, e.g. since=\"3.2\", " \
|
|
614
|
+
"or empty for explicitly-unknown, e.g. since=\"\")"
|
|
615
|
+
end
|
|
616
|
+
private :parse_kv_method_attribute
|
|
617
|
+
|
|
618
|
+
# シグネチャ行(rd の `--- ...` / md の `### def ...` 等)から、
|
|
619
|
+
# 属性の紐付け先となるメソッド名を取り出す。
|
|
620
|
+
# 特殊変数のシグネチャ名は "$SAFE" のように $ 付きだが、エントリの
|
|
621
|
+
# names は先頭の $ を除いた形("SAFE"。"$$" なら "$")で格納される
|
|
622
|
+
# (method_signature の Signature.new(nil, '$', name[1..-1]) と同じ規約)
|
|
623
|
+
# ため、since_by_name のキーも names に合わせて $ を1つ剥がす
|
|
624
|
+
def method_attribute_target_name(line)
|
|
625
|
+
normalized = line.sub(MD_METHOD_SIG_PREFIX_RE, '--- ')
|
|
626
|
+
MethodSignature.parse(normalized).name.sub(/\A\$/, '')
|
|
627
|
+
rescue ParseError
|
|
628
|
+
nil
|
|
629
|
+
end
|
|
630
|
+
private :method_attribute_target_name
|
|
631
|
+
|
|
466
632
|
def method_id(chunk)
|
|
467
633
|
id = MethodID.new
|
|
468
634
|
id.library = @library
|
|
469
|
-
id.klass = chunk.signature.klass ? @db.get_class(chunk.signature.klass) : @klass
|
|
635
|
+
id.klass = chunk.signature.klass ? @db.get_class(chunk.signature.klass) : (@klass || raise)
|
|
470
636
|
id.type = chunk.signature.typename || @type
|
|
471
637
|
id.name = chunk.names.sort.first
|
|
472
638
|
id
|
|
@@ -521,7 +687,7 @@ module BitClust
|
|
|
521
687
|
end
|
|
522
688
|
|
|
523
689
|
def typename
|
|
524
|
-
typemark2name(@type)
|
|
690
|
+
typemark2name(_ = @type)
|
|
525
691
|
end
|
|
526
692
|
|
|
527
693
|
def same_type?(other)
|
data/lib/bitclust/runner.rb
CHANGED
|
@@ -3,7 +3,7 @@ require 'pathname'
|
|
|
3
3
|
require 'optparse'
|
|
4
4
|
|
|
5
5
|
unless Object.const_defined?(:Encoding)
|
|
6
|
-
$KCODE = 'UTF-8'
|
|
6
|
+
$KCODE = 'UTF-8' # steep:ignore
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def libdir
|
|
@@ -50,6 +50,7 @@ Subcommands(for users):
|
|
|
50
50
|
list List libraries/classes/methods in database.
|
|
51
51
|
lookup Lookup a library/class/method from database.
|
|
52
52
|
search Search classes/methods from database.
|
|
53
|
+
server Run HTTP server to browse the reference manual.
|
|
53
54
|
|
|
54
55
|
Subcommands(for developers):
|
|
55
56
|
ancestors Compare class/module's ancestors between Ruby and DB.
|
|
@@ -60,9 +61,12 @@ Subcommands(for developers):
|
|
|
60
61
|
extract Extract method entries from source file.
|
|
61
62
|
classes Display defined classes for all ruby.
|
|
62
63
|
methods Display defined methods for all ruby.
|
|
64
|
+
methodsince Fill per-name since/until from a version ladder of DBs.
|
|
65
|
+
checklink Report broken [[c:]]/[[m:]]/[[lib:]]/[[d:]]/[[f:]] links.
|
|
63
66
|
|
|
64
67
|
Subcommands(for packagers):
|
|
65
68
|
statichtml Generate static HTML files.
|
|
69
|
+
searchpage Generate a static cross-version search page.
|
|
66
70
|
epub Generate EPUB file.
|
|
67
71
|
chm Generate static HTML files for CHM.
|
|
68
72
|
|
|
@@ -96,6 +100,7 @@ Global Options:
|
|
|
96
100
|
'setup' => BitClust::Subcommands::SetupCommand.new,
|
|
97
101
|
'server' => BitClust::Subcommands::ServerCommand.new,
|
|
98
102
|
'statichtml' => BitClust::Subcommands::StatichtmlCommand.new,
|
|
103
|
+
'searchpage' => BitClust::Subcommands::SearchpageCommand.new,
|
|
99
104
|
'htmlfile' => BitClust::Subcommands::HtmlfileCommand.new,
|
|
100
105
|
'chm' => BitClust::Subcommands::ChmCommand.new,
|
|
101
106
|
'epub' => BitClust::Subcommands::EPUBCommand.new,
|
|
@@ -104,6 +109,8 @@ Global Options:
|
|
|
104
109
|
'extract' => BitClust::Subcommands::ExtractCommand.new,
|
|
105
110
|
'classes' => BitClust::Subcommands::ClassesCommand.new,
|
|
106
111
|
'methods' => BitClust::Subcommands::MethodsCommand.new,
|
|
112
|
+
'methodsince' => BitClust::Subcommands::MethodsinceCommand.new,
|
|
113
|
+
'checklink' => BitClust::Subcommands::ChecklinkCommand.new,
|
|
107
114
|
}
|
|
108
115
|
end
|
|
109
116
|
|
|
@@ -134,6 +141,14 @@ Global Options:
|
|
|
134
141
|
@version ||= config[:default_version]
|
|
135
142
|
@prefix ||= "#{config[:database_prefix]}-#{@version}"
|
|
136
143
|
end
|
|
144
|
+
# DB 必須のサブコマンドで --database も ~/.bitclust/config も無ければ
|
|
145
|
+
# 案内付きで中断する。needs_database? が false のサブコマンドと、
|
|
146
|
+
# 自前で DB を探す search (Searcher) は対象外
|
|
147
|
+
needs_database = cmd.respond_to?(:needs_database?) && cmd.needs_database?
|
|
148
|
+
if needs_database && !@prefix
|
|
149
|
+
error "no database given. Use --database (-d) option or ~/.bitclust/config"
|
|
150
|
+
end
|
|
151
|
+
# @type var options: Subcommand::options
|
|
137
152
|
options = {
|
|
138
153
|
:prefix => @prefix,
|
|
139
154
|
:capi => @capi
|
|
@@ -145,7 +160,7 @@ Global Options:
|
|
|
145
160
|
end
|
|
146
161
|
|
|
147
162
|
def load_config
|
|
148
|
-
home_directory = Pathname(ENV
|
|
163
|
+
home_directory = Pathname(ENV.fetch('HOME'))
|
|
149
164
|
config_path = home_directory + ".bitclust/config"
|
|
150
165
|
if config_path.exist?
|
|
151
166
|
YAML.load_file(config_path)
|
data/lib/bitclust/screen.rb
CHANGED
|
@@ -13,6 +13,7 @@ require 'bitclust/methodsignature'
|
|
|
13
13
|
require 'bitclust/htmlutils'
|
|
14
14
|
require 'bitclust/nameutils'
|
|
15
15
|
require 'bitclust/messagecatalog'
|
|
16
|
+
require 'bitclust/version_badges'
|
|
16
17
|
require 'erb'
|
|
17
18
|
require 'json'
|
|
18
19
|
require 'stringio'
|
|
@@ -37,6 +38,7 @@ module BitClust
|
|
|
37
38
|
private :default_message_catalog
|
|
38
39
|
|
|
39
40
|
def entry_screen(entry, opt)
|
|
41
|
+
@conf[:target_version] = opt[:target_version]
|
|
40
42
|
new_screen(Screen.for_entry(entry), entry, opt)
|
|
41
43
|
end
|
|
42
44
|
|
|
@@ -141,7 +143,7 @@ module BitClust
|
|
|
141
143
|
end
|
|
142
144
|
|
|
143
145
|
def method_url(spec)
|
|
144
|
-
cname, tmark, mname =
|
|
146
|
+
cname, tmark, mname = split_method_spec(spec)
|
|
145
147
|
"#{@cgi_url}/method/#{classname2id(cname)}/#{typemark2char(tmark)}/#{encodename_url(mname)}"
|
|
146
148
|
end
|
|
147
149
|
|
|
@@ -188,7 +190,7 @@ module BitClust
|
|
|
188
190
|
private
|
|
189
191
|
|
|
190
192
|
def preproc(template)
|
|
191
|
-
template.gsub(/^\.include ([\w\-]+)/) { load($1) }
|
|
193
|
+
template.gsub(/^\.include ([\w\-]+)/) { load($1 || raise) }
|
|
192
194
|
end
|
|
193
195
|
end
|
|
194
196
|
|
|
@@ -226,7 +228,7 @@ module BitClust
|
|
|
226
228
|
<body>
|
|
227
229
|
<h1>Error</h1>
|
|
228
230
|
<pre>#{escape_html(@error.message)} (#{escape_html(@error.class.name)})
|
|
229
|
-
#{@error.backtrace
|
|
231
|
+
#{@error.backtrace&.map {|s| escape_html(s) }&.join("\n")}</pre>
|
|
230
232
|
</body>
|
|
231
233
|
</html>
|
|
232
234
|
EndHTML
|
|
@@ -264,6 +266,7 @@ module BitClust
|
|
|
264
266
|
class TemplateScreen < Screen
|
|
265
267
|
include Translatable
|
|
266
268
|
include HTMLUtils
|
|
269
|
+
include VersionBadges
|
|
267
270
|
|
|
268
271
|
def initialize(h)
|
|
269
272
|
@urlmapper = h[:urlmapper]
|
|
@@ -313,6 +316,19 @@ module BitClust
|
|
|
313
316
|
%Q(<meta name="description" content="">)
|
|
314
317
|
end
|
|
315
318
|
|
|
319
|
+
# True when the build was told (via statichtml --eol-warning) that the
|
|
320
|
+
# documented Ruby version is no longer maintained.
|
|
321
|
+
def eol_warning?
|
|
322
|
+
!!@conf[:eol_warning]
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# URL of the ruby.wasm module used by the in-browser RUN button on Ruby
|
|
326
|
+
# sample code (statichtml --run-ruby-wasm). nil disables the feature.
|
|
327
|
+
def run_ruby_wasm_url
|
|
328
|
+
url = @conf[:run_ruby_wasm] #: String?
|
|
329
|
+
url
|
|
330
|
+
end
|
|
331
|
+
|
|
316
332
|
private
|
|
317
333
|
|
|
318
334
|
def default_encoding
|
|
@@ -357,6 +373,12 @@ module BitClust
|
|
|
357
373
|
@urlmapper.custom_js_url(js)
|
|
358
374
|
end
|
|
359
375
|
|
|
376
|
+
# Relative path from the current page to the site root, used as the
|
|
377
|
+
# prefix for client-side search result links in statichtml output.
|
|
378
|
+
def html_base
|
|
379
|
+
@urlmapper.respond_to?(:bitclust_html_base) ? @urlmapper.bitclust_html_base : '.'
|
|
380
|
+
end
|
|
381
|
+
|
|
360
382
|
def favicon_url
|
|
361
383
|
@urlmapper.favicon_url
|
|
362
384
|
end
|
|
@@ -453,16 +475,55 @@ module BitClust
|
|
|
453
475
|
rdcompiler().compile(src)
|
|
454
476
|
end
|
|
455
477
|
|
|
478
|
+
# DB が Markdown ソース(update --markdowntree のネイティブパース)なら
|
|
479
|
+
# MDCompiler(GFM モード)で描画する
|
|
480
|
+
def markdown_source?
|
|
481
|
+
db = @conf[:database]
|
|
482
|
+
db && db.properties['source_format'] == 'markdown'
|
|
483
|
+
end
|
|
484
|
+
|
|
456
485
|
def rdcompiler
|
|
457
|
-
opt = {:catalog => message_catalog()}.merge(@conf)
|
|
458
|
-
|
|
486
|
+
opt = {:catalog => message_catalog()}.merge(@conf) #: RDCompiler::option
|
|
487
|
+
if markdown_source?
|
|
488
|
+
require 'bitclust/mdcompiler'
|
|
489
|
+
gfm_opt = opt.merge(:gfm => true) #: RDCompiler::option
|
|
490
|
+
MDCompiler.new(@urlmapper, @hlevel, gfm_opt)
|
|
491
|
+
else
|
|
492
|
+
RDCompiler.new(@urlmapper, @hlevel, opt)
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
# bitclust#132 P3: data/bitclust/template/class の署名欄で使う。この
|
|
497
|
+
# テンプレートは compile_method(RDCompiler)を経由せず自前でシグネチャを
|
|
498
|
+
# 描画するので、別名(alias)ごとに自分の since/until バッジを付けた
|
|
499
|
+
# <a><code>...</code></a> を1行ずつ組み立て、<br> で連結する
|
|
500
|
+
def method_row_signature_html(m, sigs)
|
|
501
|
+
sigs.map {|sig|
|
|
502
|
+
%Q(<a href="#{h(method_url(m.spec_string))}"><code>#{h(sig.friendly_string)}</code></a>) +
|
|
503
|
+
row_version_badges(m, sig.name)
|
|
504
|
+
}.join('<br>')
|
|
459
505
|
end
|
|
460
506
|
|
|
461
507
|
def foreach_method_chunk(src)
|
|
508
|
+
require 'bitclust/mdparser' if markdown_source?
|
|
509
|
+
sig_re = markdown_source? ? MDParser::SIG_RE : /\A---/
|
|
462
510
|
f = LineInput.for_string(src)
|
|
463
511
|
while f.next?
|
|
464
|
-
|
|
465
|
-
|
|
512
|
+
# シグネチャ行の直後の {: ...} メソッド属性行はシグネチャブロックの
|
|
513
|
+
# 一部として読み飛ばす(シグネチャにも本文にも含めない)
|
|
514
|
+
sigs = [] #: Array[MethodSignature]
|
|
515
|
+
while f.next?
|
|
516
|
+
if sig_re =~ f.peek
|
|
517
|
+
line = f.gets or raise
|
|
518
|
+
line = line.sub(sig_re, '--- ') if markdown_source?
|
|
519
|
+
sigs.push MethodSignature.parse(line.rstrip)
|
|
520
|
+
elsif !sigs.empty? && /\A\{:.*\}[ \t]*$/ =~ f.peek
|
|
521
|
+
f.gets
|
|
522
|
+
else
|
|
523
|
+
break
|
|
524
|
+
end
|
|
525
|
+
end
|
|
526
|
+
body = f.break(sig_re).join.split(/\n\n/, 2).first || ''
|
|
466
527
|
yield sigs, body
|
|
467
528
|
end
|
|
468
529
|
end
|
|
@@ -541,14 +602,16 @@ module BitClust
|
|
|
541
602
|
def draw_tree(cs, &block)
|
|
542
603
|
return if cs.empty?
|
|
543
604
|
if cs.first.class?
|
|
544
|
-
tree = cs.group_by{|c| c.superclass }
|
|
605
|
+
tree = cs.group_by{|c| c.superclass } # steep:ignore
|
|
545
606
|
tree.each {|key, list| list.sort_by!{|c| c ? c.name : "" } }
|
|
546
607
|
roots = tree.keys.select{|c| !c || !cs.include?(c) }
|
|
547
|
-
roots.map!{|c| tree[c] }.flatten!
|
|
608
|
+
roots.map!{|c| tree[c] }.flatten! # steep:ignore
|
|
548
609
|
else
|
|
549
610
|
tree = {}
|
|
550
611
|
roots = cs
|
|
551
612
|
end
|
|
613
|
+
# @type var roots: Array[ClassEntry]
|
|
614
|
+
# @type var tree: Hash[ClassEntry, Array[ClassEntry]]
|
|
552
615
|
draw_treed_entries(roots, tree, &block)
|
|
553
616
|
end
|
|
554
617
|
|
|
@@ -683,8 +746,14 @@ module BitClust
|
|
|
683
746
|
end
|
|
684
747
|
|
|
685
748
|
def rdcompiler
|
|
686
|
-
h = {:force => true, :catalog => message_catalog() }.merge(@conf)
|
|
687
|
-
|
|
749
|
+
h = {:force => true, :catalog => message_catalog() }.merge(@conf) #: RDCompiler::option
|
|
750
|
+
if markdown_source?
|
|
751
|
+
require 'bitclust/mdcompiler'
|
|
752
|
+
gfm_opt = h.merge(:gfm => true) #: RDCompiler::option
|
|
753
|
+
MDCompiler.new(@urlmapper, @hlevel, gfm_opt)
|
|
754
|
+
else
|
|
755
|
+
RDCompiler.new(@urlmapper, @hlevel, h)
|
|
756
|
+
end
|
|
688
757
|
end
|
|
689
758
|
|
|
690
759
|
def current_url
|