1# :markup: tomdoc 2 3# A parser for TomDoc based on TomDoc 1.0.0-rc1 (02adef9b5a) 4# 5# The TomDoc specification can be found at: 6# 7# http://tomdoc.org 8# 9# The latest version of the TomDoc specification can be found at: 10# 11# https://github.com/mojombo/tomdoc/blob/master/tomdoc.md 12# 13# To choose TomDoc as your only default format see RDoc::Options@Saved+Options 14# for instructions on setting up a <code>.rdoc_options</code> file to store 15# your project default. 16# 17# There are a few differences between this parser and the specification. A 18# best-effort was made to follow the specification as closely as possible but 19# some choices to deviate were made. 20# 21# A future version of RDoc will warn when a MUST or MUST NOT is violated and 22# may warn when a SHOULD or SHOULD NOT is violated. RDoc will always try 23# to emit documentation even if given invalid TomDoc. 24# 25# Here are some implementation choices this parser currently makes: 26# 27# This parser allows rdoc-style inline markup but you should not depended on 28# it. 29# 30# This parser allows a space between the comment and the method body. 31# 32# This parser does not require the default value to be described for an 33# optional argument. 34# 35# This parser does not examine the order of sections. An Examples section may 36# precede the Arguments section. 37# 38# This class is documented in TomDoc format. Since this is a subclass of the 39# RDoc markup parser there isn't much to see here, unfortunately. 40 41class RDoc::TomDoc < RDoc::Markup::Parser 42 43 # Internal: Token accessor 44 45 attr_reader :tokens 46 47 # Internal: Adds a post-processor which sets the RDoc section based on the 48 # comment's status. 49 # 50 # Returns nothing. 51 52 def self.add_post_processor # :nodoc: 53 RDoc::Markup::PreProcess.post_process do |comment, code_object| 54 next unless code_object and 55 RDoc::Comment === comment and comment.format == 'tomdoc' 56 57 comment.text.gsub!(/(\A\s*# )(Public|Internal|Deprecated):\s+/) do 58 section = code_object.add_section $2 59 code_object.temporary_section = section 60 61 $1 62 end 63 end 64 end 65 66 add_post_processor 67 68 # Public: Parses TomDoc from text 69 # 70 # text - A String containing TomDoc-format text. 71 # 72 # Examples 73 # 74 # RDoc::TomDoc.parse <<-TOMDOC 75 # This method does some things 76 # 77 # Returns nothing. 78 # TOMDOC 79 # # => #<RDoc::Markup::Document:0xXXX @parts=[...], @file=nil> 80 # 81 # Returns an RDoc::Markup::Document representing the TomDoc format. 82 83 def self.parse text 84 parser = new 85 86 parser.tokenize text 87 doc = RDoc::Markup::Document.new 88 parser.parse doc 89 doc 90 end 91 92 # Internal: Extracts the Signature section's method signature 93 # 94 # comment - An RDoc::Comment that will be parsed and have the signature 95 # extracted 96 # 97 # Returns a String containing the signature and nil if not 98 99 def self.signature comment 100 return unless comment.tomdoc? 101 102 document = comment.parse 103 104 signature = nil 105 found_heading = false 106 found_signature = false 107 108 document.parts.delete_if do |part| 109 next false if found_signature 110 111 found_heading ||= 112 RDoc::Markup::Heading === part && part.text == 'Signature' 113 114 next false unless found_heading 115 116 next true if RDoc::Markup::BlankLine === part 117 118 if RDoc::Markup::Verbatim === part then 119 signature = part 120 found_signature = true 121 end 122 end 123 124 signature and signature.text 125 end 126 127 # Public: Creates a new TomDoc parser. See also RDoc::Markup::parse 128 129 def initialize 130 super 131 132 @section = nil 133 end 134 135 # Internal: Builds a heading from the token stream 136 # 137 # level - The level of heading to create 138 # 139 # Returns an RDoc::Markup::Heading 140 141 def build_heading level 142 heading = super 143 144 @section = heading.text 145 146 heading 147 end 148 149 # Internal: Builds a verbatim from the token stream. A verbatim in the 150 # Examples section will be marked as in ruby format. 151 # 152 # margin - The indentation from the margin for lines that belong to this 153 # verbatim section. 154 # 155 # Returns an RDoc::Markup::Verbatim 156 157 def build_verbatim margin 158 verbatim = super 159 160 verbatim.format = :ruby if @section == 'Examples' 161 162 verbatim 163 end 164 165 # Internal: Builds a paragraph from the token stream 166 # 167 # margin - Unused 168 # 169 # Returns an RDoc::Markup::Paragraph. 170 171 def build_paragraph margin 172 p :paragraph_start => margin if @debug 173 174 paragraph = RDoc::Markup::Paragraph.new 175 176 until @tokens.empty? do 177 type, data, = get 178 179 if type == :TEXT then 180 paragraph << data 181 skip :NEWLINE 182 else 183 unget 184 break 185 end 186 end 187 188 p :paragraph_end => margin if @debug 189 190 paragraph 191 end 192 193 # Internal: Turns text into an Array of tokens 194 # 195 # text - A String containing TomDoc-format text. 196 # 197 # Returns self. 198 199 def tokenize text 200 text.sub!(/\A(Public|Internal|Deprecated):\s+/, '') 201 202 setup_scanner text 203 204 until @s.eos? do 205 pos = @s.pos 206 207 # leading spaces will be reflected by the column of the next token 208 # the only thing we loose are trailing spaces at the end of the file 209 next if @s.scan(/ +/) 210 211 @tokens << case 212 when @s.scan(/\r?\n/) then 213 token = [:NEWLINE, @s.matched, *token_pos(pos)] 214 @line_pos = char_pos @s.pos 215 @line += 1 216 token 217 when @s.scan(/(Examples|Signature)$/) then 218 @tokens << [:HEADER, 3, *token_pos(pos)] 219 220 [:TEXT, @s[1], *token_pos(pos)] 221 when @s.scan(/([:\w][\w\[\]]*)[ ]+- /) then 222 [:NOTE, @s[1], *token_pos(pos)] 223 else 224 @s.scan(/.*/) 225 [:TEXT, @s.matched.sub(/\r$/, ''), *token_pos(pos)] 226 end 227 end 228 229 self 230 end 231 232end 233 234