1##
2# A TopLevel context is a representation of the contents of a single file
3
4class RDoc::TopLevel < RDoc::Context
5
6  MARSHAL_VERSION = 0 # :nodoc:
7
8  ##
9  # This TopLevel's File::Stat struct
10
11  attr_accessor :file_stat
12
13  ##
14  # Relative name of this file
15
16  attr_accessor :relative_name
17
18  ##
19  # Absolute name of this file
20
21  attr_accessor :absolute_name
22
23  ##
24  # All the classes or modules that were declared in
25  # this file. These are assigned to either +#classes_hash+
26  # or +#modules_hash+ once we know what they really are.
27
28  attr_reader :classes_or_modules
29
30  attr_accessor :diagram # :nodoc:
31
32  ##
33  # The parser that processed this file
34
35  attr_accessor :parser
36
37  ##
38  # Creates a new TopLevel for the file at +absolute_name+.  If documentation
39  # is being generated outside the source dir +relative_name+ is relative to
40  # the source directory.
41
42  def initialize absolute_name, relative_name = absolute_name
43    super()
44    @name = nil
45    @absolute_name = absolute_name
46    @relative_name = relative_name
47    @file_stat     = File.stat(absolute_name) rescue nil # HACK for testing
48    @diagram       = nil
49    @parser        = nil
50
51    @classes_or_modules = []
52  end
53
54  ##
55  # An RDoc::TopLevel is equal to another with the same relative_name
56
57  def == other
58    self.class === other and @relative_name == other.relative_name
59  end
60
61  alias eql? ==
62
63  ##
64  # Adds +an_alias+ to +Object+ instead of +self+.
65
66  def add_alias(an_alias)
67    object_class.record_location self
68    return an_alias unless @document_self
69    object_class.add_alias an_alias
70  end
71
72  ##
73  # Adds +constant+ to +Object+ instead of +self+.
74
75  def add_constant constant
76    object_class.record_location self
77    return constant unless @document_self
78    object_class.add_constant constant
79  end
80
81  ##
82  # Adds +include+ to +Object+ instead of +self+.
83
84  def add_include(include)
85    object_class.record_location self
86    return include unless @document_self
87    object_class.add_include include
88  end
89
90  ##
91  # Adds +method+ to +Object+ instead of +self+.
92
93  def add_method(method)
94    object_class.record_location self
95    return method unless @document_self
96    object_class.add_method method
97  end
98
99  ##
100  # Adds class or module +mod+. Used in the building phase
101  # by the ruby parser.
102
103  def add_to_classes_or_modules mod
104    @classes_or_modules << mod
105  end
106
107  ##
108  # Base name of this file
109
110  def base_name
111    File.basename @relative_name
112  end
113
114  alias name base_name
115
116  ##
117  # Only a TopLevel that contains text file) will be displayed.  See also
118  # RDoc::CodeObject#display?
119
120  def display?
121    text? and super
122  end
123
124  ##
125  # See RDoc::TopLevel::find_class_or_module
126  #--
127  # TODO Why do we search through all classes/modules found, not just the
128  #       ones of this instance?
129
130  def find_class_or_module name
131    @store.find_class_or_module name
132  end
133
134  ##
135  # Finds a class or module named +symbol+
136
137  def find_local_symbol(symbol)
138    find_class_or_module(symbol) || super
139  end
140
141  ##
142  # Finds a module or class with +name+
143
144  def find_module_named(name)
145    find_class_or_module(name)
146  end
147
148  ##
149  # Returns the relative name of this file
150
151  def full_name
152    @relative_name
153  end
154
155  ##
156  # An RDoc::TopLevel has the same hash as another with the same
157  # relative_name
158
159  def hash
160    @relative_name.hash
161  end
162
163  ##
164  # URL for this with a +prefix+
165
166  def http_url(prefix)
167    path = [prefix, @relative_name.tr('.', '_')]
168
169    File.join(*path.compact) + '.html'
170  end
171
172  def inspect # :nodoc:
173    "#<%s:0x%x %p modules: %p classes: %p>" % [
174      self.class, object_id,
175      base_name,
176      @modules.map { |n,m| m },
177      @classes.map { |n,c| c }
178    ]
179  end
180
181  ##
182  # Time this file was last modified, if known
183
184  def last_modified
185    @file_stat ? file_stat.mtime : nil
186  end
187
188  ##
189  # Dumps this TopLevel for use by ri.  See also #marshal_load
190
191  def marshal_dump
192    [
193      MARSHAL_VERSION,
194      @relative_name,
195      @parser,
196      parse(@comment),
197    ]
198  end
199
200  ##
201  # Loads this TopLevel from +array+.
202
203  def marshal_load array # :nodoc:
204    initialize array[1]
205
206    @parser  = array[2]
207    @comment = array[3]
208
209    @file_stat          = nil
210  end
211
212  ##
213  # Returns the NormalClass "Object", creating it if not found.
214  #
215  # Records +self+ as a location in "Object".
216
217  def object_class
218    @object_class ||= begin
219      oc = @store.find_class_named('Object') || add_class(RDoc::NormalClass, 'Object')
220      oc.record_location self
221      oc
222    end
223  end
224
225  ##
226  # Base name of this file without the extension
227
228  def page_name
229    basename = File.basename @relative_name
230    basename =~ /\.(rb|rdoc|txt|md)$/i
231
232    $` || basename
233  end
234
235  ##
236  # Path to this file for use with HTML generator output.
237
238  def path
239    http_url @store.rdoc.generator.file_dir
240  end
241
242  def pretty_print q # :nodoc:
243    q.group 2, "[#{self.class}: ", "]" do
244      q.text "base name: #{base_name.inspect}"
245      q.breakable
246
247      items = @modules.map { |n,m| m }
248      items.concat @modules.map { |n,c| c }
249      q.seplist items do |mod| q.pp mod end
250    end
251  end
252
253  ##
254  # Search record used by RDoc::Generator::JsonIndex
255
256  def search_record
257    return unless @parser < RDoc::Parser::Text
258
259    [
260      page_name,
261      '',
262      page_name,
263      '',
264      path,
265      '',
266      snippet(@comment),
267    ]
268  end
269
270  ##
271  # Is this TopLevel from a text file instead of a source code file?
272
273  def text?
274    @parser and @parser.ancestors.include? RDoc::Parser::Text
275  end
276
277  def to_s # :nodoc:
278    "file #{full_name}"
279  end
280
281end
282
283