From 26e6c097767f4d64978b15f0974d9471447e6190 Mon Sep 17 00:00:00 2001 From: Charles Vandevoorde Date: Sun, 5 Mar 2017 10:21:47 +0100 Subject: [PATCH 1/2] Correct unicode handling for text input --- Cargo.lock | 1 + components/script/Cargo.toml | 1 + components/script/dom/htmlinputelement.rs | 2 +- components/script/lib.rs | 1 + components/script/textinput.rs | 19 +++++++++++++------ 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 875a4854d15..ab337ee246f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2296,6 +2296,7 @@ dependencies = [ "style_traits 0.0.1", "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", "tinyfiledialogs 2.5.9 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "webrender_traits 0.23.1 (git+https://github.com/servo/webrender)", diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index ff69fd74bf2..a92893c6434 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -85,6 +85,7 @@ smallvec = "0.1" style = {path = "../style"} style_traits = {path = "../style_traits"} time = "0.1.12" +unicode-segmentation = "1.1.0" url = {version = "1.2", features = ["heap_size", "query_encoding"]} uuid = {version = "0.4", features = ["v4"]} xml5ever = {version = "0.4", features = ["unstable"]} diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index f2e9f983300..e5c5b8c76d7 100755 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -1112,7 +1112,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/lib.rs b/components/script/lib.rs index 1b3f9af3540..47ce1f0cdee 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -95,6 +95,7 @@ extern crate style_traits; extern crate time; #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] extern crate tinyfiledialogs; +extern crate unicode_segmentation; extern crate url; extern crate uuid; extern crate webrender_traits; diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 5f04b5b187e..c23d8547e01 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -14,6 +14,7 @@ use std::cmp::{max, min}; use std::default::Default; use std::ops::Range; use std::usize; +use unicode_segmentation::UnicodeSegmentation; #[derive(Copy, Clone, PartialEq)] pub enum Selection { @@ -376,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 } } @@ -677,4 +676,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; + } } From 49ea443146f52f5d6558970359e26352af616585 Mon Sep 17 00:00:00 2001 From: charlesvdv Date: Sun, 16 Apr 2017 11:52:21 +0200 Subject: [PATCH 2/2] add test for text input unicode handling --- tests/unit/script/textinput.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/unit/script/textinput.rs b/tests/unit/script/textinput.rs index 01e28433ed6..d74c83fa6e8 100644 --- a/tests/unit/script/textinput.rs +++ b/tests/unit/script/textinput.rs @@ -496,3 +496,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); +}