bitclust-core 1.2.6 → 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 +49 -5
- 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 +17 -17
- data/lib/bitclust/capi_converter.rb +20 -0
- data/lib/bitclust/classentry.rb +68 -17
- data/lib/bitclust/compat.rb +3 -2
- data/lib/bitclust/completion.rb +59 -27
- data/lib/bitclust/crossrubyutils.rb +9 -5
- data/lib/bitclust/database.rb +29 -8
- data/lib/bitclust/doc_converter.rb +63 -0
- data/lib/bitclust/docentry.rb +2 -1
- data/lib/bitclust/entity_splitter.rb +221 -0
- data/lib/bitclust/entry.rb +48 -7
- data/lib/bitclust/exception.rb +1 -0
- data/lib/bitclust/functiondatabase.rb +14 -1
- data/lib/bitclust/functionentry.rb +6 -1
- data/lib/bitclust/functionreferenceparser.rb +5 -4
- data/lib/bitclust/generators/epub.rb +11 -3
- data/lib/bitclust/htmlutils.rb +2 -1
- data/lib/bitclust/include_graph.rb +375 -0
- data/lib/bitclust/include_pruner.rb +144 -0
- data/lib/bitclust/interface.rb +2 -1
- data/lib/bitclust/libraryentry.rb +9 -7
- data/lib/bitclust/lineinput.rb +8 -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 +4 -3
- data/lib/bitclust/method_since_calculator.rb +117 -0
- data/lib/bitclust/methoddatabase.rb +125 -14
- data/lib/bitclust/methodentry.rb +130 -7
- data/lib/bitclust/methodid.rb +13 -11
- data/lib/bitclust/methodsignature.rb +11 -4
- data/lib/bitclust/nameutils.rb +42 -22
- data/lib/bitclust/parseutils.rb +1 -0
- data/lib/bitclust/preprocessor.rb +70 -32
- data/lib/bitclust/progress_bar.rb +1 -0
- data/lib/bitclust/rdcompiler.rb +197 -96
- data/lib/bitclust/refsdatabase.rb +24 -8
- data/lib/bitclust/reloadable_request_handler.rb +72 -0
- data/lib/bitclust/requesthandler.rb +22 -16
- data/lib/bitclust/ridatabase.rb +11 -4
- data/lib/bitclust/rrd_to_markdown.rb +925 -0
- data/lib/bitclust/rrdparser.rb +197 -30
- data/lib/bitclust/runner.rb +18 -2
- data/lib/bitclust/screen.rb +81 -11
- data/lib/bitclust/search_index_generator.rb +288 -0
- data/lib/bitclust/searcher.rb +72 -22
- data/lib/bitclust/server.rb +6 -1
- data/lib/bitclust/silent_progress_bar.rb +1 -0
- data/lib/bitclust/simplesearcher.rb +25 -12
- data/lib/bitclust/subcommand.rb +8 -1
- data/lib/bitclust/subcommands/ancestors_command.rb +10 -2
- data/lib/bitclust/subcommands/checklink_command.rb +54 -0
- data/lib/bitclust/subcommands/chm_command.rb +16 -7
- data/lib/bitclust/subcommands/classes_command.rb +6 -0
- data/lib/bitclust/subcommands/epub_command.rb +1 -0
- data/lib/bitclust/subcommands/extract_command.rb +6 -0
- data/lib/bitclust/subcommands/htmlfile_command.rb +22 -7
- data/lib/bitclust/subcommands/init_command.rb +5 -1
- data/lib/bitclust/subcommands/list_command.rb +13 -4
- data/lib/bitclust/subcommands/lookup_command.rb +29 -9
- data/lib/bitclust/subcommands/methods_command.rb +11 -2
- data/lib/bitclust/subcommands/methodsince_command.rb +80 -0
- data/lib/bitclust/subcommands/preproc_command.rb +6 -0
- data/lib/bitclust/subcommands/property_command.rb +3 -1
- data/lib/bitclust/subcommands/query_command.rb +1 -0
- data/lib/bitclust/subcommands/searchpage_command.rb +102 -0
- data/lib/bitclust/subcommands/server_command.rb +16 -10
- data/lib/bitclust/subcommands/setup_command.rb +25 -10
- data/lib/bitclust/subcommands/statichtml_command.rb +132 -6
- data/lib/bitclust/subcommands/update_command.rb +111 -2
- data/lib/bitclust/syntax_highlighter.rb +52 -5
- data/lib/bitclust/textutils.rb +4 -3
- data/lib/bitclust/version.rb +2 -1
- data/lib/bitclust/version_badges.rb +90 -0
- data/lib/bitclust/whole_file_gate.rb +137 -0
- data/lib/bitclust.rb +1 -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 +429 -27
- 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 +153 -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 +178 -21
- data/README +0 -32
data/lib/bitclust/rrdparser.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
#
|
|
2
3
|
# bitclust/rrdparser.rb
|
|
3
4
|
#
|
|
@@ -11,6 +12,7 @@ require 'bitclust/compat'
|
|
|
11
12
|
require 'bitclust/preprocessor'
|
|
12
13
|
require 'bitclust/methodid'
|
|
13
14
|
require 'bitclust/methoddatabase'
|
|
15
|
+
require 'bitclust/methodsignature'
|
|
14
16
|
require 'bitclust/lineinput'
|
|
15
17
|
require 'bitclust/parseutils'
|
|
16
18
|
require 'bitclust/nameutils'
|
|
@@ -32,21 +34,24 @@ module BitClust
|
|
|
32
34
|
def RRDParser.parse(s, lib, params = {"version" => "1.9.0"})
|
|
33
35
|
parser = new(MethodDatabase.dummy(params))
|
|
34
36
|
if s.respond_to?(:to_io)
|
|
37
|
+
# @type var s: File
|
|
35
38
|
io = s.to_io
|
|
36
39
|
elsif s.respond_to?(:to_str)
|
|
40
|
+
# @type var s: String
|
|
37
41
|
s1 = s.to_str
|
|
38
42
|
require 'stringio'
|
|
39
43
|
io = StringIO.new(s1)
|
|
40
44
|
else
|
|
41
45
|
io = s
|
|
42
46
|
end
|
|
47
|
+
# @type var io: File | StringIO
|
|
43
48
|
l = parser.parse(io, lib, params)
|
|
44
49
|
return l, parser.db
|
|
45
50
|
end
|
|
46
51
|
|
|
47
52
|
def RRDParser.split_doc(source)
|
|
48
53
|
if m = /^=(\[a:.*?\])?( +(.*)|([^=].*))\r?\n/.match(source)
|
|
49
|
-
title = $3 || $4
|
|
54
|
+
title = $3 || $4 || raise
|
|
50
55
|
s = m.post_match
|
|
51
56
|
return title, s
|
|
52
57
|
end
|
|
@@ -140,7 +145,7 @@ module BitClust
|
|
|
140
145
|
unless m
|
|
141
146
|
parse_error "level-1 header syntax error", line
|
|
142
147
|
end
|
|
143
|
-
return m[1], isconst(m[2], line), isconst(m[3], line)
|
|
148
|
+
return (m[1] || raise), isconst((m[2]), line), isconst((m[3]), line)
|
|
144
149
|
end
|
|
145
150
|
|
|
146
151
|
def isconst(name, line)
|
|
@@ -158,7 +163,7 @@ module BitClust
|
|
|
158
163
|
read_extends f
|
|
159
164
|
read_includes f
|
|
160
165
|
f.skip_blank_lines
|
|
161
|
-
@context.klass
|
|
166
|
+
@context.klass&.source = f.break(/\A==?[^=]|\A---/).join('').rstrip
|
|
162
167
|
read_level2_blocks f
|
|
163
168
|
end
|
|
164
169
|
|
|
@@ -176,7 +181,7 @@ module BitClust
|
|
|
176
181
|
f.skip_blank_lines
|
|
177
182
|
read_extends f
|
|
178
183
|
f.skip_blank_lines
|
|
179
|
-
@context.klass
|
|
184
|
+
@context.klass&.source = f.break(/\A==?[^=]|\A---/).join('').rstrip
|
|
180
185
|
@context.visibility = :public
|
|
181
186
|
@context.type = :singleton_method
|
|
182
187
|
read_level2_blocks f
|
|
@@ -218,9 +223,11 @@ module BitClust
|
|
|
218
223
|
f.while_match(/\A==[^=]/) do |line|
|
|
219
224
|
case line.sub(/\A==/, '').strip
|
|
220
225
|
when /\A((?:public|private|protected)\s+)?(?:(class|singleton|instance)\s+)?methods?\z/i
|
|
221
|
-
@
|
|
226
|
+
# @type var visibility: :public | :private | :protected
|
|
227
|
+
visibility = ($1 || 'public').downcase.strip.intern
|
|
228
|
+
@context.visibility = visibility
|
|
222
229
|
t = ($2 || 'instance').downcase.sub(/class/, 'singleton')
|
|
223
|
-
@context.type = "#{t}_method".intern
|
|
230
|
+
@context.type = _ = "#{t}_method".intern
|
|
224
231
|
when /\AModule\s+Functions?\z/i
|
|
225
232
|
@context.module_function
|
|
226
233
|
when /\AConstants?\z/i
|
|
@@ -242,10 +249,10 @@ module BitClust
|
|
|
242
249
|
|
|
243
250
|
def concat_aliases(chunks)
|
|
244
251
|
return [] if chunks.empty?
|
|
245
|
-
result = [chunks.shift]
|
|
252
|
+
result = [chunks.shift || raise]
|
|
246
253
|
chunks.each do |chunk|
|
|
247
|
-
if result.last
|
|
248
|
-
result.last
|
|
254
|
+
if result.last&.alias?(chunk)
|
|
255
|
+
result.last&.unify chunk
|
|
249
256
|
else
|
|
250
257
|
result.push chunk
|
|
251
258
|
end
|
|
@@ -255,7 +262,7 @@ module BitClust
|
|
|
255
262
|
|
|
256
263
|
def read_chunks(f)
|
|
257
264
|
f.skip_blank_lines
|
|
258
|
-
result = []
|
|
265
|
+
result = [] #: Array[Chunk]
|
|
259
266
|
f.while_match(/\A---/) do |line|
|
|
260
267
|
f.ungets line
|
|
261
268
|
result.push read_chunk(f)
|
|
@@ -263,14 +270,31 @@ module BitClust
|
|
|
263
270
|
result
|
|
264
271
|
end
|
|
265
272
|
|
|
273
|
+
# シグネチャ行に続くメソッド属性行({: ...}。直前のシグネチャ行に束縛)
|
|
274
|
+
METHOD_ATTRIBUTE_LINE_RE = /\A\{:.*\}[ \t]*$/
|
|
275
|
+
|
|
266
276
|
def read_chunk(f)
|
|
267
|
-
|
|
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
|
|
268
292
|
body = f.break(/\A(?:---|={1,2}[^=])/)
|
|
269
293
|
src = (header + body).join('')
|
|
270
|
-
src.location =
|
|
271
|
-
sigs =
|
|
272
|
-
mainsig = check_chunk_signatures(sigs,
|
|
273
|
-
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
|
|
274
298
|
Chunk.new(mainsig, names, src)
|
|
275
299
|
end
|
|
276
300
|
|
|
@@ -312,9 +336,12 @@ module BitClust
|
|
|
312
336
|
def method_signature(line)
|
|
313
337
|
case
|
|
314
338
|
when m = SIGNATURE.match(line)
|
|
315
|
-
|
|
339
|
+
klass, typemark_, name = m.captures
|
|
340
|
+
# @type var typemark: NameUtils::typemark
|
|
341
|
+
typemark = _ = typemark_
|
|
342
|
+
Signature.new(klass, typemark, name)
|
|
316
343
|
when m = GVAR.match(line)
|
|
317
|
-
Signature.new(nil, '$', m[1][1..-1])
|
|
344
|
+
Signature.new(nil, '$', (m[1] || raise)[1..-1])
|
|
318
345
|
else
|
|
319
346
|
parse_error "wrong method signature", line
|
|
320
347
|
end
|
|
@@ -365,10 +392,13 @@ module BitClust
|
|
|
365
392
|
|
|
366
393
|
def define_object(name, singleton_object_class, location: nil)
|
|
367
394
|
singleton_object_class = @db.get_class(singleton_object_class) if singleton_object_class
|
|
395
|
+
# steep:ignore:start
|
|
368
396
|
register_class :object, name, singleton_object_class, location: location
|
|
397
|
+
# steep:ignore:end
|
|
369
398
|
end
|
|
370
399
|
|
|
371
400
|
def register_class(type, name, superclass, location: nil)
|
|
401
|
+
name or raise
|
|
372
402
|
@klass = @db.open_class(name) {|c|
|
|
373
403
|
c.type = type
|
|
374
404
|
c.superclass = superclass
|
|
@@ -400,30 +430,31 @@ module BitClust
|
|
|
400
430
|
end
|
|
401
431
|
|
|
402
432
|
def include(name)
|
|
403
|
-
@klass
|
|
433
|
+
@klass&.include @db.get_class(name)
|
|
404
434
|
end
|
|
405
435
|
|
|
406
436
|
def extend(name)
|
|
407
|
-
@klass
|
|
437
|
+
@klass&.extend @db.get_class(name)
|
|
408
438
|
end
|
|
409
439
|
|
|
410
440
|
def dynamic_include(name)
|
|
411
|
-
@klass
|
|
441
|
+
@klass&.dynamic_include(@db.get_class(name), @library)
|
|
412
442
|
end
|
|
413
443
|
|
|
414
444
|
def dynamic_extend(name)
|
|
415
|
-
@klass
|
|
445
|
+
@klass&.dynamic_extend(@db.get_class(name), @library)
|
|
416
446
|
end
|
|
417
447
|
|
|
418
448
|
# Add a alias +name+ to the alias list.
|
|
419
449
|
def alias(name)
|
|
450
|
+
klass = @klass || raise
|
|
420
451
|
@db.open_class(name) do |c|
|
|
421
|
-
c.type =
|
|
452
|
+
c.type = klass.type
|
|
422
453
|
c.library = @library
|
|
423
|
-
c.aliasof =
|
|
424
|
-
c.source = "Alias of [[c:#{
|
|
454
|
+
c.aliasof = klass
|
|
455
|
+
c.source = "Alias of [[c:#{klass.name}]]\n"
|
|
425
456
|
@library.add_class c
|
|
426
|
-
|
|
457
|
+
klass.alias c
|
|
427
458
|
end
|
|
428
459
|
end
|
|
429
460
|
|
|
@@ -436,7 +467,7 @@ module BitClust
|
|
|
436
467
|
end
|
|
437
468
|
|
|
438
469
|
def special_variable
|
|
439
|
-
unless @klass and @klass
|
|
470
|
+
unless @klass and @klass&.name == 'Kernel'
|
|
440
471
|
raise "must not happen: type=special_variable but class!=Kernel"
|
|
441
472
|
end
|
|
442
473
|
@type = :special_variable
|
|
@@ -444,17 +475,30 @@ module BitClust
|
|
|
444
475
|
|
|
445
476
|
def signature
|
|
446
477
|
return nil unless @klass
|
|
447
|
-
Signature.new(@klass
|
|
478
|
+
Signature.new(@klass&.name, @type ? typename2mark(@type || raise) : nil, nil)
|
|
448
479
|
end
|
|
449
480
|
|
|
450
481
|
def define_method(chunk)
|
|
451
482
|
id = method_id(chunk)
|
|
483
|
+
attrs = method_attributes(chunk)
|
|
452
484
|
@db.open_method(id) {|m|
|
|
453
485
|
m.names = chunk.names.sort
|
|
454
|
-
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
|
|
455
494
|
m.visibility = @visibility || :public
|
|
495
|
+
# steep:ignore:end
|
|
456
496
|
m.source = chunk.source
|
|
457
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
|
|
458
502
|
case @kind
|
|
459
503
|
when :added, :redefined
|
|
460
504
|
@library.add_method m
|
|
@@ -462,10 +506,133 @@ module BitClust
|
|
|
462
506
|
}
|
|
463
507
|
end
|
|
464
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
|
+
|
|
465
632
|
def method_id(chunk)
|
|
466
633
|
id = MethodID.new
|
|
467
634
|
id.library = @library
|
|
468
|
-
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)
|
|
469
636
|
id.type = chunk.signature.typename || @type
|
|
470
637
|
id.name = chunk.names.sort.first
|
|
471
638
|
id
|
|
@@ -520,7 +687,7 @@ module BitClust
|
|
|
520
687
|
end
|
|
521
688
|
|
|
522
689
|
def typename
|
|
523
|
-
typemark2name(@type)
|
|
690
|
+
typemark2name(_ = @type)
|
|
524
691
|
end
|
|
525
692
|
|
|
526
693
|
def same_type?(other)
|
data/lib/bitclust/runner.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'pathname'
|
|
2
3
|
require 'optparse'
|
|
3
4
|
|
|
4
5
|
unless Object.const_defined?(:Encoding)
|
|
5
|
-
$KCODE = 'UTF-8'
|
|
6
|
+
$KCODE = 'UTF-8' # steep:ignore
|
|
6
7
|
end
|
|
7
8
|
|
|
8
9
|
def libdir
|
|
@@ -49,6 +50,7 @@ Subcommands(for users):
|
|
|
49
50
|
list List libraries/classes/methods in database.
|
|
50
51
|
lookup Lookup a library/class/method from database.
|
|
51
52
|
search Search classes/methods from database.
|
|
53
|
+
server Run HTTP server to browse the reference manual.
|
|
52
54
|
|
|
53
55
|
Subcommands(for developers):
|
|
54
56
|
ancestors Compare class/module's ancestors between Ruby and DB.
|
|
@@ -59,9 +61,12 @@ Subcommands(for developers):
|
|
|
59
61
|
extract Extract method entries from source file.
|
|
60
62
|
classes Display defined classes for all ruby.
|
|
61
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.
|
|
62
66
|
|
|
63
67
|
Subcommands(for packagers):
|
|
64
68
|
statichtml Generate static HTML files.
|
|
69
|
+
searchpage Generate a static cross-version search page.
|
|
65
70
|
epub Generate EPUB file.
|
|
66
71
|
chm Generate static HTML files for CHM.
|
|
67
72
|
|
|
@@ -95,6 +100,7 @@ Global Options:
|
|
|
95
100
|
'setup' => BitClust::Subcommands::SetupCommand.new,
|
|
96
101
|
'server' => BitClust::Subcommands::ServerCommand.new,
|
|
97
102
|
'statichtml' => BitClust::Subcommands::StatichtmlCommand.new,
|
|
103
|
+
'searchpage' => BitClust::Subcommands::SearchpageCommand.new,
|
|
98
104
|
'htmlfile' => BitClust::Subcommands::HtmlfileCommand.new,
|
|
99
105
|
'chm' => BitClust::Subcommands::ChmCommand.new,
|
|
100
106
|
'epub' => BitClust::Subcommands::EPUBCommand.new,
|
|
@@ -103,6 +109,8 @@ Global Options:
|
|
|
103
109
|
'extract' => BitClust::Subcommands::ExtractCommand.new,
|
|
104
110
|
'classes' => BitClust::Subcommands::ClassesCommand.new,
|
|
105
111
|
'methods' => BitClust::Subcommands::MethodsCommand.new,
|
|
112
|
+
'methodsince' => BitClust::Subcommands::MethodsinceCommand.new,
|
|
113
|
+
'checklink' => BitClust::Subcommands::ChecklinkCommand.new,
|
|
106
114
|
}
|
|
107
115
|
end
|
|
108
116
|
|
|
@@ -133,6 +141,14 @@ Global Options:
|
|
|
133
141
|
@version ||= config[:default_version]
|
|
134
142
|
@prefix ||= "#{config[:database_prefix]}-#{@version}"
|
|
135
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
|
|
136
152
|
options = {
|
|
137
153
|
:prefix => @prefix,
|
|
138
154
|
:capi => @capi
|
|
@@ -144,7 +160,7 @@ Global Options:
|
|
|
144
160
|
end
|
|
145
161
|
|
|
146
162
|
def load_config
|
|
147
|
-
home_directory = Pathname(ENV
|
|
163
|
+
home_directory = Pathname(ENV.fetch('HOME'))
|
|
148
164
|
config_path = home_directory + ".bitclust/config"
|
|
149
165
|
if config_path.exist?
|
|
150
166
|
YAML.load_file(config_path)
|
data/lib/bitclust/screen.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
#
|
|
2
3
|
# bitclust/screen.rb
|
|
3
4
|
#
|
|
@@ -12,6 +13,7 @@ require 'bitclust/methodsignature'
|
|
|
12
13
|
require 'bitclust/htmlutils'
|
|
13
14
|
require 'bitclust/nameutils'
|
|
14
15
|
require 'bitclust/messagecatalog'
|
|
16
|
+
require 'bitclust/version_badges'
|
|
15
17
|
require 'erb'
|
|
16
18
|
require 'json'
|
|
17
19
|
require 'stringio'
|
|
@@ -36,6 +38,7 @@ module BitClust
|
|
|
36
38
|
private :default_message_catalog
|
|
37
39
|
|
|
38
40
|
def entry_screen(entry, opt)
|
|
41
|
+
@conf[:target_version] = opt[:target_version]
|
|
39
42
|
new_screen(Screen.for_entry(entry), entry, opt)
|
|
40
43
|
end
|
|
41
44
|
|
|
@@ -140,7 +143,7 @@ module BitClust
|
|
|
140
143
|
end
|
|
141
144
|
|
|
142
145
|
def method_url(spec)
|
|
143
|
-
cname, tmark, mname =
|
|
146
|
+
cname, tmark, mname = split_method_spec(spec)
|
|
144
147
|
"#{@cgi_url}/method/#{classname2id(cname)}/#{typemark2char(tmark)}/#{encodename_url(mname)}"
|
|
145
148
|
end
|
|
146
149
|
|
|
@@ -187,7 +190,7 @@ module BitClust
|
|
|
187
190
|
private
|
|
188
191
|
|
|
189
192
|
def preproc(template)
|
|
190
|
-
template.gsub(/^\.include ([\w\-]+)/) { load($1) }
|
|
193
|
+
template.gsub(/^\.include ([\w\-]+)/) { load($1 || raise) }
|
|
191
194
|
end
|
|
192
195
|
end
|
|
193
196
|
|
|
@@ -225,7 +228,7 @@ module BitClust
|
|
|
225
228
|
<body>
|
|
226
229
|
<h1>Error</h1>
|
|
227
230
|
<pre>#{escape_html(@error.message)} (#{escape_html(@error.class.name)})
|
|
228
|
-
#{@error.backtrace
|
|
231
|
+
#{@error.backtrace&.map {|s| escape_html(s) }&.join("\n")}</pre>
|
|
229
232
|
</body>
|
|
230
233
|
</html>
|
|
231
234
|
EndHTML
|
|
@@ -263,6 +266,7 @@ module BitClust
|
|
|
263
266
|
class TemplateScreen < Screen
|
|
264
267
|
include Translatable
|
|
265
268
|
include HTMLUtils
|
|
269
|
+
include VersionBadges
|
|
266
270
|
|
|
267
271
|
def initialize(h)
|
|
268
272
|
@urlmapper = h[:urlmapper]
|
|
@@ -312,6 +316,19 @@ module BitClust
|
|
|
312
316
|
%Q(<meta name="description" content="">)
|
|
313
317
|
end
|
|
314
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
|
+
|
|
315
332
|
private
|
|
316
333
|
|
|
317
334
|
def default_encoding
|
|
@@ -356,6 +373,12 @@ module BitClust
|
|
|
356
373
|
@urlmapper.custom_js_url(js)
|
|
357
374
|
end
|
|
358
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
|
+
|
|
359
382
|
def favicon_url
|
|
360
383
|
@urlmapper.favicon_url
|
|
361
384
|
end
|
|
@@ -452,16 +475,55 @@ module BitClust
|
|
|
452
475
|
rdcompiler().compile(src)
|
|
453
476
|
end
|
|
454
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
|
+
|
|
455
485
|
def rdcompiler
|
|
456
|
-
opt = {:catalog => message_catalog()}.merge(@conf)
|
|
457
|
-
|
|
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>')
|
|
458
505
|
end
|
|
459
506
|
|
|
460
507
|
def foreach_method_chunk(src)
|
|
508
|
+
require 'bitclust/mdparser' if markdown_source?
|
|
509
|
+
sig_re = markdown_source? ? MDParser::SIG_RE : /\A---/
|
|
461
510
|
f = LineInput.for_string(src)
|
|
462
511
|
while f.next?
|
|
463
|
-
|
|
464
|
-
|
|
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 || ''
|
|
465
527
|
yield sigs, body
|
|
466
528
|
end
|
|
467
529
|
end
|
|
@@ -540,14 +602,16 @@ module BitClust
|
|
|
540
602
|
def draw_tree(cs, &block)
|
|
541
603
|
return if cs.empty?
|
|
542
604
|
if cs.first.class?
|
|
543
|
-
tree = cs.group_by{|c| c.superclass }
|
|
605
|
+
tree = cs.group_by{|c| c.superclass } # steep:ignore
|
|
544
606
|
tree.each {|key, list| list.sort_by!{|c| c ? c.name : "" } }
|
|
545
607
|
roots = tree.keys.select{|c| !c || !cs.include?(c) }
|
|
546
|
-
roots.map!{|c| tree[c] }.flatten!
|
|
608
|
+
roots.map!{|c| tree[c] }.flatten! # steep:ignore
|
|
547
609
|
else
|
|
548
610
|
tree = {}
|
|
549
611
|
roots = cs
|
|
550
612
|
end
|
|
613
|
+
# @type var roots: Array[ClassEntry]
|
|
614
|
+
# @type var tree: Hash[ClassEntry, Array[ClassEntry]]
|
|
551
615
|
draw_treed_entries(roots, tree, &block)
|
|
552
616
|
end
|
|
553
617
|
|
|
@@ -682,8 +746,14 @@ module BitClust
|
|
|
682
746
|
end
|
|
683
747
|
|
|
684
748
|
def rdcompiler
|
|
685
|
-
h = {:force => true, :catalog => message_catalog() }.merge(@conf)
|
|
686
|
-
|
|
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
|
|
687
757
|
end
|
|
688
758
|
|
|
689
759
|
def current_url
|