1## 2# A section of documentation like: 3# 4# # :section: The title 5# # The body 6# 7# Sections can be referenced multiple times and will be collapsed into a 8# single section. 9 10class RDoc::Context::Section 11 12 include RDoc::Text 13 14 MARSHAL_VERSION = 0 # :nodoc: 15 16 ## 17 # Section comment 18 19 attr_reader :comment 20 21 ## 22 # Section comments 23 24 attr_reader :comments 25 26 ## 27 # Context this Section lives in 28 29 attr_reader :parent 30 31 ## 32 # Section title 33 34 attr_reader :title 35 36 @@sequence = "SEC00000" 37 38 ## 39 # Creates a new section with +title+ and +comment+ 40 41 def initialize parent, title, comment 42 @parent = parent 43 @title = title ? title.strip : title 44 45 @@sequence.succ! 46 @sequence = @@sequence.dup 47 48 @comments = [] 49 50 add_comment comment 51 end 52 53 ## 54 # Sections are equal when they have the same #title 55 56 def == other 57 self.class === other and @title == other.title 58 end 59 60 ## 61 # Adds +comment+ to this section 62 63 def add_comment comment 64 comment = extract_comment comment 65 66 return if comment.empty? 67 68 case comment 69 when RDoc::Comment then 70 @comments << comment 71 when RDoc::Markup::Document then 72 @comments.concat comment.parts 73 when Array then 74 @comments.concat comment 75 else 76 raise TypeError, "unknown comment type: #{comment.inspect}" 77 end 78 end 79 80 ## 81 # Anchor reference for linking to this section 82 83 def aref 84 title = @title || '[untitled]' 85 86 CGI.escape(title).gsub('%', '-').sub(/^-/, '') 87 end 88 89 ## 90 # Extracts the comment for this section from the original comment block. 91 # If the first line contains :section:, strip it and use the rest. 92 # Otherwise remove lines up to the line containing :section:, and look 93 # for those lines again at the end and remove them. This lets us write 94 # 95 # # :section: The title 96 # # The body 97 98 def extract_comment comment 99 case comment 100 when Array then 101 comment.map do |c| 102 extract_comment c 103 end 104 when nil 105 RDoc::Comment.new '' 106 when RDoc::Comment then 107 if comment.text =~ /^#[ \t]*:section:.*\n/ then 108 start = $` 109 rest = $' 110 111 comment.text = if start.empty? then 112 rest 113 else 114 rest.sub(/#{start.chomp}\Z/, '') 115 end 116 end 117 118 comment 119 when RDoc::Markup::Document then 120 comment 121 else 122 raise TypeError, "unknown comment #{comment.inspect}" 123 end 124 end 125 126 def inspect # :nodoc: 127 "#<%s:0x%x %p>" % [self.class, object_id, title] 128 end 129 130 ## 131 # The files comments in this section come from 132 133 def in_files 134 return [] if @comments.empty? 135 136 case @comments 137 when Array then 138 @comments.map do |comment| 139 comment.file 140 end 141 when RDoc::Markup::Document then 142 @comment.parts.map do |document| 143 document.file 144 end 145 else 146 raise RDoc::Error, "BUG: unknown comment class #{@comments.class}" 147 end 148 end 149 150 ## 151 # Serializes this Section. The title and parsed comment are saved, but not 152 # the section parent which must be restored manually. 153 154 def marshal_dump 155 [ 156 MARSHAL_VERSION, 157 @title, 158 parse, 159 ] 160 end 161 162 ## 163 # De-serializes this Section. The section parent must be restored manually. 164 165 def marshal_load array 166 @parent = nil 167 168 @title = array[1] 169 @comments = array[2] 170 end 171 172 ## 173 # Parses +comment_location+ into an RDoc::Markup::Document composed of 174 # multiple RDoc::Markup::Documents with their file set. 175 176 def parse 177 case @comments 178 when String then 179 super 180 when Array then 181 docs = @comments.map do |comment, location| 182 doc = super comment 183 doc.file = location if location 184 doc 185 end 186 187 RDoc::Markup::Document.new(*docs) 188 when RDoc::Comment then 189 doc = super @comments.text, comments.format 190 doc.file = @comments.location 191 doc 192 when RDoc::Markup::Document then 193 return @comments 194 else 195 raise ArgumentError, "unknown comment class #{comments.class}" 196 end 197 end 198 199 ## 200 # The section's title, or 'Top Section' if the title is nil. 201 # 202 # This is used by the table of contents template so the name is silly. 203 204 def plain_html 205 @title || 'Top Section' 206 end 207 208 ## 209 # Removes a comment from this section if it is from the same file as 210 # +comment+ 211 212 def remove_comment comment 213 return if @comments.empty? 214 215 case @comments 216 when Array then 217 @comments.delete_if do |my_comment| 218 my_comment.file == comment.file 219 end 220 when RDoc::Markup::Document then 221 @comments.parts.delete_if do |document| 222 document.file == comment.file.name 223 end 224 else 225 raise RDoc::Error, "BUG: unknown comment class #{@comments.class}" 226 end 227 end 228 229 ## 230 # Section sequence number (deprecated) 231 232 def sequence 233 warn "RDoc::Context::Section#sequence is deprecated, use #aref" 234 @sequence 235 end 236 237end 238 239