From 0a5e9ccaa815b5fbbb6c2d79462465085212adb7 Mon Sep 17 00:00:00 2001 From: Sarkhan Date: Mon, 5 Mar 2018 19:34:53 -0500 Subject: [PATCH 1/5] textinput.rs replace_selection now slices wrt bytes rather than characters --- components/script/textinput.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 1133c9cdd1c0..11f831e3d479 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -378,9 +378,12 @@ impl TextInput { self.clear_selection(); + // Calculate byte start and end for handling multi-byte characters + let start_index_b = len_of_first_n_code_units(&self.lines[start.line], start.index); + let end_index_b = len_of_first_n_code_units(&self.lines[end.line], end.index); let new_lines = { - let prefix = &self.lines[start.line][..start.index]; - let suffix = &self.lines[end.line][end.index..]; + let prefix = &self.lines[start.line][..start_index_b]; + let suffix = &self.lines[end.line][end_index_b..]; let lines_prefix = &self.lines[..start.line]; let lines_suffix = &self.lines[end.line + 1..]; From d18629feecf9f597641c4abaf04439c013fe1078 Mon Sep 17 00:00:00 2001 From: sarkhanbayramli Date: Sun, 11 Mar 2018 01:32:48 -0500 Subject: [PATCH 2/5] textinput new len n codepoints messages --- components/script/textinput.rs | 44 ++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 11f831e3d479..9f7bac4cd6a0 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -159,6 +159,27 @@ fn len_of_first_n_chars(text: &str, n: usize) -> usize { /// The length in bytes of the first n code units a string when encoded in UTF-16. /// /// If the string is fewer than n code units, returns the length of the whole string. + + +fn len_of_first_n_code_units_new(text: &str, n: usize) -> Option {//usize { + if n == 0 { + return Some(0); + } + let mut utf8_len = 0; + let mut utf16_len = 0; + for c in text.chars() { + utf16_len += c.len_utf16(); + utf8_len += c.len_utf8(); + if utf16_len == n { + break; + } + if utf16_len > n { + return None; + } + } + Some(utf8_len) +} + fn len_of_first_n_code_units(text: &str, n: usize) -> usize { let mut utf8_len = 0; let mut utf16_len = 0; @@ -372,18 +393,27 @@ impl TextInput { } else { usize::MAX }; - + println!("line: {}", &self.lines[start.line]); + println!("line bytes:"); + for x in self.lines[start.line].bytes() { + println!("byte: {}", x); + } + + let last_char_index_new = len_of_first_n_code_units_new(&*insert, allowed_to_insert_count).unwrap_or(999 as usize); let last_char_index = len_of_first_n_code_units(&*insert, allowed_to_insert_count); + println!("last_char_index_new: {} last_char_index: {}", last_char_index_new, last_char_index); let chars_to_insert = &insert[..last_char_index]; self.clear_selection(); - - // Calculate byte start and end for handling multi-byte characters - let start_index_b = len_of_first_n_code_units(&self.lines[start.line], start.index); - let end_index_b = len_of_first_n_code_units(&self.lines[end.line], end.index); + let start_index_code_units_new = len_of_first_n_code_units_new(&self.lines[start.line], start.index).unwrap_or(999 as usize); + let end_index_code_units_new = len_of_first_n_code_units_new(&self.lines[end.line], end.index).unwrap_or(999 as usize); + let start_index_code_units = len_of_first_n_code_units(&self.lines[start.line], start.index); + let end_index_code_units = len_of_first_n_code_units(&self.lines[end.line], end.index); + println!("start.index: {} end.index: {} start_index_code_units: {} end_index_code_units: {} start_index_code_units_new: {} end_index_code_units_new: {}", start.index, end.index, start_index_code_units, end_index_code_units, start_index_code_units_new, end_index_code_units_new); + println!("old new start end index diff: {} {}", start.index == start_index_code_units_new, end.index == end_index_code_units_new); let new_lines = { - let prefix = &self.lines[start.line][..start_index_b]; - let suffix = &self.lines[end.line][end_index_b..]; + let prefix = &self.lines[start.line][..start.index]; + let suffix = &self.lines[end.line][end.index..]; let lines_prefix = &self.lines[..start.line]; let lines_suffix = &self.lines[end.line + 1..]; From ff3f501cc0a916f8f563a06af1324a81b342fb13 Mon Sep 17 00:00:00 2001 From: sarkhanbayramli Date: Mon, 12 Mar 2018 15:39:31 -0400 Subject: [PATCH 3/5] len_of_first_code_units updated, set_selection_range now converts start end code unit counts to bytes --- components/script/textinput.rs | 98 +++++++++++++--------------------- 1 file changed, 38 insertions(+), 60 deletions(-) diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 9f7bac4cd6a0..7b255008d95c 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -161,7 +161,7 @@ fn len_of_first_n_chars(text: &str, n: usize) -> usize { /// If the string is fewer than n code units, returns the length of the whole string. -fn len_of_first_n_code_units_new(text: &str, n: usize) -> Option {//usize { +fn len_of_first_n_code_units(text: &str, n: usize) -> Option {//usize { if n == 0 { return Some(0); } @@ -180,19 +180,6 @@ fn len_of_first_n_code_units_new(text: &str, n: usize) -> Option {//usize Some(utf8_len) } -fn len_of_first_n_code_units(text: &str, n: usize) -> usize { - let mut utf8_len = 0; - let mut utf16_len = 0; - for c in text.chars() { - utf16_len += c.len_utf16(); - if utf16_len > n { - break; - } - utf8_len += c.len_utf8(); - } - utf8_len -} - impl TextInput { /// Instantiate a new text input control pub fn new(lines: Lines, initial: DOMString, @@ -393,58 +380,48 @@ impl TextInput { } else { usize::MAX }; - println!("line: {}", &self.lines[start.line]); - println!("line bytes:"); - for x in self.lines[start.line].bytes() { - println!("byte: {}", x); - } - let last_char_index_new = len_of_first_n_code_units_new(&*insert, allowed_to_insert_count).unwrap_or(999 as usize); - let last_char_index = len_of_first_n_code_units(&*insert, allowed_to_insert_count); - println!("last_char_index_new: {} last_char_index: {}", last_char_index_new, last_char_index); - let chars_to_insert = &insert[..last_char_index]; - + // If len_of_first_n_code_units returns None, we are over allowed_to_insert_count + let last_char_index_opt = len_of_first_n_code_units(&*insert, allowed_to_insert_count); self.clear_selection(); - let start_index_code_units_new = len_of_first_n_code_units_new(&self.lines[start.line], start.index).unwrap_or(999 as usize); - let end_index_code_units_new = len_of_first_n_code_units_new(&self.lines[end.line], end.index).unwrap_or(999 as usize); - let start_index_code_units = len_of_first_n_code_units(&self.lines[start.line], start.index); - let end_index_code_units = len_of_first_n_code_units(&self.lines[end.line], end.index); - println!("start.index: {} end.index: {} start_index_code_units: {} end_index_code_units: {} start_index_code_units_new: {} end_index_code_units_new: {}", start.index, end.index, start_index_code_units, end_index_code_units, start_index_code_units_new, end_index_code_units_new); - println!("old new start end index diff: {} {}", start.index == start_index_code_units_new, end.index == end_index_code_units_new); - let new_lines = { - let prefix = &self.lines[start.line][..start.index]; - let suffix = &self.lines[end.line][end.index..]; - let lines_prefix = &self.lines[..start.line]; - let lines_suffix = &self.lines[end.line + 1..]; - - let mut insert_lines = if self.multiline { - chars_to_insert.split('\n').map(|s| DOMString::from(s)).collect() - } else { - vec!(DOMString::from(chars_to_insert)) - }; - - // FIXME(ajeffrey): effecient append for DOMStrings - let mut new_line = prefix.to_owned(); + if last_char_index_opt.is_some() { + let chars_to_insert = &insert[..last_char_index_opt.unwrap()]; + + let new_lines = { + let prefix = &self.lines[start.line][..start.index]; + let suffix = &self.lines[end.line][end.index..]; + let lines_prefix = &self.lines[..start.line]; + let lines_suffix = &self.lines[end.line + 1..]; + + let mut insert_lines = if self.multiline { + chars_to_insert.split('\n').map(|s| DOMString::from(s)).collect() + } else { + vec!(DOMString::from(chars_to_insert)) + }; - new_line.push_str(&insert_lines[0]); - insert_lines[0] = DOMString::from(new_line); + // FIXME(ajeffrey): effecient append for DOMStrings + let mut new_line = prefix.to_owned(); - let last_insert_lines_index = insert_lines.len() - 1; - self.edit_point.index = insert_lines[last_insert_lines_index].len(); - self.edit_point.line = start.line + last_insert_lines_index; + new_line.push_str(&insert_lines[0]); + insert_lines[0] = DOMString::from(new_line); - // FIXME(ajeffrey): effecient append for DOMStrings - insert_lines[last_insert_lines_index].push_str(suffix); + let last_insert_lines_index = insert_lines.len() - 1; + self.edit_point.index = insert_lines[last_insert_lines_index].len(); + self.edit_point.line = start.line + last_insert_lines_index; - let mut new_lines = vec!(); - new_lines.extend_from_slice(lines_prefix); - new_lines.extend_from_slice(&insert_lines); - new_lines.extend_from_slice(lines_suffix); - new_lines - }; + // FIXME(ajeffrey): effecient append for DOMStrings + insert_lines[last_insert_lines_index].push_str(suffix); - self.lines = new_lines; + let mut new_lines = vec!(); + new_lines.extend_from_slice(lines_prefix); + new_lines.extend_from_slice(&insert_lines); + new_lines.extend_from_slice(lines_suffix); + new_lines + }; + self.lines = new_lines; + } self.assert_ok_selection(); + } /// Return the length in UTF-8 bytes of the current line under the editing point. @@ -957,8 +934,9 @@ impl TextInput { } pub fn set_selection_range(&mut self, start: u32, end: u32, direction: SelectionDirection) { - let mut start = start as usize; - let mut end = end as usize; + // convert code point counts start and end to byte counts + let mut start = len_of_first_n_code_units(&self.get_content(), start as usize).unwrap(); + let mut end = len_of_first_n_code_units(&self.get_content(), end as usize).unwrap(); let text_end = self.get_content().len(); if end > text_end { From 90e7f7262c40ace79c1e22b4959457b9bd4e3adf Mon Sep 17 00:00:00 2001 From: sarkhanbayramli Date: Mon, 12 Mar 2018 15:54:55 -0400 Subject: [PATCH 4/5] cleaning up --- components/script/textinput.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 7b255008d95c..803f94f54f93 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -159,9 +159,8 @@ fn len_of_first_n_chars(text: &str, n: usize) -> usize { /// The length in bytes of the first n code units a string when encoded in UTF-16. /// /// If the string is fewer than n code units, returns the length of the whole string. - - -fn len_of_first_n_code_units(text: &str, n: usize) -> Option {//usize { +/// Return `None` if `n` would split a non-BMP code point. +fn len_of_first_n_code_units(text: &str, n: usize) -> Option { if n == 0 { return Some(0); } From f08850b9ab7fdf393f3cb337a940809ef0567f96 Mon Sep 17 00:00:00 2001 From: sarkhanbayramli Date: Mon, 12 Mar 2018 18:28:21 -0400 Subject: [PATCH 5/5] cleaned code, added testcase --- components/script/textinput.rs | 6 +++--- tests/unit/script/textinput.rs | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/components/script/textinput.rs b/components/script/textinput.rs index 803f94f54f93..8f47ffaa5992 100644 --- a/components/script/textinput.rs +++ b/components/script/textinput.rs @@ -379,11 +379,11 @@ impl TextInput { } else { usize::MAX }; - + // If len_of_first_n_code_units returns None, we are over allowed_to_insert_count let last_char_index_opt = len_of_first_n_code_units(&*insert, allowed_to_insert_count); self.clear_selection(); - if last_char_index_opt.is_some() { + if last_char_index_opt.is_some() { let chars_to_insert = &insert[..last_char_index_opt.unwrap()]; let new_lines = { @@ -420,7 +420,7 @@ impl TextInput { self.lines = new_lines; } self.assert_ok_selection(); - + } /// Return the length in UTF-8 bytes of the current line under the editing point. diff --git a/tests/unit/script/textinput.rs b/tests/unit/script/textinput.rs index 3880d641d851..8525f4675274 100644 --- a/tests/unit/script/textinput.rs +++ b/tests/unit/script/textinput.rs @@ -619,6 +619,9 @@ fn test_textinput_set_selection_with_direction() { assert_eq!(textinput.selection_origin().unwrap().line, 0); assert_eq!(textinput.selection_origin().unwrap().index, 0); + textinput = text_input(Lines::Single, "€"); + textinput.set_selection_range(1, 1, SelectionDirection::Forward); + assert_eq!(textinput.edit_point().index, 2); } #[test]