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