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
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BitClust
|
|
4
|
+
# 新パイプラインのファイル発見(MARKUP_SPEC §1.1)。
|
|
5
|
+
#
|
|
6
|
+
# md ツリー(manual/api/**/*.md 相当)を glob し、各ファイルの front matter と
|
|
7
|
+
# エンティティ H1 から次の3種に分類する。旧 LIBRARIES マニフェストと
|
|
8
|
+
# grouping 用 #@include は使わない。
|
|
9
|
+
# - エンティティ: エンティティ H1 を1つ以上持つ(front matter の library が所属)
|
|
10
|
+
# - ライブラリ: type: library を持つ(名前 = パスから .md を除いたもの)
|
|
11
|
+
# - 共有断片: どちらでもなく、いずれかの #@include から参照される
|
|
12
|
+
#
|
|
13
|
+
# 検証(ビルド警告に相当):
|
|
14
|
+
# - 孤児: どの分類にも入らないファイル(H1 の書き忘れ・参照されない断片の検出)
|
|
15
|
+
# - library の無いエンティティ / 未知の library を指すエンティティ
|
|
16
|
+
# - 関係リント: include/extend/alias の記述場所は front matter のみ。
|
|
17
|
+
# マルチエンティティファイルの front matter 関係キーと、
|
|
18
|
+
# 本文の H1 直後の関係行(ファイル種を問わず)を警告する
|
|
19
|
+
# - #@include 先の欠損
|
|
20
|
+
class MarkdownTree
|
|
21
|
+
ENTITY_H1_RE = /\A#(?!#)\s*(class|module|object|reopen|redefine)\s+(\S+)/
|
|
22
|
+
RELATION_LINE_RE = /\A(?:include|extend|alias)\s+\S/
|
|
23
|
+
RELATION_KEY_RE = /\A(?:include|extend|alias):\s*\z/
|
|
24
|
+
INCLUDE_RE = /\A\#[@%]include\s*\((.*?)\)/
|
|
25
|
+
FENCE_RE = /\A`{3,}/
|
|
26
|
+
BLANK_RE = /\A\s*\z/
|
|
27
|
+
|
|
28
|
+
def self.scan(root)
|
|
29
|
+
new(root).scan
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
attr_reader :libraries, :entities, :fragments, :warnings
|
|
33
|
+
|
|
34
|
+
def initialize(root)
|
|
35
|
+
@root = root
|
|
36
|
+
@libraries = {} # name => { path: }
|
|
37
|
+
@entities = {} # path => { names:, library: }
|
|
38
|
+
@fragments = []
|
|
39
|
+
@warnings = []
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def scan
|
|
43
|
+
files = Dir.glob('**/*.md', base: @root).sort
|
|
44
|
+
warn_case_collisions(files)
|
|
45
|
+
infos = files.to_h { |f| [f, parse_file(f)] }
|
|
46
|
+
|
|
47
|
+
referenced = {} #: Hash[String, bool]
|
|
48
|
+
infos.each do |path, info|
|
|
49
|
+
info[:includes].each do |target|
|
|
50
|
+
if (resolved = resolve(path, target, infos))
|
|
51
|
+
referenced[resolved] = true
|
|
52
|
+
else
|
|
53
|
+
@warnings << "include target not found: #{target} (from #{path})"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
infos.each do |path, info|
|
|
59
|
+
if info[:library_file]
|
|
60
|
+
# 名前は原則パス由来。ファイル名衝突回避で改名されたファイル
|
|
61
|
+
# (rdoc/rdoc.lib.md)は front matter の name: が正
|
|
62
|
+
@libraries[info[:name] || path.sub(/\.md\z/, '')] =
|
|
63
|
+
{ path: path, since: info[:since], until: info[:until] }
|
|
64
|
+
end
|
|
65
|
+
# include 参照され front matter を持たないファイルは、H1 を含んでいても
|
|
66
|
+
# 断片(transclusion 用。fiddle の版分岐チェーン等)
|
|
67
|
+
info[:fragment] = referenced[path] && !info[:library_file] && info[:library].nil?
|
|
68
|
+
if info[:fragment]
|
|
69
|
+
@fragments << path
|
|
70
|
+
elsif !info[:kinds].empty?
|
|
71
|
+
@entities[path] = { names: info[:kinds].map(&:last), kinds: info[:kinds],
|
|
72
|
+
library: info[:library],
|
|
73
|
+
memberships: info[:memberships],
|
|
74
|
+
since: info[:since], until: info[:until] }
|
|
75
|
+
elsif !info[:library_file]
|
|
76
|
+
@warnings << "orphan file (no entity H1, no type: library, not included): #{path}"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
# 検証は全ライブラリの登録後に行う(辞書順で先に来るエンティティが
|
|
80
|
+
# 未登録ライブラリを誤って「未知」と判定しないように)
|
|
81
|
+
infos.each do |path, info|
|
|
82
|
+
validate(path, info) unless info[:fragment]
|
|
83
|
+
end
|
|
84
|
+
self
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def validate(path, info)
|
|
90
|
+
unless info[:kinds].empty?
|
|
91
|
+
if info[:memberships].empty? && !info[:library_file]
|
|
92
|
+
@warnings << "entity with no library: #{path}"
|
|
93
|
+
else
|
|
94
|
+
info[:memberships].each do |m|
|
|
95
|
+
next if @libraries.key?(m[:library] || raise)
|
|
96
|
+
@warnings << "entity refers to unknown library #{m[:library]}: #{path}"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
if info[:kinds].size > 1 && info[:front_matter_relations]
|
|
100
|
+
@warnings << "relations in front matter of multi-entity file: #{path}"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
if info[:body_relations]
|
|
104
|
+
@warnings << "relations in body (front matter is the only place): #{path}"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# front matter(raw 走査。#@ を含むため YAML パーサは使わない)と本文
|
|
109
|
+
# (フェンス外の H1・H1 直後の関係行・#@include)を読む
|
|
110
|
+
def parse_file(path)
|
|
111
|
+
# @type var info: file_info
|
|
112
|
+
info = { kinds: [], library: nil, library_file: false, memberships: [],
|
|
113
|
+
front_matter_relations: false, body_relations: false, includes: [] }
|
|
114
|
+
lines = File.readlines(File.join(@root, path))
|
|
115
|
+
i = 0
|
|
116
|
+
if lines[0] =~ /\A---\s*\z/
|
|
117
|
+
i = 1
|
|
118
|
+
in_library_block = false
|
|
119
|
+
gate_stack = [] #: Array[[Symbol, String?]]
|
|
120
|
+
while i < lines.length && lines[i] !~ /\A---\s*\z/
|
|
121
|
+
line = lines[i]
|
|
122
|
+
if in_library_block
|
|
123
|
+
# 多重所属のゲート付きリスト(MARKUP_SPEC §1.2)。
|
|
124
|
+
# 項目は積まれているゲートをすべてまとって membership になる
|
|
125
|
+
case line
|
|
126
|
+
when /\A\s+- (\S+)/
|
|
127
|
+
m = { library: $1 }
|
|
128
|
+
gate_stack.each { |kind, ver| m[kind] = ver }
|
|
129
|
+
info[:memberships] << m
|
|
130
|
+
i += 1; next
|
|
131
|
+
when /\A\#[@%]since\s+(\S+)/ then gate_stack.push([:since, $1]); i += 1; next
|
|
132
|
+
when /\A\#[@%]until\s+(\S+)/ then gate_stack.push([:until, $1]); i += 1; next
|
|
133
|
+
when /\A\#[@%]end\s*\z/ then gate_stack.pop; i += 1; next
|
|
134
|
+
else
|
|
135
|
+
in_library_block = false # ブロック終端。この行は通常キーとして処理
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
case line
|
|
139
|
+
when /\Atype:\s*library\s*\z/ then info[:library_file] = true
|
|
140
|
+
when /\Aname:\s*(\S+)/ then info[:name] = $1
|
|
141
|
+
when /\Alibrary:\s*\z/ then in_library_block = true; gate_stack = [] #: Array[[Symbol, String?]]
|
|
142
|
+
when /\Alibrary:\s*(\S+)/ then info[:memberships] << { library: $1 }
|
|
143
|
+
when /\Asince:\s*"?([^"\s]+)"?/ then info[:since] = $1
|
|
144
|
+
when /\Auntil:\s*"?([^"\s]+)"?/ then info[:until] = $1
|
|
145
|
+
when RELATION_KEY_RE then info[:front_matter_relations] = true
|
|
146
|
+
end
|
|
147
|
+
i += 1
|
|
148
|
+
end
|
|
149
|
+
i += 1
|
|
150
|
+
end
|
|
151
|
+
info[:library] = info[:memberships].dig(0, :library)
|
|
152
|
+
|
|
153
|
+
in_fence = false
|
|
154
|
+
in_header = false
|
|
155
|
+
h1_gated = false
|
|
156
|
+
gate_depth = 0
|
|
157
|
+
(lines[i..] || raise).each do |line|
|
|
158
|
+
if line =~ FENCE_RE
|
|
159
|
+
in_fence = !in_fence
|
|
160
|
+
in_header = false
|
|
161
|
+
next
|
|
162
|
+
end
|
|
163
|
+
next if in_fence
|
|
164
|
+
|
|
165
|
+
# 版ゲートの深度。#@/#% ブロック内のヘッダ関係行は body 残置が正しい
|
|
166
|
+
# (H1 自体が版分岐する rbconfig 等)ので lint 対象から外す
|
|
167
|
+
case line
|
|
168
|
+
when /\A\#[@%](?:since|until|if|version)\b/ then gate_depth += 1
|
|
169
|
+
when /\A\#[@%]end\b/ then gate_depth -= 1 if gate_depth > 0
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
if line =~ ENTITY_H1_RE
|
|
173
|
+
info[:kinds] << [$1 || raise, $2 || raise]
|
|
174
|
+
in_header = true
|
|
175
|
+
h1_gated = gate_depth > 0
|
|
176
|
+
elsif line =~ INCLUDE_RE
|
|
177
|
+
info[:includes] << ($1 || raise)
|
|
178
|
+
in_header = false
|
|
179
|
+
elsif in_header
|
|
180
|
+
case line
|
|
181
|
+
when RELATION_LINE_RE
|
|
182
|
+
# 版分岐 H1(ゲート内の H1)のヘッダ領域は、ゲート直後の
|
|
183
|
+
# 共通関係行も含めて body 残置が正しいので lint しない
|
|
184
|
+
info[:body_relations] = true if gate_depth.zero? && !h1_gated
|
|
185
|
+
when /\A\#[@%]/, BLANK_RE then nil
|
|
186
|
+
else in_header = false
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
info
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# 大文字小文字のみが異なる名前は macOS/Windows の case-insensitive FS で
|
|
194
|
+
# チェックアウト不能になるため、衝突を警告する(途中のディレクトリ名も見る)
|
|
195
|
+
def warn_case_collisions(files)
|
|
196
|
+
entries = files.flat_map { |f|
|
|
197
|
+
parts = f.split('/')
|
|
198
|
+
(1..parts.size).map { |n| parts.first(n).join('/') }
|
|
199
|
+
}.uniq
|
|
200
|
+
entries.group_by(&:downcase).each_value do |names|
|
|
201
|
+
next if names.size < 2
|
|
202
|
+
@warnings << "case-insensitive filename collision: #{names.sort.join(', ')}"
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# include 元ディレクトリ相対で target → target.md → (.rd を .md に)の順に解決
|
|
207
|
+
def resolve(from, target, infos)
|
|
208
|
+
base = File.dirname(from)
|
|
209
|
+
[target, "#{target}.md", "#{target.sub(/\.rd\z/, '')}.md"].uniq.each do |cand|
|
|
210
|
+
rel = base == '.' ? cand : File.join(base, cand)
|
|
211
|
+
rel = File.expand_path(rel, '/').delete_prefix('/')
|
|
212
|
+
return rel if infos.key?(rel)
|
|
213
|
+
end
|
|
214
|
+
nil
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|