diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index db26905b3b4..baff4983504 100755
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -1117,7 +1117,7 @@ impl VirtualMethods for HTMLInputElement {
translated_y
);
if let Some(i) = index {
- self.textinput.borrow_mut().edit_point.index = i as usize;
+ self.textinput.borrow_mut().set_edit_point_index(i as usize);
// trigger redraw
self.upcast::().dirty(NodeDamage::OtherNodeDamage);
event.PreventDefault();
diff --git a/components/script/textinput.rs b/components/script/textinput.rs
index d600b819e4c..144be9dbd78 100644
--- a/components/script/textinput.rs
+++ b/components/script/textinput.rs
@@ -377,18 +377,16 @@ impl TextInput {
}
let adjust = {
let current_line = &self.lines[self.edit_point.line];
- // FIXME: We adjust by one code point, but it proably should be one grapheme cluster
- // https://github.com/unicode-rs/unicode-segmentation
match direction {
Direction::Forward => {
- match current_line[self.edit_point.index..].chars().next() {
- Some(c) => c.len_utf8() as isize,
+ match current_line[self.edit_point.index..].graphemes(true).next() {
+ Some(c) => c.len() as isize,
None => 1, // Going to the next line is a "one byte" offset
}
}
Direction::Backward => {
- match current_line[..self.edit_point.index].chars().next_back() {
- Some(c) => -(c.len_utf8() as isize),
+ match current_line[..self.edit_point.index].graphemes(true).next_back() {
+ Some(c) => -(c.len() as isize),
None => -1, // Going to the previous line is a "one byte" offset
}
}
@@ -845,4 +843,12 @@ impl TextInput {
selection_start as u32
}
+
+ pub fn set_edit_point_index(&mut self, index: usize) {
+ let byte_size = self.lines[self.edit_point.line]
+ .graphemes(true)
+ .take(index)
+ .fold(0, |acc, x| acc + x.len());
+ self.edit_point.index = byte_size;
+ }
}
diff --git a/tests/unit/script/textinput.rs b/tests/unit/script/textinput.rs
index a9e5a9fc08b..620368b4d8f 100644
--- a/tests/unit/script/textinput.rs
+++ b/tests/unit/script/textinput.rs
@@ -602,3 +602,13 @@ fn test_textinput_set_selection_with_direction() {
assert_eq!(textinput.selection_begin.unwrap().line, 0);
assert_eq!(textinput.selection_begin.unwrap().index, 6);
}
+
+#[test]
+fn test_textinput_unicode_handling() {
+ let mut textinput = text_input(Lines::Single, "éèùµ$£");
+ assert_eq!(textinput.edit_point.index, 0);
+ textinput.set_edit_point_index(1);
+ assert_eq!(textinput.edit_point.index, 2);
+ textinput.set_edit_point_index(4);
+ assert_eq!(textinput.edit_point.index, 8);
+}