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
|
@@ -20,7 +20,7 @@ module BitClust
|
|
|
20
20
|
@prepare = nil
|
|
21
21
|
@cleanup = nil
|
|
22
22
|
@purge = nil
|
|
23
|
-
@versions = ["
|
|
23
|
+
@versions = ["3.3.0", "3.4.0", "4.0.0"]
|
|
24
24
|
@update = true
|
|
25
25
|
@parser.banner = "Usage: #{File.basename($0, '.*')} setup [options]"
|
|
26
26
|
@parser.on('--prepare', 'Prepare config file and checkout repository. Do not create database.') {
|
|
@@ -40,6 +40,11 @@ module BitClust
|
|
|
40
40
|
}
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
# DB は ~/.bitclust 配下に自前で作るのでグローバル --database は不要
|
|
44
|
+
def needs_database?
|
|
45
|
+
false
|
|
46
|
+
end
|
|
47
|
+
|
|
43
48
|
def exec(argv, options)
|
|
44
49
|
purge if @purge
|
|
45
50
|
prepare
|
|
@@ -49,18 +54,18 @@ module BitClust
|
|
|
49
54
|
prefix = "#{@config[:database_prefix]}-#{version}"
|
|
50
55
|
FileUtils.rm_rf(prefix) if @cleanup
|
|
51
56
|
init_argv = ["version=#{version}", "encoding=#{@config[:encoding]}"]
|
|
52
|
-
|
|
57
|
+
# @type var init_options: Subcommand::options
|
|
58
|
+
init_options = { :prefix => prefix, :capi => false }
|
|
53
59
|
InitCommand.new.exec(init_argv, init_options)
|
|
54
|
-
update_method_database(prefix, ["--
|
|
55
|
-
|
|
56
|
-
update_function_database(prefix, update_argv)
|
|
60
|
+
update_method_database(prefix, ["--markdowntree=#{@config[:mdtree]}"])
|
|
61
|
+
update_function_database(prefix, ["--markdowntree=#{@config[:capi_mdtree]}"])
|
|
57
62
|
end
|
|
58
63
|
end
|
|
59
64
|
|
|
60
65
|
private
|
|
61
66
|
|
|
62
67
|
def purge
|
|
63
|
-
home_directory = Pathname(ENV
|
|
68
|
+
home_directory = Pathname(ENV.fetch("HOME"))
|
|
64
69
|
config_dir = home_directory + ".bitclust"
|
|
65
70
|
print "Remove all generated files..."
|
|
66
71
|
FileUtils.rm_rf(config_dir.to_s)
|
|
@@ -69,7 +74,7 @@ module BitClust
|
|
|
69
74
|
end
|
|
70
75
|
|
|
71
76
|
def prepare
|
|
72
|
-
home_directory = Pathname(ENV
|
|
77
|
+
home_directory = Pathname(ENV.fetch("HOME")).expand_path
|
|
73
78
|
config_dir = home_directory + ".bitclust"
|
|
74
79
|
config_dir.mkpath
|
|
75
80
|
config_path = config_dir + "config"
|
|
@@ -79,17 +84,24 @@ module BitClust
|
|
|
79
84
|
:encoding => "utf-8",
|
|
80
85
|
:versions => @versions,
|
|
81
86
|
:default_version => @versions.max,
|
|
82
|
-
:
|
|
83
|
-
:
|
|
87
|
+
:mdtree => (rubydoc_dir + "manual/api").to_s,
|
|
88
|
+
:capi_mdtree => (rubydoc_dir + "manual/capi").to_s,
|
|
84
89
|
:baseurl => "http://localhost:10080",
|
|
85
90
|
:port => "10080",
|
|
86
91
|
:pid_file => "/tmp/bitclust.pid",
|
|
87
92
|
}
|
|
88
93
|
if config_path.exist?
|
|
89
94
|
@config = YAML.load_file(config_path)
|
|
95
|
+
unless @config[:mdtree]
|
|
96
|
+
# Markdown 移行(2026-07)前の設定ファイル。ソースの取得形態が
|
|
97
|
+
# 変わっているため、作り直しを案内する
|
|
98
|
+
warn "Old config file (before the Markdown migration) found: #{config_path}"
|
|
99
|
+
warn "Run `#{File.basename($0, '.*')} setup --purge` and setup again."
|
|
100
|
+
exit 1
|
|
101
|
+
end
|
|
90
102
|
unless @config[:versions].sort == @versions.sort
|
|
91
103
|
print("overwrite config file? > [y/N]")
|
|
92
|
-
if /\Ay\z/i =~ $stdin.gets
|
|
104
|
+
if /\Ay\z/i =~ $stdin.gets&.chomp
|
|
93
105
|
@config[:versions] = @versions
|
|
94
106
|
@config[:default_version] = @versions.max
|
|
95
107
|
generate_config(config_path, @config)
|
|
@@ -132,6 +144,7 @@ module BitClust
|
|
|
132
144
|
end
|
|
133
145
|
|
|
134
146
|
def update_method_database(prefix, argv)
|
|
147
|
+
# @type var options: Subcommand::options
|
|
135
148
|
options = {
|
|
136
149
|
:prefix => prefix,
|
|
137
150
|
:capi => false,
|
|
@@ -142,6 +155,7 @@ module BitClust
|
|
|
142
155
|
end
|
|
143
156
|
|
|
144
157
|
def update_function_database(prefix, argv)
|
|
158
|
+
# @type var options: Subcommand::options
|
|
145
159
|
options = {
|
|
146
160
|
:prefix => prefix,
|
|
147
161
|
:capi => true,
|
|
@@ -12,6 +12,7 @@ require 'bitclust/nameutils'
|
|
|
12
12
|
require 'bitclust/subcommand'
|
|
13
13
|
require 'bitclust/progress_bar'
|
|
14
14
|
require 'bitclust/silent_progress_bar'
|
|
15
|
+
require 'bitclust/search_index_generator'
|
|
15
16
|
|
|
16
17
|
module BitClust
|
|
17
18
|
module Subcommands
|
|
@@ -45,7 +46,7 @@ module BitClust
|
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
def method_url(spec)
|
|
48
|
-
cname, tmark, mname =
|
|
49
|
+
cname, tmark, mname = split_method_spec(spec)
|
|
49
50
|
filename = html_filename(encodename_package(mname), @suffix)
|
|
50
51
|
@bitclust_html_base +
|
|
51
52
|
"/method/#{encodename_package(cname)}/#{typemark2char(tmark)}/#{filename}"
|
|
@@ -90,7 +91,7 @@ module BitClust
|
|
|
90
91
|
end
|
|
91
92
|
|
|
92
93
|
def edit_url(location)
|
|
93
|
-
"#{@edit_base_url}/#{location.file}
|
|
94
|
+
"#{@edit_base_url}/#{location.file}".sub(/([^:])\/\/+/, "\\1/")
|
|
94
95
|
end
|
|
95
96
|
|
|
96
97
|
def encodename_package(str)
|
|
@@ -120,6 +121,10 @@ module BitClust
|
|
|
120
121
|
@gtm_tracking_id = nil
|
|
121
122
|
@meta_robots_content = ["noindex"]
|
|
122
123
|
@stop_on_syntax_error = true
|
|
124
|
+
@eol_warning = false
|
|
125
|
+
@run_ruby_wasm = nil
|
|
126
|
+
@sitemap_baseurl = nil
|
|
127
|
+
@sitemap_paths = []
|
|
123
128
|
@parser.banner = "Usage: #{File.basename($0, '.*')} statichtml [options]"
|
|
124
129
|
@parser.on('-o', '--outputdir=PATH', 'Output directory') do |path|
|
|
125
130
|
begin
|
|
@@ -156,6 +161,15 @@ module BitClust
|
|
|
156
161
|
@parser.on('--meta-robots-content=VALUE1,VALUE2,...', Array, 'HTML <meta> element: <meta name="robots" content="VALUE1,VALUE2..."') do |values|
|
|
157
162
|
@meta_robots_content = values
|
|
158
163
|
end
|
|
164
|
+
@parser.on('--[no-]eol-warning', 'Show a warning banner that this Ruby version is no longer maintained') do |boolean|
|
|
165
|
+
@eol_warning = boolean
|
|
166
|
+
end
|
|
167
|
+
@parser.on('--run-ruby-wasm=WASM_URL', 'Add a RUN button to Ruby sample code, executing it in-browser with the given ruby.wasm') do |url|
|
|
168
|
+
@run_ruby_wasm = url
|
|
169
|
+
end
|
|
170
|
+
@parser.on('--sitemap-baseurl=URL', 'Generate sitemap.xml under the output directory, with <loc> built from this base URL (e.g. https://docs.ruby-lang.org/ja/3.4/). Omit to skip sitemap.xml generation (default)') do |url|
|
|
171
|
+
@sitemap_baseurl = url
|
|
172
|
+
end
|
|
159
173
|
@parser.on('--no-stop-on-syntax-error', 'Do not stop on syntax error') do |boolean|
|
|
160
174
|
@stop_on_syntax_error = boolean
|
|
161
175
|
end
|
|
@@ -173,7 +187,7 @@ module BitClust
|
|
|
173
187
|
manager = ScreenManager.new(@manager_config)
|
|
174
188
|
|
|
175
189
|
db.transaction do
|
|
176
|
-
methods = {}
|
|
190
|
+
methods = {} #: Hash[String, Array[MethodEntry]]
|
|
177
191
|
db.methods.each_with_index do |entry, i|
|
|
178
192
|
next if entry.undefined?
|
|
179
193
|
|
|
@@ -193,22 +207,33 @@ module BitClust
|
|
|
193
207
|
end
|
|
194
208
|
|
|
195
209
|
@urlmapper.bitclust_html_base = '..'
|
|
196
|
-
|
|
210
|
+
library_index_path = @outputdir + "library/#{html_filename("index", @suffix)}"
|
|
211
|
+
create_file(library_index_path,
|
|
197
212
|
manager.library_index_screen(db.libraries.sort, {:database => db}).body,
|
|
198
213
|
:verbose => @verbose)
|
|
199
|
-
|
|
214
|
+
record_sitemap_path(library_index_path)
|
|
215
|
+
class_index_path = @outputdir + "class/#{html_filename("index", @suffix)}"
|
|
216
|
+
create_file(class_index_path,
|
|
200
217
|
manager.class_index_screen(db.classes.sort, {:database => db}).body,
|
|
201
218
|
:verbose => @verbose)
|
|
202
|
-
|
|
219
|
+
record_sitemap_path(class_index_path)
|
|
220
|
+
function_index_path = @outputdir + "function/#{html_filename("index", @suffix)}"
|
|
221
|
+
create_file(function_index_path,
|
|
203
222
|
manager.function_index_screen(fdb.functions.sort, { :database => fdb }).body,
|
|
204
223
|
:verbose => @verbose)
|
|
224
|
+
record_sitemap_path(function_index_path)
|
|
205
225
|
create_index_html(@outputdir)
|
|
226
|
+
create_search_index(@outputdir, db, fdb)
|
|
227
|
+
if baseurl = @sitemap_baseurl
|
|
228
|
+
create_sitemap(@outputdir, baseurl)
|
|
229
|
+
end
|
|
206
230
|
FileUtils.cp(@manager_config[:themedir] + @manager_config[:css_url],
|
|
207
231
|
@outputdir.to_s, :verbose => @verbose, :preserve => true)
|
|
208
232
|
FileUtils.cp(@manager_config[:themedir] + "syntax-highlight.css",
|
|
209
233
|
@outputdir.to_s, :verbose => @verbose, :preserve => true)
|
|
210
234
|
FileUtils.cp(@manager_config[:themedir] + "script.js",
|
|
211
235
|
@outputdir.to_s, :verbose => @verbose, :preserve => true)
|
|
236
|
+
copy_run_ruby_wasm_script if @run_ruby_wasm
|
|
212
237
|
FileUtils.cp(@manager_config[:themedir] + @manager_config[:favicon_url],
|
|
213
238
|
@outputdir.to_s, :verbose => @verbose, :preserve => true)
|
|
214
239
|
Dir.mktmpdir do |tmpdir|
|
|
@@ -240,6 +265,8 @@ module BitClust
|
|
|
240
265
|
:gtm_tracking_id => @gtm_tracking_id,
|
|
241
266
|
:meta_robots_content => @meta_robots_content,
|
|
242
267
|
:stop_on_syntax_error => @stop_on_syntax_error,
|
|
268
|
+
:eol_warning => @eol_warning,
|
|
269
|
+
:run_ruby_wasm => @run_ruby_wasm,
|
|
243
270
|
}
|
|
244
271
|
@manager_config[:urlmapper] = URLMapperEx.new(@manager_config)
|
|
245
272
|
@urlmapper = @manager_config[:urlmapper]
|
|
@@ -290,6 +317,101 @@ HERE
|
|
|
290
317
|
}
|
|
291
318
|
end
|
|
292
319
|
|
|
320
|
+
SEARCH_JS_FILES = %w[
|
|
321
|
+
search_navigation.js search_ranker.js search_controller.js search_init.js
|
|
322
|
+
].freeze
|
|
323
|
+
|
|
324
|
+
def create_search_index(outputdir, db, fdb)
|
|
325
|
+
generator = SearchIndexGenerator.new(suffix: @suffix,
|
|
326
|
+
fs_casesensitive: @fs_casesensitive)
|
|
327
|
+
jsdir = outputdir + "js"
|
|
328
|
+
FileUtils.mkdir_p(jsdir) unless jsdir.directory?
|
|
329
|
+
create_file(jsdir + "search_data.js",
|
|
330
|
+
generator.to_js(db, fdb),
|
|
331
|
+
:verbose => @verbose)
|
|
332
|
+
themedir = @manager_config[:themedir]
|
|
333
|
+
SEARCH_JS_FILES.each do |js|
|
|
334
|
+
FileUtils.cp(themedir + "js" + js, jsdir.to_s,
|
|
335
|
+
:verbose => @verbose, :preserve => true)
|
|
336
|
+
end
|
|
337
|
+
# Ship the MIT notice for the vendored Aliki files alongside them.
|
|
338
|
+
FileUtils.cp(themedir + "js" + "NOTICE", jsdir.to_s,
|
|
339
|
+
:verbose => @verbose, :preserve => true)
|
|
340
|
+
FileUtils.cp(themedir + "search.css", outputdir.to_s,
|
|
341
|
+
:verbose => @verbose, :preserve => true)
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# A single sitemap.xml file supports at most 50,000 URLs
|
|
345
|
+
# (https://www.sitemaps.org/protocol.html#index). Splitting into a
|
|
346
|
+
# sitemap index is out of scope for now (small start); if the site
|
|
347
|
+
# grows past this, only the first MAX_SITEMAP_URLS pages are listed
|
|
348
|
+
# and a warning is printed.
|
|
349
|
+
MAX_SITEMAP_URLS = 50_000
|
|
350
|
+
|
|
351
|
+
# The 5 characters that need escaping to embed arbitrary text as XML
|
|
352
|
+
# character data (used for the <loc> contents below).
|
|
353
|
+
XML_ESCAPES = {
|
|
354
|
+
'&' => '&',
|
|
355
|
+
'<' => '<',
|
|
356
|
+
'>' => '>',
|
|
357
|
+
'"' => '"',
|
|
358
|
+
"'" => ''',
|
|
359
|
+
}.freeze
|
|
360
|
+
|
|
361
|
+
def escape_xml(str)
|
|
362
|
+
str.gsub(/[&<>"']/) {|c| XML_ESCAPES.fetch(c) }
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
# Remember +path+ (an HTML page actually written under @outputdir) so
|
|
366
|
+
# that create_sitemap can list it later. A no-op unless
|
|
367
|
+
# --sitemap-baseurl was given, so sitemap generation has no effect on
|
|
368
|
+
# the rest of the run when the option is not used.
|
|
369
|
+
def record_sitemap_path(path)
|
|
370
|
+
return unless @sitemap_baseurl
|
|
371
|
+
@sitemap_paths << path.relative_path_from(@outputdir).to_s
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# Generate outputdir/sitemap.xml: a plain XML sitemap
|
|
375
|
+
# (https://www.sitemaps.org/protocol.html) listing every page
|
|
376
|
+
# recorded via record_sitemap_path, as baseurl + relative path.
|
|
377
|
+
def create_sitemap(outputdir, baseurl)
|
|
378
|
+
paths = @sitemap_paths
|
|
379
|
+
if paths.size > MAX_SITEMAP_URLS
|
|
380
|
+
$stderr.puts "warning: #{paths.size} pages found, but a single sitemap.xml supports at most #{MAX_SITEMAP_URLS} URLs; only the first #{MAX_SITEMAP_URLS} are included. Consider a sitemap index if you need the rest."
|
|
381
|
+
paths = paths.first(MAX_SITEMAP_URLS)
|
|
382
|
+
end
|
|
383
|
+
base = baseurl.end_with?('/') ? baseurl : "#{baseurl}/"
|
|
384
|
+
xml = +%(<?xml version="1.0" encoding="UTF-8"?>\n)
|
|
385
|
+
xml << %(<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n)
|
|
386
|
+
paths.each do |path|
|
|
387
|
+
xml << "<url><loc>#{escape_xml(base + path)}</loc></url>\n"
|
|
388
|
+
end
|
|
389
|
+
xml << "</urlset>\n"
|
|
390
|
+
create_file(outputdir + "sitemap.xml", xml, :verbose => @verbose)
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
# Scripts for the RUN-button feature (statichtml --run-ruby-wasm):
|
|
394
|
+
# run.js sets up the button and compiles the wasm module on the main
|
|
395
|
+
# thread; run-worker.js is the module Worker it spawns per execution
|
|
396
|
+
# (see theme/default/js/run.js for why: STOP/timeout both just
|
|
397
|
+
# Worker#terminate() it). Both are required for the feature to work.
|
|
398
|
+
RUN_RUBY_WASM_JS_FILES = %w[run.js run-worker.js].freeze
|
|
399
|
+
|
|
400
|
+
# Copy the RUN-button scripts. A themedir missing one of them is
|
|
401
|
+
# tolerated: warn and skip instead of aborting the whole build.
|
|
402
|
+
def copy_run_ruby_wasm_script
|
|
403
|
+
jsdir = @outputdir + "js"
|
|
404
|
+
RUN_RUBY_WASM_JS_FILES.each do |name|
|
|
405
|
+
src = @manager_config[:themedir] + "js" + name
|
|
406
|
+
unless src.file?
|
|
407
|
+
$stderr.puts "warning: #{src} not found; RUN button script not copied"
|
|
408
|
+
next
|
|
409
|
+
end
|
|
410
|
+
FileUtils.mkdir_p(jsdir) unless jsdir.directory?
|
|
411
|
+
FileUtils.cp(src.to_s, jsdir.to_s, :verbose => @verbose, :preserve => true)
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
293
415
|
def create_html_file(entry, manager, outputdir, db)
|
|
294
416
|
e = entry.is_a?(Array) ? entry.sort.first : entry
|
|
295
417
|
case e.type_id
|
|
@@ -328,6 +450,7 @@ HERE
|
|
|
328
450
|
path.open('w') do |f|
|
|
329
451
|
f.write(html)
|
|
330
452
|
end
|
|
453
|
+
record_sitemap_path(path)
|
|
331
454
|
end
|
|
332
455
|
|
|
333
456
|
def create_file(path, str, options = {})
|
|
@@ -16,11 +16,15 @@ module BitClust
|
|
|
16
16
|
def initialize
|
|
17
17
|
super
|
|
18
18
|
@root = nil
|
|
19
|
+
@markdowntree = nil
|
|
19
20
|
@library = nil
|
|
20
21
|
@parser.banner = "Usage: #{File.basename($0, '.*')} update [<file>...]"
|
|
21
22
|
@parser.on('--stdlibtree=ROOT', 'Process stdlib source directory tree.') {|path|
|
|
22
23
|
@root = path
|
|
23
24
|
}
|
|
25
|
+
@parser.on('--markdowntree=ROOT', 'Process Markdown source directory tree.') {|path|
|
|
26
|
+
@markdowntree = path
|
|
27
|
+
}
|
|
24
28
|
@parser.on('--library-name=NAME', 'Use NAME for library name in file mode.') {|name|
|
|
25
29
|
@library = name
|
|
26
30
|
}
|
|
@@ -28,25 +32,129 @@ module BitClust
|
|
|
28
32
|
|
|
29
33
|
def parse(argv)
|
|
30
34
|
super
|
|
31
|
-
if not @root and argv.empty?
|
|
35
|
+
if not @root and not @markdowntree and argv.empty?
|
|
32
36
|
error "no input file given"
|
|
33
37
|
end
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
def exec(argv, options)
|
|
37
41
|
super
|
|
42
|
+
if @markdowntree
|
|
43
|
+
# ネイティブ md パース(M3)。source には md が入り、描画は
|
|
44
|
+
# MDCompiler(GFM)が選択される。ブリッジは検証用に温存
|
|
45
|
+
# (BITCLUST_MD_BRIDGE=1 で旧経路)
|
|
46
|
+
if ENV['BITCLUST_MD_BRIDGE'] == '1'
|
|
47
|
+
argv = @db.is_a?(FunctionDatabase) ? prepare_capi_markdowntree
|
|
48
|
+
: (prepare_markdowntree; argv)
|
|
49
|
+
else
|
|
50
|
+
@db.transaction {
|
|
51
|
+
@db.update_by_markdowntree(@markdowntree || raise)
|
|
52
|
+
}
|
|
53
|
+
return
|
|
54
|
+
end
|
|
55
|
+
end
|
|
38
56
|
@db.transaction {
|
|
39
57
|
if @root
|
|
40
|
-
|
|
58
|
+
db = @db
|
|
59
|
+
db.is_a?(MethodDatabase) or raise
|
|
60
|
+
db.update_by_stdlibtree(@root || raise)
|
|
41
61
|
end
|
|
42
62
|
argv.each do |path|
|
|
43
63
|
@db.update_by_file path, @library || guess_library_name(path)
|
|
44
64
|
end
|
|
45
65
|
}
|
|
66
|
+
remap_source_locations if @markdowntree
|
|
67
|
+
ensure
|
|
68
|
+
FileUtils.remove_entry(@bridge_dir) if @bridge_dir
|
|
46
69
|
end
|
|
47
70
|
|
|
48
71
|
private
|
|
49
72
|
|
|
73
|
+
# Markdown ツリーを旧形式の rd ツリーへブリッジし、既存の
|
|
74
|
+
# stdlibtree 更新機構にそのまま渡す。
|
|
75
|
+
# doc(散文ページ)は stdlibtree/../../doc から読まれるため、
|
|
76
|
+
# md ツリー側に doc/ が同居していればレイアウトを合わせる
|
|
77
|
+
# (manual/api を渡すと manual/doc を拾う。md ならブリッジ変換、
|
|
78
|
+
# .rd のまま(移行中)なら symlink)。
|
|
79
|
+
def prepare_markdowntree
|
|
80
|
+
require 'tmpdir'
|
|
81
|
+
require 'bitclust/markdown_bridge'
|
|
82
|
+
@bridge_dir = Dir.mktmpdir('bitclust-md-bridge')
|
|
83
|
+
root = File.join(@bridge_dir, 'api/src')
|
|
84
|
+
bridge = MarkdownBridge.build(@markdowntree || raise, root)
|
|
85
|
+
@location_map = { root => [@markdowntree, bridge.source_map] }
|
|
86
|
+
# markdowntree と同じ渡され方(相対/絶対)を保った隣接 doc パス。
|
|
87
|
+
# copy_doc は stdlibtree 相対(../../doc/...)の location を記録する
|
|
88
|
+
doc = File.join(File.dirname(@markdowntree || raise), 'doc')
|
|
89
|
+
if File.directory?(doc)
|
|
90
|
+
if Dir.glob(File.join(doc, '**/*.md')).any?
|
|
91
|
+
doc_map = MarkdownBridge.build_doc(doc, File.join(@bridge_dir, 'doc'))
|
|
92
|
+
else
|
|
93
|
+
FileUtils.ln_s(File.expand_path(doc), File.join(@bridge_dir, 'doc'))
|
|
94
|
+
doc_map = nil
|
|
95
|
+
end
|
|
96
|
+
@location_map[File.join(@bridge_dir, 'doc')] = [doc, doc_map]
|
|
97
|
+
@location_map['../../doc'] = [doc, doc_map]
|
|
98
|
+
end
|
|
99
|
+
@root ||= root
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# C API の Markdown ツリー(manual/capi)を旧形式の .rd 群へ変換し、
|
|
103
|
+
# ファイル引数として返す(--capi update <files> の経路に載せる)
|
|
104
|
+
def prepare_capi_markdowntree
|
|
105
|
+
require 'tmpdir'
|
|
106
|
+
require 'bitclust/markdown_to_rrd'
|
|
107
|
+
@bridge_dir = Dir.mktmpdir('bitclust-capi-bridge')
|
|
108
|
+
# src/ 配下に置く: guess_library_name が src/ 以降を id とするため
|
|
109
|
+
# (関数の filename が旧経路と同じ「eval.c」等になる)
|
|
110
|
+
src = File.join(@bridge_dir, 'src')
|
|
111
|
+
FileUtils.mkdir_p(src)
|
|
112
|
+
capi_map = {} #: Hash[String, String]
|
|
113
|
+
files = Dir.glob(File.join(@markdowntree || raise, '*.md')).sort.map do |f|
|
|
114
|
+
name = "#{File.basename(f, '.md')}.rd"
|
|
115
|
+
rd = File.join(src, name)
|
|
116
|
+
File.write(rd, MarkdownToRRD.convert(File.read(f), capi: true))
|
|
117
|
+
capi_map[name] = "#{File.basename(f, '.md')}.md"
|
|
118
|
+
rd
|
|
119
|
+
end
|
|
120
|
+
@location_map = { src => [@markdowntree, capi_map] }
|
|
121
|
+
files
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# source_location のブリッジ一時パスを manual/ の md パスへ再マップする。
|
|
125
|
+
# 編集リンク(statichtml --edit-base-url)が凍結 refm でなく編集対象の
|
|
126
|
+
# md を指すようにするため。行番号はブリッジ rd 基準の近似で、
|
|
127
|
+
# front matter 等の分だけ md とずれることがある
|
|
128
|
+
def remap_source_locations
|
|
129
|
+
db = @db
|
|
130
|
+
entries =
|
|
131
|
+
if db.is_a?(FunctionDatabase)
|
|
132
|
+
db.functions
|
|
133
|
+
else
|
|
134
|
+
db.is_a?(MethodDatabase) or raise
|
|
135
|
+
db.libraries + db.classes + db.methods + db.docs
|
|
136
|
+
end
|
|
137
|
+
entries.each do |entry|
|
|
138
|
+
loc = entry.source_location or next
|
|
139
|
+
remapped = remap_location(loc) or next
|
|
140
|
+
entry.source_location = remapped
|
|
141
|
+
entry.save
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def remap_location(loc)
|
|
146
|
+
file = loc.file
|
|
147
|
+
file.is_a?(String) or raise
|
|
148
|
+
@location_map.each do |bridge_root, (md_root, map)|
|
|
149
|
+
prefix = "#{bridge_root}/"
|
|
150
|
+
next unless file.start_with?(prefix)
|
|
151
|
+
rel = file.delete_prefix(prefix)
|
|
152
|
+
rel = map[rel] || rel if map
|
|
153
|
+
return Location.new(File.join(md_root, rel), loc.line)
|
|
154
|
+
end
|
|
155
|
+
nil
|
|
156
|
+
end
|
|
157
|
+
|
|
50
158
|
def guess_library_name(path)
|
|
51
159
|
if %r<(\A|/)src/> =~ path
|
|
52
160
|
path.sub(%r<.*(\A|/)src/>, '').sub(/\.rd\z/, '')
|
|
@@ -56,6 +56,7 @@ module BitClust
|
|
|
56
56
|
nl: nil, # \n
|
|
57
57
|
op: "o", # operator
|
|
58
58
|
period: "p", # .
|
|
59
|
+
qsymbols_beg: "ss", # %i(
|
|
59
60
|
qwords_beg: "sx", # %w(
|
|
60
61
|
rbrace: "p", # }
|
|
61
62
|
rbracket: "p", # ]
|
|
@@ -65,6 +66,7 @@ module BitClust
|
|
|
65
66
|
semicolon: nil, # ;
|
|
66
67
|
sp: nil, # space
|
|
67
68
|
symbeg: "ss", # :
|
|
69
|
+
symbols_beg: "ss", # %I(
|
|
68
70
|
tlambda: "o", # ->
|
|
69
71
|
tlambeg: "p", # (->) {
|
|
70
72
|
tstring_beg: nil, # " (string")
|
|
@@ -131,7 +133,7 @@ module BitClust
|
|
|
131
133
|
end
|
|
132
134
|
|
|
133
135
|
def on_default(event, token, data)
|
|
134
|
-
event_name = event.to_s.sub(/\Aon_/, "")
|
|
136
|
+
event_name = event.to_s.sub(/\Aon_/, "") ## :on_event --> "event"
|
|
135
137
|
style = COLORS[event_name.to_sym]
|
|
136
138
|
escaped_token = escape_html(token)
|
|
137
139
|
data << (style ? "<span class=\"#{style}\">#{escaped_token}</span>" : escaped_token)
|
|
@@ -181,6 +183,9 @@ module BitClust
|
|
|
181
183
|
@name_buffer << token
|
|
182
184
|
when @stack.last == :module
|
|
183
185
|
@name_buffer << token
|
|
186
|
+
when @stack.last == :symbol
|
|
187
|
+
data << "#{token}</span>"
|
|
188
|
+
@stack.pop
|
|
184
189
|
else
|
|
185
190
|
on_default(:on_const, token, data)
|
|
186
191
|
end
|
|
@@ -216,8 +221,11 @@ module BitClust
|
|
|
216
221
|
|
|
217
222
|
def on_op(token, data)
|
|
218
223
|
case
|
|
219
|
-
when token == "::" && [:class, :module].include?(@stack.last)
|
|
224
|
+
when token == "::" && [:class, :module].include?(_ = @stack.last)
|
|
220
225
|
@name_buffer << token
|
|
226
|
+
when token == "<<" && @stack.last == :class
|
|
227
|
+
@stack.pop
|
|
228
|
+
on_default(:on_op, token, data)
|
|
221
229
|
else
|
|
222
230
|
@stack.pop if @stack.last == :method_call
|
|
223
231
|
on_default(:on_op, token, data)
|
|
@@ -269,6 +277,28 @@ module BitClust
|
|
|
269
277
|
on_default(:on_nl, token, data)
|
|
270
278
|
end
|
|
271
279
|
|
|
280
|
+
def on_semicolon(token, data)
|
|
281
|
+
case
|
|
282
|
+
when @name_buffer.empty?
|
|
283
|
+
return on_default(:on_semicolon, token, data)
|
|
284
|
+
when @stack.last == :module
|
|
285
|
+
name = @name_buffer.join
|
|
286
|
+
data << "<span class=\"nn\">#{name}</span>"
|
|
287
|
+
@stack.pop
|
|
288
|
+
@name_buffer.clear
|
|
289
|
+
when @stack.last == :class
|
|
290
|
+
namespace = @name_buffer.values_at(0..-3).join
|
|
291
|
+
operator = @name_buffer[-2]
|
|
292
|
+
name = @name_buffer.last
|
|
293
|
+
data << "<span class=\"nn\">#{namespace}</span>"
|
|
294
|
+
data << "<span class=\"o\">#{operator}</span>"
|
|
295
|
+
data << "<span class=\"nc\">#{name}</span>"
|
|
296
|
+
@stack.pop
|
|
297
|
+
@name_buffer.clear
|
|
298
|
+
end
|
|
299
|
+
on_default(:on_semicolon, token, data)
|
|
300
|
+
end
|
|
301
|
+
|
|
272
302
|
def on_regexp_beg(token, data)
|
|
273
303
|
style = COLORS[:regexp_beg]
|
|
274
304
|
data << "<span class=\"#{style}\">#{token}"
|
|
@@ -316,7 +346,7 @@ module BitClust
|
|
|
316
346
|
case
|
|
317
347
|
when token == "'"
|
|
318
348
|
data << "#{token}</span>"
|
|
319
|
-
when [
|
|
349
|
+
when %i[qwords words qsymbols symbols].include?(_ = @stack.last)
|
|
320
350
|
@stack.pop
|
|
321
351
|
data << "#{token}</span>"
|
|
322
352
|
else
|
|
@@ -340,6 +370,20 @@ module BitClust
|
|
|
340
370
|
data
|
|
341
371
|
end
|
|
342
372
|
|
|
373
|
+
def on_qsymbols_beg(token, data)
|
|
374
|
+
@stack.push(:qsymbols)
|
|
375
|
+
style = COLORS[:qsymbols_beg]
|
|
376
|
+
data << "<span class=\"#{style}\">#{token}"
|
|
377
|
+
data
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def on_symbols_beg(token, data)
|
|
381
|
+
@stack.push(:symbols)
|
|
382
|
+
style = COLORS[:symbols_beg]
|
|
383
|
+
data << "<span class=\"#{style}\">#{token}"
|
|
384
|
+
data
|
|
385
|
+
end
|
|
386
|
+
|
|
343
387
|
def on_heredoc_beg(token, data)
|
|
344
388
|
@stack.push(:heredoc)
|
|
345
389
|
on_default(:on_heredoc_beg, token, data)
|
|
@@ -363,7 +407,7 @@ module BitClust
|
|
|
363
407
|
def on___end__(token, data)
|
|
364
408
|
on_default(:on___end__, token, data)
|
|
365
409
|
style = COLORS[:comment]
|
|
366
|
-
data << "<span class=\"#{style}\">#{escape_html(@src.lines[lineno..-1].join)}</span>"
|
|
410
|
+
data << "<span class=\"#{style}\">#{escape_html((@src.lines[lineno..-1] || raise).join)}</span>"
|
|
367
411
|
data
|
|
368
412
|
end
|
|
369
413
|
|
data/lib/bitclust/textutils.rb
CHANGED
|
@@ -18,7 +18,7 @@ module BitClust
|
|
|
18
18
|
def detab(str, ts = 8)
|
|
19
19
|
add = 0
|
|
20
20
|
str.gsub(/\t/) {
|
|
21
|
-
len = ts - (
|
|
21
|
+
len = ts - ((($~ || raise).begin(0) || raise) + add) % ts
|
|
22
22
|
add += len - 1
|
|
23
23
|
' ' * len
|
|
24
24
|
}
|
|
@@ -30,11 +30,11 @@ module BitClust
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def n_minimum_indent(lines)
|
|
33
|
-
lines.reject {|line| line.strip.empty? }.map {|line| n_indent(line) }.min
|
|
33
|
+
lines.reject {|line| line.strip.empty? }.map {|line| n_indent(line) }.min || raise
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def n_indent(line)
|
|
37
|
-
line.slice(/\A\s*/).size
|
|
37
|
+
(line.slice(/\A\s*/) || raise).size
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
INDENT_RE = {
|
data/lib/bitclust/version.rb
CHANGED