bitclust-core 0.5.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 (127) hide show
  1. data/ChangeLog +2907 -0
  2. data/Gemfile +7 -0
  3. data/README +21 -0
  4. data/Rakefile +20 -0
  5. data/bin/bitclust +14 -0
  6. data/bin/refe +36 -0
  7. data/bitclust-dev.gemspec +33 -0
  8. data/bitclust.gemspec +30 -0
  9. data/config.in +23 -0
  10. data/config.ru +48 -0
  11. data/config.ru.sample +31 -0
  12. data/data/bitclust/catalog/ja_JP.EUC-JP +78 -0
  13. data/data/bitclust/catalog/ja_JP.UTF-8 +78 -0
  14. data/data/bitclust/template.lillia/class +98 -0
  15. data/data/bitclust/template.lillia/class-index +28 -0
  16. data/data/bitclust/template.lillia/doc +48 -0
  17. data/data/bitclust/template.lillia/layout +19 -0
  18. data/data/bitclust/template.lillia/library +129 -0
  19. data/data/bitclust/template.lillia/library-index +32 -0
  20. data/data/bitclust/template.lillia/method +20 -0
  21. data/data/bitclust/template.lillia/rd_file +6 -0
  22. data/data/bitclust/template.offline/class +67 -0
  23. data/data/bitclust/template.offline/class-index +28 -0
  24. data/data/bitclust/template.offline/doc +13 -0
  25. data/data/bitclust/template.offline/function +22 -0
  26. data/data/bitclust/template.offline/function-index +24 -0
  27. data/data/bitclust/template.offline/layout +18 -0
  28. data/data/bitclust/template.offline/library +87 -0
  29. data/data/bitclust/template.offline/library-index +32 -0
  30. data/data/bitclust/template.offline/method +21 -0
  31. data/data/bitclust/template.offline/rd_file +6 -0
  32. data/data/bitclust/template/class +133 -0
  33. data/data/bitclust/template/class-index +30 -0
  34. data/data/bitclust/template/doc +14 -0
  35. data/data/bitclust/template/function +21 -0
  36. data/data/bitclust/template/function-index +25 -0
  37. data/data/bitclust/template/layout +19 -0
  38. data/data/bitclust/template/library +89 -0
  39. data/data/bitclust/template/library-index +35 -0
  40. data/data/bitclust/template/method +24 -0
  41. data/data/bitclust/template/opensearchdescription +10 -0
  42. data/data/bitclust/template/search +57 -0
  43. data/lib/bitclust.rb +9 -0
  44. data/lib/bitclust/app.rb +129 -0
  45. data/lib/bitclust/classentry.rb +425 -0
  46. data/lib/bitclust/compat.rb +39 -0
  47. data/lib/bitclust/completion.rb +531 -0
  48. data/lib/bitclust/crossrubyutils.rb +91 -0
  49. data/lib/bitclust/database.rb +181 -0
  50. data/lib/bitclust/docentry.rb +83 -0
  51. data/lib/bitclust/entry.rb +223 -0
  52. data/lib/bitclust/exception.rb +38 -0
  53. data/lib/bitclust/functiondatabase.rb +115 -0
  54. data/lib/bitclust/functionentry.rb +81 -0
  55. data/lib/bitclust/functionreferenceparser.rb +76 -0
  56. data/lib/bitclust/htmlutils.rb +80 -0
  57. data/lib/bitclust/interface.rb +87 -0
  58. data/lib/bitclust/libraryentry.rb +211 -0
  59. data/lib/bitclust/lineinput.rb +165 -0
  60. data/lib/bitclust/messagecatalog.rb +95 -0
  61. data/lib/bitclust/methoddatabase.rb +401 -0
  62. data/lib/bitclust/methodentry.rb +202 -0
  63. data/lib/bitclust/methodid.rb +209 -0
  64. data/lib/bitclust/methodsignature.rb +82 -0
  65. data/lib/bitclust/nameutils.rb +236 -0
  66. data/lib/bitclust/parseutils.rb +60 -0
  67. data/lib/bitclust/preprocessor.rb +273 -0
  68. data/lib/bitclust/rdcompiler.rb +507 -0
  69. data/lib/bitclust/refsdatabase.rb +66 -0
  70. data/lib/bitclust/requesthandler.rb +330 -0
  71. data/lib/bitclust/ridatabase.rb +349 -0
  72. data/lib/bitclust/rrdparser.rb +522 -0
  73. data/lib/bitclust/runner.rb +143 -0
  74. data/lib/bitclust/screen.rb +554 -0
  75. data/lib/bitclust/searcher.rb +518 -0
  76. data/lib/bitclust/server.rb +59 -0
  77. data/lib/bitclust/simplesearcher.rb +84 -0
  78. data/lib/bitclust/subcommand.rb +746 -0
  79. data/lib/bitclust/textutils.rb +51 -0
  80. data/lib/bitclust/version.rb +3 -0
  81. data/packer.rb +224 -0
  82. data/refe2.gemspec +29 -0
  83. data/server.exe +0 -0
  84. data/server.exy +159 -0
  85. data/server.rb +10 -0
  86. data/setup.rb +1596 -0
  87. data/standalone.rb +193 -0
  88. data/test/run_test.rb +15 -0
  89. data/test/test_bitclust.rb +81 -0
  90. data/test/test_entry.rb +39 -0
  91. data/test/test_functiondatabase.rb +55 -0
  92. data/test/test_libraryentry.rb +31 -0
  93. data/test/test_methoddatabase.rb +81 -0
  94. data/test/test_methodsignature.rb +14 -0
  95. data/test/test_nameutils.rb +324 -0
  96. data/test/test_preprocessor.rb +84 -0
  97. data/test/test_rdcompiler.rb +534 -0
  98. data/test/test_refsdatabase.rb +76 -0
  99. data/test/test_rrdparser.rb +26 -0
  100. data/test/test_runner.rb +102 -0
  101. data/test/test_simplesearcher.rb +48 -0
  102. data/theme/default/images/external.png +0 -0
  103. data/theme/default/rurema.png +0 -0
  104. data/theme/default/style.css +288 -0
  105. data/theme/default/test.css +254 -0
  106. data/theme/lillia/rurema.png +0 -0
  107. data/theme/lillia/style.css +331 -0
  108. data/theme/lillia/test.css +254 -0
  109. data/tools/bc-ancestors.rb +153 -0
  110. data/tools/bc-checkparams.rb +246 -0
  111. data/tools/bc-classes.rb +80 -0
  112. data/tools/bc-convert.rb +165 -0
  113. data/tools/bc-list.rb +63 -0
  114. data/tools/bc-methods.rb +171 -0
  115. data/tools/bc-preproc.rb +42 -0
  116. data/tools/bc-rdoc.rb +343 -0
  117. data/tools/bc-tochm.rb +301 -0
  118. data/tools/bc-tohtml.rb +125 -0
  119. data/tools/bc-tohtmlpackage.rb +241 -0
  120. data/tools/check-signature.rb +19 -0
  121. data/tools/forall-ruby.rb +20 -0
  122. data/tools/gencatalog.rb +69 -0
  123. data/tools/statrefm.rb +98 -0
  124. data/tools/stattodo.rb +150 -0
  125. data/tools/update-database.rb +146 -0
  126. data/view.cgi +6 -0
  127. metadata +222 -0
@@ -0,0 +1,60 @@
1
+ #
2
+ # bitclust/parseutils.rb
3
+ #
4
+ # Copyright (c) 2006-2007 Minero Aoki
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the Ruby License.
8
+ #
9
+
10
+ require 'bitclust/exception'
11
+
12
+ class String # reopen
13
+ attr_accessor :location
14
+ end
15
+
16
+ module BitClust
17
+
18
+ class LineStream
19
+ def initialize(f)
20
+ @f = f
21
+ end
22
+
23
+ def gets
24
+ line = @f.gets
25
+ return nil unless line
26
+ if @f.respond_to?(:path)
27
+ path = @f.path
28
+ else
29
+ path = nil
30
+ end
31
+ line.location = Location.new(path, @f.lineno)
32
+ line
33
+ end
34
+ end
35
+
36
+ class Location
37
+ def initialize(file, line)
38
+ @file = file
39
+ @line = line
40
+ end
41
+
42
+ attr_reader :file
43
+ attr_reader :line
44
+
45
+ def to_s
46
+ "#{@file}:#{@line}"
47
+ end
48
+
49
+ def inspect
50
+ "\#<#{self.class} #{@file}:#{@line}>"
51
+ end
52
+ end
53
+
54
+ module ParseUtils
55
+ def parse_error(msg, line)
56
+ raise ParseError, "#{line.location}: #{msg}: #{line.inspect}"
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,273 @@
1
+ #
2
+ # bitclust/preprocessor.rb
3
+ #
4
+ # Copyright (c) 2006-2007 Minero Aoki
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the Ruby License.
8
+ #
9
+
10
+ require 'bitclust/parseutils'
11
+ require 'strscan'
12
+
13
+ module BitClust
14
+
15
+ class LineFilter
16
+
17
+ include ParseUtils
18
+ include Enumerable
19
+
20
+ def initialize(f)
21
+ @f = f
22
+ @buf = []
23
+ end
24
+
25
+ def gets
26
+ @buf.shift || next_line(@f)
27
+ end
28
+
29
+ def each
30
+ while line = gets()
31
+ yield line
32
+ end
33
+ end
34
+
35
+ # abstract next_line
36
+
37
+ end
38
+
39
+
40
+ class Preprocessor < LineFilter
41
+
42
+ def self.read(path, params = {})
43
+ if path.respond_to?(:gets)
44
+ io = wrap(path, params)
45
+ else
46
+ io = wrap(fopen(path, 'r:UTF-8'), params)
47
+ end
48
+ ret = ""
49
+ while s = io.gets
50
+ ret << s
51
+ end
52
+ ret
53
+ end
54
+
55
+ def Preprocessor.process(path, params = {})
56
+ fopen(path, 'r:UTF-8') {|f|
57
+ return wrap(f, params).to_a
58
+ }
59
+ end
60
+
61
+ def Preprocessor.wrap(f, params = {})
62
+ new(LineStream.new(f), params)
63
+ end
64
+
65
+ def initialize(f, params = {})
66
+ super f
67
+ @params = params
68
+ @last_if = nil
69
+ cond_init
70
+ end
71
+
72
+ private
73
+
74
+ def next_line(f)
75
+ while line = f.gets
76
+ case line
77
+ when /\A\#@\#/ # preprocessor comment
78
+ ;
79
+ when /\A\#@todo/i
80
+ @buf.push line.gsub(/\A#/, '') if current_cond
81
+ when /\A\#@include\s*\((.*?)\)/
82
+ next unless current_cond
83
+ begin
84
+ file = $1.strip
85
+ basedir = File.dirname(line.location.file)
86
+ @buf.concat Preprocessor.process("#{basedir}/#{file}", @params)
87
+ rescue Errno::ENOENT => err
88
+ raise WrongInclude, "#{line.location}: \#@include'ed file not exist: #{file}"
89
+ end
90
+ when /\A\#@since\b/
91
+ cond_stmt_begin line, build_cond_by_value(line, 'version >=')
92
+ when /\A\#@until\b/
93
+ cond_stmt_begin line, build_cond_by_value(line, 'version <')
94
+ when /\A\#@if\b/
95
+ cond_stmt_begin line, line.sub(/\A\#@if/, '').strip
96
+ when /\A\#@else\s*\z/
97
+ parse_error "no matching #@if", line if cond_toplevel?
98
+ cond_invert
99
+ when /\A\#@end\s*\z/
100
+ parse_error "no matching #@if", line if cond_toplevel?
101
+ cond_pop
102
+ when /\A\#@/
103
+ parse_error "unknown preprocessor directive", line
104
+ else
105
+ if current_cond
106
+ @buf.push line
107
+ break
108
+ end
109
+ end
110
+ end
111
+ if @buf.empty?
112
+ unless cond_toplevel?
113
+ parse_error "unterminated \#@if", @last_if
114
+ end
115
+ end
116
+ @buf.shift
117
+ end
118
+
119
+ def cond_stmt_begin(line, cond)
120
+ @last_if = line
121
+ begin
122
+ cond_push eval_cond(cond)
123
+ rescue ScanError => err
124
+ parse_error err.message, line
125
+ end
126
+ end
127
+
128
+ def build_cond_by_value(line, left)
129
+ case ver = line.sub(/\A\#@\w+/, '').strip
130
+ when /\A[\d\.]+\z/
131
+ %Q(#{left} "#{ver}")
132
+ when /\A"[\d\.]+"\z/
133
+ "#{left} #{ver}"
134
+ else
135
+ parse_error "wrong conditional expr", line
136
+ end
137
+ end
138
+
139
+ def current_cond
140
+ @cond_stack.last
141
+ end
142
+
143
+ def cond_init
144
+ @cond_stack = [true]
145
+ end
146
+
147
+ def cond_toplevel?
148
+ @cond_stack.size == 1
149
+ end
150
+
151
+ def cond_push(bool)
152
+ @cond_stack.push(@cond_stack.last && bool)
153
+ end
154
+
155
+ def cond_invert
156
+ b = @cond_stack.pop
157
+ @cond_stack.push(!b && @cond_stack.last)
158
+ end
159
+
160
+ def cond_pop
161
+ @cond_stack.pop
162
+ end
163
+
164
+ def eval_cond(str)
165
+ s = StringScanner.new(str)
166
+ result = eval_expr(s) ? true : false
167
+ unless s.eos?
168
+ scan_error "parse error at: #{s.inspect}"
169
+ end
170
+ result
171
+ end
172
+
173
+ def eval_expr(s)
174
+ paren_open = s.scan(/\s*\(/)
175
+ val = eval_expr_p(s)
176
+ while conj = read_conj(s)
177
+ case conj
178
+ when 'and'
179
+ val = eval_expr_p(s) && val
180
+ when 'or'
181
+ val = eval_expr_p(s) || val
182
+ end
183
+ end
184
+ if paren_open
185
+ unless s.skip(/\s*\)/)
186
+ scan_error "paren opened but not closed"
187
+ end
188
+ end
189
+ val
190
+ end
191
+
192
+ def eval_expr_p(s)
193
+ val = eval_primary(s)
194
+ while op = read_op(s)
195
+ if op == '!='
196
+ val = (val != eval_primary(s))
197
+ else
198
+ val = val.__send__(op, eval_primary(s))
199
+ end
200
+ end
201
+ val
202
+ end
203
+
204
+ def read_conj(s)
205
+ s.skip(/\s+/)
206
+ s.scan(/and|or/)
207
+ end
208
+
209
+ def read_op(s)
210
+ s.skip(/\s+/)
211
+ s.scan(/>=|<=|==|<|>|!=/)
212
+ end
213
+
214
+ def eval_primary(s)
215
+ s.skip(/\s+/)
216
+ if t = s.scan(/\w+/)
217
+ unless @params.key?(t)
218
+ scan_error "database property `#{t}' not exist"
219
+ end
220
+ @params[t]
221
+ elsif t = s.scan(/".*?"/)
222
+ eval(t)
223
+ elsif t = s.scan(/'.*?'/)
224
+ eval(t)
225
+ elsif t = s.scan(/\d+/)
226
+ t.to_i
227
+ else
228
+ scan_error "parse error at: #{s.inspect}"
229
+ end
230
+ end
231
+
232
+ def scan_error(msg)
233
+ raise ScanError, msg
234
+ end
235
+
236
+ end
237
+
238
+
239
+ class LineCollector < LineFilter
240
+
241
+ def LineCollector.process(path)
242
+ fopen(path) {|f|
243
+ return wrap(f).to_a
244
+ }
245
+ end
246
+
247
+ def LineCollector.wrap(f)
248
+ new(LineStream.new(f))
249
+ end
250
+
251
+ private
252
+
253
+ def next_line(f)
254
+ while line = f.gets
255
+ if /\A\#@include\s*\((.*?)\)/ =~ line
256
+ begin
257
+ file = $1.strip
258
+ basedir = File.dirname(line.location.file)
259
+ @buf.concat LineCollector.process("#{basedir}/#{file}")
260
+ rescue Errno::ENOENT => err
261
+ raise WrongInclude, "#{line.location}: \#@include'ed file not exist: #{file}"
262
+ end
263
+ else
264
+ @buf.push line
265
+ end
266
+ break unless @buf.empty?
267
+ end
268
+ @buf.shift
269
+ end
270
+
271
+ end
272
+
273
+ end
@@ -0,0 +1,507 @@
1
+ #
2
+ # bitclust/rdcompiler.rb
3
+ #
4
+ # Copyright (C) 2006-2008 Minero Aoki
5
+ #
6
+ # This program is free software.
7
+ # You can distribute/modify this program under the Ruby License.
8
+ #
9
+
10
+ require 'bitclust/methodsignature'
11
+ require 'bitclust/lineinput'
12
+ require 'bitclust/htmlutils'
13
+ require 'bitclust/textutils'
14
+ require 'stringio'
15
+
16
+ module BitClust
17
+
18
+ class RDCompiler
19
+
20
+ include HTMLUtils
21
+ include TextUtils
22
+
23
+ def initialize(urlmapper, hlevel = 1, opt = {})
24
+ @urlmapper = urlmapper
25
+ @catalog = opt[:catalog]
26
+ @hlevel = hlevel
27
+ @type = nil
28
+ @library = nil
29
+ @class = nil
30
+ @method = nil
31
+ @option = opt.dup
32
+ end
33
+
34
+ def compile(src)
35
+ setup(src) {
36
+ library_file
37
+ }
38
+ end
39
+
40
+ # FIXME
41
+ def compile_method(m, opt = nil)
42
+ @opt = opt
43
+ @type = :method
44
+ @method = m
45
+ setup(m.source) {
46
+ method_entry
47
+ }
48
+ ensure
49
+ @opt = nil
50
+ end
51
+
52
+ private
53
+
54
+ def setup(src)
55
+ @f = LineInput.new(StringIO.new(src))
56
+ @out = StringIO.new
57
+ yield
58
+ @out.string
59
+ end
60
+
61
+ def library_file
62
+ while @f.next?
63
+ case @f.peek
64
+ when /\A---/
65
+ method_entry_chunk
66
+ when /\A=+/
67
+ headline @f.gets
68
+ when /\A\s+\*\s/
69
+ ulist
70
+ when /\A\s+\(\d+\)\s/
71
+ olist
72
+ when %r<\A//emlist\{>
73
+ emlist
74
+ when /\A:\s/
75
+ dlist
76
+ when /\A\s+\S/
77
+ list
78
+ else
79
+ if @f.peek.strip.empty?
80
+ @f.gets
81
+ else
82
+ paragraph
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ def method_entry
89
+ while @f.next?
90
+ method_entry_chunk
91
+ end
92
+ end
93
+
94
+ def method_entry_chunk
95
+ @out.puts '<dl>' if @option[:force]
96
+ @f.while_match(/\A---/) do |line|
97
+ method_signature line
98
+ end
99
+ props = {}
100
+ @f.while_match(/\A:/) do |line|
101
+ k, v = line.sub(/\A:/, '').split(':', 2)
102
+ props[k.strip] = v.strip
103
+ end
104
+ @out.puts '<dd class="method-description">'
105
+ while @f.next?
106
+ case @f.peek
107
+ when /\A===+/
108
+ headline @f.gets
109
+ when /\A==?/
110
+ if @option[:force]
111
+ break
112
+ else
113
+ raise "method entry includes headline: #{@f.peek.inspect}"
114
+ end
115
+ when /\A---/
116
+ break
117
+ when /\A\s+\*\s/
118
+ ulist
119
+ when /\A\s+\(\d+\)\s/
120
+ olist
121
+ when /\A:\s/
122
+ dlist
123
+ when %r<\A//emlist\{>
124
+ emlist
125
+ when /\A\s+\S/
126
+ list
127
+ when /@see/
128
+ see
129
+ when /@todo/
130
+ todo
131
+ when /\A@[a-z]/
132
+ method_info
133
+ else
134
+ if @f.peek.strip.empty?
135
+ @f.gets
136
+ else
137
+ method_entry_paragraph
138
+ end
139
+ end
140
+ end
141
+ @out.puts '</dd>'
142
+ @out.puts '</dl>' if @option[:force]
143
+ end
144
+
145
+ def headline(line)
146
+ level = @hlevel + (line.slice(/\A=+/).size - 3)
147
+ label = line.sub(/\A=+(\[a:(.*?)\])?/, '').strip
148
+ frag = $2 if $2 and not $2.empty?
149
+ line h(level, escape_html(label), frag)
150
+ end
151
+
152
+ def h(level, label, frag = nil)
153
+ name = frag ? "id='#{escape_html(frag)}'" : ""
154
+ "<h#{level} #{name}>#{label}</h#{level}>"
155
+ end
156
+
157
+ def ulist
158
+ @out.puts '<ul>'
159
+ @f.while_match(/\A\s+\*\s/) do |line|
160
+ string '<li>'
161
+ string compile_text(line.sub(/\A\s+\*/, '').strip)
162
+ @f.while_match(/\A\s+[^\*\s]/) do |cont|
163
+ nl
164
+ string compile_text(cont.strip)
165
+ end
166
+ line '</li>'
167
+ end
168
+ line '</ul>'
169
+ end
170
+
171
+ def olist
172
+ @out.puts '<ol>'
173
+ @f.while_match(/\A\s+\(\d+\)/) do |line|
174
+ string '<li>'
175
+ string compile_text(line.sub(/\A\s+\(\d+\)/, '').strip)
176
+ @f.while_match(/\A\s+(?!\(\d+\))\S/) do |cont|
177
+ string "\n"
178
+ string compile_text(cont.strip)
179
+ end
180
+ line '</li>'
181
+ end
182
+ line '</ol>'
183
+ end
184
+
185
+ def dlist
186
+ line '<dl>'
187
+ while @f.next? and /\A:/ =~ @f.peek
188
+ @f.while_match(/\A:/) do |line|
189
+ line dt(compile_text(line.sub(/\A:/, '').strip))
190
+ end
191
+ dd_with_p
192
+ end
193
+ line '</dl>'
194
+ end
195
+
196
+ # empty lines separate paragraphs.
197
+ def dd_with_p
198
+ line '<dd>'
199
+ while /\A(?:\s|\z)/ =~ @f.peek or %r!\A//emlist\{! =~ @f.peek
200
+ case @f.peek
201
+ when /\A$/
202
+ @f.gets
203
+ when /\A[ \t\z]/
204
+ line '<p>'
205
+ @f.while_match(/\A[ \t\z]/) do |line|
206
+ line compile_text(line.strip)
207
+ end
208
+ line '</p>'
209
+ when %r!\A//emlist\{!
210
+ emlist
211
+ else
212
+ raise 'must not happen'
213
+ end
214
+ end
215
+ line '</dd>'
216
+ end
217
+
218
+ # empty lines do not separate paragraphs.
219
+ def dd_without_p
220
+ line '<dd>'
221
+ while /\A[ \t]/ =~ @f.peek or %r!\A//emlist\{! =~ @f.peek
222
+ case @f.peek
223
+ when /\A[ \t\z]/
224
+ @f.while_match(/\A[ \t\z]/) do |line|
225
+ line compile_text(line.strip)
226
+ end
227
+ when %r!\A//emlist\{!
228
+ emlist
229
+ end
230
+ end
231
+ line '</dd>'
232
+ end
233
+
234
+ def dt(s)
235
+ "<dt>#{s}</dt>"
236
+ end
237
+
238
+ def emlist
239
+ @f.gets # discard "//emlist{"
240
+ line '<pre>'
241
+ @f.until_terminator(%r<\A//\}>) do |line|
242
+ line escape_html(line.rstrip)
243
+ end
244
+ line '</pre>'
245
+ end
246
+
247
+ def list
248
+ lines = unindent_block(canonicalize(@f.break(/\A\S/)))
249
+ while lines.last.empty?
250
+ lines.pop
251
+ end
252
+ line '<pre>'
253
+ lines.each do |line|
254
+ line escape_html(line)
255
+ end
256
+ line '</pre>'
257
+ end
258
+
259
+ def canonicalize(lines)
260
+ lines.map {|line| detab(line.rstrip) }
261
+ end
262
+
263
+ def paragraph
264
+ line '<p>'
265
+ read_paragraph(@f).each do |line|
266
+ line compile_text(line.strip)
267
+ end
268
+ line '</p>'
269
+ end
270
+
271
+ def read_paragraph(f)
272
+ f.span(%r<\A(?!---|=|//emlist\{)\S>)
273
+ end
274
+
275
+ def see
276
+ header = @f.gets
277
+ cmd = header.slice!(/\A\@\w+/)
278
+ body = [header] + @f.span(/\A\s+\S/)
279
+ line '<p>'
280
+ line '[SEE_ALSO] ' + compile_text(body.join('').strip)
281
+ line '</p>'
282
+ end
283
+
284
+ def todo
285
+ header = @f.gets
286
+ cmd = header.slice!(/\A\@\w+/)
287
+ body = header
288
+ line '<p class="todo">'
289
+ line '[TODO]' + body
290
+ line '</p>'
291
+ end
292
+
293
+ def method_info
294
+ line '<dl>'
295
+ while @f.next? and /\A\@(?!see)\w+|\A$/ =~ @f.peek
296
+ header = @f.gets
297
+ next if /\A$/ =~ header
298
+ cmd = header.slice!(/\A\@\w+/)
299
+ @f.ungets(header)
300
+ case cmd
301
+ when '@param', '@arg'
302
+ name = header.slice!(/\A\s*\w+/) || '?'
303
+ line "<dt class='method-param'>[PARAM] #{escape_html(name.strip)}:</dt>"
304
+ when '@raise'
305
+ ex = header.slice!(/\A\s*[\w:]+/) || '?'
306
+ line "<dt>[EXCEPTION] #{escape_html(ex.strip)}:</dt>"
307
+ when '@return'
308
+ line "<dt>[RETURN]</dt>"
309
+ else
310
+ line "<dt>[UNKNOWN_META_INFO] #{escape_html(cmd)}:</dt>"
311
+ end
312
+ dd_without_p
313
+ end
314
+ line '</dl>'
315
+ end
316
+
317
+ # FIXME: parse @param, @return, ...
318
+ def method_entry_paragraph
319
+ line '<p>'
320
+ read_method_entry_paragraph(@f).each do |line|
321
+ line compile_text(line.strip)
322
+ end
323
+ line '</p>'
324
+ end
325
+
326
+ def read_method_entry_paragraph(f)
327
+ f.span(%r<\A(?!---|=|//emlist\{|@[a-z])\S>)
328
+ end
329
+
330
+ def method_signature(sig_line)
331
+ # FIXME: check parameters, types, etc.
332
+ sig = MethodSignature.parse(sig_line)
333
+ string '<dt class="method-heading"><code>'
334
+ string @method.klass.name + @method.typemark if @opt
335
+ string escape_html(sig.friendly_string)
336
+ string '</code>'
337
+ if @method and not @method.defined?
338
+ line %Q( <span class="kindinfo">[#{@method.kind} by #{library_link(@method.library.name)}]</span>)
339
+ end
340
+ line '</dt>'
341
+ end
342
+
343
+ BracketLink = /\[\[[\w-]+?:[!-~]+?(?:\[\] )?\]\]/n
344
+ NeedESC = /[&"<>]/
345
+
346
+ def compile_text(str)
347
+ escape_table = HTMLUtils::ESC
348
+ str.gsub(/(#{NeedESC})|(#{BracketLink})/o) {
349
+ if char = $1 then escape_table[char]
350
+ elsif tok = $2 then bracket_link(tok[2..-3])
351
+ elsif tok = $3 then seems_code(tok)
352
+ else
353
+ raise 'must not happen'
354
+ end
355
+ }
356
+ end
357
+
358
+ def bracket_link(link, label = nil, frag = nil)
359
+ type, _arg = link.split(':', 2)
360
+ arg = _arg.rstrip
361
+ case type
362
+ when 'lib'
363
+ then protect(link) {
364
+ case arg
365
+ when '/', '_index'
366
+ label = 'All libraries'
367
+ when '_builtin'
368
+ label = 'Builtin libraries'
369
+ end
370
+ library_link(arg, label, frag)
371
+ }
372
+ when 'c' then protect(link) { class_link(arg, label, frag) }
373
+ when 'm' then protect(link) { method_link(complete_spec(arg), label || arg, frag) }
374
+ when 'f'
375
+ then protect(link) {
376
+ case arg
377
+ when '/', '_index'
378
+ arg, label = '', 'All C API'
379
+ end
380
+ function_link(arg, label || arg, frag)
381
+ }
382
+ when 'd' then protect(link) { document_link(arg, label, frag) }
383
+ when 'ref' then protect(link) { reference_link(arg) }
384
+ when 'url' then direct_url(arg)
385
+ when 'man' then man_link(arg)
386
+ when 'rfc', 'RFC'
387
+ rfc_link(arg)
388
+ when 'ruby-list', 'ruby-dev', 'ruby-ext', 'ruby-talk', 'ruby-core'
389
+ blade_link(type, arg)
390
+ else
391
+ "[[#{escape_html(link)}]]"
392
+ end
393
+ end
394
+
395
+ def protect(src)
396
+ yield
397
+ rescue => err
398
+ %Q(<span class="compileerror">[[compile error: #{escape_html(err.message)}: #{escape_html(src)}]]</span>)
399
+ end
400
+
401
+ def direct_url(url)
402
+ %Q(<a class="external" href="#{escape_html(url)}">#{escape_html(url)}</a>)
403
+ end
404
+
405
+ def reference_link(arg)
406
+ case arg
407
+ when /(\w+):(.*)\#(\w+)\z/
408
+ type, name, frag = $1, $2, $3
409
+ case type
410
+ when 'lib'
411
+ title, t, id = name, LibraryEntry.type_id.to_s, name
412
+ when 'c'
413
+ title, t, id = name, ClassEntry.type_id.to_s, name
414
+ when 'm'
415
+ title, t, id = name, MethodEntry.type_id.to_s, name
416
+ when 'd'
417
+ title, t, id = @option[:database].get_doc(name).title, DocEntry.type_id.to_s, name
418
+ else
419
+ raise "must not happen"
420
+ end
421
+ label = @option[:database].refs[t, id, frag]
422
+ label = title + '/' + label if label and name
423
+ bracket_link("#{type}:#{name}", label, frag)
424
+ when /\A(\w+)\z/
425
+ e = @option[:entry]
426
+ frag = $1
427
+ type = e.type_id.to_s
428
+ label = @option[:database].refs[type, e.name, frag] || frag
429
+ a_href('#' + frag, label)
430
+ else
431
+ raise "must not happen"
432
+ end
433
+ end
434
+
435
+ BLADE_URL = 'http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/%s/%s'
436
+
437
+ def blade_link(ml, num)
438
+ url = sprintf(BLADE_URL, ml, num)
439
+ %Q(<a class="external" href="#{escape_html(url)}">[#{escape_html("#{ml}:#{num}")}]</a>)
440
+ end
441
+
442
+ RFC_URL = 'http://www.ietf.org/rfc/rfc%s.txt'
443
+
444
+ def rfc_link(num)
445
+ url = sprintf(RFC_URL, num)
446
+ %Q(<a class="external" href="#{escape_html(url)}">[RFC#{escape_html(num)}]</a>)
447
+ end
448
+
449
+ opengroup_url = 'http://www.opengroup.org/onlinepubs/009695399'
450
+ MAN_CMD_URL = "#{opengroup_url}/utilities/%s.html"
451
+ MAN_FCN_URL = "#{opengroup_url}/functions/%s.html"
452
+ MAN_HEADER_URL = "#{opengroup_url}/basedefs/%s.html"
453
+ MAN_LINUX_URL = "http://man7.org/linux/man-pages/man%1$s/%2$s.%1$s.html"
454
+ MAN_FREEBSD_URL = "http://www.freebsd.org/cgi/man.cgi?query=%2$s&sektion=%1$s&manpath=FreeBSD+9.0-RELEASE"
455
+
456
+ def man_url(section, page)
457
+ case section
458
+ when "1"
459
+ sprintf(MAN_CMD_URL, page)
460
+ when "2", "3"
461
+ sprintf(MAN_FCN_URL, page)
462
+ when "header"
463
+ sprintf(MAN_HEADER_URL, page)
464
+ when /\A([23457])linux\Z/
465
+ sprintf(MAN_LINUX_URL, $1, page)
466
+ when /\A([1-9])freebsd\Z/
467
+ sprintf(MAN_FREEBSD_URL, $1, page)
468
+ else
469
+ nil
470
+ end
471
+ end
472
+
473
+ def man_link(spec)
474
+ m = /([\w\.\/]+)\((\w+)\)/.match(spec) or return escape_html(spec)
475
+ url = man_url(m[2], escape_html(m[1])) or return escape_html(spec)
476
+ %Q(<a class="external" href="#{escape_html(url)}">#{escape_html("#{m[1]}(#{m[2]})")}</a>)
477
+ end
478
+
479
+ def complete_spec(spec0)
480
+ case spec0
481
+ when /\A\$/
482
+ "Kernel#{spec0}"
483
+ else
484
+ spec0
485
+ end
486
+ end
487
+
488
+ def seems_code(text)
489
+ # FIXME
490
+ escape_html(text)
491
+ end
492
+
493
+ def string(str)
494
+ @out.print str
495
+ end
496
+
497
+ def line(str)
498
+ @out.puts str
499
+ end
500
+
501
+ def nl
502
+ @out.puts
503
+ end
504
+
505
+ end
506
+
507
+ end