comment.rb 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. ##
  2. # A comment holds the text comment for a RDoc::CodeObject and provides a
  3. # unified way of cleaning it up and parsing it into an RDoc::Markup::Document.
  4. #
  5. # Each comment may have a different markup format set by #format=. By default
  6. # 'rdoc' is used. The :markup: directive tells RDoc which format to use.
  7. #
  8. # See RDoc::Markup@Other+directives for instructions on adding an alternate
  9. # format.
  10. class RDoc::Comment
  11. include RDoc::Text
  12. ##
  13. # The format of this comment. Defaults to RDoc::Markup
  14. attr_reader :format
  15. ##
  16. # The RDoc::TopLevel this comment was found in
  17. attr_accessor :location
  18. ##
  19. # The text for this comment
  20. attr_reader :text
  21. ##
  22. # Overrides the content returned by #parse. Use when there is no #text
  23. # source for this comment
  24. attr_writer :document
  25. ##
  26. # Creates a new comment with +text+ that is found in the RDoc::TopLevel
  27. # +location+.
  28. def initialize text = nil, location = nil
  29. @location = location
  30. @text = text
  31. @document = nil
  32. @format = 'rdoc'
  33. @normalized = false
  34. end
  35. ##
  36. #--
  37. # TODO deep copy @document
  38. def initialize_copy copy # :nodoc:
  39. @text = copy.text.dup
  40. end
  41. def == other # :nodoc:
  42. self.class === other and
  43. other.text == @text and other.location == @location
  44. end
  45. ##
  46. # Look for a 'call-seq' in the comment to override the normal parameter
  47. # handling. The :call-seq: is indented from the baseline. All lines of the
  48. # same indentation level and prefix are consumed.
  49. #
  50. # For example, all of the following will be used as the :call-seq:
  51. #
  52. # # :call-seq:
  53. # # ARGF.readlines(sep=$/) -> array
  54. # # ARGF.readlines(limit) -> array
  55. # # ARGF.readlines(sep, limit) -> array
  56. # #
  57. # # ARGF.to_a(sep=$/) -> array
  58. # # ARGF.to_a(limit) -> array
  59. # # ARGF.to_a(sep, limit) -> array
  60. def extract_call_seq method
  61. # we must handle situations like the above followed by an unindented first
  62. # comment. The difficulty is to make sure not to match lines starting
  63. # with ARGF at the same indent, but that are after the first description
  64. # paragraph.
  65. if @text =~ /^\s*:?call-seq:(.*?(?:\S).*?)^\s*$/m then
  66. all_start, all_stop = $~.offset(0)
  67. seq_start, seq_stop = $~.offset(1)
  68. # we get the following lines that start with the leading word at the
  69. # same indent, even if they have blank lines before
  70. if $1 =~ /(^\s*\n)+^(\s*\w+)/m then
  71. leading = $2 # ' * ARGF' in the example above
  72. re = %r%
  73. \A(
  74. (^\s*\n)+
  75. (^#{Regexp.escape leading}.*?\n)+
  76. )+
  77. ^\s*$
  78. %xm
  79. if @text[seq_stop..-1] =~ re then
  80. all_stop = seq_stop + $~.offset(0).last
  81. seq_stop = seq_stop + $~.offset(1).last
  82. end
  83. end
  84. seq = @text[seq_start..seq_stop]
  85. seq.gsub!(/^\s*(\S|\n)/m, '\1')
  86. @text.slice! all_start...all_stop
  87. method.call_seq = seq.chomp
  88. elsif @text.sub!(/^\s*:?call-seq:(.*?)(^\s*$|\z)/m, '') then
  89. seq = $1
  90. seq.gsub!(/^\s*/, '')
  91. method.call_seq = seq
  92. end
  93. #elsif @text.sub!(/\A\/\*\s*call-seq:(.*?)\*\/\Z/, '') then
  94. # method.call_seq = $1.strip
  95. #end
  96. method
  97. end
  98. ##
  99. # A comment is empty if its text String is empty.
  100. def empty?
  101. @text.empty?
  102. end
  103. ##
  104. # HACK dubious
  105. def force_encoding encoding
  106. @text.force_encoding encoding
  107. end
  108. ##
  109. # Sets the format of this comment and resets any parsed document
  110. def format= format
  111. @format = format
  112. @document = nil
  113. end
  114. def inspect # :nodoc:
  115. "#<%s:%x %s %p>" % [self.class, object_id, @location.absolute_name, @text]
  116. end
  117. ##
  118. # Normalizes the text. See RDoc::Text#normalize_comment for details
  119. def normalize
  120. return self unless @text
  121. return self if @normalized # TODO eliminate duplicate normalization
  122. @text = normalize_comment @text
  123. @normalized = true
  124. self
  125. end
  126. ##
  127. # Was this text normalized?
  128. def normalized? # :nodoc:
  129. @normalized
  130. end
  131. ##
  132. # Parses the comment into an RDoc::Markup::Document. The parsed document is
  133. # cached until the text is changed.
  134. def parse
  135. return @document if @document
  136. @document = super @text, @format
  137. @document.file = @location
  138. @document
  139. end
  140. ##
  141. # Removes private sections from this comment. Private sections are flush to
  142. # the comment marker and start with <tt>--</tt> and end with <tt>++</tt>.
  143. # For C-style comments, a private marker may not start at the opening of the
  144. # comment.
  145. #
  146. # /*
  147. # *--
  148. # * private
  149. # *++
  150. # * public
  151. # */
  152. def remove_private
  153. # Workaround for gsub encoding for Ruby 1.9.2 and earlier
  154. empty = ''
  155. empty.force_encoding @text.encoding if Object.const_defined? :Encoding
  156. @text = @text.gsub(%r%^\s*([#*]?)--.*?^\s*(\1)\+\+\n?%m, empty)
  157. @text = @text.sub(%r%^\s*[#*]?--.*%m, '')
  158. end
  159. ##
  160. # Replaces this comment's text with +text+ and resets the parsed document.
  161. #
  162. # An error is raised if the comment contains a document but no text.
  163. def text= text
  164. raise RDoc::Error, 'replacing document-only comment is not allowed' if
  165. @text.nil? and @document
  166. @document = nil
  167. @text = text
  168. end
  169. ##
  170. # Returns true if this comment is in TomDoc format.
  171. def tomdoc?
  172. @format == 'tomdoc'
  173. end
  174. end