bitclust-core 1.7.0 → 1.8.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/data/bitclust/template/class-index +0 -1
- data/data/bitclust/template.epub/class +5 -5
- data/data/bitclust/template.epub/class-index +0 -1
- data/data/bitclust/template.epub/contents +7 -4
- data/data/bitclust/template.epub/layout +4 -4
- data/data/bitclust/template.offline/class-index +0 -1
- data/lib/bitclust/generators/epub.rb +69 -5
- data/lib/bitclust/irb.rb +14 -8
- data/lib/bitclust/markdown_bridge.rb +11 -6
- data/lib/bitclust/markdown_tree.rb +118 -11
- data/lib/bitclust/methoddatabase.rb +17 -4
- data/lib/bitclust/nameutils.rb +13 -0
- data/lib/bitclust/preprocessor.rb +0 -36
- data/lib/bitclust/remote.rb +405 -0
- data/lib/bitclust/runner.rb +5 -10
- data/lib/bitclust/screen.rb +2 -1
- data/lib/bitclust/searcher.rb +43 -24
- data/lib/bitclust/subcommands/chm_command.rb +1 -1
- data/lib/bitclust/subcommands/epub_command.rb +2 -2
- data/lib/bitclust/subcommands/extract_command.rb +7 -0
- data/lib/bitclust/subcommands/htmlfile_command.rb +7 -0
- data/lib/bitclust/subcommands/methods_command.rb +5 -28
- data/lib/bitclust/subcommands/query_command.rb +1 -0
- data/lib/bitclust/subcommands/server_command.rb +21 -10
- data/lib/bitclust/subcommands/setup_command.rb +28 -10
- data/lib/bitclust/syntax_highlighter.rb +65 -4
- data/lib/bitclust/user_dirs.rb +62 -0
- data/lib/bitclust/version.rb +1 -1
- data/test/test_chm_command.rb +22 -0
- data/test/test_epub_command.rb +60 -0
- data/test/test_epub_generator.rb +330 -0
- data/test/test_extract_command.rb +45 -0
- data/test/test_irb_plugin.rb +100 -6
- data/test/test_markdown_bridge.rb +35 -0
- data/test/test_markdown_tree.rb +144 -0
- data/test/test_mdparser.rb +39 -0
- data/test/test_methods_command.rb +32 -0
- data/test/test_opensearchdescription_screen.rb +39 -0
- data/test/test_query_command.rb +30 -0
- data/test/test_remote.rb +374 -0
- data/test/test_runner.rb +61 -1
- data/test/test_searcher.rb +63 -0
- data/test/test_server_command.rb +41 -0
- data/test/test_setup_command.rb +143 -0
- data/test/test_syntax_highlighter.rb +124 -0
- data/test/test_user_dirs.rb +62 -0
- metadata +37 -2
- data/lib/bitclust/ridatabase.rb +0 -359
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 299762c07571e6e3b72eebe00c9d1e1d520ef9f5c3b68de759adc0c173d2ee18
|
|
4
|
+
data.tar.gz: beab538e8caec8fb1f7265808adceaa5574b22793dfb11a521651687204416d1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a4d23713bd97acaa6cb98de51e95766c87f09329c0dba4c2b42152838023ec4ce681f1c7f38db582b7e3e20809a2749152da84a74399741dc113ad9251f0875d
|
|
7
|
+
data.tar.gz: ce441c5b5a4abc9cbef8a46bf9efa2a57a5cae8bdd827c9edf7af095e60bea9c95a85ceee54baa73e850b0a7a4682b1d5afd41ce41e386040e78761ca88038fd
|
|
@@ -24,20 +24,20 @@
|
|
|
24
24
|
<% end %>
|
|
25
25
|
|
|
26
26
|
<% unless @entry.extended.empty? %>
|
|
27
|
-
<br
|
|
27
|
+
<br />extend: <%= @entry.extended.map {|c| class_link(c.name) }.join(', ') %>
|
|
28
28
|
<% end %>
|
|
29
29
|
<% unless @entry.aliases.empty? %>
|
|
30
|
-
<br
|
|
30
|
+
<br />aliases: <%=h @entry.aliases.map{|c| c.name}.join(', ') %>
|
|
31
31
|
<% end %>
|
|
32
32
|
<% unless @entry.dynamically_included.empty? %>
|
|
33
|
-
<br
|
|
33
|
+
<br /> dynamic include:
|
|
34
34
|
<%= @entry.dynamically_included.map{|m|
|
|
35
35
|
class_link(m.name) + " (by " + library_link(m.library.name) + ")"
|
|
36
36
|
}.join(", ")
|
|
37
37
|
%>
|
|
38
38
|
<% end %>
|
|
39
39
|
<% unless @entry.dynamically_extended.empty? %>
|
|
40
|
-
<br
|
|
40
|
+
<br /> dynamic extend:
|
|
41
41
|
<%= @entry.dynamically_extended.map{|m|
|
|
42
42
|
class_link(m.name) + " (by " + library_link(m.library.name) + ")"
|
|
43
43
|
}.join(", ")
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
entries.each do |m|
|
|
73
73
|
m.names.each do |mname|
|
|
74
74
|
%>
|
|
75
|
-
<a href="#<%= m.index_id %>"><%= "#{prefix}#{mname}" %></a>
|
|
75
|
+
<a href="#<%= m.index_id %>"><%= escape_html "#{prefix}#{mname}" %></a>
|
|
76
76
|
<%
|
|
77
77
|
end
|
|
78
78
|
end
|
|
@@ -8,15 +8,18 @@
|
|
|
8
8
|
</metadata>
|
|
9
9
|
<manifest>
|
|
10
10
|
<item id="toc" properties="nav" href="nav.xhtml" media-type="application/xhtml+xml" />
|
|
11
|
-
<item id="index" href="OEBPS/doc/index.
|
|
12
|
-
<%
|
|
13
|
-
<item id="<%= item[:id] %>" href="<%= item[:path] %>" media-type="
|
|
11
|
+
<item id="index" href="OEBPS/doc/index.xhtml" media-type="application/xhtml+xml" />
|
|
12
|
+
<% class_items.each do |item| -%>
|
|
13
|
+
<item id="<%= item[:id] %>" href="<%= item[:path] %>" media-type="<%= item[:media_type] %>" />
|
|
14
|
+
<% end -%>
|
|
15
|
+
<% other_items.each do |item| -%>
|
|
16
|
+
<item id="<%= item[:id] %>" href="<%= item[:path] %>" media-type="<%= item[:media_type] %>" />
|
|
14
17
|
<% end -%>
|
|
15
18
|
</manifest>
|
|
16
19
|
<spine>
|
|
17
20
|
<itemref idref="toc" />
|
|
18
21
|
<itemref idref="index" />
|
|
19
|
-
<%
|
|
22
|
+
<% class_items.each do |item| -%>
|
|
20
23
|
<itemref idref="<%= item[:id] %>" />
|
|
21
24
|
<% end -%>
|
|
22
25
|
</spine>
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
<!DOCTYPE html>
|
|
3
3
|
<html lang="ja-JP">
|
|
4
4
|
<head>
|
|
5
|
-
<meta http-equiv="Content-Type" content="text/html; charset=<%=h charset() %>"
|
|
6
|
-
<meta http-equiv="Content-Language" content="ja-JP"
|
|
7
|
-
<link rel="stylesheet" type="text/css" href="<%=h css_url() %>"
|
|
8
|
-
<link rel="icon" type="image/png" href="<%=h favicon_url() %>"
|
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=<%=h charset() %>" />
|
|
6
|
+
<meta http-equiv="Content-Language" content="ja-JP" />
|
|
7
|
+
<link rel="stylesheet" type="text/css" href="<%=h css_url() %>" />
|
|
8
|
+
<link rel="icon" type="image/png" href="<%=h favicon_url() %>" />
|
|
9
9
|
<title><%=h @title %> (Ruby <%=h ruby_version %>)</title>
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
@@ -24,11 +24,17 @@ module BitClust
|
|
|
24
24
|
|
|
25
25
|
CONTENTS_DIR_NAME = 'OEBPS'
|
|
26
26
|
|
|
27
|
+
# StatichtmlCommand が出力ルートに書く 2 行のリダイレクト用スタブ
|
|
28
|
+
# (<meta http-equiv="refresh"> と <a>)。ルート要素が無く XHTML として
|
|
29
|
+
# 整形式でない。EPUB の入口は nav.xhtml なのでスタブは同梱せず削除する
|
|
30
|
+
INDEX_STUB_NAME = 'index.xhtml'
|
|
31
|
+
|
|
27
32
|
def generate
|
|
28
33
|
make_epub_directory do |epub_directory|
|
|
29
34
|
contents_directory = epub_directory + CONTENTS_DIR_NAME
|
|
30
35
|
copy_static_files(epub_directory)
|
|
31
36
|
generate_xhtml_files(contents_directory)
|
|
37
|
+
remove_index_stub(contents_directory)
|
|
32
38
|
generate_contents_opf(epub_directory)
|
|
33
39
|
pack_epub(epub_directory)
|
|
34
40
|
end
|
|
@@ -71,15 +77,45 @@ module BitClust
|
|
|
71
77
|
cmd.exec(argv, options)
|
|
72
78
|
end
|
|
73
79
|
|
|
80
|
+
# manifest に id="index" で固定的に載せる doc/index ページの href
|
|
81
|
+
# (epub_directory 相対)。other_items からは除いて二重掲載を避ける
|
|
82
|
+
INDEX_PATH = Pathname.new("#{CONTENTS_DIR_NAME}/doc/index.xhtml")
|
|
83
|
+
|
|
84
|
+
MEDIA_TYPES = {
|
|
85
|
+
'.xhtml' => 'application/xhtml+xml',
|
|
86
|
+
'.css' => 'text/css',
|
|
87
|
+
'.js' => 'application/javascript',
|
|
88
|
+
'.png' => 'image/png',
|
|
89
|
+
'.jpg' => 'image/jpeg',
|
|
90
|
+
'.jpeg' => 'image/jpeg',
|
|
91
|
+
'.gif' => 'image/gif',
|
|
92
|
+
'.svg' => 'image/svg+xml',
|
|
93
|
+
}.freeze
|
|
94
|
+
|
|
74
95
|
def generate_contents_opf(epub_directory)
|
|
75
|
-
|
|
96
|
+
class_items = [] #: Array[{:id => String, :path => Pathname, :media_type => String}]
|
|
76
97
|
glob_relative_path(epub_directory, "#{CONTENTS_DIR_NAME}/class/*.xhtml").each do |path|
|
|
77
|
-
|
|
78
|
-
:id => decodename_package(path.basename(".*").to_s),
|
|
79
|
-
:path => path
|
|
98
|
+
class_items << {
|
|
99
|
+
:id => "class-#{decodename_package(path.basename(".*").to_s)}",
|
|
100
|
+
:path => path,
|
|
101
|
+
:media_type => media_type_for(path),
|
|
80
102
|
}
|
|
81
103
|
end
|
|
82
|
-
|
|
104
|
+
class_items.sort_by!{|item| item[:path] }
|
|
105
|
+
|
|
106
|
+
excluded_paths = class_items.map{|item| item[:path] } + [INDEX_PATH]
|
|
107
|
+
other_items = [] #: Array[{:id => String, :path => Pathname, :media_type => String}]
|
|
108
|
+
oebps_file_paths(epub_directory).each do |path|
|
|
109
|
+
next if excluded_paths.include?(path)
|
|
110
|
+
|
|
111
|
+
other_items << {
|
|
112
|
+
:id => manifest_id_for(path),
|
|
113
|
+
:path => path,
|
|
114
|
+
:media_type => media_type_for(path),
|
|
115
|
+
}
|
|
116
|
+
end
|
|
117
|
+
other_items.sort_by!{|item| item[:path] }
|
|
118
|
+
|
|
83
119
|
template = (@templatedir + "contents").read
|
|
84
120
|
if ::ERB.instance_method(:initialize).parameters.last.first == :key
|
|
85
121
|
erb = ::ERB.new(template, trim_mode: '-')
|
|
@@ -92,6 +128,34 @@ module BitClust
|
|
|
92
128
|
end
|
|
93
129
|
end
|
|
94
130
|
|
|
131
|
+
# OEBPS 配下の通常ファイル全部(method/library/doc/function ページと、
|
|
132
|
+
# statichtml がコピーした CSS・画像)を epub_directory 相対で返す。
|
|
133
|
+
# class ページも含む(呼び出し側で class_items と重複排除する)
|
|
134
|
+
def oebps_file_paths(epub_directory)
|
|
135
|
+
glob_relative_path(epub_directory, "#{CONTENTS_DIR_NAME}/**/*").select do |relative_path|
|
|
136
|
+
(epub_directory + relative_path).file?
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def media_type_for(path)
|
|
141
|
+
MEDIA_TYPES[path.extname.downcase] || 'application/octet-stream'
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# class ページ以外("OEBPS/method/Array/i/each.xhtml" 等)の manifest
|
|
145
|
+
# item id。XML の NCName として妥当で一意になるよう、使えない文字は "-" に
|
|
146
|
+
# 置き換え、ディレクトリ境界は "--" にして encodename 由来の "-" と
|
|
147
|
+
# 区別する
|
|
148
|
+
def manifest_id_for(path)
|
|
149
|
+
relative = path.to_s.sub(%r{\A#{Regexp.escape(CONTENTS_DIR_NAME)}/}, "")
|
|
150
|
+
segments = relative.split("/").map{|segment| segment.gsub(/[^A-Za-z0-9_.-]/, "-") }
|
|
151
|
+
"item-#{segments.join('--')}"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def remove_index_stub(contents_directory)
|
|
155
|
+
stub_path = contents_directory + INDEX_STUB_NAME
|
|
156
|
+
FileUtils.rm_f(stub_path.to_s, :verbose => @verbose) if stub_path.file?
|
|
157
|
+
end
|
|
158
|
+
|
|
95
159
|
def pack_epub(epub_directory)
|
|
96
160
|
epub_filename = @outputdir + @filename
|
|
97
161
|
Dir.chdir(epub_directory.to_s) do
|
data/lib/bitclust/irb.rb
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
#
|
|
3
3
|
# irb からるりま(Ruby リファレンスマニュアル)を引く refe コマンド。
|
|
4
4
|
# ~/.irbrc に `require "bitclust/irb"` と書くと irb に `refe` コマンドが
|
|
5
|
-
# 登録される。検索対象の DB は refe コマンドと同じ場所
|
|
6
|
-
#
|
|
5
|
+
# 登録される。検索対象の DB は refe コマンドと同じ場所(bitclust setup が
|
|
6
|
+
# 作る設定ファイル)から探し、DB が無ければ docs.ruby-lang.org の検索索引と
|
|
7
|
+
# Markdown 配信から取得する(BitClust::Remote)。
|
|
7
8
|
|
|
8
9
|
require 'stringio'
|
|
9
10
|
require 'bitclust'
|
|
@@ -18,15 +19,18 @@ module BitClust
|
|
|
18
19
|
refe String#gsub インスタンスメソッド
|
|
19
20
|
refe Array.new 特異メソッド
|
|
20
21
|
refe Comparable クラス・モジュール
|
|
21
|
-
refe
|
|
22
|
+
refe each 名前だけでの検索(該当が複数なら一覧)
|
|
22
23
|
|
|
23
|
-
DB が無い場合は
|
|
24
|
+
DB が無い場合は docs.ruby-lang.org から取得します(1 日キャッシュ)。
|
|
25
|
+
`bitclust setup` で DB を作ると手元で検索できます。
|
|
24
26
|
USAGE
|
|
25
27
|
|
|
26
28
|
# pattern を検索して整形済みテキストを io へ書く。db が nil なら
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
|
|
29
|
+
# 既定の場所から探し、そこにも無ければ remote(省略時は
|
|
30
|
+
# Remote.default。nil ならフォールバックしない)で検索する。
|
|
31
|
+
# 検索の失敗は例外にせず io へメッセージを書く(irb セッションを
|
|
32
|
+
# 止めないため)
|
|
33
|
+
def self.lookup(pattern, io: $stdout, db: nil, remote: Remote.default)
|
|
30
34
|
words = pattern.to_s.split
|
|
31
35
|
if words.empty?
|
|
32
36
|
io.puts USAGE
|
|
@@ -35,7 +39,9 @@ module BitClust
|
|
|
35
39
|
view = TerminalView.new(Plain.new,
|
|
36
40
|
{ describe_all: false, line: false, encoding: nil },
|
|
37
41
|
io: io)
|
|
38
|
-
Searcher.new
|
|
42
|
+
searcher = Searcher.new
|
|
43
|
+
searcher.remote = remote
|
|
44
|
+
searcher.run_query(db, words, view)
|
|
39
45
|
rescue BitClust::UserError => err
|
|
40
46
|
io.puts err.message
|
|
41
47
|
end
|
|
@@ -134,12 +134,17 @@ module BitClust
|
|
|
134
134
|
reopen_only = members[path][:kinds].all? { |kind, _| %w[reopen redefine].include?(kind) }
|
|
135
135
|
[reopen_only ? 1 : 0, path]
|
|
136
136
|
end
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
137
|
+
# 同じライブラリへの membership が複数ある(#%version A...B の #%else =
|
|
138
|
+
# A 未満と B 以上)ときは、それぞれのゲートで include サイトを繰り返す
|
|
139
|
+
"\n" + sorted.flat_map { |path|
|
|
140
|
+
members[path][:memberships].select { |mm| mm[:library] == libname }.map { |m|
|
|
141
|
+
inc = "\#@include(#{relative(emitted[path], base)})\n"
|
|
142
|
+
inc = "\#@since #{m[:since]}\n#{inc}\#@end\n" if m[:since]
|
|
143
|
+
inc = "\#@until #{m[:until]}\n#{inc}\#@end\n" if m[:until]
|
|
144
|
+
inc = "\#@if (version == \"#{m[:version]}\")\n#{inc}\#@end\n" if m[:version]
|
|
145
|
+
(m[:except] || []).each { |x| inc = "\#@if (version != \"#{x}\")\n#{inc}\#@end\n" }
|
|
146
|
+
inc
|
|
147
|
+
}
|
|
143
148
|
}.join
|
|
144
149
|
end
|
|
145
150
|
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'bitclust/exception'
|
|
4
|
+
require 'bitclust/preprocessor'
|
|
5
|
+
|
|
3
6
|
module BitClust
|
|
4
7
|
# 新パイプラインのファイル発見(MARKUP_SPEC §1.1)。
|
|
5
8
|
#
|
|
@@ -116,36 +119,45 @@ module BitClust
|
|
|
116
119
|
if lines[0] =~ /\A---\s*\z/
|
|
117
120
|
i = 1
|
|
118
121
|
in_library_block = false
|
|
119
|
-
gate_stack = [] #: Array[
|
|
122
|
+
gate_stack = [] #: Array[list_gate]
|
|
120
123
|
while i < lines.length && lines[i] !~ /\A---\s*\z/
|
|
121
124
|
line = lines[i]
|
|
122
125
|
if in_library_block
|
|
123
|
-
# 多重所属のゲート付きリスト(MARKUP_SPEC §1.2)。
|
|
126
|
+
# 多重所属のゲート付きリスト(MARKUP_SPEC §1.2.1)。
|
|
124
127
|
# 項目は積まれているゲートをすべてまとって membership になる
|
|
125
128
|
case line
|
|
126
129
|
when /\A\s+- (\S+)/
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
info[:memberships].concat gated_memberships($1 || raise, gate_stack)
|
|
131
|
+
i += 1; next
|
|
132
|
+
when /\A\#[@%]\#/ # 前処理コメント(Preprocessor と同じく無視)
|
|
133
|
+
i += 1; next
|
|
134
|
+
when /\A\#[@%]/
|
|
135
|
+
list_directive(line, gate_stack, path, i + 1)
|
|
136
|
+
i += 1; next
|
|
137
|
+
when /\A\s+\#[@%]/
|
|
138
|
+
# 列 0 でない #% 行は Preprocessor でも指令にならない(YAML の
|
|
139
|
+
# コメント扱い)。ゲートが無音で無視されないようエラーにする
|
|
140
|
+
raise ParseError, "#{path}:#{i + 1}: preprocessor directive must start at column 0: #{line.strip}"
|
|
141
|
+
when /\A\s+\#/ # YAML コメント
|
|
130
142
|
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
143
|
else
|
|
135
|
-
|
|
144
|
+
# ブロック終端。この行は通常キーとして処理
|
|
145
|
+
list_end(gate_stack, path, i + 1)
|
|
146
|
+
in_library_block = false
|
|
136
147
|
end
|
|
137
148
|
end
|
|
138
149
|
case line
|
|
139
150
|
when /\Atype:\s*library\s*\z/ then info[:library_file] = true
|
|
140
151
|
when /\Aname:\s*(\S+)/ then info[:name] = $1
|
|
141
|
-
when /\Alibrary:\s*\z/ then in_library_block = true; gate_stack = [] #: Array[
|
|
142
|
-
when /\Alibrary:\s*(\S+)/ then info[:memberships] << { library: $1 }
|
|
152
|
+
when /\Alibrary:\s*\z/ then in_library_block = true; gate_stack = [] #: Array[list_gate]
|
|
153
|
+
when /\Alibrary:\s*(\S+)/ then info[:memberships] << { library: $1 || raise }
|
|
143
154
|
when /\Asince:\s*"?([^"\s]+)"?/ then info[:since] = $1
|
|
144
155
|
when /\Auntil:\s*"?([^"\s]+)"?/ then info[:until] = $1
|
|
145
156
|
when RELATION_KEY_RE then info[:front_matter_relations] = true
|
|
146
157
|
end
|
|
147
158
|
i += 1
|
|
148
159
|
end
|
|
160
|
+
list_end(gate_stack, path, i + 1) if in_library_block
|
|
149
161
|
i += 1
|
|
150
162
|
end
|
|
151
163
|
info[:library] = info[:memberships].dig(0, :library)
|
|
@@ -190,6 +202,101 @@ module BitClust
|
|
|
190
202
|
info
|
|
191
203
|
end
|
|
192
204
|
|
|
205
|
+
# --- front matter ゲート付きリストのディレクティブ ---
|
|
206
|
+
# 本文側 Preprocessor と同じ集合のうち #%since/#%until/#%version/#%else/#%end
|
|
207
|
+
# を解釈する。#%if(任意の条件式)は非対応。解釈できない行は無音で
|
|
208
|
+
# リストを打ち切らず ParseError にする(本文側の unknown directive と同じ扱い)。
|
|
209
|
+
#
|
|
210
|
+
# ゲート1段は clause の選言。clause は since(以上)/until(未満)/
|
|
211
|
+
# version(その版のみ)/except(その版以外)の交差で、#%else は直前ゲートの
|
|
212
|
+
# 補集合(#%version A...B なら「A 未満」または「B 以上」の 2 clause)
|
|
213
|
+
|
|
214
|
+
def list_directive(line, stack, path, lineno)
|
|
215
|
+
err = ->(msg) { raise ParseError, "#{path}:#{lineno}: #{msg}: #{line.strip}" }
|
|
216
|
+
case line
|
|
217
|
+
when /\A\#[@%](since|until)\b(.*)/
|
|
218
|
+
kind, raw = $1, $2 || raise
|
|
219
|
+
ver = version_literal(raw.strip) or err.("wrong conditional expr")
|
|
220
|
+
clause = kind == 'since' ? { since: ver } : { until: ver } #: gate_clause
|
|
221
|
+
stack.push({ clauses: [clause], elsed: false })
|
|
222
|
+
when /\A\#[@%]version\b(.*)/
|
|
223
|
+
clause = version_range(($1 || raise).strip) or
|
|
224
|
+
err.("wrong version range (expected V / A...B / A... / ...B)")
|
|
225
|
+
stack.push({ clauses: [clause], elsed: false })
|
|
226
|
+
when /\A\#[@%]if\b/
|
|
227
|
+
err.("#%if is not supported in front matter library list (use #%since/#%until/#%version/#%else)")
|
|
228
|
+
when /\A\#[@%]else\s*\z/
|
|
229
|
+
gate = stack.last or err.("no matching #%since/#%until/#%version")
|
|
230
|
+
err.("duplicate #%else") if gate[:elsed]
|
|
231
|
+
stack[-1] = { clauses: gate[:clauses].flat_map { |c| complement(c) }, elsed: true }
|
|
232
|
+
when /\A\#[@%]end\s*\z/
|
|
233
|
+
stack.pop or err.("no matching #%since/#%until/#%version")
|
|
234
|
+
else
|
|
235
|
+
err.("unknown preprocessor directive")
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def list_end(stack, path, lineno)
|
|
240
|
+
return if stack.empty?
|
|
241
|
+
raise ParseError, "#{path}:#{lineno}: unterminated #%since/#%until/#%version in front matter library list"
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
VERSION_LITERAL = Preprocessor::VERSION_LITERAL
|
|
245
|
+
|
|
246
|
+
def version_literal(raw)
|
|
247
|
+
raw =~ /\A#{VERSION_LITERAL}\z/o ? ($1 || $2) : nil
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# #%version の版範囲(Preprocessor#build_cond_by_range と同じ記法)
|
|
251
|
+
def version_range(raw)
|
|
252
|
+
case raw
|
|
253
|
+
when /\A#{VERSION_LITERAL}\.\.\.#{VERSION_LITERAL}\z/o
|
|
254
|
+
{ since: $1 || $2 || raise, until: $3 || $4 || raise }
|
|
255
|
+
when /\A#{VERSION_LITERAL}\.\.\.\z/o then { since: $1 || $2 || raise }
|
|
256
|
+
when /\A\.\.\.#{VERSION_LITERAL}\z/o then { until: $1 || $2 || raise }
|
|
257
|
+
when /\A#{VERSION_LITERAL}\z/o then { version: $1 || $2 || raise }
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def complement(clause)
|
|
262
|
+
return [{ except: [clause[:version] || raise] }] if clause[:version]
|
|
263
|
+
alts = [] #: Array[gate_clause]
|
|
264
|
+
alts << { until: clause[:since] || raise } if clause[:since]
|
|
265
|
+
alts << { since: clause[:until] || raise } if clause[:until]
|
|
266
|
+
alts
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# 積まれた全ゲートの交差。各段が選言なので組合せごとに1つの membership
|
|
270
|
+
# (矛盾する組合せ = 異なる単一版どうしは捨てる)
|
|
271
|
+
def gated_memberships(library, stack)
|
|
272
|
+
seed = [{}] #: Array[gate_clause]
|
|
273
|
+
combos = stack.inject(seed) { |acc, gate|
|
|
274
|
+
acc.product(gate[:clauses]).filter_map { |a, b| intersect(a, b) }
|
|
275
|
+
}
|
|
276
|
+
combos.map { |c|
|
|
277
|
+
m = { library: library } #: membership
|
|
278
|
+
m[:since] = c[:since] || raise if c[:since]
|
|
279
|
+
m[:until] = c[:until] || raise if c[:until]
|
|
280
|
+
m[:version] = c[:version] || raise if c[:version]
|
|
281
|
+
m[:except] = c[:except] || raise if c[:except]
|
|
282
|
+
m
|
|
283
|
+
}
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def intersect(a, b)
|
|
287
|
+
r = {} #: gate_clause
|
|
288
|
+
sinces = [a[:since], b[:since]].compact
|
|
289
|
+
r[:since] = sinces.max_by { |v| Gem::Version.new(v) } || raise unless sinces.empty?
|
|
290
|
+
untils = [a[:until], b[:until]].compact
|
|
291
|
+
r[:until] = untils.min_by { |v| Gem::Version.new(v) } || raise unless untils.empty?
|
|
292
|
+
versions = [a[:version], b[:version]].compact.uniq
|
|
293
|
+
return nil if versions.size > 1
|
|
294
|
+
r[:version] = versions.first if versions.size == 1
|
|
295
|
+
excepts = (a[:except] || []) + (b[:except] || [])
|
|
296
|
+
r[:except] = excepts.uniq unless excepts.empty?
|
|
297
|
+
r
|
|
298
|
+
end
|
|
299
|
+
|
|
193
300
|
# 大文字小文字のみが異なる名前は macOS/Windows の case-insensitive FS で
|
|
194
301
|
# チェックアウト不能になるため、衝突を警告する(途中のディレクトリ名も見る)
|
|
195
302
|
def warn_case_collisions(files)
|
|
@@ -186,7 +186,7 @@ module BitClust
|
|
|
186
186
|
# membership を持つライブラリだけがメンバーとして取り込む
|
|
187
187
|
members = tree.entities.select { |_, e|
|
|
188
188
|
e[:memberships].any? { |m|
|
|
189
|
-
m[:library] == libname &&
|
|
189
|
+
m[:library] == libname && md_gate_covers?(version, m)
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
sorted = members.keys.sort_by { |path|
|
|
@@ -211,12 +211,25 @@ module BitClust
|
|
|
211
211
|
# since は「その版以降」、until は「その版未満」(ブリッジの
|
|
212
212
|
# #@since/#@until ラッパーと同じ意味論)
|
|
213
213
|
def md_version_covers?(version, since_version, until_version)
|
|
214
|
+
gate = {} #: MarkdownTree::gate_clause
|
|
215
|
+
gate[:since] = since_version if since_version
|
|
216
|
+
gate[:until] = until_version if until_version
|
|
217
|
+
md_gate_covers?(version, gate)
|
|
218
|
+
end
|
|
219
|
+
private :md_version_covers?
|
|
220
|
+
|
|
221
|
+
# membership のゲート(MarkdownTree の since/until/version/except)がこの版を
|
|
222
|
+
# 含むか。version は「その版のみ」、except は「その版以外」(#%version V と
|
|
223
|
+
# その #%else)。比較は Gem::Version なので 3.4 と 3.4.0 は同じ版
|
|
224
|
+
def md_gate_covers?(version, gate)
|
|
214
225
|
v = Gem::Version.new(version)
|
|
215
|
-
return false if
|
|
216
|
-
return false if
|
|
226
|
+
return false if (s = gate[:since]) && v < Gem::Version.new(s)
|
|
227
|
+
return false if (u = gate[:until]) && v >= Gem::Version.new(u)
|
|
228
|
+
return false if (only = gate[:version]) && v != Gem::Version.new(only)
|
|
229
|
+
return false if (except = gate[:except]) && except.any? { |x| v == Gem::Version.new(x) }
|
|
217
230
|
true
|
|
218
231
|
end
|
|
219
|
-
private :
|
|
232
|
+
private :md_gate_covers?
|
|
220
233
|
|
|
221
234
|
def refs
|
|
222
235
|
@refs ||= RefsDatabase.load(realpath('refs'))
|
data/lib/bitclust/nameutils.rb
CHANGED
|
@@ -222,6 +222,19 @@ module BitClust
|
|
|
222
222
|
Gem::Version.new(version) >= Gem::Version.new('4.0') ? '?.' : mark
|
|
223
223
|
end
|
|
224
224
|
|
|
225
|
+
# refe の検索パターン("String#gsub"・"Array.new"・"Kernel.#printf" や
|
|
226
|
+
# "," を "#" の代用にした "String,gsub" など)を [クラス名, typemark,
|
|
227
|
+
# メソッド名] に分ける。区切りは反転文字列上で探す。"?." は 4.0 以降の
|
|
228
|
+
# module function の表示形式(反転すると ".?")で、内部表記の ".#" に
|
|
229
|
+
# 正規化する
|
|
230
|
+
def parse_method_spec_pattern(pat)
|
|
231
|
+
_m, _t, _c = pat.reverse.split(/([\#,]\.|\.[\#,]|\.\?|[\#\.\,])/, 2)
|
|
232
|
+
c = (_c || raise).reverse
|
|
233
|
+
t = (_t || raise).tr(',', '#').sub(/\#\.|\.\?/, '.#')
|
|
234
|
+
m = (_m || raise).reverse
|
|
235
|
+
return c, t, m
|
|
236
|
+
end
|
|
237
|
+
|
|
225
238
|
def functionname?(n)
|
|
226
239
|
/\A\w+\z/ =~ n ? true : false
|
|
227
240
|
end
|
|
@@ -343,40 +343,4 @@ module BitClust
|
|
|
343
343
|
end
|
|
344
344
|
end
|
|
345
345
|
end
|
|
346
|
-
|
|
347
|
-
# Used by tools/stattodo.rb
|
|
348
|
-
class LineCollector < LineFilter
|
|
349
|
-
|
|
350
|
-
def LineCollector.process(path)
|
|
351
|
-
fopen(path) {|f|
|
|
352
|
-
return wrap(f).to_a
|
|
353
|
-
}
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
def LineCollector.wrap(f)
|
|
357
|
-
new(LineStream.new(f))
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
private
|
|
361
|
-
|
|
362
|
-
def next_line(f)
|
|
363
|
-
while line = f.gets
|
|
364
|
-
if /\A\#[@%]include\s*\((.*?)\)/ =~ line
|
|
365
|
-
begin
|
|
366
|
-
file = ($1 || raise).strip
|
|
367
|
-
basedir = File.dirname(line.location.file || raise)
|
|
368
|
-
@buf.concat LineCollector.process("#{basedir}/#{file}")
|
|
369
|
-
rescue Errno::ENOENT => _err
|
|
370
|
-
raise WrongInclude, "#{line.location}: \#@include'ed file not exist: #{file}"
|
|
371
|
-
end
|
|
372
|
-
else
|
|
373
|
-
@buf.push line
|
|
374
|
-
end
|
|
375
|
-
break unless @buf.empty?
|
|
376
|
-
end
|
|
377
|
-
@buf.shift
|
|
378
|
-
end
|
|
379
|
-
|
|
380
|
-
end
|
|
381
|
-
|
|
382
346
|
end
|