ruby-changes:44899
From: nobu <ko1@a...>
Date: Sat, 3 Dec 2016 23:18:08 +0900 (JST)
Subject: [ruby-changes:44899] nobu:r56972 (trunk): string.c: chomp option
nobu 2016-12-03 23:18:03 +0900 (Sat, 03 Dec 2016) New Revision: 56972 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=56972 Log: string.c: chomp option * string.c (rb_str_enumerate_lines): implement chomp option. Modified files: trunk/string.c trunk/test/ruby/test_string.rb Index: string.c =================================================================== --- string.c (revision 56971) +++ string.c (revision 56972) @@ -7376,22 +7376,41 @@ rb_str_split(VALUE str, const char *sep0 https://github.com/ruby/ruby/blob/trunk/string.c#L7376 return rb_str_split_m(1, &sep, str); } +static const char * +chomp_newline(const char *p, const char *e, rb_encoding *enc) +{ + const char *prev = rb_enc_prev_char(p, e, e, enc); + if (rb_enc_is_newline(prev, e, enc)) { + e = prev; + prev = rb_enc_prev_char(p, e, e, enc); + if (rb_enc_ascget(prev, e, NULL, enc) == '\r') + e = prev; + } + return e; +} static VALUE rb_str_enumerate_lines(int argc, VALUE *argv, VALUE str, int wantarray) { rb_encoding *enc; - VALUE line, rs, orig = str; + VALUE line, rs, orig = str, opts = Qnil, chomp = Qfalse; const char *ptr, *pend, *subptr, *subend, *rsptr, *hit, *adjusted; long pos, len, rslen; int paragraph_mode = 0; + int rsnewline = 0; VALUE MAYBE_UNUSED(ary); - if (argc == 0) + if (rb_scan_args(argc, argv, "01:", &rs, &opts) == 0) rs = rb_rs; - else - rb_scan_args(argc, argv, "01", &rs); + if (!NIL_P(opts)) { + static ID keywords[1]; + if (!keywords[0]) { + keywords[0] = rb_intern_const("chomp"); + } + rb_get_kwargs(opts, keywords, 0, 1, &chomp); + chomp = (chomp != Qundef && RTEST(chomp)); + } if (rb_block_given_p()) { if (wantarray) { @@ -7438,9 +7457,14 @@ rb_str_enumerate_lines(int argc, VALUE * https://github.com/ruby/ruby/blob/trunk/string.c#L7457 rsptr = "\n\n"; rslen = 2; paragraph_mode = 1; + rsnewline = 1; } else { rsptr = RSTRING_PTR(rs); + if (RSTRING_LEN(rs) == rb_enc_mbminlen(enc) && + rb_enc_is_newline(rsptr, rsptr + RSTRING_LEN(rs), enc)) { + rsnewline = 1; + } } if ((rs == rb_default_rs || paragraph_mode) && !rb_enc_asciicompat(enc)) { @@ -7461,10 +7485,24 @@ rb_str_enumerate_lines(int argc, VALUE * https://github.com/ruby/ruby/blob/trunk/string.c#L7485 } subend = hit + rslen; if (paragraph_mode) { - while (subend < pend && rb_enc_is_newline(subend, pend, enc)) { + while (subend < pend) { + int n; + if (rb_enc_ascget(subend, pend, &n, enc) != '\r') + n = 0; + if (!rb_enc_is_newline(subend + n, pend, enc)) break; + subend += n; subend += rb_enc_mbclen(subend, pend, enc); } } + hit = subend; + if (chomp) { + if (rsnewline) { + subend = chomp_newline(subptr, subend, enc); + } + else { + subend -= rslen; + } + } line = rb_str_subseq(str, subptr - ptr, subend - subptr); if (wantarray) { rb_ary_push(ary, line); @@ -7473,10 +7511,13 @@ rb_str_enumerate_lines(int argc, VALUE * https://github.com/ruby/ruby/blob/trunk/string.c#L7511 rb_yield(line); str_mod_check(str, ptr, len); } - subptr = subend; + subptr = hit; } if (subptr != pend) { + if (chomp && paragraph_mode) { + pend = chomp_newline(subptr, pend, enc); + } line = rb_str_subseq(str, subptr - ptr, pend - subptr); if (wantarray) rb_ary_push(ary, line); Index: test/ruby/test_string.rb =================================================================== --- test/ruby/test_string.rb (revision 56971) +++ test/ruby/test_string.rb (revision 56972) @@ -816,6 +816,35 @@ CODE https://github.com/ruby/ruby/blob/trunk/test/ruby/test_string.rb#L816 $/ = save end + def test_each_line_chomp + res = [] + S("hello\nworld").each_line("\n", chomp: true) {|x| res << x} + assert_equal(S("hello"), res[0]) + assert_equal(S("world"), res[1]) + + res = [] + S("hello\n\n\nworld").each_line(S(''), chomp: true) {|x| res << x} + assert_equal(S("hello\n\n"), res[0]) + assert_equal(S("world"), res[1]) + + res = [] + S("hello!world").each_line(S('!'), chomp: true) {|x| res << x} + assert_equal(S("hello"), res[0]) + assert_equal(S("world"), res[1]) + + res = [] + S("a").each_line(S('ab'), chomp: true).each {|x| res << x} + assert_equal(1, res.size) + assert_equal(S("a"), res[0]) + + s = nil + "foo\nbar".each_line(nil, chomp: true) {|s2| s = s2 } + assert_equal("foo\nbar", s) + + assert_equal "hello", S("hello\nworld").each_line(chomp: true).next + assert_equal "hello\nworld", S("hello\nworld").each_line(nil, chomp: true).next + end + def test_lines s = S("hello\nworld") assert_equal ["hello\n", "world"], s.lines -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/