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); +}