Skip to content

Commit f2629ff

Browse files
znzclaude
andcommitted
Markdown 対応で追加されたコードに RBS 型定義を付ける
rake sig(rbs subtract)が検出する型定義の不足を解消する: - Markdown 対応で追加された 13 ファイル分の sig を新規作成 (mdparser, mdcompiler, markdown_to_rrd, rrd_to_markdown, markdown_tree, markdown_bridge, markdown_orchestrator, include_graph, include_pruner, entity_splitter, whole_file_gate, capi_converter, doc_converter) - 既存クラスに追加された markdown 系メンバーを既存 sig へ追記 (MethodDatabase#update_by_markdowntree・copy_doc_md、 TemplateScreen#markdown_source?・html_base、 UpdateCommand#prepare_markdowntree ほか、 StatichtmlCommand#create_search_index・SEARCH_JS_FILES) - database_propkey へ 'source_format' を追加、Rouge の最小スタブを追加、 RDCompiler::option の :catalog を実装に合わせて省略可へ、 Location の line を Integer? へ(line 不明の Location があるため) steep check をエラー 0・警告 0 にする(従来はエラー 471・警告 317): - マッチ直後で非 nil が保証される $1 等の参照は 既存慣行の ($1 || raise) 形に揃える - steep が推論できない空リテラルやブロック越し代入には インライン型注釈(#: / @type var)を付ける - File.write(Pathname, ...) → Pathname#write 等、 rbs core の型が受けない等価な呼び出しを書き換える 挙動の変更はない(テスト 17589 件 green・rbs validate / rake sig 通過)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent f06cf50 commit f2629ff

44 files changed

Lines changed: 1344 additions & 164 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

lib/bitclust/doc_converter.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def reduce(rrd)
2727
when /\A(={1,4}\[a:[^\]]+\])\s{2,}(.*)/m
2828
"#{$1} #{$2}" # アンカー見出しの余分なスペース
2929
when /\A\t+/
30-
line.sub(/\A\t+/) { ' ' * $&.length } # 行頭タブ(doc の散文1行のみ)
30+
line.sub(/\A\t+/) { ' ' * ($& || raise).length } # 行頭タブ(doc の散文1行のみ)
3131
when /\A\#@include\((?:\.\.\/)+api\/src\//
3232
# 旧レイアウト ../api/src/X → 新レイアウト ../api/X
3333
# (manual/ 配下では src 階層が無い。ブリッジが逆変換する)
@@ -54,7 +54,7 @@ def files(doc_root)
5454
base = File.dirname(f)
5555
File.read(File.join(doc_root, f))
5656
.scan(/^\#@include\((?!(?:\.\.\/)+api\/)(.*?)\)/)
57-
.map { |t| File.expand_path(base == '.' ? t[0] : File.join(base, t[0]), '/')
57+
.map { |t| File.expand_path(base == '.' ? (t[0] || raise) : File.join(base, t[0] || raise), '/')
5858
.delete_prefix('/') }
5959
}
6060
(pages + (all & referenced)).sort

lib/bitclust/entity_splitter.rb

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ module EntitySplitter
2929
# H1 を含まないゲート(散文の版分岐・gated relations)は触らない。
3030
def resolve_header_gates(src, scope)
3131
lines = src.lines
32-
out = []
32+
out = [] #: Array[String]
3333
i = 0
3434
while i < lines.length
3535
line = lines[i]
@@ -87,7 +87,8 @@ def contains_entity_h1?(lines)
8787
# ベースセグメントとして返す。連結すると入力に一致する。
8888
def segments(src)
8989
lines = src.lines
90-
boundaries = [] # [行index, エンティティ名, ゲート付きか]
90+
# [行index, エンティティ名, ゲート付きか]
91+
boundaries = [] #: Array[[Integer, String?, bool]]
9192
depth = 0
9293
lines.each_with_index do |line, i|
9394
case line
@@ -111,25 +112,25 @@ def segments(src)
111112
next true unless gated
112113
stop = idx + 1 < boundaries.length ? boundaries[idx + 1][0] : lines.length
113114
gate_wraps_segment?(lines, start, stop)
114-
}.map { |(start, name, _), _| [start, name] }
115+
}.map { |(start, name, _), _| [start, name] } #: Array[[Integer, String?]]
115116
return nil if boundaries.empty?
116-
if lines[0...boundaries.first[0]].all? { |l| l =~ BLANK_RE }
117+
if (lines[0...(boundaries.first || raise)[0]] || raise).all? { |l| l =~ BLANK_RE }
117118
boundaries[0] = [0, boundaries[0][1]] # 先頭の空行は最初のセグメントへ
118119
else
119120
boundaries.unshift([0, nil]) # ライブラリ概要部(ベースセグメント)
120121
end
121122

122123
boundaries.each_with_index.map do |(start, name), idx|
123124
stop = idx + 1 < boundaries.length ? boundaries[idx + 1][0] : lines.length
124-
[name, lines[start...stop].join]
125+
[name, (lines[start...stop] || raise).join]
125126
end
126127
end
127128

128129
# start のゲート開き行に対応する #@end がセグメント終端(末尾の空行は許容)に
129130
# あるか。ゲートがセグメント全体を包むときのみ真
130131
def gate_wraps_segment?(lines, start, stop)
131132
depth = 0
132-
close = nil
133+
close = nil #: Integer?
133134
(start...stop).each do |i|
134135
case lines[i]
135136
when BLOCK_OPEN_RE then depth += 1
@@ -142,7 +143,7 @@ def gate_wraps_segment?(lines, start, stop)
142143
end
143144
end
144145
return false unless close
145-
lines[(close + 1)...stop].all? { |l| l =~ BLANK_RE }
146+
(lines[(close + 1)...stop] || raise).all? { |l| l =~ BLANK_RE }
146147
end
147148

148149
# エンティティ名 → 出力ファイル名(拡張子なし)。既存の命名規約に合わせ :: → __
@@ -171,15 +172,15 @@ def header_relations?(src)
171172
def gate_condition(line)
172173
return nil unless line =~ GATE_OPEN_RE
173174
# Preprocessor は #@since "1.8.5" のクォート形式も受理する
174-
IncludeGraph::Condition.new($1.to_sym, $2.strip.delete_prefix('"').delete_suffix('"'))
175+
IncludeGraph::Condition.new(($1 || raise).to_sym, ($2 || raise).strip.delete_prefix('"').delete_suffix('"'))
175176
end
176177

177178
# i のゲート開始行から対応を取り、{body:, else_body:, next:} を返す。
178179
# body/else_body は枝の行列、next は #@end の次の行 index。対応が取れなければ nil
179180
def parse_block(lines, i)
180181
depth = 0
181-
body = []
182-
else_body = []
182+
body = [] #: Array[String]
183+
else_body = [] #: Array[String]
183184
current = body
184185
j = i
185186
while j < lines.length
@@ -213,7 +214,7 @@ def first_content_is_h1?(branch_lines)
213214

214215
# i 以降の最初の非空行が H1 ならその名前を返す
215216
def first_h1_name(lines, i)
216-
first = lines[i..].find { |l| l !~ BLANK_RE }
217+
first = (lines[i..] || raise).find { |l| l !~ BLANK_RE }
217218
first =~ H1_RE ? $1 : nil
218219
end
219220
end

lib/bitclust/include_graph.rb

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ def self.interval(conditions)
4040
# 分解して境界に寄与させる。分解できない連言子は寄与しない(保守的に広く扱う。
4141
# 恒偽の証明は Scope#never? が連言子単位で別途行う)
4242
def self.bounds(conditions)
43-
sinces = []
44-
untils = []
43+
sinces = [] #: Array[bound]
44+
untils = [] #: Array[bound]
4545
conditions.each do |c|
4646
case c.kind
4747
when :since then sinces << [Gem::Version.new(c.version), c.version]
@@ -60,7 +60,7 @@ def self.bounds(conditions)
6060
# <= や否定(#@else 反転)は正確に表現できないので分解しない
6161
def self.if_bounds(expr)
6262
return [] if expr.lstrip.start_with?('!')
63-
bounds = []
63+
bounds = [] #: Array[[Symbol, String?]]
6464
expr.split(/\band\b/).each do |c|
6565
case c
6666
when /version\s*<\s*"([\d.]+)"/ then bounds << [:until, $1]
@@ -142,7 +142,7 @@ def never_conjunct?(conjunct)
142142
def gate(conditions)
143143
return nil unless cover?(conditions)
144144
lo, hi = IncludeGraph.bounds(conditions)
145-
gate = {}
145+
gate = {} #: IncludeGraph::gate
146146
gate[:since] = lo[1] if lo && lo[0] > @lo
147147
gate[:until] = hi[1] if hi && hi[0] < @hi
148148
gate
@@ -199,7 +199,7 @@ def grouping_include_sites
199199
# LIBRARIES 内の版ゲート(fiber: until 3.1 等)をスコープ適用して付与する。
200200
# スコープ外のライブラリ(cmath/scanf/sync 等)は含まない。
201201
def library_front_matter_map(scope)
202-
result = {}
202+
result = {} #: Hash[String, Hash[String, String]]
203203
@library_gates.each do |name, gate|
204204
root = "#{name}.rd"
205205
scoped = scope.gate(gate)
@@ -227,7 +227,7 @@ def library_front_matter_map(scope)
227227
# - 同一ライブラリ内の複数 include サイトは、いずれかが有効なら
228228
# エンティティが存在するため、ゲートは区間の hull(弱い方)を取る
229229
def front_matter_map(scope)
230-
result = {}
230+
result = {} #: Hash[String, front_matter]
231231
groupings.each do |path, ms|
232232
covered = ms.select { |m| scope.cover?(m.conditions) }
233233
next if covered.empty?
@@ -264,16 +264,16 @@ def hull(gates)
264264
return {} if gates.any?(&:empty?)
265265
sinces = gates.map { |g| g[:since] }
266266
untils = gates.map { |g| g[:until] }
267-
result = {}
267+
result = {} #: gate
268268
result[:since] = sinces.min_by { |v| Gem::Version.new(v) } if sinces.all?
269269
result[:until] = untils.max_by { |v| Gem::Version.new(v) } if untils.all?
270270
result
271271
end
272272

273273
# LIBRARIES を版ゲート付きで読む。 [[name, [Condition]], ...]
274274
def read_libraries
275-
entries = {}
276-
stack = []
275+
entries = {} #: Hash[String, Array[Condition]]
276+
stack = [] #: Array[Condition]
277277
File.foreach(File.join(@src_root, 'LIBRARIES')) do |line|
278278
line = line.chomp
279279
if line.start_with?('#@#') || apply_directive(stack, line)
@@ -292,7 +292,7 @@ def apply_directive(stack, line)
292292
case line
293293
when /\A\#@since\s+(\S+)/ then stack.push(Condition.new(:since, $1))
294294
when /\A\#@until\s+(\S+)/ then stack.push(Condition.new(:until, $1))
295-
when /\A\#@if\s*(.*)/ then stack.push(Condition.new(:if, $1.strip))
295+
when /\A\#@if\s*(.*)/ then stack.push(Condition.new(:if, ($1 || raise).strip))
296296
when /\A\#@samplecode\b/ then stack.push(Condition.new(:samplecode, nil))
297297
when /\A\#@else\b/ then (cond = stack.pop) && stack.push(invert(cond))
298298
when /\A\#@end\b/ then stack.pop
@@ -313,12 +313,12 @@ def invert(cond)
313313
# relpath のファイル内の #@include を条件スタック付きで走査する。
314314
# base_conditions は LIBRARIES ゲート+ここまでの include 経路の条件。
315315
def walk(relpath, library, base_conditions, path_stack)
316-
stack = []
316+
stack = [] #: Array[Condition]
317317
File.foreach(File.join(@src_root, relpath)) do |line|
318318
line = line.chomp
319319
if line =~ /\A\#@include\s*\((.*?)\)/
320320
conditions = (base_conditions + stack).reject { |c| c.kind == :samplecode }
321-
add_include(relpath, $1, library, conditions, path_stack)
321+
add_include(relpath, $1 || raise, library, conditions, path_stack)
322322
else
323323
apply_directive(stack, line)
324324
end

lib/bitclust/include_pruner.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ module IncludePruner
3333

3434
def prune(src, targets)
3535
return src if targets.empty?
36-
lookup = {}
36+
lookup = {} #: Hash[String?, bool]
3737
targets.each { |t| lookup[t] = true }
3838

3939
lines = src.lines
4040
nodes = catch(:unbalanced) { parse(lines) }
4141
return src if nodes.nil?
4242

43-
stream = []
43+
stream = [] #: stream
4444
changed = emit(nodes, lookup, stream)
4545
return src unless changed
4646
tidy(stream).join
@@ -54,7 +54,7 @@ def parse(lines)
5454
end
5555

5656
def parse_nodes(lines, i)
57-
nodes = []
57+
nodes = [] #: Array[node]
5858
while i < lines.length
5959
line = lines[i]
6060
if line =~ ELSE_RE || line =~ END_RE
@@ -63,7 +63,7 @@ def parse_nodes(lines, i)
6363
gate = line !~ CODE_OPEN_RE
6464
body, j = parse_nodes(lines, i + 1)
6565
else_line = nil
66-
else_body = []
66+
else_body = [] #: Array[node]
6767
if j < lines.length && lines[j] =~ ELSE_RE
6868
else_line = lines[j]
6969
else_body, j = parse_nodes(lines, j + 1)
@@ -96,9 +96,9 @@ def emit(nodes, lookup, stream)
9696
end
9797

9898
def emit_block(block, lookup, stream)
99-
body = []
99+
body = [] #: stream
100100
body_changed = emit(block.body, lookup, body)
101-
else_body = []
101+
else_body = [] #: stream
102102
else_changed = block.else_line ? emit(block.else_body, lookup, else_body) : false
103103
changed = body_changed || else_changed
104104

@@ -124,7 +124,7 @@ def blank_only?(stream)
124124
# REMOVED 番兵を取り除きつつ、除去痕の前後が空行同士なら1つに畳み、
125125
# 先頭に残った空行を削る
126126
def tidy(stream)
127-
result = []
127+
result = [] #: Array[String]
128128
pending = false
129129
stream.each do |item|
130130
if item.equal?(REMOVED)

lib/bitclust/markdown_bridge.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def self.build_doc(md_doc_root, out_doc_root)
3838
referenced = files.flat_map { |f|
3939
base = File.dirname(f)
4040
File.read(File.join(md_doc_root, f)).scan(/^\#@include\((?!(?:\.\.\/)+api\/)(.*?)\)/)
41-
.map { |t| File.expand_path(base == '.' ? t[0] : File.join(base, t[0]), '/')
41+
.map { |t| File.expand_path(base == '.' ? (t[0] || raise) : File.join(base, t[0] || raise), '/')
4242
.delete_prefix('/') }
4343
}.to_h { |t| [t, true] }
4444

@@ -135,7 +135,7 @@ def member_includes(libname, emitted)
135135
[reopen_only ? 1 : 0, path]
136136
end
137137
"\n" + sorted.map { |path|
138-
m = members[path][:memberships].find { |mm| mm[:library] == libname }
138+
m = members[path][:memberships].find { |mm| mm[:library] == libname } || raise
139139
inc = "\#@include(#{relative(emitted[path], base)})\n"
140140
inc = "\#@since #{m[:since]}\n#{inc}\#@end\n" if m[:since]
141141
inc = "\#@until #{m[:until]}\n#{inc}\#@end\n" if m[:until]
@@ -156,7 +156,7 @@ def rewrite_includes(rrd, md_file, emitted)
156156
return rrd unless rrd.include?('#@include')
157157
base = File.dirname(md_file)
158158
rrd.gsub(INCLUDE_RE) do
159-
pre, target, post = $1, $2, $3
159+
pre, target, post = $1, ($2 || raise), $3
160160
resolved = resolve(base, target, emitted)
161161
"#{pre}#{resolved ? relative(emitted[resolved], File.dirname(emitted[md_file])) : target}#{post}"
162162
end

lib/bitclust/markdown_orchestrator.rb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def units(relpath, rrd)
6363
dir = library ? relpath.sub(/\.rd\z/, '') : File.dirname(relpath)
6464
entity_fm =
6565
if library
66-
fm = { 'library' => relpath.sub(/\.rd\z/, '') }
66+
fm = { 'library' => relpath.sub(/\.rd\z/, '') } #: IncludeGraph::front_matter
6767
%w[since until].each { |k| fm[k] = front_matter[k] if front_matter[k] }
6868
fm
6969
else
@@ -74,15 +74,15 @@ def units(relpath, rrd)
7474
units = segments.map do |name, text|
7575
next Unit.new(output_path(relpath, front_matter), text, front_matter) if name.nil? # 概要部
7676

77-
fm = entity_fm.dup
77+
seg_fm = entity_fm.dup
7878
if (unwrapped = WholeFileGate.unwrap_for_scope(text, @scope))
7979
# エンティティセグメントのゲートは自身の存在ゲート → 常に front matter へ
8080
text = unwrapped[0]
81-
merge_gate(fm, unwrapped[1])
81+
merge_gate(seg_fm, unwrapped[1])
8282
end
8383
text = rewrite_includes(text, source_dir, dir)
8484
filename = "#{EntitySplitter.entity_filename(name)}.md"
85-
Unit.new(dir == '.' ? filename : File.join(dir, filename), text, fm)
85+
Unit.new(dir == '.' ? filename : File.join(dir, filename), text, seg_fm)
8686
end
8787
if library && segments.none? { |name, _| name.nil? }
8888
# 概要部が無くてもライブラリ自体が発見から消えないよう front matter のみ合成
@@ -145,7 +145,7 @@ def rewrite_includes(text, source_dir, new_dir)
145145
return text if source_dir == new_dir || text !~ /\#@include/
146146
base = File.expand_path(new_dir, '/')
147147
text.gsub(/^(\#@include\s*\()(.*?)(\))/) do
148-
pre, target, post = $1, $2, $3
148+
pre, target, post = $1, ($2 || raise), $3
149149
abs = File.expand_path(source_dir == '.' ? target : File.join(source_dir, target), '/')
150150
"#{pre}#{Pathname.new(abs).relative_path_from(base)}#{post}"
151151
end
@@ -182,25 +182,25 @@ def output_path(relpath, front_matter)
182182
# 関係を持たない H1 の直後や本文の空行・散文ゲートは触らない
183183
def normalize_header_regions(rrd)
184184
lines = rrd.lines
185-
out = []
185+
out = [] #: Array[String]
186186
i = 0
187187
while i < lines.length
188188
line = lines[i]
189189
out << line
190190
i += 1
191191
next unless line =~ EntitySplitter::H1_RE
192-
region = []
192+
region = [] #: Array[String]
193193
while i < lines.length &&
194194
(lines[i] =~ RELATION_RE || lines[i] =~ HEADER_DIR_RE || lines[i] =~ BLANK_LINE_RE)
195195
region << lines[i]
196196
i += 1
197197
end
198198
last_rel = region.rindex { |l| l =~ RELATION_RE }
199199
if last_rel
200-
head = region[0..last_rel].reject { |l| l =~ BLANK_LINE_RE }
201-
.map { |l| l =~ RELATION_RE ? "#{l.rstrip}\n" : l }
200+
head = (region[0..last_rel] || raise).reject { |l| l =~ BLANK_LINE_RE }
201+
.map { |l| l =~ RELATION_RE ? "#{l.rstrip}\n" : l }
202202
out.concat(head)
203-
out.concat(region[(last_rel + 1)..])
203+
out.concat(region[(last_rel + 1)..] || raise)
204204
else
205205
out.concat(region)
206206
end

0 commit comments

Comments
 (0)