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.
Files changed (152) 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 +48 -4
  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 +16 -17
  24. data/lib/bitclust/capi_converter.rb +20 -0
  25. data/lib/bitclust/classentry.rb +67 -17
  26. data/lib/bitclust/compat.rb +2 -2
  27. data/lib/bitclust/completion.rb +58 -27
  28. data/lib/bitclust/crossrubyutils.rb +8 -5
  29. data/lib/bitclust/database.rb +28 -8
  30. data/lib/bitclust/doc_converter.rb +63 -0
  31. data/lib/bitclust/docentry.rb +1 -1
  32. data/lib/bitclust/entity_splitter.rb +221 -0
  33. data/lib/bitclust/entry.rb +47 -7
  34. data/lib/bitclust/functiondatabase.rb +13 -1
  35. data/lib/bitclust/functionentry.rb +5 -1
  36. data/lib/bitclust/functionreferenceparser.rb +4 -4
  37. data/lib/bitclust/generators/epub.rb +10 -3
  38. data/lib/bitclust/htmlutils.rb +1 -1
  39. data/lib/bitclust/include_graph.rb +375 -0
  40. data/lib/bitclust/include_pruner.rb +144 -0
  41. data/lib/bitclust/interface.rb +1 -1
  42. data/lib/bitclust/libraryentry.rb +8 -7
  43. data/lib/bitclust/lineinput.rb +7 -5
  44. data/lib/bitclust/link_checker.rb +179 -0
  45. data/lib/bitclust/markdown_bridge.rb +186 -0
  46. data/lib/bitclust/markdown_orchestrator.rb +267 -0
  47. data/lib/bitclust/markdown_to_rrd.rb +824 -0
  48. data/lib/bitclust/markdown_tree.rb +217 -0
  49. data/lib/bitclust/mdcompiler.rb +883 -0
  50. data/lib/bitclust/mdparser.rb +347 -0
  51. data/lib/bitclust/messagecatalog.rb +3 -3
  52. data/lib/bitclust/method_since_calculator.rb +117 -0
  53. data/lib/bitclust/methoddatabase.rb +124 -14
  54. data/lib/bitclust/methodentry.rb +125 -7
  55. data/lib/bitclust/methodid.rb +12 -11
  56. data/lib/bitclust/methodsignature.rb +10 -4
  57. data/lib/bitclust/nameutils.rb +41 -22
  58. data/lib/bitclust/preprocessor.rb +66 -29
  59. data/lib/bitclust/rdcompiler.rb +195 -95
  60. data/lib/bitclust/refsdatabase.rb +23 -8
  61. data/lib/bitclust/reloadable_request_handler.rb +72 -0
  62. data/lib/bitclust/requesthandler.rb +21 -16
  63. data/lib/bitclust/ridatabase.rb +10 -4
  64. data/lib/bitclust/rrd_to_markdown.rb +925 -0
  65. data/lib/bitclust/rrdparser.rb +196 -30
  66. data/lib/bitclust/runner.rb +17 -2
  67. data/lib/bitclust/screen.rb +80 -11
  68. data/lib/bitclust/search_index_generator.rb +288 -0
  69. data/lib/bitclust/searcher.rb +69 -20
  70. data/lib/bitclust/simplesearcher.rb +24 -12
  71. data/lib/bitclust/subcommand.rb +7 -1
  72. data/lib/bitclust/subcommands/ancestors_command.rb +9 -2
  73. data/lib/bitclust/subcommands/checklink_command.rb +54 -0
  74. data/lib/bitclust/subcommands/chm_command.rb +15 -7
  75. data/lib/bitclust/subcommands/classes_command.rb +5 -0
  76. data/lib/bitclust/subcommands/extract_command.rb +5 -0
  77. data/lib/bitclust/subcommands/htmlfile_command.rb +21 -7
  78. data/lib/bitclust/subcommands/init_command.rb +4 -1
  79. data/lib/bitclust/subcommands/list_command.rb +12 -4
  80. data/lib/bitclust/subcommands/lookup_command.rb +28 -9
  81. data/lib/bitclust/subcommands/methods_command.rb +10 -2
  82. data/lib/bitclust/subcommands/methodsince_command.rb +80 -0
  83. data/lib/bitclust/subcommands/preproc_command.rb +5 -0
  84. data/lib/bitclust/subcommands/property_command.rb +2 -1
  85. data/lib/bitclust/subcommands/searchpage_command.rb +102 -0
  86. data/lib/bitclust/subcommands/server_command.rb +10 -8
  87. data/lib/bitclust/subcommands/setup_command.rb +24 -10
  88. data/lib/bitclust/subcommands/statichtml_command.rb +129 -6
  89. data/lib/bitclust/subcommands/update_command.rb +110 -2
  90. data/lib/bitclust/syntax_highlighter.rb +48 -4
  91. data/lib/bitclust/textutils.rb +3 -3
  92. data/lib/bitclust/version.rb +1 -1
  93. data/lib/bitclust/version_badges.rb +90 -0
  94. data/lib/bitclust/whole_file_gate.rb +137 -0
  95. data/test/test_app.rb +269 -0
  96. data/test/test_bitclust.rb +2 -0
  97. data/test/test_capi_converter.rb +91 -0
  98. data/test/test_class_screen.rb +413 -0
  99. data/test/test_copy_doc_md.rb +53 -0
  100. data/test/test_doc_converter.rb +78 -0
  101. data/test/test_entity_splitter.rb +231 -0
  102. data/test/test_entry.rb +207 -2
  103. data/test/test_eol_warning.rb +43 -0
  104. data/test/test_functionreferenceparser.rb +11 -0
  105. data/test/test_include_graph.rb +640 -0
  106. data/test/test_include_pruner.rb +133 -0
  107. data/test/test_link_checker.rb +114 -0
  108. data/test/test_lookup_command.rb +135 -0
  109. data/test/test_markdown_bridge.rb +217 -0
  110. data/test/test_markdown_orchestrator.rb +478 -0
  111. data/test/test_markdown_to_rrd.rb +757 -0
  112. data/test/test_markdown_tree.rb +369 -0
  113. data/test/test_mdcompiler.rb +1276 -0
  114. data/test/test_mdparser.rb +538 -0
  115. data/test/test_method_screen.rb +166 -0
  116. data/test/test_method_since_calculator.rb +290 -0
  117. data/test/test_methoddatabase.rb +16 -0
  118. data/test/test_methodentry.rb +328 -0
  119. data/test/test_methodsignature.rb +3 -1
  120. data/test/test_methodsince_command.rb +168 -0
  121. data/test/test_nameutils.rb +22 -0
  122. data/test/test_preprocessor.rb +215 -0
  123. data/test/test_rdcompiler.rb +401 -30
  124. data/test/test_refsdatabase.rb +32 -0
  125. data/test/test_rrd_to_markdown.rb +968 -0
  126. data/test/test_rrdparser.rb +295 -2
  127. data/test/test_run_ruby_wasm.rb +54 -0
  128. data/test/test_run_worker_prelude.rb +83 -0
  129. data/test/test_runner.rb +51 -0
  130. data/test/test_search_index_generator.rb +513 -0
  131. data/test/test_search_screen.rb +48 -0
  132. data/test/test_searcher.rb +132 -0
  133. data/test/test_searchpage_command.rb +167 -0
  134. data/test/test_simplesearcher.rb +28 -0
  135. data/test/test_statichtml_command.rb +204 -0
  136. data/test/test_syntax_highlighter.rb +60 -0
  137. data/test/test_whole_file_gate.rb +175 -0
  138. data/theme/default/js/NOTICE +42 -0
  139. data/theme/default/js/run-worker.js +111 -0
  140. data/theme/default/js/run.js +350 -0
  141. data/theme/default/js/search_controller.js +134 -0
  142. data/theme/default/js/search_init.js +176 -0
  143. data/theme/default/js/search_navigation.js +110 -0
  144. data/theme/default/js/search_page.js +202 -0
  145. data/theme/default/js/search_ranker.js +244 -0
  146. data/theme/default/script.js +91 -18
  147. data/theme/default/search.css +149 -0
  148. data/theme/default/style.css +193 -21
  149. data/theme/default/syntax-highlight.css +8 -0
  150. data/theme/lillia/style.css +45 -1
  151. metadata +161 -20
  152. 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