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.
Files changed (160) 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 +49 -5
  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 +17 -17
  24. data/lib/bitclust/capi_converter.rb +20 -0
  25. data/lib/bitclust/classentry.rb +68 -17
  26. data/lib/bitclust/compat.rb +3 -2
  27. data/lib/bitclust/completion.rb +59 -27
  28. data/lib/bitclust/crossrubyutils.rb +9 -5
  29. data/lib/bitclust/database.rb +29 -8
  30. data/lib/bitclust/doc_converter.rb +63 -0
  31. data/lib/bitclust/docentry.rb +2 -1
  32. data/lib/bitclust/entity_splitter.rb +221 -0
  33. data/lib/bitclust/entry.rb +48 -7
  34. data/lib/bitclust/exception.rb +1 -0
  35. data/lib/bitclust/functiondatabase.rb +14 -1
  36. data/lib/bitclust/functionentry.rb +6 -1
  37. data/lib/bitclust/functionreferenceparser.rb +5 -4
  38. data/lib/bitclust/generators/epub.rb +11 -3
  39. data/lib/bitclust/htmlutils.rb +2 -1
  40. data/lib/bitclust/include_graph.rb +375 -0
  41. data/lib/bitclust/include_pruner.rb +144 -0
  42. data/lib/bitclust/interface.rb +2 -1
  43. data/lib/bitclust/libraryentry.rb +9 -7
  44. data/lib/bitclust/lineinput.rb +8 -5
  45. data/lib/bitclust/link_checker.rb +179 -0
  46. data/lib/bitclust/markdown_bridge.rb +186 -0
  47. data/lib/bitclust/markdown_orchestrator.rb +267 -0
  48. data/lib/bitclust/markdown_to_rrd.rb +824 -0
  49. data/lib/bitclust/markdown_tree.rb +217 -0
  50. data/lib/bitclust/mdcompiler.rb +883 -0
  51. data/lib/bitclust/mdparser.rb +347 -0
  52. data/lib/bitclust/messagecatalog.rb +4 -3
  53. data/lib/bitclust/method_since_calculator.rb +117 -0
  54. data/lib/bitclust/methoddatabase.rb +125 -14
  55. data/lib/bitclust/methodentry.rb +130 -7
  56. data/lib/bitclust/methodid.rb +13 -11
  57. data/lib/bitclust/methodsignature.rb +11 -4
  58. data/lib/bitclust/nameutils.rb +42 -22
  59. data/lib/bitclust/parseutils.rb +1 -0
  60. data/lib/bitclust/preprocessor.rb +70 -32
  61. data/lib/bitclust/progress_bar.rb +1 -0
  62. data/lib/bitclust/rdcompiler.rb +197 -96
  63. data/lib/bitclust/refsdatabase.rb +24 -8
  64. data/lib/bitclust/reloadable_request_handler.rb +72 -0
  65. data/lib/bitclust/requesthandler.rb +22 -16
  66. data/lib/bitclust/ridatabase.rb +11 -4
  67. data/lib/bitclust/rrd_to_markdown.rb +925 -0
  68. data/lib/bitclust/rrdparser.rb +197 -30
  69. data/lib/bitclust/runner.rb +18 -2
  70. data/lib/bitclust/screen.rb +81 -11
  71. data/lib/bitclust/search_index_generator.rb +288 -0
  72. data/lib/bitclust/searcher.rb +72 -22
  73. data/lib/bitclust/server.rb +6 -1
  74. data/lib/bitclust/silent_progress_bar.rb +1 -0
  75. data/lib/bitclust/simplesearcher.rb +25 -12
  76. data/lib/bitclust/subcommand.rb +8 -1
  77. data/lib/bitclust/subcommands/ancestors_command.rb +10 -2
  78. data/lib/bitclust/subcommands/checklink_command.rb +54 -0
  79. data/lib/bitclust/subcommands/chm_command.rb +16 -7
  80. data/lib/bitclust/subcommands/classes_command.rb +6 -0
  81. data/lib/bitclust/subcommands/epub_command.rb +1 -0
  82. data/lib/bitclust/subcommands/extract_command.rb +6 -0
  83. data/lib/bitclust/subcommands/htmlfile_command.rb +22 -7
  84. data/lib/bitclust/subcommands/init_command.rb +5 -1
  85. data/lib/bitclust/subcommands/list_command.rb +13 -4
  86. data/lib/bitclust/subcommands/lookup_command.rb +29 -9
  87. data/lib/bitclust/subcommands/methods_command.rb +11 -2
  88. data/lib/bitclust/subcommands/methodsince_command.rb +80 -0
  89. data/lib/bitclust/subcommands/preproc_command.rb +6 -0
  90. data/lib/bitclust/subcommands/property_command.rb +3 -1
  91. data/lib/bitclust/subcommands/query_command.rb +1 -0
  92. data/lib/bitclust/subcommands/searchpage_command.rb +102 -0
  93. data/lib/bitclust/subcommands/server_command.rb +16 -10
  94. data/lib/bitclust/subcommands/setup_command.rb +25 -10
  95. data/lib/bitclust/subcommands/statichtml_command.rb +132 -6
  96. data/lib/bitclust/subcommands/update_command.rb +111 -2
  97. data/lib/bitclust/syntax_highlighter.rb +52 -5
  98. data/lib/bitclust/textutils.rb +4 -3
  99. data/lib/bitclust/version.rb +2 -1
  100. data/lib/bitclust/version_badges.rb +90 -0
  101. data/lib/bitclust/whole_file_gate.rb +137 -0
  102. data/lib/bitclust.rb +1 -0
  103. data/test/test_app.rb +269 -0
  104. data/test/test_bitclust.rb +2 -0
  105. data/test/test_capi_converter.rb +91 -0
  106. data/test/test_class_screen.rb +413 -0
  107. data/test/test_copy_doc_md.rb +53 -0
  108. data/test/test_doc_converter.rb +78 -0
  109. data/test/test_entity_splitter.rb +231 -0
  110. data/test/test_entry.rb +207 -2
  111. data/test/test_eol_warning.rb +43 -0
  112. data/test/test_functionreferenceparser.rb +11 -0
  113. data/test/test_include_graph.rb +640 -0
  114. data/test/test_include_pruner.rb +133 -0
  115. data/test/test_link_checker.rb +114 -0
  116. data/test/test_lookup_command.rb +135 -0
  117. data/test/test_markdown_bridge.rb +217 -0
  118. data/test/test_markdown_orchestrator.rb +478 -0
  119. data/test/test_markdown_to_rrd.rb +757 -0
  120. data/test/test_markdown_tree.rb +369 -0
  121. data/test/test_mdcompiler.rb +1276 -0
  122. data/test/test_mdparser.rb +538 -0
  123. data/test/test_method_screen.rb +166 -0
  124. data/test/test_method_since_calculator.rb +290 -0
  125. data/test/test_methoddatabase.rb +16 -0
  126. data/test/test_methodentry.rb +328 -0
  127. data/test/test_methodsignature.rb +3 -1
  128. data/test/test_methodsince_command.rb +168 -0
  129. data/test/test_nameutils.rb +22 -0
  130. data/test/test_preprocessor.rb +215 -0
  131. data/test/test_rdcompiler.rb +429 -27
  132. data/test/test_refsdatabase.rb +32 -0
  133. data/test/test_rrd_to_markdown.rb +968 -0
  134. data/test/test_rrdparser.rb +295 -2
  135. data/test/test_run_ruby_wasm.rb +54 -0
  136. data/test/test_run_worker_prelude.rb +83 -0
  137. data/test/test_runner.rb +51 -0
  138. data/test/test_search_index_generator.rb +513 -0
  139. data/test/test_search_screen.rb +48 -0
  140. data/test/test_searcher.rb +153 -0
  141. data/test/test_searchpage_command.rb +167 -0
  142. data/test/test_simplesearcher.rb +28 -0
  143. data/test/test_statichtml_command.rb +204 -0
  144. data/test/test_syntax_highlighter.rb +60 -0
  145. data/test/test_whole_file_gate.rb +175 -0
  146. data/theme/default/js/NOTICE +42 -0
  147. data/theme/default/js/run-worker.js +111 -0
  148. data/theme/default/js/run.js +350 -0
  149. data/theme/default/js/search_controller.js +134 -0
  150. data/theme/default/js/search_init.js +176 -0
  151. data/theme/default/js/search_navigation.js +110 -0
  152. data/theme/default/js/search_page.js +202 -0
  153. data/theme/default/js/search_ranker.js +244 -0
  154. data/theme/default/script.js +91 -18
  155. data/theme/default/search.css +149 -0
  156. data/theme/default/style.css +193 -21
  157. data/theme/default/syntax-highlight.css +8 -0
  158. data/theme/lillia/style.css +45 -1
  159. metadata +178 -21
  160. data/README +0 -32
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # bitclust/rdcompiler.rb
3
4
  #
@@ -13,6 +14,8 @@ require 'bitclust/htmlutils'
13
14
  require 'bitclust/textutils'
14
15
  require 'bitclust/messagecatalog'
15
16
  require 'bitclust/syntax_highlighter'
17
+ require 'bitclust/version_badges'
18
+ require 'rouge'
16
19
  require 'stringio'
17
20
 
18
21
  module BitClust
@@ -23,17 +26,16 @@ module BitClust
23
26
  include HTMLUtils
24
27
  include TextUtils
25
28
  include Translatable
29
+ include VersionBadges
26
30
 
27
31
  def initialize(urlmapper, hlevel = 1, opt = {})
28
32
  @urlmapper = urlmapper
29
33
  @catalog = opt[:catalog]
30
34
  @hlevel = hlevel
31
35
  @type = nil
32
- @library = nil
33
- @class = nil
34
36
  @method = nil
35
37
  @option = opt.dup
36
- init_message_catalog(@catalog)
38
+ init_message_catalog(@catalog || MessageCatalog.new({}, 'C'))
37
39
  end
38
40
 
39
41
  def compile(src)
@@ -79,10 +81,10 @@ module BitClust
79
81
  when /\A---/
80
82
  entry_chunk
81
83
  when /\A=+/
82
- headline @f.gets
84
+ headline @f.gets || raise
83
85
  when /\A(\s+)\*\s/, /\A(\s+)\(\d+\)\s/
84
86
  @item_stack = []
85
- item_list($1.size)
87
+ item_list(($1 || raise).size)
86
88
  raise "@item_stack should be empty. #{@item_stack.inspect}" unless @item_stack.empty?
87
89
  when %r<\A//emlist(?:\[(?:[^\[\]]+?)?\]\[\w+?\])?\{>
88
90
  emlist
@@ -90,8 +92,12 @@ module BitClust
90
92
  dlist
91
93
  when /\A\s+\S/
92
94
  list
95
+ when /\A@see\b/
96
+ # findings#1: doc/lib ページの @see もメソッドエントリと同じく
97
+ # SEE_ALSO として解釈する(従来は段落テキストだった)
98
+ see
93
99
  else
94
- if @f.peek.strip.empty?
100
+ if @f.peek&.strip&.empty?
95
101
  @f.gets
96
102
  else
97
103
  paragraph
@@ -106,23 +112,35 @@ module BitClust
106
112
  end
107
113
  end
108
114
 
115
+ # シグネチャ行に続くメソッド属性行({: ...}。直前のシグネチャ行に束縛)
116
+ METHOD_ATTRIBUTE_LINE_RE = /\A\{:.*\}[ \t]*$/
117
+
109
118
  def entry_chunk
110
119
  @out.puts '<dl>' if @option[:force]
111
120
  first = true
112
- @f.while_match(/\A---/) do |line|
113
- method_signature(line, first)
114
- first = false
121
+ attrs = [] #: Array[String]
122
+ while @f.next?
123
+ if /\A---/ =~ @f.peek
124
+ method_signature(@f.gets || raise, first)
125
+ first = false
126
+ elsif !first && METHOD_ATTRIBUTE_LINE_RE =~ @f.peek
127
+ # メソッド属性行は本文には描画しない(undef のみ後でメッセージ)
128
+ attrs.concat attribute_tokens(@f.gets)
129
+ else
130
+ break
131
+ end
115
132
  end
116
- props = {}
133
+ props = {} #: Hash[String?, String?]
117
134
  @f.while_match(/\A:/) do |line|
118
135
  k, v = line.sub(/\A:/, '').split(':', 2)
119
- props[k.strip] = v.strip
136
+ props[k&.strip] = v&.strip
120
137
  end if @type == :method
121
138
  @out.puts %Q(<dd class="#{@type.to_s}-description">)
139
+ undef_message if attrs.include?('undef')
122
140
  while @f.next?
123
141
  case @f.peek
124
142
  when /\A===+/
125
- headline @f.gets
143
+ headline @f.gets || raise
126
144
  when /\A==?/
127
145
  if @option[:force]
128
146
  break
@@ -133,7 +151,7 @@ module BitClust
133
151
  break
134
152
  when /\A(\s+)\*\s/, /\A(\s+)\(\d+\)\s/
135
153
  @item_stack = []
136
- item_list($1.size)
154
+ item_list(($1 || raise).size)
137
155
  raise "@item_stack should be empty. #{@item_stack.inspect}" unless @item_stack.empty?
138
156
  when /\A:\s/
139
157
  dlist
@@ -141,14 +159,16 @@ module BitClust
141
159
  emlist
142
160
  when /\A\s+\S/
143
161
  list
144
- when /@see/
162
+ when /\A@see\b/
163
+ # findings#3: 無アンカーの /@see/ は行の途中に @see を含む
164
+ # 本文行を吸ってしまう(@todo も同様)
145
165
  see
146
- when /@todo/
166
+ when /\A@todo\b/
147
167
  todo
148
168
  when /\A@[a-z]/
149
169
  entry_info
150
170
  else
151
- if @f.peek.strip.empty?
171
+ if @f.peek&.strip&.empty?
152
172
  @f.gets
153
173
  else
154
174
  entry_paragraph
@@ -160,9 +180,9 @@ module BitClust
160
180
  end
161
181
 
162
182
  def headline(line)
163
- level = @hlevel + (line.slice(/\A=+/).size - 3)
183
+ level = @hlevel + (line.slice(/\A=+/)&.size.to_i - 3)
164
184
  label = line.sub(/\A=+(\[a:(.*?)\])?/, '').strip
165
- frag = $2 if $2 and not $2.empty?
185
+ frag = $2 if $2 and not ($2 || raise).empty?
166
186
  line h(level, escape_html(label), frag)
167
187
  end
168
188
 
@@ -171,7 +191,9 @@ module BitClust
171
191
  "<h#{level} #{name}>#{label}</h#{level}>"
172
192
  end
173
193
 
174
- def item_list(level = 0, indent = true)
194
+ ITEM_RE = /\A(\s+)(?:\*\s|\(\d+\))/
195
+
196
+ def item_list(level = 0)
175
197
  open_tag = nil
176
198
  close_tag = nil
177
199
  case @f.peek
@@ -182,11 +204,9 @@ module BitClust
182
204
  open_tag = "<ol>"
183
205
  close_tag = "</ol>"
184
206
  end
185
- if indent
186
- line open_tag
187
- @item_stack.push(close_tag)
188
- end
189
- @f.while_match(/\A(\s+)(?:\*\s|\(\d+\))/) do |line|
207
+ line open_tag
208
+ @item_stack.push(close_tag)
209
+ @f.while_match(ITEM_RE) do |line|
190
210
  string "<li>"
191
211
  @item_stack.push("</li>")
192
212
  string compile_text(line.sub(/\A(\s+)(?:\*|\(\d+\))/, '').strip)
@@ -195,15 +215,14 @@ module BitClust
195
215
  nl
196
216
  string compile_text(cont.strip)
197
217
  end
218
+ end
219
+ if (m = ITEM_RE.match(@f.peek)) && level < (m[1] || raise).size
220
+ item_list(($1 || raise).size)
198
221
  line @item_stack.pop # current level li
199
- elsif /\A(\s+)(?:\*\s|\(\d+\))/ =~ @f.peek and level < $1.size
200
- item_list($1.size)
201
- line @item_stack.pop # current level ul or ol
202
- elsif /\A(\s+)(?:\*\s|\(\d+\))/ =~ @f.peek and level > $1.size
222
+ break if ITEM_RE =~ @f.peek and level > ($1 || raise).size
223
+ elsif m && level > (m[1] || raise).size
203
224
  line @item_stack.pop # current level li
204
- line @item_stack.pop # current level ul or ol
205
- line @item_stack.pop # previous level li
206
- item_list($1.size, false)
225
+ break
207
226
  else
208
227
  line @item_stack.pop # current level li
209
228
  end
@@ -256,8 +275,8 @@ module BitClust
256
275
  line '</dd>'
257
276
  end
258
277
 
259
- def dt(s)
260
- "<dt>#{s}</dt>"
278
+ def dt(s, id = nil)
279
+ id ? %Q(<dt id="#{escape_html(id)}">#{s}</dt>) : "<dt>#{s}</dt>"
261
280
  end
262
281
 
263
282
  def stop_on_syntax_error?
@@ -265,31 +284,48 @@ module BitClust
265
284
  @option[:stop_on_syntax_error]
266
285
  end
267
286
 
287
+ # lang 指定付きコードブロックのハイライト HTML を返す。
288
+ # ruby(rb などの Rouge 上の alias を含む)は構文チェックを兼ねる
289
+ # Ripper ベースの SyntaxHighlighter、その他の言語は Rouge を使う。
290
+ # Rouge が知らない言語はエスケープのみ(従来この経路と ruby の構文
291
+ # エラー時フォールバックはエスケープされずに出力されていたのを修正)。
292
+ # invalid: true(md の ```ruby invalid)は構文として完全でないコードの
293
+ # 印で、Ripper の構文チェックをせず Rouge の lexer で色付けする(#251)
294
+ def highlight_source(src, lang, caption, invalid: false)
295
+ lexer = ::Rouge::Lexer.find(lang)
296
+ if lexer && lexer.tag == 'ruby' && !invalid
297
+ begin
298
+ filename = (caption&.size || 0) > 2 ? caption : @f.name or raise
299
+ BitClust::SyntaxHighlighter.new(src, filename).highlight
300
+ rescue BitClust::SyntaxHighlighter::Error => ex
301
+ $stderr.puts ex.message
302
+ exit(false) if stop_on_syntax_error?
303
+ escape_html(src)
304
+ end
305
+ elsif lexer
306
+ ::Rouge::Formatters::HTML.new.format(lexer.lex(src))
307
+ else
308
+ escape_html(src)
309
+ end
310
+ end
311
+
268
312
  def emlist
269
313
  command = @f.gets
270
314
  if %r!\A//emlist\[(?<caption>[^\[\]]+?)?\]\[(?<lang>\w+?)\]! =~ command
271
- line "<pre class=\"highlight #{lang}\">"
315
+ # @type var caption: String?
316
+ # @type var lang: String
317
+ # caption は pre 内の absolute 配置ではなく、pre の上に密着した
318
+ # タブとして描画する(pre 内だと編集・貼り付けで先頭行と重なる)
272
319
  line "<span class=\"caption\">#{escape_html(caption)}</span>" if caption
273
- line "<code>"
274
- src = ""
320
+ # <code> の直後に改行を入れると pre の内容の先頭に余計な空行として
321
+ # 表示される(pre 開始タグ直後の改行と違いブラウザは無視しない)
322
+ # ため、コード本体は <code> に直接続ける(#254)
323
+ string "<pre class=\"highlight #{lang}\"><code>"
324
+ src = +""
275
325
  @f.until_terminator(%r<\A//\}>) do |line|
276
326
  src << line
277
327
  end
278
- if lang == "ruby"
279
- begin
280
- filename = (caption&.size || 0) > 2 ? caption : @f.name
281
- string BitClust::SyntaxHighlighter.new(src, filename).highlight
282
- rescue BitClust::SyntaxHighlighter::Error => ex
283
- $stderr.puts ex.message
284
- if stop_on_syntax_error?
285
- exit(false)
286
- else
287
- string src
288
- end
289
- end
290
- else
291
- string src
292
- end
328
+ string highlight_source(src, lang, caption)
293
329
  line '</code></pre>'
294
330
  else
295
331
  line '<pre>'
@@ -302,7 +338,7 @@ module BitClust
302
338
 
303
339
  def list
304
340
  lines = unindent_block(canonicalize(@f.break(/\A\S/)))
305
- while lines.last.empty?
341
+ while lines.last&.empty?
306
342
  lines.pop
307
343
  end
308
344
  line '<pre>'
@@ -327,7 +363,7 @@ module BitClust
327
363
  end
328
364
 
329
365
  def see
330
- header = @f.gets
366
+ header = @f.gets or raise
331
367
  header.slice!(/\A\@\w+/)
332
368
  body = [header] + @f.span(/\A\s+\S/)
333
369
  line '<p>'
@@ -336,7 +372,7 @@ module BitClust
336
372
  end
337
373
 
338
374
  def todo
339
- header = @f.gets
375
+ header = @f.gets or raise
340
376
  header.slice!(/\A\@\w+/)
341
377
  body = header
342
378
  line '<p class="todo">'
@@ -344,12 +380,25 @@ module BitClust
344
380
  line '</p>'
345
381
  end
346
382
 
383
+ def attribute_tokens(attr_line)
384
+ (attr_line || '')[/\A\{:(.*)\}/, 1].to_s.strip.split(/\s+/)
385
+ end
386
+
387
+ # {: undef} 属性付きエントリのメッセージ。ページを見に来た人向け
388
+ # (statichtml は undefined エントリを skip するので server 等の動的経路用)。
389
+ # nomethod は説明が本文に書かれている前提のマーカーなので何も描画しない
390
+ def undef_message
391
+ line '<p>'
392
+ line 'このメソッドは定義されていません。'
393
+ line '</p>'
394
+ end
395
+
347
396
  def entry_info
348
397
  line '<dl>'
349
398
  while @f.next? and /\A\@(?!see)\w+|\A$/ =~ @f.peek
350
- header = @f.gets
399
+ header = @f.gets or raise
351
400
  next if /\A$/ =~ header
352
- cmd = header.slice!(/\A\@\w+/)
401
+ cmd = header.slice!(/\A\@\w+/) or raise
353
402
  @f.ungets(header)
354
403
  case cmd
355
404
  when '@param', '@arg'
@@ -385,9 +434,10 @@ module BitClust
385
434
  string %Q(<dt class="method-heading")
386
435
  string %Q( id="#{@method.index_id}") if first
387
436
  string '><code>'
388
- string @method.klass.name + @method.typemark if @opt
437
+ string @method.klass.name + @method.display_typemark if @opt
389
438
  string escape_html(sig.friendly_string)
390
439
  string '</code>'
440
+ string heading_version_badges(@method, sig.name, first) if @method
391
441
  if first
392
442
  string '<span class="permalink">['
393
443
  string a_href(@urlmapper.method_url(methodid2specstring(@method.id)), "permalink")
@@ -395,7 +445,9 @@ module BitClust
395
445
  string rdoc_link(@method.id, @option[:database].properties["version"])
396
446
  if @option[:edit_base_url] && @method.source_location
397
447
  string ']['
398
- string a_href(@urlmapper.edit_url(@method.source_location), 'edit')
448
+ # @type var urlmapper: ::BitClust::Subcommands::StatichtmlCommand::URLMapperEx
449
+ urlmapper = _ = @urlmapper
450
+ string a_href(urlmapper.edit_url(@method.source_location), 'edit')
399
451
  end
400
452
  string ']</span>'
401
453
  end
@@ -406,54 +458,68 @@ module BitClust
406
458
  end
407
459
 
408
460
  BracketLink = /\[\[[\w-]+?:[!-~]+?(?:\[\] )?\]\]/n
461
+ # BitClust には ISO/JIS へのリンク機能がないため、[[ISO:8601]]/[[JIS:X 0301]] は
462
+ # リンクにせず平文で表示する (rurema/bitclust#236)。引数に空白を含む JIS も拾う。
463
+ StandardRef = /\[\[(?:ISO|JIS):[!-~][!-~ ]*?\]\]/n
409
464
  NeedESC = /[&"<>]/
410
465
 
411
466
  def compile_text(str)
412
467
  escape_table = HTMLUtils::ESC
413
- str.gsub(/(#{NeedESC})|(#{BracketLink})/o) {
414
- if char = $1 then escape_table[char]
415
- elsif tok = $2 then bracket_link(tok[2..-3])
416
- elsif tok = $3 then seems_code(tok)
468
+ str.gsub(/(#{NeedESC})|(#{StandardRef})|(#{BracketLink})/o) {
469
+ # @type var char: '&' | '"' | '<' | '>'
470
+ if char = _ = $1 then escape_table[char]
471
+ elsif tok = $2 then standard_ref(tok[2..-3] || raise)
472
+ elsif tok = $3 then bracket_link(tok[2..-3] || raise)
417
473
  else
418
474
  raise 'must not happen'
419
475
  end
420
476
  }
421
477
  end
422
478
 
479
+ def standard_ref(link)
480
+ # 例: "ISO:8601" -> "ISO 8601", "JIS:X 0301" -> "JIS X 0301"
481
+ type, _arg = link.split(':', 2)
482
+ arg = _arg&.strip or raise
483
+ escape_html("#{type} #{arg}")
484
+ end
485
+
423
486
  def bracket_link(link, label = nil, frag = nil)
424
487
  type, _arg = link.split(':', 2)
425
- arg = _arg.rstrip
488
+ arg = _arg&.rstrip or raise
489
+ # link_checker(bitclust/link_checker.rb)が指定されているときは
490
+ # 参照を検証用に記録する。描画には影響しない
491
+ @option[:link_checker]&.note_ref(type || raise, arg)
426
492
  case type
427
493
  when 'lib'
428
494
  protect(link) {
429
495
  case arg
430
496
  when '/', '_index'
431
- label = 'All libraries'
497
+ label = _('Library Index')
432
498
  when '_builtin'
433
- label = 'Builtin libraries'
499
+ label = _('Builtin Library')
434
500
  end
435
501
  library_link(arg, label, frag)
436
502
  }
437
503
  when 'c'
438
504
  protect(link) { class_link(arg, label, frag) }
439
505
  when 'm'
440
- protect(link) { method_link(complete_spec(arg), label || arg, frag) }
506
+ protect(link) { method_link(complete_spec(arg), label || display_spec(arg), frag) }
441
507
  when 'f'
442
508
  protect(link) {
443
509
  case arg
444
510
  when '/', '_index'
445
- arg, label = '', 'All C API'
511
+ arg, label = '', _('Function Index')
446
512
  end
447
513
  function_link(arg, label || arg, frag)
448
514
  }
449
515
  when 'd'
450
516
  protect(link) { document_link(arg, label, frag) }
451
517
  when 'ref'
452
- protect(link) { reference_link(arg) }
518
+ protect(link) { reference_link(arg, label) }
453
519
  when 'url'
454
520
  direct_url(arg)
455
521
  when 'man'
456
- man_link(arg)
522
+ man_link(arg, label)
457
523
  when 'rfc', 'RFC'
458
524
  rfc_link(arg)
459
525
  when 'ruby-list', 'ruby-dev', 'ruby-ext', 'ruby-talk', 'ruby-core'
@@ -475,37 +541,51 @@ module BitClust
475
541
  %Q(<a class="external" href="#{escape_html(url)}">#{escape_html(url)}</a>)
476
542
  end
477
543
 
478
- def reference_link(arg)
544
+ # label はラベル付き参照(MDCompiler#ref_md_link)からの表示テキスト
545
+ # 上書き。指定時は refs テーブルからのセクションラベル計算を行わない
546
+ def reference_link(arg, label = nil)
479
547
  case arg
480
- when /(\w+):(.*)\#(\w+)\z/
548
+ when /(\w+):(.*)\#([-\w]+)\z/
549
+ # @type var type: String
550
+ # @type var name: String
551
+ # @type var frag: String
481
552
  type, name, frag = $1, $2, $3
482
- case type
483
- when 'lib'
484
- title, t, id = name, LibraryEntry.type_id.to_s, name
485
- when 'c'
486
- title, t, id = name, ClassEntry.type_id.to_s, name
487
- when 'm'
488
- title, t, id = name, MethodEntry.type_id.to_s, name
489
- when 'd'
490
- title, t, id = @option[:database].get_doc(name).title, DocEntry.type_id.to_s, name
491
- else
492
- raise "must not happen"
553
+ unless label
554
+ # @type var title: String
555
+ # @type var t: String
556
+ # @type var id: String
557
+ case type
558
+ when 'lib'
559
+ title, t, id = name, LibraryEntry.type_id.to_s, name
560
+ when 'c'
561
+ title, t, id = name, ClassEntry.type_id.to_s, name
562
+ when 'm'
563
+ # 表示側(title)のみ display_spec で畳む。id は refs テーブルの
564
+ # キーなので記載どおり(bitclust#282)
565
+ title, t, id = display_spec(name), MethodEntry.type_id.to_s, name
566
+ when 'd'
567
+ title, t, id = @option[:database].get_doc(name).title, DocEntry.type_id.to_s, name
568
+ else
569
+ raise "must not happen"
570
+ end
571
+ label = @option[:database].refs[t, id, frag]
572
+ label = title + '/' + label if label and name
493
573
  end
494
- label = @option[:database].refs[t, id, frag]
495
- label = title + '/' + label if label and name
496
574
  bracket_link("#{type}:#{name}", label, frag)
497
- when /\A(\w+)\z/
498
- e = @option[:entry]
499
- frag = $1
500
- type = e.type_id.to_s
501
- label = @option[:database].refs[type, e.name, frag] || frag
575
+ when /\A([-\w]+)\z/
576
+ frag = $1 || raise
577
+ unless label
578
+ e = @option[:entry]
579
+ type = e.type_id.to_s
580
+ label = @option[:database].refs[type, e.name, frag] || frag
581
+ end
502
582
  a_href('#' + frag, label)
503
583
  else
504
584
  raise "must not happen"
505
585
  end
506
586
  end
507
587
 
508
- BLADE_URL = 'http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/%s/%s'
588
+ BLADE_URL = 'https://blade.ruby-lang.org/%s/%s'
509
589
 
510
590
  def blade_link(ml, num)
511
591
  url = sprintf(BLADE_URL, ml, num)
@@ -543,10 +623,12 @@ module BitClust
543
623
  end
544
624
  end
545
625
 
546
- def man_link(spec)
547
- m = /([\w\.\/]+)\((\w+)\)/.match(spec) or return escape_html(spec)
548
- url = man_url(m[2], escape_html(m[1])) or return escape_html(spec)
549
- %Q(<a class="external" href="#{escape_html(url)}">#{escape_html("#{m[1]}(#{m[2]})")}</a>)
626
+ # label はラベル付き参照(MDCompiler#ref_md_link)からの表示テキスト
627
+ # 上書き。リンクにできない spec ではラベルをプレーン表示に使う
628
+ def man_link(spec, label = nil)
629
+ m = /([\w\.\/]+)\((\w+)\)/.match(spec) or return escape_html(label || spec)
630
+ url = man_url(m[2], escape_html(m[1] || raise)) or return escape_html(label || spec)
631
+ %Q(<a class="external" href="#{escape_html(url)}">#{escape_html(label || "#{m[1]}(#{m[2]})")}</a>)
550
632
  end
551
633
 
552
634
  BUGS_URL = "https://bugs.ruby-lang.org/issues/%s"
@@ -558,8 +640,14 @@ module BitClust
558
640
 
559
641
  def rdoc_url(method_id, version)
560
642
  cname, tmark, mname, _libname = methodid2specparts(method_id)
561
- tchar = typemark2char(tmark) == 'i' ? 'i' : 'c'
562
- cname = cname.split(".").first
643
+ cname = cname.split(".").first || raise
644
+ # rdoc は Kernel のモジュール関数(rb_define_global_function 由来)
645
+ # private インスタンスメソッドとして掲載しているため i にする
646
+ if typemark2char(tmark) == 'i' || (cname == "Kernel" && tmark == '.#')
647
+ tchar = 'i'
648
+ else
649
+ tchar = 'c'
650
+ end
563
651
  cname = cname.gsub('::', '/')
564
652
  id = "method-#{tchar}-#{encodename_rdocurl(mname)}"
565
653
 
@@ -579,6 +667,19 @@ module BitClust
579
667
  end
580
668
  end
581
669
 
670
+ # bitclust#282: [[m:...]] のデフォルトラベル(参照の記載文字列)は表示
671
+ # 専用なので、module function の ".#" は DB バージョンが 4.0 以降なら
672
+ # "?." で表示する(#250/#277 の display_typemark と同じ選択規則。
673
+ # Markdown ソースの [m:Kernel?.x] も inline 復元時に ".#" へ正規化されて
674
+ # ここに来るため、どちらの表記で書かれていても見出し表記と一致する)。
675
+ # spec・URL・アンカーキーは不変。明示ラベル(label 引数)には触れない
676
+ def display_spec(spec)
677
+ return spec unless spec.include?('.#')
678
+ db = @option[:database]
679
+ version = db ? db.propget('version') : nil
680
+ spec.sub('.#', NameUtils.display_typemark('.#', version))
681
+ end
682
+
582
683
  def seems_code(text)
583
684
  # FIXME
584
685
  escape_html(text)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # bitclust/refsdatabase.rb
3
4
  #
@@ -11,17 +12,21 @@ module BitClust
11
12
  class RefsDatabase
12
13
  def self.load(src)
13
14
  if src.respond_to?(:to_str)
15
+ # @type var src: _ToStr
14
16
  buf = fopen(src.to_str, 'r:UTF-8'){|f| f.read}
15
17
  elsif src.respond_to?(:to_io)
18
+ # @type var src: _ToIO
16
19
  buf = src.to_io.read
17
20
  else
21
+ # @type var src: _Reader
18
22
  buf = src.read
19
23
  end
20
24
 
21
25
  refs = self.new
22
- buf.each_line{|l|
26
+ buf&.each_line{|l|
23
27
  if /((?:\\,|[^,])+),((?:\\,|[^,])+),((?:\\,|[^,])+),((?:\\,|[^,])+)\n/ =~ l
24
- type, id, linkid, desc = [$1, $2, $3, $4].map{|e| e.gsub(/\\(.)/){|s| $1 == ',' ? ',' : s } }
28
+ type, id, linkid, desc = [$1, $2, $3, $4].map{|e| e&.gsub(/\\(.)/){|s| $1 == ',' ? ',' : s } }
29
+ type || raise; id || raise; linkid || raise; desc || raise
25
30
  refs[type, id, linkid] = desc
26
31
  end
27
32
  }
@@ -42,15 +47,18 @@ module BitClust
42
47
 
43
48
  def save(s)
44
49
  if s.respond_to?(:to_str)
50
+ # @type var s: _ToStr
45
51
  path = s.to_str
46
52
  io = fopen(path, 'w:UTF-8')
47
53
  elsif s.respond_to?(:to_io)
54
+ # @type var s: _ToIO
48
55
  io = s.to_io
49
56
  else
50
57
  io = s
51
58
  end
59
+ # @type var io: IO
52
60
 
53
- @h.each{|k, v|
61
+ @h.sort.each{|k, v|
54
62
  io.write( [k, v].flatten.map{|e| e.gsub(/,/, '\\,') }.join(',') + "\n" )
55
63
  }
56
64
  io.close
@@ -58,11 +66,19 @@ module BitClust
58
66
 
59
67
  def extract(entry)
60
68
  entry.source.each_line{|l|
61
- if /\A={1,6}\[a:(\w+)\] *(.*)/ =~ l
62
- entry.labels.each{|name|
63
- self[entry.class.type_id, name, $1] = $2
64
- }
65
- end
69
+ anchor, label =
70
+ if /\A={1,6}\[a:([-\w]+)\] *(.*)/ =~ l
71
+ [$1, $2]
72
+ elsif /\A\#{1,6} +(.*?) *\{#([-\w]+)\}\s*\z/ =~ l
73
+ # md ソース: 「### 見出し {#anchor}」形式(M3 ネイティブパース)。
74
+ # ラベルは rd 表示形へ戻す(\` エスケープ解除等。pattern_matching)
75
+ Kernel.require 'bitclust/markdown_to_rrd'
76
+ [$2, ::BitClust::MarkdownToRRD.restore_description($1 || raise)]
77
+ end
78
+ next unless anchor
79
+ entry.labels.each{|name|
80
+ self[entry.class.type_id, name, anchor || raise] = label || raise
81
+ }
66
82
  }
67
83
  end
68
84
  end