diff --git a/lib/rdoc/comment.rb b/lib/rdoc/comment.rb index 9e90999eac..63197492c4 100644 --- a/lib/rdoc/comment.rb +++ b/lib/rdoc/comment.rb @@ -97,42 +97,26 @@ def extract_call_seq method # comment. The difficulty is to make sure not to match lines starting # with ARGF at the same indent, but that are after the first description # paragraph. - if @text =~ /^\s*:?call-seq:(.*?(?:\S).*?)^\s*$/m then + if /^(? ((?!\n)\s)*+ (?# whitespaces except newline)) + :?call-seq: + (? \g(?\n|\z) (?# trailing spaces))? + (? + (\g(?!\w)\S.*\g)* + (?> + (? \g\w+ (?# ' # ARGF' in the example above)) + .*\g)? + (\g\S.*\g (?# other non-blank line))*+ + (\g+(\k.*\g (?# ARGF.to_a lines))++)*+ + ) + (?m:^\s*$|\z) + /x =~ @text + seq = $~[:seq] + all_start, all_stop = $~.offset(0) - seq_start, seq_stop = $~.offset(1) - - # we get the following lines that start with the leading word at the - # same indent, even if they have blank lines before - if $1 =~ /(^\s*\n)+^(\s*\w+)/m then - leading = $2 # ' * ARGF' in the example above - re = %r% - \A( - (^\s*\n)+ - (^#{Regexp.escape leading}.*?\n)+ - )+ - ^\s*$ - %xm - - if @text[seq_stop..-1] =~ re then - all_stop = seq_stop + $~.offset(0).last - seq_stop = seq_stop + $~.offset(1).last - end - end - - seq = @text[seq_start..seq_stop] - seq.gsub!(/^\s*(\S|\n)/m, '\1') @text.slice! all_start...all_stop - method.call_seq = seq.chomp - - else - regexp = /^\s*:?call-seq:(.*?)(^\s*$|\z)/m - if regexp =~ @text then - @text = @text.sub(regexp, '') - seq = $1 - seq.gsub!(/^\s*/, '') - method.call_seq = seq - end + seq.gsub!(/^\s*/, '') + method.call_seq = seq end method diff --git a/lib/rdoc/parser/ruby.rb b/lib/rdoc/parser/ruby.rb index 0323e4de41..2c8a19f608 100644 --- a/lib/rdoc/parser/ruby.rb +++ b/lib/rdoc/parser/ruby.rb @@ -2134,7 +2134,7 @@ def read_directive allowed if :on_nl == tk[:kind] or (:on_kw == tk[:kind] && 'def' == tk[:text]) then return elsif :on_comment == tk[:kind] or :on_embdoc == tk[:kind] then - return unless tk[:text] =~ /\s*:?([\w-]+):\s*(.*)/ + return unless tk[:text] =~ /:?\b([\w-]+):\s*(.*)/ directive = $1.downcase diff --git a/test/rdoc/test_rdoc_comment.rb b/test/rdoc/test_rdoc_comment.rb index d3c16bceca..28e8cf76b4 100644 --- a/test/rdoc/test_rdoc_comment.rb +++ b/test/rdoc/test_rdoc_comment.rb @@ -206,6 +206,15 @@ def test_extract_call_seq_c_separator assert_equal expected, comment.text end + def test_extract_call_linear_performance + pre = ->(n) {[n, RDoc::Comment.new("\n"*n + 'call-seq:' + 'a'*n)]} + method_obj = RDoc::AnyMethod.new nil, 'blah' + assert_linear_performance((2..5).map {|i| 10**i}, pre: pre) do |n, comment| + comment.extract_call_seq method_obj + assert_equal n, method_obj.call_seq.size + end + end + def test_force_encoding @comment = RDoc::Encoding.change_encoding @comment, Encoding::UTF_8 diff --git a/test/rdoc/test_rdoc_parser_ruby.rb b/test/rdoc/test_rdoc_parser_ruby.rb index 76fb28808c..aea923765c 100644 --- a/test/rdoc/test_rdoc_parser_ruby.rb +++ b/test/rdoc/test_rdoc_parser_ruby.rb @@ -3350,6 +3350,13 @@ def test_read_directive_one_liner assert_equal :on_const, parser.get_tk[:kind] end + def test_read_directive_linear_performance + pre = ->(i) {util_parser '# ' + '0'*i + '=000:'} + assert_linear_performance((1..5).map{|i|10**i}, pre: pre) do |parser| + assert_nil parser.read_directive [] + end + end + def test_read_documentation_modifiers c = RDoc::Context.new