From c7e5ac1c1b53602bb32259de91df3dc3a86d7526 Mon Sep 17 00:00:00 2001 From: Jonathan Bender Date: Mon, 11 Apr 2016 16:14:37 -0400 Subject: [PATCH] handle array access syntax on strings with emoji --- motion/core_ext/string/access.rb | 29 +++++++++++++++++++ .../core_ext/string/access_spec.rb | 5 ++++ 2 files changed, 34 insertions(+) diff --git a/motion/core_ext/string/access.rb b/motion/core_ext/string/access.rb index 8fa8157..76996d2 100644 --- a/motion/core_ext/string/access.rb +++ b/motion/core_ext/string/access.rb @@ -101,4 +101,33 @@ def last(limit = 1) from(-limit) end end + + # Emojis pose a problem for array-like access of a string. If you try to + # grab one register you'll get am error: "You can't cut a surrogate in two in + # an encoding that is not UTF-16 (IndexError)" + # Calling split(''), which splits the string into array of characters, works + # correctly even with emojis. So to make this method work as expected for + # strings, first split it then join. + + # These are the method definitions of String#[]. The first three match the + # method definition of an array so only apply the patch if the arguments + # look like that. + # - str[index] => new_str or nil + # - str[start, length] => new_str or nil + # - str[range] => new_str or nil + # - str[regexp] => new_str or nil + # - str[regexp, capture] => new_str or nil + # - str[match_str] => new_str or nil + alias_method :bracket_access_original, :[] + + def [](*args) + unless args[0].is_a?(Numeric) || args[0].is_a?(Range) + return bracket_access_original(*args) + end + + # Could be nil, string (one character), or array of characters + characters = split('')[*args] + + characters.is_a?(Array) ? characters.join : characters + end end diff --git a/spec/motion-support/core_ext/string/access_spec.rb b/spec/motion-support/core_ext/string/access_spec.rb index e366ce1..84a2a4b 100644 --- a/spec/motion-support/core_ext/string/access_spec.rb +++ b/spec/motion-support/core_ext/string/access_spec.rb @@ -49,5 +49,10 @@ hash["hello123".first(5)] = true hash.keys.should == %w(hello) end + + it "should handle emojis" do + "😊 hello"[0].should == "😊" + "😊 hello".last(5).should == "hello" + end end end