source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/rdoc/generators/html_generator.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 38.4 KB
Line 
1# We're responsible for generating all the HTML files
2# from the object tree defined in code_objects.rb. We
3# generate:
4#
5# [files] an html file for each input file given. These
6# input files appear as objects of class
7# TopLevel
8#
9# [classes] an html file for each class or module encountered.
10# These classes are not grouped by file: if a file
11# contains four classes, we'll generate an html
12# file for the file itself, and four html files
13# for the individual classes.
14#
15# [indices] we generate three indices for files, classes,
16# and methods. These are displayed in a browser
17# like window with three index panes across the
18# top and the selected description below
19#
20# Method descriptions appear in whatever entity (file, class,
21# or module) that contains them.
22#
23# We generate files in a structure below a specified subdirectory,
24# normally +doc+.
25#
26# opdir
27# |
28# |___ files
29# | |__ per file summaries
30# |
31# |___ classes
32# |__ per class/module descriptions
33#
34# HTML is generated using the Template class.
35#
36
37require 'ftools'
38
39require 'rdoc/options'
40require 'rdoc/template'
41require 'rdoc/markup/simple_markup'
42require 'rdoc/markup/simple_markup/to_html'
43require 'cgi'
44
45module Generators
46
47 # Name of sub-direcories that hold file and class/module descriptions
48
49 FILE_DIR = "files"
50 CLASS_DIR = "classes"
51 CSS_NAME = "rdoc-style.css"
52
53
54 ##
55 # Build a hash of all items that can be cross-referenced.
56 # This is used when we output required and included names:
57 # if the names appear in this hash, we can generate
58 # an html cross reference to the appropriate description.
59 # We also use this when parsing comment blocks: any decorated
60 # words matching an entry in this list are hyperlinked.
61
62 class AllReferences
63 @@refs = {}
64
65 def AllReferences::reset
66 @@refs = {}
67 end
68
69 def AllReferences.add(name, html_class)
70 @@refs[name] = html_class
71 end
72
73 def AllReferences.[](name)
74 @@refs[name]
75 end
76
77 def AllReferences.keys
78 @@refs.keys
79 end
80 end
81
82
83 ##
84 # Subclass of the SM::ToHtml class that supports looking
85 # up words in the AllReferences list. Those that are
86 # found (like AllReferences in this comment) will
87 # be hyperlinked
88
89 class HyperlinkHtml < SM::ToHtml
90 # We need to record the html path of our caller so we can generate
91 # correct relative paths for any hyperlinks that we find
92 def initialize(from_path, context)
93 super()
94 @from_path = from_path
95
96 @parent_name = context.parent_name
97 @parent_name += "::" if @parent_name
98 @context = context
99 end
100
101 # We're invoked when any text matches the CROSSREF pattern
102 # (defined in MarkUp). If we fine the corresponding reference,
103 # generate a hyperlink. If the name we're looking for contains
104 # no punctuation, we look for it up the module/class chain. For
105 # example, HyperlinkHtml is found, even without the Generators::
106 # prefix, because we look for it in module Generators first.
107
108 def handle_special_CROSSREF(special)
109 name = special.text
110 if name[0,1] == '#'
111 lookup = name[1..-1]
112 name = lookup unless Options.instance.show_hash
113 else
114 lookup = name
115 end
116
117 # Find class, module, or method in class or module.
118 if /([A-Z]\w*)[.\#](\w+[!?=]?)/ =~ lookup
119 container = $1
120 method = $2
121 ref = @context.find_symbol(container, method)
122 elsif /([A-Za-z]\w*)[.\#](\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?)/ =~ lookup
123 container = $1
124 method = $2
125 ref = @context.find_symbol(container, method)
126 else
127 ref = @context.find_symbol(lookup)
128 end
129
130 if ref and ref.document_self
131 "<a href=\"#{ref.as_href(@from_path)}\">#{name}</a>"
132 else
133 name
134 end
135 end
136
137
138 # Generate a hyperlink for url, labeled with text. Handle the
139 # special cases for img: and link: described under handle_special_HYPEDLINK
140 def gen_url(url, text)
141 if url =~ /([A-Za-z]+):(.*)/
142 type = $1
143 path = $2
144 else
145 type = "http"
146 path = url
147 url = "http://#{url}"
148 end
149
150 if type == "link"
151 if path[0,1] == '#' # is this meaningful?
152 url = path
153 else
154 url = HTMLGenerator.gen_url(@from_path, path)
155 end
156 end
157
158 if (type == "http" || type == "link") &&
159 url =~ /\.(gif|png|jpg|jpeg|bmp)$/
160
161 "<img src=\"#{url}\" />"
162 else
163 "<a href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
164 end
165 end
166
167 # And we're invoked with a potential external hyperlink mailto:
168 # just gets inserted. http: links are checked to see if they
169 # reference an image. If so, that image gets inserted using an
170 # <img> tag. Otherwise a conventional <a href> is used. We also
171 # support a special type of hyperlink, link:, which is a reference
172 # to a local file whose path is relative to the --op directory.
173
174 def handle_special_HYPERLINK(special)
175 url = special.text
176 gen_url(url, url)
177 end
178
179 # HEre's a hypedlink where the label is different to the URL
180 # <label>[url]
181 #
182
183 def handle_special_TIDYLINK(special)
184 text = special.text
185# unless text =~ /(\S+)\[(.*?)\]/
186 unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
187 return text
188 end
189 label = $1
190 url = $2
191 gen_url(url, label)
192 end
193
194 end
195
196
197
198 #####################################################################
199 #
200 # Handle common markup tasks for the various Html classes
201 #
202
203 module MarkUp
204
205 # Convert a string in markup format into HTML. We keep a cached
206 # SimpleMarkup object lying around after the first time we're
207 # called per object.
208
209 def markup(str, remove_para=false)
210 return '' unless str
211 unless defined? @markup
212 @markup = SM::SimpleMarkup.new
213
214 # class names, variable names, or instance variables
215 @markup.add_special(/(
216 \w+(::\w+)*[.\#]\w+(\([\.\w+\*\/\+\-\=\<\>]+\))? # A::B.meth(**) (for operator in Fortran95)
217 | \#\w+(\([.\w\*\/\+\-\=\<\>]+\))? # meth(**) (for operator in Fortran95)
218 | \b([A-Z]\w*(::\w+)*[.\#]\w+) # A::B.meth
219 | \b([A-Z]\w+(::\w+)*) # A::B..
220 | \#\w+[!?=]? # #meth_name
221 | \b\w+([_\/\.]+\w+)*[!?=]? # meth_name
222 )/x,
223 :CROSSREF)
224
225 # external hyperlinks
226 @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
227
228 # and links of the form <text>[<url>]
229 @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
230# @markup.add_special(/\b(\S+?\[\S+?\.\S+?\])/, :TIDYLINK)
231
232 end
233 unless defined? @html_formatter
234 @html_formatter = HyperlinkHtml.new(self.path, self)
235 end
236
237 # Convert leading comment markers to spaces, but only
238 # if all non-blank lines have them
239
240 if str =~ /^(?>\s*)[^\#]/
241 content = str
242 else
243 content = str.gsub(/^\s*(#+)/) { $1.tr('#',' ') }
244 end
245
246 res = @markup.convert(content, @html_formatter)
247 if remove_para
248 res.sub!(/^<p>/, '')
249 res.sub!(/<\/p>$/, '')
250 end
251 res
252 end
253
254 # Qualify a stylesheet URL; if if +css_name+ does not begin with '/' or
255 # 'http[s]://', prepend a prefix relative to +path+. Otherwise, return it
256 # unmodified.
257
258 def style_url(path, css_name=nil)
259# $stderr.puts "style_url( #{path.inspect}, #{css_name.inspect} )"
260 css_name ||= CSS_NAME
261 if %r{^(https?:/)?/} =~ css_name
262 return css_name
263 else
264 return HTMLGenerator.gen_url(path, css_name)
265 end
266 end
267
268 # Build a webcvs URL with the given 'url' argument. URLs with a '%s' in them
269 # get the file's path sprintfed into them; otherwise they're just catenated
270 # together.
271
272 def cvs_url(url, full_path)
273 if /%s/ =~ url
274 return sprintf( url, full_path )
275 else
276 return url + full_path
277 end
278 end
279 end
280
281
282 #####################################################################
283 #
284 # A Context is built by the parser to represent a container: contexts
285 # hold classes, modules, methods, require lists and include lists.
286 # ClassModule and TopLevel are the context objects we process here
287 #
288 class ContextUser
289
290 include MarkUp
291
292 attr_reader :context
293
294 def initialize(context, options)
295 @context = context
296 @options = options
297 end
298
299 # convenience method to build a hyperlink
300 def href(link, cls, name)
301 %{<a href="#{link}" class="#{cls}">#{name}</a>} #"
302 end
303
304 # return a reference to outselves to be used as an href=
305 # the form depends on whether we're all in one file
306 # or in multiple files
307
308 def as_href(from_path)
309 if @options.all_one_file
310 "#" + path
311 else
312 HTMLGenerator.gen_url(from_path, path)
313 end
314 end
315
316 # Create a list of HtmlMethod objects for each method
317 # in the corresponding context object. If the @options.show_all
318 # variable is set (corresponding to the <tt>--all</tt> option,
319 # we include all methods, otherwise just the public ones.
320
321 def collect_methods
322 list = @context.method_list
323 unless @options.show_all
324 list = list.find_all {|m| m.visibility == :public || m.visibility == :protected || m.force_documentation }
325 end
326 @methods = list.collect {|m| HtmlMethod.new(m, self, @options) }
327 end
328
329 # Build a summary list of all the methods in this context
330 def build_method_summary_list(path_prefix="")
331 collect_methods unless @methods
332 meths = @methods.sort
333 res = []
334 meths.each do |meth|
335 res << {
336 "name" => CGI.escapeHTML(meth.name),
337 "aref" => "#{path_prefix}\##{meth.aref}"
338 }
339 end
340 res
341 end
342
343
344 # Build a list of aliases for which we couldn't find a
345 # corresponding method
346 def build_alias_summary_list(section)
347 values = []
348 @context.aliases.each do |al|
349 next unless al.section == section
350 res = {
351 'old_name' => al.old_name,
352 'new_name' => al.new_name,
353 }
354 if al.comment && !al.comment.empty?
355 res['desc'] = markup(al.comment, true)
356 end
357 values << res
358 end
359 values
360 end
361
362 # Build a list of constants
363 def build_constants_summary_list(section)
364 values = []
365 @context.constants.each do |co|
366 next unless co.section == section
367 res = {
368 'name' => co.name,
369 'value' => CGI.escapeHTML(co.value)
370 }
371 res['desc'] = markup(co.comment, true) if co.comment && !co.comment.empty?
372 values << res
373 end
374 values
375 end
376
377 def build_requires_list(context)
378 potentially_referenced_list(context.requires) {|fn| [fn + ".rb"] }
379 end
380
381 def build_include_list(context)
382 potentially_referenced_list(context.includes)
383 end
384
385 # Build a list from an array of <i>Htmlxxx</i> items. Look up each
386 # in the AllReferences hash: if we find a corresponding entry,
387 # we generate a hyperlink to it, otherwise just output the name.
388 # However, some names potentially need massaging. For example,
389 # you may require a Ruby file without the .rb extension,
390 # but the file names we know about may have it. To deal with
391 # this, we pass in a block which performs the massaging,
392 # returning an array of alternative names to match
393
394 def potentially_referenced_list(array)
395 res = []
396 array.each do |i|
397 ref = AllReferences[i.name]
398# if !ref
399# container = @context.parent
400# while !ref && container
401# name = container.name + "::" + i.name
402# ref = AllReferences[name]
403# container = container.parent
404# end
405# end
406
407 ref = @context.find_symbol(i.name)
408 ref = ref.viewer if ref
409
410 if !ref && block_given?
411 possibles = yield(i.name)
412 while !ref and !possibles.empty?
413 ref = AllReferences[possibles.shift]
414 end
415 end
416 h_name = CGI.escapeHTML(i.name)
417 if ref and ref.document_self
418 path = url(ref.path)
419 res << { "name" => h_name, "aref" => path }
420 else
421 res << { "name" => h_name }
422 end
423 end
424 res
425 end
426
427 # Build an array of arrays of method details. The outer array has up
428 # to six entries, public, private, and protected for both class
429 # methods, the other for instance methods. The inner arrays contain
430 # a hash for each method
431
432 def build_method_detail_list(section)
433 outer = []
434
435 methods = @methods.sort
436 for singleton in [true, false]
437 for vis in [ :public, :protected, :private ]
438 res = []
439 methods.each do |m|
440 if m.section == section and
441 m.document_self and
442 m.visibility == vis and
443 m.singleton == singleton
444 row = {}
445 if m.call_seq
446 row["callseq"] = m.call_seq.gsub(/->/, '&rarr;')
447 else
448 row["name"] = CGI.escapeHTML(m.name)
449 row["params"] = m.params
450 end
451 desc = m.description.strip
452 row["m_desc"] = desc unless desc.empty?
453 row["aref"] = m.aref
454 row["visibility"] = m.visibility.to_s
455
456 alias_names = []
457 m.aliases.each do |other|
458 if other.viewer # won't be if the alias is private
459 alias_names << {
460 'name' => other.name,
461 'aref' => other.viewer.as_href(path)
462 }
463 end
464 end
465 unless alias_names.empty?
466 row["aka"] = alias_names
467 end
468
469 if @options.inline_source
470 code = m.source_code
471 row["sourcecode"] = code if code
472 else
473 code = m.src_url
474 if code
475 row["codeurl"] = code
476 row["imgurl"] = m.img_url
477 end
478 end
479 res << row
480 end
481 end
482 if res.size > 0
483 outer << {
484 "type" => vis.to_s.capitalize,
485 "category" => singleton ? "Class" : "Instance",
486 "methods" => res
487 }
488 end
489 end
490 end
491 outer
492 end
493
494 # Build the structured list of classes and modules contained
495 # in this context.
496
497 def build_class_list(level, from, section, infile=nil)
498 res = ""
499 prefix = "&nbsp;&nbsp;::" * level;
500
501 from.modules.sort.each do |mod|
502 next unless mod.section == section
503 next if infile && !mod.defined_in?(infile)
504 if mod.document_self
505 res <<
506 prefix <<
507 "Module " <<
508 href(url(mod.viewer.path), "link", mod.full_name) <<
509 "<br />\n" <<
510 build_class_list(level + 1, mod, section, infile)
511 end
512 end
513
514 from.classes.sort.each do |cls|
515 next unless cls.section == section
516 next if infile && !cls.defined_in?(infile)
517 if cls.document_self
518 res <<
519 prefix <<
520 "Class " <<
521 href(url(cls.viewer.path), "link", cls.full_name) <<
522 "<br />\n" <<
523 build_class_list(level + 1, cls, section, infile)
524 end
525 end
526
527 res
528 end
529
530 def url(target)
531 HTMLGenerator.gen_url(path, target)
532 end
533
534 def aref_to(target)
535 if @options.all_one_file
536 "#" + target
537 else
538 url(target)
539 end
540 end
541
542 def document_self
543 @context.document_self
544 end
545
546 def diagram_reference(diagram)
547 res = diagram.gsub(/((?:src|href)=")(.*?)"/) {
548 $1 + url($2) + '"'
549 }
550 res
551 end
552
553
554 # Find a symbol in ourselves or our parent
555 def find_symbol(symbol, method=nil)
556 res = @context.find_symbol(symbol, method)
557 if res
558 res = res.viewer
559 end
560 res
561 end
562
563 # create table of contents if we contain sections
564
565 def add_table_of_sections
566 toc = []
567 @context.sections.each do |section|
568 if section.title
569 toc << {
570 'secname' => section.title,
571 'href' => section.sequence
572 }
573 end
574 end
575
576 @values['toc'] = toc unless toc.empty?
577 end
578
579
580 end
581
582 #####################################################################
583 #
584 # Wrap a ClassModule context
585
586 class HtmlClass < ContextUser
587
588 attr_reader :path
589
590 def initialize(context, html_file, prefix, options)
591 super(context, options)
592
593 @html_file = html_file
594 @is_module = context.is_module?
595 @values = {}
596
597 context.viewer = self
598
599 if options.all_one_file
600 @path = context.full_name
601 else
602 @path = http_url(context.full_name, prefix)
603 end
604
605 collect_methods
606
607 AllReferences.add(name, self)
608 end
609
610 # return the relative file name to store this class in,
611 # which is also its url
612 def http_url(full_name, prefix)
613 path = full_name.dup
614 if path['<<']
615 path.gsub!(/<<\s*(\w*)/) { "from-#$1" }
616 end
617 File.join(prefix, path.split("::")) + ".html"
618 end
619
620
621 def name
622 @context.full_name
623 end
624
625 def parent_name
626 @context.parent.full_name
627 end
628
629 def index_name
630 name
631 end
632
633 def write_on(f)
634 value_hash
635 template = TemplatePage.new(RDoc::Page::BODY,
636 RDoc::Page::CLASS_PAGE,
637 RDoc::Page::METHOD_LIST)
638 template.write_html_on(f, @values)
639 end
640
641 def value_hash
642 class_attribute_values
643 add_table_of_sections
644
645 @values["charset"] = @options.charset
646 @values["style_url"] = style_url(path, @options.css)
647
648 d = markup(@context.comment)
649 @values["description"] = d unless d.empty?
650
651 ml = build_method_summary_list
652 @values["methods"] = ml unless ml.empty?
653
654 il = build_include_list(@context)
655 @values["includes"] = il unless il.empty?
656
657 @values["sections"] = @context.sections.map do |section|
658
659 secdata = {
660 "sectitle" => section.title,
661 "secsequence" => section.sequence,
662 "seccomment" => markup(section.comment)
663 }
664
665 al = build_alias_summary_list(section)
666 secdata["aliases"] = al unless al.empty?
667
668 co = build_constants_summary_list(section)
669 secdata["constants"] = co unless co.empty?
670
671 al = build_attribute_list(section)
672 secdata["attributes"] = al unless al.empty?
673
674 cl = build_class_list(0, @context, section)
675 secdata["classlist"] = cl unless cl.empty?
676
677 mdl = build_method_detail_list(section)
678 secdata["method_list"] = mdl unless mdl.empty?
679
680 secdata
681 end
682
683 @values
684 end
685
686 def build_attribute_list(section)
687 atts = @context.attributes.sort
688 res = []
689 atts.each do |att|
690 next unless att.section == section
691 if att.visibility == :public || att.visibility == :protected || @options.show_all
692 entry = {
693 "name" => CGI.escapeHTML(att.name),
694 "rw" => att.rw,
695 "a_desc" => markup(att.comment, true)
696 }
697 unless att.visibility == :public || att.visibility == :protected
698 entry["rw"] << "-"
699 end
700 res << entry
701 end
702 end
703 res
704 end
705
706 def class_attribute_values
707 h_name = CGI.escapeHTML(name)
708
709 @values["classmod"] = @is_module ? "Module" : "Class"
710 @values["title"] = "#{@values['classmod']}: #{h_name}"
711
712 c = @context
713 c = c.parent while c and !c.diagram
714 if c && c.diagram
715 @values["diagram"] = diagram_reference(c.diagram)
716 end
717
718 @values["full_name"] = h_name
719
720 parent_class = @context.superclass
721
722 if parent_class
723 @values["parent"] = CGI.escapeHTML(parent_class)
724
725 if parent_name
726 lookup = parent_name + "::" + parent_class
727 else
728 lookup = parent_class
729 end
730
731 parent_url = AllReferences[lookup] || AllReferences[parent_class]
732
733 if parent_url and parent_url.document_self
734 @values["par_url"] = aref_to(parent_url.path)
735 end
736 end
737
738 files = []
739 @context.in_files.each do |f|
740 res = {}
741 full_path = CGI.escapeHTML(f.file_absolute_name)
742
743 res["full_path"] = full_path
744 res["full_path_url"] = aref_to(f.viewer.path) if f.document_self
745
746 if @options.webcvs
747 res["cvsurl"] = cvs_url( @options.webcvs, full_path )
748 end
749
750 files << res
751 end
752
753 @values['infiles'] = files
754 end
755
756 def <=>(other)
757 self.name <=> other.name
758 end
759
760 end
761
762 #####################################################################
763 #
764 # Handles the mapping of a file's information to HTML. In reality,
765 # a file corresponds to a +TopLevel+ object, containing modules,
766 # classes, and top-level methods. In theory it _could_ contain
767 # attributes and aliases, but we ignore these for now.
768
769 class HtmlFile < ContextUser
770
771 attr_reader :path
772 attr_reader :name
773
774 def initialize(context, options, file_dir)
775 super(context, options)
776
777 @values = {}
778
779 if options.all_one_file
780 @path = filename_to_label
781 else
782 @path = http_url(file_dir)
783 end
784
785 @name = @context.file_relative_name
786
787 collect_methods
788 AllReferences.add(name, self)
789 context.viewer = self
790 end
791
792 def http_url(file_dir)
793 File.join(file_dir, @context.file_relative_name.tr('.', '_')) +
794 ".html"
795 end
796
797 def filename_to_label
798 @context.file_relative_name.gsub(/%|\/|\?|\#/) {|s| '%' + ("%x" % s[0]) }
799 end
800
801 def index_name
802 name
803 end
804
805 def parent_name
806 nil
807 end
808
809 def value_hash
810 file_attribute_values
811 add_table_of_sections
812
813 @values["charset"] = @options.charset
814 @values["href"] = path
815 @values["style_url"] = style_url(path, @options.css)
816
817 if @context.comment
818 d = markup(@context.comment)
819 @values["description"] = d if d.size > 0
820 end
821
822 ml = build_method_summary_list
823 @values["methods"] = ml unless ml.empty?
824
825 il = build_include_list(@context)
826 @values["includes"] = il unless il.empty?
827
828 rl = build_requires_list(@context)
829 @values["requires"] = rl unless rl.empty?
830
831 if @options.promiscuous
832 file_context = nil
833 else
834 file_context = @context
835 end
836
837
838 @values["sections"] = @context.sections.map do |section|
839
840 secdata = {
841 "sectitle" => section.title,
842 "secsequence" => section.sequence,
843 "seccomment" => markup(section.comment)
844 }
845
846 cl = build_class_list(0, @context, section, file_context)
847 @values["classlist"] = cl unless cl.empty?
848
849 mdl = build_method_detail_list(section)
850 secdata["method_list"] = mdl unless mdl.empty?
851
852 al = build_alias_summary_list(section)
853 secdata["aliases"] = al unless al.empty?
854
855 co = build_constants_summary_list(section)
856 @values["constants"] = co unless co.empty?
857
858 secdata
859 end
860
861 @values
862 end
863
864 def write_on(f)
865 value_hash
866 template = TemplatePage.new(RDoc::Page::BODY,
867 RDoc::Page::FILE_PAGE,
868 RDoc::Page::METHOD_LIST)
869 template.write_html_on(f, @values)
870 end
871
872 def file_attribute_values
873 full_path = @context.file_absolute_name
874 short_name = File.basename(full_path)
875
876 @values["title"] = CGI.escapeHTML("File: #{short_name}")
877
878 if @context.diagram
879 @values["diagram"] = diagram_reference(@context.diagram)
880 end
881
882 @values["short_name"] = CGI.escapeHTML(short_name)
883 @values["full_path"] = CGI.escapeHTML(full_path)
884 @values["dtm_modified"] = @context.file_stat.mtime.to_s
885
886 if @options.webcvs
887 @values["cvsurl"] = cvs_url( @options.webcvs, @values["full_path"] )
888 end
889 end
890
891 def <=>(other)
892 self.name <=> other.name
893 end
894 end
895
896 #####################################################################
897
898 class HtmlMethod
899 include MarkUp
900
901 attr_reader :context
902 attr_reader :src_url
903 attr_reader :img_url
904 attr_reader :source_code
905
906 @@seq = "M000000"
907
908 @@all_methods = []
909
910 def HtmlMethod::reset
911 @@all_methods = []
912 end
913
914 def initialize(context, html_class, options)
915 @context = context
916 @html_class = html_class
917 @options = options
918 @@seq = @@seq.succ
919 @seq = @@seq
920 @@all_methods << self
921
922 context.viewer = self
923
924 if (ts = @context.token_stream)
925 @source_code = markup_code(ts)
926 unless @options.inline_source
927 @src_url = create_source_code_file(@source_code)
928 @img_url = HTMLGenerator.gen_url(path, 'source.png')
929 end
930 end
931
932 AllReferences.add(name, self)
933 end
934
935 # return a reference to outselves to be used as an href=
936 # the form depends on whether we're all in one file
937 # or in multiple files
938
939 def as_href(from_path)
940 if @options.all_one_file
941 "#" + path
942 else
943 HTMLGenerator.gen_url(from_path, path)
944 end
945 end
946
947 def name
948 @context.name
949 end
950
951 def section
952 @context.section
953 end
954
955 def index_name
956 "#{@context.name} (#{@html_class.name})"
957 end
958
959 def parent_name
960 if @context.parent.parent
961 @context.parent.parent.full_name
962 else
963 nil
964 end
965 end
966
967 def aref
968 @seq
969 end
970
971 def path
972 if @options.all_one_file
973 aref
974 else
975 @html_class.path + "#" + aref
976 end
977 end
978
979 def description
980 markup(@context.comment)
981 end
982
983 def visibility
984 @context.visibility
985 end
986
987 def singleton
988 @context.singleton
989 end
990
991 def call_seq
992 cs = @context.call_seq
993 if cs
994 cs.gsub(/\n/, "<br />\n")
995 else
996 nil
997 end
998 end
999
1000 def params
1001 # params coming from a call-seq in 'C' will start with the
1002 # method name
1003 p = @context.params
1004 if p !~ /^\w/
1005 p = @context.params.gsub(/\s*\#.*/, '')
1006 p = p.tr("\n", " ").squeeze(" ")
1007 p = "(" + p + ")" unless p[0] == ?(
1008
1009 if (block = @context.block_params)
1010 # If this method has explicit block parameters, remove any
1011 # explicit &block
1012
1013 p.sub!(/,?\s*&\w+/, '')
1014
1015 block.gsub!(/\s*\#.*/, '')
1016 block = block.tr("\n", " ").squeeze(" ")
1017 if block[0] == ?(
1018 block.sub!(/^\(/, '').sub!(/\)/, '')
1019 end
1020 p << " {|#{block.strip}| ...}"
1021 end
1022 end
1023 CGI.escapeHTML(p)
1024 end
1025
1026 def create_source_code_file(code_body)
1027 meth_path = @html_class.path.sub(/\.html$/, '.src')
1028 File.makedirs(meth_path)
1029 file_path = File.join(meth_path, @seq) + ".html"
1030
1031 template = TemplatePage.new(RDoc::Page::SRC_PAGE)
1032 File.open(file_path, "w") do |f|
1033 values = {
1034 'title' => CGI.escapeHTML(index_name),
1035 'code' => code_body,
1036 'style_url' => style_url(file_path, @options.css),
1037 'charset' => @options.charset
1038 }
1039 template.write_html_on(f, values)
1040 end
1041 HTMLGenerator.gen_url(path, file_path)
1042 end
1043
1044 def HtmlMethod.all_methods
1045 @@all_methods
1046 end
1047
1048 def <=>(other)
1049 @context <=> other.context
1050 end
1051
1052 ##
1053 # Given a sequence of source tokens, mark up the source code
1054 # to make it look purty.
1055
1056
1057 def markup_code(tokens)
1058 src = ""
1059 tokens.each do |t|
1060 next unless t
1061 # p t.class
1062# style = STYLE_MAP[t.class]
1063 style = case t
1064 when RubyToken::TkCONSTANT then "ruby-constant"
1065 when RubyToken::TkKW then "ruby-keyword kw"
1066 when RubyToken::TkIVAR then "ruby-ivar"
1067 when RubyToken::TkOp then "ruby-operator"
1068 when RubyToken::TkId then "ruby-identifier"
1069 when RubyToken::TkNode then "ruby-node"
1070 when RubyToken::TkCOMMENT then "ruby-comment cmt"
1071 when RubyToken::TkREGEXP then "ruby-regexp re"
1072 when RubyToken::TkSTRING then "ruby-value str"
1073 when RubyToken::TkVal then "ruby-value"
1074 else
1075 nil
1076 end
1077
1078 text = CGI.escapeHTML(t.text)
1079
1080 if style
1081 src << "<span class=\"#{style}\">#{text}</span>"
1082 else
1083 src << text
1084 end
1085 end
1086
1087 add_line_numbers(src) if Options.instance.include_line_numbers
1088 src
1089 end
1090
1091 # we rely on the fact that the first line of a source code
1092 # listing has
1093 # # File xxxxx, line dddd
1094
1095 def add_line_numbers(src)
1096 if src =~ /\A.*, line (\d+)/
1097 first = $1.to_i - 1
1098 last = first + src.count("\n")
1099 size = last.to_s.length
1100 real_fmt = "%#{size}d: "
1101 fmt = " " * (size+2)
1102 src.gsub!(/^/) do
1103 res = sprintf(fmt, first)
1104 first += 1
1105 fmt = real_fmt
1106 res
1107 end
1108 end
1109 end
1110
1111 def document_self
1112 @context.document_self
1113 end
1114
1115 def aliases
1116 @context.aliases
1117 end
1118
1119 def find_symbol(symbol, method=nil)
1120 res = @context.parent.find_symbol(symbol, method)
1121 if res
1122 res = res.viewer
1123 end
1124 res
1125 end
1126 end
1127
1128 #####################################################################
1129
1130 class HTMLGenerator
1131
1132 include MarkUp
1133
1134 ##
1135 # convert a target url to one that is relative to a given
1136 # path
1137
1138 def HTMLGenerator.gen_url(path, target)
1139 from = File.dirname(path)
1140 to, to_file = File.split(target)
1141
1142 from = from.split("/")
1143 to = to.split("/")
1144
1145 while from.size > 0 and to.size > 0 and from[0] == to[0]
1146 from.shift
1147 to.shift
1148 end
1149
1150 from.fill("..")
1151 from.concat(to)
1152 from << to_file
1153 File.join(*from)
1154 end
1155
1156 # Generators may need to return specific subclasses depending
1157 # on the options they are passed. Because of this
1158 # we create them using a factory
1159
1160 def HTMLGenerator.for(options)
1161 AllReferences::reset
1162 HtmlMethod::reset
1163
1164 if options.all_one_file
1165 HTMLGeneratorInOne.new(options)
1166 else
1167 HTMLGenerator.new(options)
1168 end
1169 end
1170
1171 class <<self
1172 protected :new
1173 end
1174
1175 # Set up a new HTML generator. Basically all we do here is load
1176 # up the correct output temlate
1177
1178 def initialize(options) #:not-new:
1179 @options = options
1180 load_html_template
1181 end
1182
1183
1184 ##
1185 # Build the initial indices and output objects
1186 # based on an array of TopLevel objects containing
1187 # the extracted information.
1188
1189 def generate(toplevels)
1190 @toplevels = toplevels
1191 @files = []
1192 @classes = []
1193
1194 write_style_sheet
1195 gen_sub_directories()
1196 build_indices
1197 generate_html
1198 end
1199
1200 private
1201
1202 ##
1203 # Load up the HTML template specified in the options.
1204 # If the template name contains a slash, use it literally
1205 #
1206 def load_html_template
1207 template = @options.template
1208 unless template =~ %r{/|\\}
1209 template = File.join("rdoc/generators/template",
1210 @options.generator.key, template)
1211 end
1212 require template
1213 extend RDoc::Page
1214 rescue LoadError
1215 $stderr.puts "Could not find HTML template '#{template}'"
1216 exit 99
1217 end
1218
1219 ##
1220 # Write out the style sheet used by the main frames
1221 #
1222
1223 def write_style_sheet
1224 template = TemplatePage.new(RDoc::Page::STYLE)
1225 unless @options.css
1226 File.open(CSS_NAME, "w") do |f|
1227 values = { "fonts" => RDoc::Page::FONTS }
1228 template.write_html_on(f, values)
1229 end
1230 end
1231 end
1232
1233 ##
1234 # See the comments at the top for a description of the
1235 # directory structure
1236
1237 def gen_sub_directories
1238 File.makedirs(FILE_DIR, CLASS_DIR)
1239 rescue
1240 $stderr.puts $!.message
1241 exit 1
1242 end
1243
1244 ##
1245 # Generate:
1246 #
1247 # * a list of HtmlFile objects for each TopLevel object.
1248 # * a list of HtmlClass objects for each first level
1249 # class or module in the TopLevel objects
1250 # * a complete list of all hyperlinkable terms (file,
1251 # class, module, and method names)
1252
1253 def build_indices
1254
1255 @toplevels.each do |toplevel|
1256 @files << HtmlFile.new(toplevel, @options, FILE_DIR)
1257 end
1258
1259 RDoc::TopLevel.all_classes_and_modules.each do |cls|
1260 build_class_list(cls, @files[0], CLASS_DIR)
1261 end
1262 end
1263
1264 def build_class_list(from, html_file, class_dir)
1265 @classes << HtmlClass.new(from, html_file, class_dir, @options)
1266 from.each_classmodule do |mod|
1267 build_class_list(mod, html_file, class_dir)
1268 end
1269 end
1270
1271 ##
1272 # Generate all the HTML
1273 #
1274 def generate_html
1275 # the individual descriptions for files and classes
1276 gen_into(@files)
1277 gen_into(@classes)
1278 # and the index files
1279 gen_file_index
1280 gen_class_index
1281 gen_method_index
1282 gen_main_index
1283
1284 # this method is defined in the template file
1285 write_extra_pages if defined? write_extra_pages
1286 end
1287
1288 def gen_into(list)
1289 list.each do |item|
1290 if item.document_self
1291 op_file = item.path
1292 File.makedirs(File.dirname(op_file))
1293 File.open(op_file, "w") { |file| item.write_on(file) }
1294 end
1295 end
1296
1297 end
1298
1299 def gen_file_index
1300 gen_an_index(@files, 'Files',
1301 RDoc::Page::FILE_INDEX,
1302 "fr_file_index.html")
1303 end
1304
1305 def gen_class_index
1306 gen_an_index(@classes, 'Classes',
1307 RDoc::Page::CLASS_INDEX,
1308 "fr_class_index.html")
1309 end
1310
1311 def gen_method_index
1312 gen_an_index(HtmlMethod.all_methods, 'Methods',
1313 RDoc::Page::METHOD_INDEX,
1314 "fr_method_index.html")
1315 end
1316
1317
1318 def gen_an_index(collection, title, template, filename)
1319 template = TemplatePage.new(RDoc::Page::FR_INDEX_BODY, template)
1320 res = []
1321 collection.sort.each do |f|
1322 if f.document_self
1323 res << { "href" => f.path, "name" => f.index_name }
1324 end
1325 end
1326
1327 values = {
1328 "entries" => res,
1329 'list_title' => CGI.escapeHTML(title),
1330 'index_url' => main_url,
1331 'charset' => @options.charset,
1332 'style_url' => style_url('', @options.css),
1333 }
1334
1335 File.open(filename, "w") do |f|
1336 template.write_html_on(f, values)
1337 end
1338 end
1339
1340 # The main index page is mostly a template frameset, but includes
1341 # the initial page. If the <tt>--main</tt> option was given,
1342 # we use this as our main page, otherwise we use the
1343 # first file specified on the command line.
1344
1345 def gen_main_index
1346 template = TemplatePage.new(RDoc::Page::INDEX)
1347 File.open("index.html", "w") do |f|
1348 values = {
1349 "initial_page" => main_url,
1350 'title' => CGI.escapeHTML(@options.title),
1351 'charset' => @options.charset
1352 }
1353 if @options.inline_source
1354 values['inline_source'] = true
1355 end
1356 template.write_html_on(f, values)
1357 end
1358 end
1359
1360 # return the url of the main page
1361 def main_url
1362 main_page = @options.main_page
1363 ref = nil
1364 if main_page
1365 ref = AllReferences[main_page]
1366 if ref
1367 ref = ref.path
1368 else
1369 $stderr.puts "Could not find main page #{main_page}"
1370 end
1371 end
1372
1373 unless ref
1374 for file in @files
1375 if file.document_self
1376 ref = file.path
1377 break
1378 end
1379 end
1380 end
1381
1382 unless ref
1383 $stderr.puts "Couldn't find anything to document"
1384 $stderr.puts "Perhaps you've used :stopdoc: in all classes"
1385 exit(1)
1386 end
1387
1388 ref
1389 end
1390
1391
1392 end
1393
1394
1395 ######################################################################
1396
1397
1398 class HTMLGeneratorInOne < HTMLGenerator
1399
1400 def initialize(*args)
1401 super
1402 end
1403
1404 ##
1405 # Build the initial indices and output objects
1406 # based on an array of TopLevel objects containing
1407 # the extracted information.
1408
1409 def generate(info)
1410 @toplevels = info
1411 @files = []
1412 @classes = []
1413 @hyperlinks = {}
1414
1415 build_indices
1416 generate_xml
1417 end
1418
1419
1420 ##
1421 # Generate:
1422 #
1423 # * a list of HtmlFile objects for each TopLevel object.
1424 # * a list of HtmlClass objects for each first level
1425 # class or module in the TopLevel objects
1426 # * a complete list of all hyperlinkable terms (file,
1427 # class, module, and method names)
1428
1429 def build_indices
1430
1431 @toplevels.each do |toplevel|
1432 @files << HtmlFile.new(toplevel, @options, FILE_DIR)
1433 end
1434
1435 RDoc::TopLevel.all_classes_and_modules.each do |cls|
1436 build_class_list(cls, @files[0], CLASS_DIR)
1437 end
1438 end
1439
1440 def build_class_list(from, html_file, class_dir)
1441 @classes << HtmlClass.new(from, html_file, class_dir, @options)
1442 from.each_classmodule do |mod|
1443 build_class_list(mod, html_file, class_dir)
1444 end
1445 end
1446
1447 ##
1448 # Generate all the HTML. For the one-file case, we generate
1449 # all the information in to one big hash
1450 #
1451 def generate_xml
1452 values = {
1453 'charset' => @options.charset,
1454 'files' => gen_into(@files),
1455 'classes' => gen_into(@classes),
1456 'title' => CGI.escapeHTML(@options.title),
1457 }
1458
1459 # this method is defined in the template file
1460 write_extra_pages if defined? write_extra_pages
1461
1462 template = TemplatePage.new(RDoc::Page::ONE_PAGE)
1463
1464 if @options.op_name
1465 opfile = File.open(@options.op_name, "w")
1466 else
1467 opfile = $stdout
1468 end
1469 template.write_html_on(opfile, values)
1470 end
1471
1472 def gen_into(list)
1473 res = []
1474 list.each do |item|
1475 res << item.value_hash
1476 end
1477 res
1478 end
1479
1480 def gen_file_index
1481 gen_an_index(@files, 'Files')
1482 end
1483
1484 def gen_class_index
1485 gen_an_index(@classes, 'Classes')
1486 end
1487
1488 def gen_method_index
1489 gen_an_index(HtmlMethod.all_methods, 'Methods')
1490 end
1491
1492
1493 def gen_an_index(collection, title)
1494 res = []
1495 collection.sort.each do |f|
1496 if f.document_self
1497 res << { "href" => f.path, "name" => f.index_name }
1498 end
1499 end
1500
1501 return {
1502 "entries" => res,
1503 'list_title' => title,
1504 'index_url' => main_url,
1505 }
1506 end
1507
1508 end
1509end
Note: See TracBrowser for help on using the repository browser.