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/rdcompiler.rb
CHANGED
|
@@ -14,6 +14,8 @@ require 'bitclust/htmlutils'
|
|
|
14
14
|
require 'bitclust/textutils'
|
|
15
15
|
require 'bitclust/messagecatalog'
|
|
16
16
|
require 'bitclust/syntax_highlighter'
|
|
17
|
+
require 'bitclust/version_badges'
|
|
18
|
+
require 'rouge'
|
|
17
19
|
require 'stringio'
|
|
18
20
|
|
|
19
21
|
module BitClust
|
|
@@ -24,17 +26,16 @@ module BitClust
|
|
|
24
26
|
include HTMLUtils
|
|
25
27
|
include TextUtils
|
|
26
28
|
include Translatable
|
|
29
|
+
include VersionBadges
|
|
27
30
|
|
|
28
31
|
def initialize(urlmapper, hlevel = 1, opt = {})
|
|
29
32
|
@urlmapper = urlmapper
|
|
30
33
|
@catalog = opt[:catalog]
|
|
31
34
|
@hlevel = hlevel
|
|
32
35
|
@type = nil
|
|
33
|
-
@library = nil
|
|
34
|
-
@class = nil
|
|
35
36
|
@method = nil
|
|
36
37
|
@option = opt.dup
|
|
37
|
-
init_message_catalog(@catalog)
|
|
38
|
+
init_message_catalog(@catalog || MessageCatalog.new({}, 'C'))
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def compile(src)
|
|
@@ -80,10 +81,10 @@ module BitClust
|
|
|
80
81
|
when /\A---/
|
|
81
82
|
entry_chunk
|
|
82
83
|
when /\A=+/
|
|
83
|
-
headline @f.gets
|
|
84
|
+
headline @f.gets || raise
|
|
84
85
|
when /\A(\s+)\*\s/, /\A(\s+)\(\d+\)\s/
|
|
85
86
|
@item_stack = []
|
|
86
|
-
item_list($1.size)
|
|
87
|
+
item_list(($1 || raise).size)
|
|
87
88
|
raise "@item_stack should be empty. #{@item_stack.inspect}" unless @item_stack.empty?
|
|
88
89
|
when %r<\A//emlist(?:\[(?:[^\[\]]+?)?\]\[\w+?\])?\{>
|
|
89
90
|
emlist
|
|
@@ -91,8 +92,12 @@ module BitClust
|
|
|
91
92
|
dlist
|
|
92
93
|
when /\A\s+\S/
|
|
93
94
|
list
|
|
95
|
+
when /\A@see\b/
|
|
96
|
+
# findings#1: doc/lib ページの @see もメソッドエントリと同じく
|
|
97
|
+
# SEE_ALSO として解釈する(従来は段落テキストだった)
|
|
98
|
+
see
|
|
94
99
|
else
|
|
95
|
-
if @f.peek
|
|
100
|
+
if @f.peek&.strip&.empty?
|
|
96
101
|
@f.gets
|
|
97
102
|
else
|
|
98
103
|
paragraph
|
|
@@ -107,23 +112,35 @@ module BitClust
|
|
|
107
112
|
end
|
|
108
113
|
end
|
|
109
114
|
|
|
115
|
+
# シグネチャ行に続くメソッド属性行({: ...}。直前のシグネチャ行に束縛)
|
|
116
|
+
METHOD_ATTRIBUTE_LINE_RE = /\A\{:.*\}[ \t]*$/
|
|
117
|
+
|
|
110
118
|
def entry_chunk
|
|
111
119
|
@out.puts '<dl>' if @option[:force]
|
|
112
120
|
first = true
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
|
116
132
|
end
|
|
117
|
-
props = {}
|
|
133
|
+
props = {} #: Hash[String?, String?]
|
|
118
134
|
@f.while_match(/\A:/) do |line|
|
|
119
135
|
k, v = line.sub(/\A:/, '').split(':', 2)
|
|
120
|
-
props[k
|
|
136
|
+
props[k&.strip] = v&.strip
|
|
121
137
|
end if @type == :method
|
|
122
138
|
@out.puts %Q(<dd class="#{@type.to_s}-description">)
|
|
139
|
+
undef_message if attrs.include?('undef')
|
|
123
140
|
while @f.next?
|
|
124
141
|
case @f.peek
|
|
125
142
|
when /\A===+/
|
|
126
|
-
headline @f.gets
|
|
143
|
+
headline @f.gets || raise
|
|
127
144
|
when /\A==?/
|
|
128
145
|
if @option[:force]
|
|
129
146
|
break
|
|
@@ -134,7 +151,7 @@ module BitClust
|
|
|
134
151
|
break
|
|
135
152
|
when /\A(\s+)\*\s/, /\A(\s+)\(\d+\)\s/
|
|
136
153
|
@item_stack = []
|
|
137
|
-
item_list($1.size)
|
|
154
|
+
item_list(($1 || raise).size)
|
|
138
155
|
raise "@item_stack should be empty. #{@item_stack.inspect}" unless @item_stack.empty?
|
|
139
156
|
when /\A:\s/
|
|
140
157
|
dlist
|
|
@@ -142,14 +159,16 @@ module BitClust
|
|
|
142
159
|
emlist
|
|
143
160
|
when /\A\s+\S/
|
|
144
161
|
list
|
|
145
|
-
when
|
|
162
|
+
when /\A@see\b/
|
|
163
|
+
# findings#3: 無アンカーの /@see/ は行の途中に @see を含む
|
|
164
|
+
# 本文行を吸ってしまう(@todo も同様)
|
|
146
165
|
see
|
|
147
|
-
when
|
|
166
|
+
when /\A@todo\b/
|
|
148
167
|
todo
|
|
149
168
|
when /\A@[a-z]/
|
|
150
169
|
entry_info
|
|
151
170
|
else
|
|
152
|
-
if @f.peek
|
|
171
|
+
if @f.peek&.strip&.empty?
|
|
153
172
|
@f.gets
|
|
154
173
|
else
|
|
155
174
|
entry_paragraph
|
|
@@ -161,9 +180,9 @@ module BitClust
|
|
|
161
180
|
end
|
|
162
181
|
|
|
163
182
|
def headline(line)
|
|
164
|
-
level = @hlevel + (line.slice(/\A=+/).
|
|
183
|
+
level = @hlevel + (line.slice(/\A=+/)&.size.to_i - 3)
|
|
165
184
|
label = line.sub(/\A=+(\[a:(.*?)\])?/, '').strip
|
|
166
|
-
frag = $2 if $2 and not $2.empty?
|
|
185
|
+
frag = $2 if $2 and not ($2 || raise).empty?
|
|
167
186
|
line h(level, escape_html(label), frag)
|
|
168
187
|
end
|
|
169
188
|
|
|
@@ -172,7 +191,9 @@ module BitClust
|
|
|
172
191
|
"<h#{level} #{name}>#{label}</h#{level}>"
|
|
173
192
|
end
|
|
174
193
|
|
|
175
|
-
|
|
194
|
+
ITEM_RE = /\A(\s+)(?:\*\s|\(\d+\))/
|
|
195
|
+
|
|
196
|
+
def item_list(level = 0)
|
|
176
197
|
open_tag = nil
|
|
177
198
|
close_tag = nil
|
|
178
199
|
case @f.peek
|
|
@@ -183,11 +204,9 @@ module BitClust
|
|
|
183
204
|
open_tag = "<ol>"
|
|
184
205
|
close_tag = "</ol>"
|
|
185
206
|
end
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
end
|
|
190
|
-
@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|
|
|
191
210
|
string "<li>"
|
|
192
211
|
@item_stack.push("</li>")
|
|
193
212
|
string compile_text(line.sub(/\A(\s+)(?:\*|\(\d+\))/, '').strip)
|
|
@@ -196,15 +215,14 @@ module BitClust
|
|
|
196
215
|
nl
|
|
197
216
|
string compile_text(cont.strip)
|
|
198
217
|
end
|
|
218
|
+
end
|
|
219
|
+
if (m = ITEM_RE.match(@f.peek)) && level < (m[1] || raise).size
|
|
220
|
+
item_list(($1 || raise).size)
|
|
199
221
|
line @item_stack.pop # current level li
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
line @item_stack.pop # current level ul or ol
|
|
203
|
-
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
|
|
204
224
|
line @item_stack.pop # current level li
|
|
205
|
-
|
|
206
|
-
line @item_stack.pop # previous level li
|
|
207
|
-
item_list($1.size, false)
|
|
225
|
+
break
|
|
208
226
|
else
|
|
209
227
|
line @item_stack.pop # current level li
|
|
210
228
|
end
|
|
@@ -257,8 +275,8 @@ module BitClust
|
|
|
257
275
|
line '</dd>'
|
|
258
276
|
end
|
|
259
277
|
|
|
260
|
-
def dt(s)
|
|
261
|
-
"<dt>#{s}</dt>"
|
|
278
|
+
def dt(s, id = nil)
|
|
279
|
+
id ? %Q(<dt id="#{escape_html(id)}">#{s}</dt>) : "<dt>#{s}</dt>"
|
|
262
280
|
end
|
|
263
281
|
|
|
264
282
|
def stop_on_syntax_error?
|
|
@@ -266,31 +284,48 @@ module BitClust
|
|
|
266
284
|
@option[:stop_on_syntax_error]
|
|
267
285
|
end
|
|
268
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
|
+
|
|
269
312
|
def emlist
|
|
270
313
|
command = @f.gets
|
|
271
314
|
if %r!\A//emlist\[(?<caption>[^\[\]]+?)?\]\[(?<lang>\w+?)\]! =~ command
|
|
272
|
-
|
|
315
|
+
# @type var caption: String?
|
|
316
|
+
# @type var lang: String
|
|
317
|
+
# caption は pre 内の absolute 配置ではなく、pre の上に密着した
|
|
318
|
+
# タブとして描画する(pre 内だと編集・貼り付けで先頭行と重なる)
|
|
273
319
|
line "<span class=\"caption\">#{escape_html(caption)}</span>" if caption
|
|
274
|
-
|
|
320
|
+
# <code> の直後に改行を入れると pre の内容の先頭に余計な空行として
|
|
321
|
+
# 表示される(pre 開始タグ直後の改行と違いブラウザは無視しない)
|
|
322
|
+
# ため、コード本体は <code> に直接続ける(#254)
|
|
323
|
+
string "<pre class=\"highlight #{lang}\"><code>"
|
|
275
324
|
src = +""
|
|
276
325
|
@f.until_terminator(%r<\A//\}>) do |line|
|
|
277
326
|
src << line
|
|
278
327
|
end
|
|
279
|
-
|
|
280
|
-
begin
|
|
281
|
-
filename = (caption&.size || 0) > 2 ? caption : @f.name
|
|
282
|
-
string BitClust::SyntaxHighlighter.new(src, filename).highlight
|
|
283
|
-
rescue BitClust::SyntaxHighlighter::Error => ex
|
|
284
|
-
$stderr.puts ex.message
|
|
285
|
-
if stop_on_syntax_error?
|
|
286
|
-
exit(false)
|
|
287
|
-
else
|
|
288
|
-
string src
|
|
289
|
-
end
|
|
290
|
-
end
|
|
291
|
-
else
|
|
292
|
-
string src
|
|
293
|
-
end
|
|
328
|
+
string highlight_source(src, lang, caption)
|
|
294
329
|
line '</code></pre>'
|
|
295
330
|
else
|
|
296
331
|
line '<pre>'
|
|
@@ -303,7 +338,7 @@ module BitClust
|
|
|
303
338
|
|
|
304
339
|
def list
|
|
305
340
|
lines = unindent_block(canonicalize(@f.break(/\A\S/)))
|
|
306
|
-
while lines.last
|
|
341
|
+
while lines.last&.empty?
|
|
307
342
|
lines.pop
|
|
308
343
|
end
|
|
309
344
|
line '<pre>'
|
|
@@ -328,7 +363,7 @@ module BitClust
|
|
|
328
363
|
end
|
|
329
364
|
|
|
330
365
|
def see
|
|
331
|
-
header = @f.gets
|
|
366
|
+
header = @f.gets or raise
|
|
332
367
|
header.slice!(/\A\@\w+/)
|
|
333
368
|
body = [header] + @f.span(/\A\s+\S/)
|
|
334
369
|
line '<p>'
|
|
@@ -337,7 +372,7 @@ module BitClust
|
|
|
337
372
|
end
|
|
338
373
|
|
|
339
374
|
def todo
|
|
340
|
-
header = @f.gets
|
|
375
|
+
header = @f.gets or raise
|
|
341
376
|
header.slice!(/\A\@\w+/)
|
|
342
377
|
body = header
|
|
343
378
|
line '<p class="todo">'
|
|
@@ -345,12 +380,25 @@ module BitClust
|
|
|
345
380
|
line '</p>'
|
|
346
381
|
end
|
|
347
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
|
+
|
|
348
396
|
def entry_info
|
|
349
397
|
line '<dl>'
|
|
350
398
|
while @f.next? and /\A\@(?!see)\w+|\A$/ =~ @f.peek
|
|
351
|
-
header = @f.gets
|
|
399
|
+
header = @f.gets or raise
|
|
352
400
|
next if /\A$/ =~ header
|
|
353
|
-
cmd = header.slice!(/\A\@\w+/)
|
|
401
|
+
cmd = header.slice!(/\A\@\w+/) or raise
|
|
354
402
|
@f.ungets(header)
|
|
355
403
|
case cmd
|
|
356
404
|
when '@param', '@arg'
|
|
@@ -386,9 +434,10 @@ module BitClust
|
|
|
386
434
|
string %Q(<dt class="method-heading")
|
|
387
435
|
string %Q( id="#{@method.index_id}") if first
|
|
388
436
|
string '><code>'
|
|
389
|
-
string @method.klass.name + @method.
|
|
437
|
+
string @method.klass.name + @method.display_typemark if @opt
|
|
390
438
|
string escape_html(sig.friendly_string)
|
|
391
439
|
string '</code>'
|
|
440
|
+
string heading_version_badges(@method, sig.name, first) if @method
|
|
392
441
|
if first
|
|
393
442
|
string '<span class="permalink">['
|
|
394
443
|
string a_href(@urlmapper.method_url(methodid2specstring(@method.id)), "permalink")
|
|
@@ -396,7 +445,9 @@ module BitClust
|
|
|
396
445
|
string rdoc_link(@method.id, @option[:database].properties["version"])
|
|
397
446
|
if @option[:edit_base_url] && @method.source_location
|
|
398
447
|
string ']['
|
|
399
|
-
|
|
448
|
+
# @type var urlmapper: ::BitClust::Subcommands::StatichtmlCommand::URLMapperEx
|
|
449
|
+
urlmapper = _ = @urlmapper
|
|
450
|
+
string a_href(urlmapper.edit_url(@method.source_location), 'edit')
|
|
400
451
|
end
|
|
401
452
|
string ']</span>'
|
|
402
453
|
end
|
|
@@ -407,54 +458,68 @@ module BitClust
|
|
|
407
458
|
end
|
|
408
459
|
|
|
409
460
|
BracketLink = /\[\[[\w-]+?:[!-~]+?(?:\[\] )?\]\]/n
|
|
461
|
+
# BitClust には ISO/JIS へのリンク機能がないため、[[ISO:8601]]/[[JIS:X 0301]] は
|
|
462
|
+
# リンクにせず平文で表示する (rurema/bitclust#236)。引数に空白を含む JIS も拾う。
|
|
463
|
+
StandardRef = /\[\[(?:ISO|JIS):[!-~][!-~ ]*?\]\]/n
|
|
410
464
|
NeedESC = /[&"<>]/
|
|
411
465
|
|
|
412
466
|
def compile_text(str)
|
|
413
467
|
escape_table = HTMLUtils::ESC
|
|
414
|
-
str.gsub(/(#{NeedESC})|(#{BracketLink})/o) {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
elsif 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)
|
|
418
473
|
else
|
|
419
474
|
raise 'must not happen'
|
|
420
475
|
end
|
|
421
476
|
}
|
|
422
477
|
end
|
|
423
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
|
+
|
|
424
486
|
def bracket_link(link, label = nil, frag = nil)
|
|
425
487
|
type, _arg = link.split(':', 2)
|
|
426
|
-
arg = _arg
|
|
488
|
+
arg = _arg&.rstrip or raise
|
|
489
|
+
# link_checker(bitclust/link_checker.rb)が指定されているときは
|
|
490
|
+
# 参照を検証用に記録する。描画には影響しない
|
|
491
|
+
@option[:link_checker]&.note_ref(type || raise, arg)
|
|
427
492
|
case type
|
|
428
493
|
when 'lib'
|
|
429
494
|
protect(link) {
|
|
430
495
|
case arg
|
|
431
496
|
when '/', '_index'
|
|
432
|
-
label = '
|
|
497
|
+
label = _('Library Index')
|
|
433
498
|
when '_builtin'
|
|
434
|
-
label = 'Builtin
|
|
499
|
+
label = _('Builtin Library')
|
|
435
500
|
end
|
|
436
501
|
library_link(arg, label, frag)
|
|
437
502
|
}
|
|
438
503
|
when 'c'
|
|
439
504
|
protect(link) { class_link(arg, label, frag) }
|
|
440
505
|
when 'm'
|
|
441
|
-
protect(link) { method_link(complete_spec(arg), label || arg, frag) }
|
|
506
|
+
protect(link) { method_link(complete_spec(arg), label || display_spec(arg), frag) }
|
|
442
507
|
when 'f'
|
|
443
508
|
protect(link) {
|
|
444
509
|
case arg
|
|
445
510
|
when '/', '_index'
|
|
446
|
-
arg, label = '', '
|
|
511
|
+
arg, label = '', _('Function Index')
|
|
447
512
|
end
|
|
448
513
|
function_link(arg, label || arg, frag)
|
|
449
514
|
}
|
|
450
515
|
when 'd'
|
|
451
516
|
protect(link) { document_link(arg, label, frag) }
|
|
452
517
|
when 'ref'
|
|
453
|
-
protect(link) { reference_link(arg) }
|
|
518
|
+
protect(link) { reference_link(arg, label) }
|
|
454
519
|
when 'url'
|
|
455
520
|
direct_url(arg)
|
|
456
521
|
when 'man'
|
|
457
|
-
man_link(arg)
|
|
522
|
+
man_link(arg, label)
|
|
458
523
|
when 'rfc', 'RFC'
|
|
459
524
|
rfc_link(arg)
|
|
460
525
|
when 'ruby-list', 'ruby-dev', 'ruby-ext', 'ruby-talk', 'ruby-core'
|
|
@@ -476,37 +541,51 @@ module BitClust
|
|
|
476
541
|
%Q(<a class="external" href="#{escape_html(url)}">#{escape_html(url)}</a>)
|
|
477
542
|
end
|
|
478
543
|
|
|
479
|
-
|
|
544
|
+
# label はラベル付き参照(MDCompiler#ref_md_link)からの表示テキスト
|
|
545
|
+
# 上書き。指定時は refs テーブルからのセクションラベル計算を行わない
|
|
546
|
+
def reference_link(arg, label = nil)
|
|
480
547
|
case arg
|
|
481
|
-
when /(\w+):(.*)\#(
|
|
548
|
+
when /(\w+):(.*)\#([-\w]+)\z/
|
|
549
|
+
# @type var type: String
|
|
550
|
+
# @type var name: String
|
|
551
|
+
# @type var frag: String
|
|
482
552
|
type, name, frag = $1, $2, $3
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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
|
|
494
573
|
end
|
|
495
|
-
label = @option[:database].refs[t, id, frag]
|
|
496
|
-
label = title + '/' + label if label and name
|
|
497
574
|
bracket_link("#{type}:#{name}", label, frag)
|
|
498
|
-
when /\A(
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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
|
|
503
582
|
a_href('#' + frag, label)
|
|
504
583
|
else
|
|
505
584
|
raise "must not happen"
|
|
506
585
|
end
|
|
507
586
|
end
|
|
508
587
|
|
|
509
|
-
BLADE_URL = '
|
|
588
|
+
BLADE_URL = 'https://blade.ruby-lang.org/%s/%s'
|
|
510
589
|
|
|
511
590
|
def blade_link(ml, num)
|
|
512
591
|
url = sprintf(BLADE_URL, ml, num)
|
|
@@ -544,10 +623,12 @@ module BitClust
|
|
|
544
623
|
end
|
|
545
624
|
end
|
|
546
625
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
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>)
|
|
551
632
|
end
|
|
552
633
|
|
|
553
634
|
BUGS_URL = "https://bugs.ruby-lang.org/issues/%s"
|
|
@@ -559,8 +640,14 @@ module BitClust
|
|
|
559
640
|
|
|
560
641
|
def rdoc_url(method_id, version)
|
|
561
642
|
cname, tmark, mname, _libname = methodid2specparts(method_id)
|
|
562
|
-
|
|
563
|
-
|
|
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
|
|
564
651
|
cname = cname.gsub('::', '/')
|
|
565
652
|
id = "method-#{tchar}-#{encodename_rdocurl(mname)}"
|
|
566
653
|
|
|
@@ -580,6 +667,19 @@ module BitClust
|
|
|
580
667
|
end
|
|
581
668
|
end
|
|
582
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
|
+
|
|
583
683
|
def seems_code(text)
|
|
584
684
|
# FIXME
|
|
585
685
|
escape_html(text)
|
|
@@ -12,17 +12,21 @@ module BitClust
|
|
|
12
12
|
class RefsDatabase
|
|
13
13
|
def self.load(src)
|
|
14
14
|
if src.respond_to?(:to_str)
|
|
15
|
+
# @type var src: _ToStr
|
|
15
16
|
buf = fopen(src.to_str, 'r:UTF-8'){|f| f.read}
|
|
16
17
|
elsif src.respond_to?(:to_io)
|
|
18
|
+
# @type var src: _ToIO
|
|
17
19
|
buf = src.to_io.read
|
|
18
20
|
else
|
|
21
|
+
# @type var src: _Reader
|
|
19
22
|
buf = src.read
|
|
20
23
|
end
|
|
21
24
|
|
|
22
25
|
refs = self.new
|
|
23
|
-
buf
|
|
26
|
+
buf&.each_line{|l|
|
|
24
27
|
if /((?:\\,|[^,])+),((?:\\,|[^,])+),((?:\\,|[^,])+),((?:\\,|[^,])+)\n/ =~ l
|
|
25
|
-
type, id, linkid, desc = [$1, $2, $3, $4].map{|e| e
|
|
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
|
|
26
30
|
refs[type, id, linkid] = desc
|
|
27
31
|
end
|
|
28
32
|
}
|
|
@@ -43,15 +47,18 @@ module BitClust
|
|
|
43
47
|
|
|
44
48
|
def save(s)
|
|
45
49
|
if s.respond_to?(:to_str)
|
|
50
|
+
# @type var s: _ToStr
|
|
46
51
|
path = s.to_str
|
|
47
52
|
io = fopen(path, 'w:UTF-8')
|
|
48
53
|
elsif s.respond_to?(:to_io)
|
|
54
|
+
# @type var s: _ToIO
|
|
49
55
|
io = s.to_io
|
|
50
56
|
else
|
|
51
57
|
io = s
|
|
52
58
|
end
|
|
59
|
+
# @type var io: IO
|
|
53
60
|
|
|
54
|
-
@h.each{|k, v|
|
|
61
|
+
@h.sort.each{|k, v|
|
|
55
62
|
io.write( [k, v].flatten.map{|e| e.gsub(/,/, '\\,') }.join(',') + "\n" )
|
|
56
63
|
}
|
|
57
64
|
io.close
|
|
@@ -59,11 +66,19 @@ module BitClust
|
|
|
59
66
|
|
|
60
67
|
def extract(entry)
|
|
61
68
|
entry.source.each_line{|l|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
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
|
+
}
|
|
67
82
|
}
|
|
68
83
|
end
|
|
69
84
|
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
#
|
|
3
|
+
# bitclust/reloadable_request_handler.rb
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
require 'bitclust/methoddatabase'
|
|
7
|
+
require 'bitclust/functiondatabase'
|
|
8
|
+
require 'bitclust/requesthandler'
|
|
9
|
+
|
|
10
|
+
module BitClust
|
|
11
|
+
|
|
12
|
+
# `bitclust server` を再起動しなくても `rake generate` で再生成された DB を
|
|
13
|
+
# 読み直せるようにするラッパー(bitclust#275)。
|
|
14
|
+
#
|
|
15
|
+
# MethodDatabase/FunctionDatabase はエントリを一度読むとメモリ上にメモ化
|
|
16
|
+
# し続けるため、App#initialize で一度だけ生成した RequestHandler をずっと
|
|
17
|
+
# 使い回す従来の作りでは、DB をディスク上で再生成してもプロセスは古い内容
|
|
18
|
+
# を返し続けてしまう。
|
|
19
|
+
#
|
|
20
|
+
# 新鮮さの判定は DB ディレクトリ直下の "properties" ファイルの mtime で行う。
|
|
21
|
+
# `rake generate` は対象を rm_rf してから MethodDatabase#init(properties を
|
|
22
|
+
# touch) → update という手順を踏むため、再生成のたびに properties は必ず
|
|
23
|
+
# 書き直される。#handle は毎リクエストその mtime を stat するだけなので
|
|
24
|
+
# コストは無視できる。
|
|
25
|
+
#
|
|
26
|
+
# 再生成の途中(rm_rf 直後〜init 前)は properties が一時的に存在しない。
|
|
27
|
+
# その間は新鮮さを判定できないので、古い RequestHandler をそのまま使い
|
|
28
|
+
# 続けてリクエストを失敗させない。次のリクエストで改めて判定する。
|
|
29
|
+
class ReloadableRequestHandler
|
|
30
|
+
|
|
31
|
+
def initialize(dbpath, capi, manager, request_handler_class)
|
|
32
|
+
@dbpath = dbpath
|
|
33
|
+
@capi = capi
|
|
34
|
+
@manager = manager
|
|
35
|
+
@request_handler_class = request_handler_class
|
|
36
|
+
@properties_mtime = properties_mtime
|
|
37
|
+
@handler = build_handler
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def handle(req)
|
|
41
|
+
reload_if_stale
|
|
42
|
+
@handler.handle(req)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def reload_if_stale
|
|
48
|
+
mtime = properties_mtime
|
|
49
|
+
return unless mtime # 再生成中で properties が一時的に無い: 判定を諦めて継続
|
|
50
|
+
return if @properties_mtime == mtime
|
|
51
|
+
|
|
52
|
+
@properties_mtime = mtime
|
|
53
|
+
@handler = build_handler
|
|
54
|
+
$bitclust_context_cache = nil # clear cache
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def properties_mtime
|
|
58
|
+
File.mtime("#{@dbpath}/properties")
|
|
59
|
+
rescue Errno::ENOENT
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def build_handler
|
|
64
|
+
db = BitClust::MethodDatabase.new(@dbpath)
|
|
65
|
+
if @capi
|
|
66
|
+
db = [db, BitClust::FunctionDatabase.new(@dbpath)] #: [MethodDatabase, FunctionDatabase]
|
|
67
|
+
end
|
|
68
|
+
@request_handler_class.new(db, @manager)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
end
|