[rubygems/rubygems] Use a constant empty tar header to avoid extra allocations
[ruby.git] / lib / rdoc / tom_doc.rb
blobe161fcf42f6efcfe518b6994a4cd1191bd577544
1 # frozen_string_literal: true
2 # :markup: tomdoc
4 # A parser for TomDoc based on TomDoc 1.0.0-rc1 (02adef9b5a)
6 # The TomDoc specification can be found at:
8 # http://tomdoc.org
10 # The latest version of the TomDoc specification can be found at:
12 # https://github.com/mojombo/tomdoc/blob/master/tomdoc.md
14 # To choose TomDoc as your only default format see RDoc::Options@Saved+Options
15 # for instructions on setting up a <code>.rdoc_options</code> file to store
16 # your project default.
18 # There are a few differences between this parser and the specification.  A
19 # best-effort was made to follow the specification as closely as possible but
20 # some choices to deviate were made.
22 # A future version of RDoc will warn when a MUST or MUST NOT is violated and
23 # may warn when a SHOULD or SHOULD NOT is violated.  RDoc will always try
24 # to emit documentation even if given invalid TomDoc.
26 # Here are some implementation choices this parser currently makes:
28 # This parser allows rdoc-style inline markup but you should not depended on
29 # it.
31 # This parser allows a space between the comment and the method body.
33 # This parser does not require the default value to be described for an
34 # optional argument.
36 # This parser does not examine the order of sections.  An Examples section may
37 # precede the Arguments section.
39 # This class is documented in TomDoc format.  Since this is a subclass of the
40 # RDoc markup parser there isn't much to see here, unfortunately.
42 class RDoc::TomDoc < RDoc::Markup::Parser
44   # Internal: Token accessor
46   attr_reader :tokens
48   # Internal: Adds a post-processor which sets the RDoc section based on the
49   # comment's status.
50   #
51   # Returns nothing.
53   def self.add_post_processor # :nodoc:
54     RDoc::Markup::PreProcess.post_process do |comment, code_object|
55       next unless code_object and
56                   RDoc::Comment === comment and comment.format == 'tomdoc'
58       comment.text.gsub!(/(\A\s*# )(Public|Internal|Deprecated):\s+/) do
59         section = code_object.add_section $2
60         code_object.temporary_section = section
62         $1
63       end
64     end
65   end
67   add_post_processor
69   # Public: Parses TomDoc from text
70   #
71   # text - A String containing TomDoc-format text.
72   #
73   # Examples
74   #
75   #   RDoc::TomDoc.parse <<-TOMDOC
76   #   This method does some things
77   #
78   #   Returns nothing.
79   #   TOMDOC
80   #   # => #<RDoc::Markup::Document:0xXXX @parts=[...], @file=nil>
81   #
82   # Returns an RDoc::Markup::Document representing the TomDoc format.
84   def self.parse text
85     parser = new
87     parser.tokenize text
88     doc = RDoc::Markup::Document.new
89     parser.parse doc
90     doc
91   end
93   # Internal: Extracts the Signature section's method signature
94   #
95   # comment - An RDoc::Comment that will be parsed and have the signature
96   #           extracted
97   #
98   # Returns a String containing the signature and nil if not
100   def self.signature comment
101     return unless comment.tomdoc?
103     document = comment.parse
105     signature = nil
106     found_heading = false
107     found_signature = false
109     document.parts.delete_if do |part|
110       next false if found_signature
112       found_heading ||=
113         RDoc::Markup::Heading === part && part.text == 'Signature'
115       next false unless found_heading
117       next true if RDoc::Markup::BlankLine === part
119       if RDoc::Markup::Verbatim === part then
120         signature = part
121         found_signature = true
122       end
123     end
125     signature and signature.text
126   end
128   # Public: Creates a new TomDoc parser.  See also RDoc::Markup::parse
130   def initialize
131     super
133     @section      = nil
134     @seen_returns = false
135   end
137   # Internal: Builds a heading from the token stream
138   #
139   # level - The level of heading to create
140   #
141   # Returns an RDoc::Markup::Heading
143   def build_heading level
144     heading = super
146     @section = heading.text
148     heading
149   end
151   # Internal: Builds a verbatim from the token stream.  A verbatim in the
152   # Examples section will be marked as in Ruby format.
153   #
154   # margin - The indentation from the margin for lines that belong to this
155   #          verbatim section.
156   #
157   # Returns an RDoc::Markup::Verbatim
159   def build_verbatim margin
160     verbatim = super
162     verbatim.format = :ruby if @section == 'Examples'
164     verbatim
165   end
167   # Internal: Builds a paragraph from the token stream
168   #
169   # margin - Unused
170   #
171   # Returns an RDoc::Markup::Paragraph.
173   def build_paragraph margin
174     p :paragraph_start => margin if @debug
176     paragraph = RDoc::Markup::Paragraph.new
178     until @tokens.empty? do
179       type, data, = get
181       case type
182       when :TEXT then
183         @section = 'Returns' if data =~ /\A(Returns|Raises)/
185         paragraph << data
186       when :NEWLINE then
187         if :TEXT == peek_token[0] then
188           # Lines beginning with 'Raises' in the Returns section should not be
189           # treated as multiline text
190           if 'Returns' == @section and
191             peek_token[1].start_with?('Raises') then
192             break
193           else
194             paragraph << ' '
195           end
196         else
197           break
198         end
199       else
200         unget
201         break
202       end
203     end
205     p :paragraph_end => margin if @debug
207     paragraph
208   end
210   ##
211   # Detects a section change to "Returns" and adds a heading
213   def parse_text parent, indent # :nodoc:
214     paragraph = build_paragraph indent
216     if false == @seen_returns and 'Returns' == @section then
217       @seen_returns = true
218       parent << RDoc::Markup::Heading.new(3, 'Returns')
219       parent << RDoc::Markup::BlankLine.new
220     end
222     parent << paragraph
223   end
225   # Internal: Turns text into an Array of tokens
226   #
227   # text - A String containing TomDoc-format text.
228   #
229   # Returns self.
231   def tokenize text
232     text = text.sub(/\A(Public|Internal|Deprecated):\s+/, '')
234     setup_scanner text
236     until @s.eos? do
237       pos = @s.pos
239       # leading spaces will be reflected by the column of the next token
240       # the only thing we loose are trailing spaces at the end of the file
241       next if @s.scan(/ +/)
243       @tokens << case
244                  when @s.scan(/\r?\n/) then
245                    token = [:NEWLINE, @s.matched, *pos]
246                    @s.newline!
247                    token
248                  when @s.scan(/(Examples|Signature)$/) then
249                    @tokens << [:HEADER, 3, *pos]
251                    [:TEXT, @s[1], *pos]
252                  when @s.scan(/([:\w][\w\[\]]*)[ ]+- /) then
253                    [:NOTE, @s[1], *pos]
254                  else
255                    @s.scan(/.*/)
256                    [:TEXT, @s.matched.sub(/\r$/, ''), *pos]
257                  end
258     end
260     self
261   end