~ubuntu-branches/ubuntu/hardy/ruby1.8/hardy-updates

« back to all changes in this revision

Viewing changes to lib/rdoc/generators/ri_generator.rb

  • Committer: Bazaar Package Importer
  • Author(s): akira yamada
  • Date: 2007-03-13 22:11:58 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20070313221158-h3oql37brlaf2go2
Tags: 1.8.6-1
* new upstream version, 1.8.6.
* libruby1.8 conflicts with libopenssl-ruby1.8 (< 1.8.6) (closes: #410018)
* changed packaging style to cdbs from dbs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
37
require 'ftools'
 
38
 
 
39
require 'rdoc/options'
 
40
require 'rdoc/template'
 
41
require 'rdoc/markup/simple_markup'
 
42
require 'rdoc/markup/simple_markup/to_flow'
 
43
require 'cgi'
 
44
 
 
45
require 'rdoc/ri/ri_cache'
 
46
require 'rdoc/ri/ri_reader'
 
47
require 'rdoc/ri/ri_writer'
 
48
require 'rdoc/ri/ri_descriptions'
 
49
 
 
50
module Generators
 
51
 
 
52
 
 
53
  class RIGenerator
 
54
 
 
55
    # Generators may need to return specific subclasses depending
 
56
    # on the options they are passed. Because of this
 
57
    # we create them using a factory
 
58
 
 
59
    def RIGenerator.for(options)
 
60
      new(options)
 
61
    end
 
62
 
 
63
    class <<self
 
64
      protected :new
 
65
    end
 
66
 
 
67
    # Set up a new HTML generator. Basically all we do here is load
 
68
    # up the correct output temlate
 
69
 
 
70
    def initialize(options) #:not-new:
 
71
      @options   = options
 
72
      @ri_writer = RI::RiWriter.new(".")
 
73
      @markup    = SM::SimpleMarkup.new
 
74
      @to_flow   = SM::ToFlow.new
 
75
    end
 
76
 
 
77
 
 
78
    ##
 
79
    # Build the initial indices and output objects
 
80
    # based on an array of TopLevel objects containing
 
81
    # the extracted information. 
 
82
 
 
83
    def generate(toplevels)
 
84
      RDoc::TopLevel.all_classes_and_modules.each do |cls|
 
85
        process_class(cls)
 
86
      end
 
87
    end
 
88
 
 
89
    def process_class(from_class)
 
90
      generate_class_info(from_class)
 
91
 
 
92
      # now recure into this classes constituent classess
 
93
      from_class.each_classmodule do |mod|
 
94
        process_class(mod)
 
95
      end
 
96
    end
 
97
 
 
98
    def generate_class_info(cls)
 
99
      if cls === RDoc::NormalModule
 
100
        cls_desc = RI::ModuleDescription.new
 
101
      else
 
102
        cls_desc = RI::ClassDescription.new
 
103
        cls_desc.superclass  = cls.superclass
 
104
      end
 
105
      cls_desc.name        = cls.name
 
106
      cls_desc.full_name   = cls.full_name
 
107
      cls_desc.comment     = markup(cls.comment)
 
108
 
 
109
      cls_desc.attributes =cls.attributes.sort.map do |a|
 
110
        RI::Attribute.new(a.name, a.rw, markup(a.comment))
 
111
      end
 
112
 
 
113
      cls_desc.constants = cls.constants.map do |c|
 
114
        RI::Constant.new(c.name, c.value, markup(c.comment))
 
115
      end
 
116
 
 
117
      cls_desc.includes = cls.includes.map do |i|
 
118
        RI::IncludedModule.new(i.name)
 
119
      end
 
120
 
 
121
      class_methods, instance_methods = method_list(cls)
 
122
 
 
123
      cls_desc.class_methods = class_methods.map do |m|
 
124
        RI::MethodSummary.new(m.name)
 
125
      end
 
126
      cls_desc.instance_methods = instance_methods.map do |m|
 
127
        RI::MethodSummary.new(m.name)
 
128
      end
 
129
 
 
130
      update_or_replace(cls_desc)
 
131
 
 
132
      class_methods.each do |m|
 
133
        generate_method_info(cls_desc, m)
 
134
      end
 
135
 
 
136
      instance_methods.each do |m|
 
137
        generate_method_info(cls_desc, m)
 
138
      end
 
139
    end
 
140
 
 
141
 
 
142
    def generate_method_info(cls_desc, method)
 
143
      meth_desc = RI::MethodDescription.new
 
144
      meth_desc.name = method.name
 
145
      meth_desc.full_name = cls_desc.full_name
 
146
      if method.singleton
 
147
        meth_desc.full_name += "::"
 
148
      else
 
149
        meth_desc.full_name += "#"
 
150
      end
 
151
      meth_desc.full_name << method.name
 
152
 
 
153
      meth_desc.comment = markup(method.comment)
 
154
      meth_desc.params = params_of(method)
 
155
      meth_desc.visibility = method.visibility.to_s
 
156
      meth_desc.is_singleton = method.singleton
 
157
      meth_desc.block_params = method.block_params
 
158
 
 
159
      meth_desc.aliases = method.aliases.map do |a|
 
160
        RI::AliasName.new(a.name)
 
161
      end
 
162
 
 
163
      @ri_writer.add_method(cls_desc, meth_desc)
 
164
    end
 
165
 
 
166
    private
 
167
 
 
168
    # return a list of class and instance methods that we'll be
 
169
    # documenting
 
170
 
 
171
    def method_list(cls)
 
172
      list = cls.method_list
 
173
      unless @options.show_all
 
174
        list = list.find_all do |m|
 
175
          m.visibility == :public || m.visibility == :protected || m.force_documentation
 
176
        end
 
177
      end
 
178
 
 
179
      c = []
 
180
      i = []
 
181
      list.sort.each do |m|
 
182
        if m.singleton
 
183
          c << m
 
184
        else
 
185
          i << m
 
186
        end
 
187
      end
 
188
      return c,i
 
189
    end
 
190
    
 
191
    def params_of(method)
 
192
      if method.call_seq
 
193
        method.call_seq
 
194
      else
 
195
        params = method.params || ""
 
196
        
 
197
        p = params.gsub(/\s*\#.*/, '')
 
198
        p = p.tr("\n", " ").squeeze(" ")
 
199
        p = "(" + p + ")" unless p[0] == ?(
 
200
        
 
201
        if (block = method.block_params)
 
202
          block.gsub!(/\s*\#.*/, '')
 
203
          block = block.tr("\n", " ").squeeze(" ")
 
204
          if block[0] == ?(
 
205
            block.sub!(/^\(/, '').sub!(/\)/, '')
 
206
          end
 
207
          p << " {|#{block.strip}| ...}"
 
208
        end
 
209
        p
 
210
      end
 
211
    end
 
212
 
 
213
    def markup(comment)
 
214
      return nil if !comment || comment.empty?
 
215
 
 
216
      # Convert leading comment markers to spaces, but only
 
217
      # if all non-blank lines have them
 
218
      
 
219
      if comment =~ /^(?>\s*)[^\#]/
 
220
        content = comment
 
221
      else
 
222
        content = comment.gsub(/^\s*(#+)/)  { $1.tr('#',' ') }
 
223
      end
 
224
      @markup.convert(content, @to_flow)
 
225
    end
 
226
 
 
227
 
 
228
    # By default we replace existing classes with the
 
229
    # same name. If the --merge option was given, we instead
 
230
    # merge this definition into an existing class. We add
 
231
    # our methods, aliases, etc to that class, but do not
 
232
    # change the class's description.
 
233
 
 
234
    def update_or_replace(cls_desc)
 
235
      old_cls = nil
 
236
 
 
237
      if @options.merge
 
238
        rdr = RI::RiReader.new(RI::RiCache.new(@options.op_dir))
 
239
 
 
240
        namespace = rdr.top_level_namespace
 
241
        namespace = rdr.lookup_namespace_in(cls_desc.name, namespace)
 
242
        if namespace.empty?
 
243
          $stderr.puts "You asked me to merge this source into existing "
 
244
          $stderr.puts "documentation. This file references a class or "
 
245
          $stderr.puts "module called #{cls_desc.name} which I don't"
 
246
          $stderr.puts "have existing documentation for."
 
247
          $stderr.puts 
 
248
          $stderr.puts "Perhaps you need to generate its documentation first"
 
249
          exit 1
 
250
        else
 
251
          old_cls = namespace[0]
 
252
        end
 
253
      end
 
254
 
 
255
      if old_cls.nil?
 
256
        # no merge: simply overwrite
 
257
        @ri_writer.remove_class(cls_desc)
 
258
        @ri_writer.add_class(cls_desc)
 
259
      else
 
260
        # existing class: merge in
 
261
        old_desc = rdr.get_class(old_cls)
 
262
 
 
263
        old_desc.merge_in(cls_desc)
 
264
        @ri_writer.add_class(old_desc)
 
265
      end
 
266
    end
 
267
  end
 
268
end