From 41a02531e1102c19b0177baaed5f7a5db11226ee Mon Sep 17 00:00:00 2001 From: Jun Mukai Date: Mon, 12 Mar 2018 17:15:03 -0700 Subject: [PATCH] Fix patterns of provide_context gets panicked This PR fixes two cases where provide_context panics even though the previous call of `is_boundary` returns a PreContext error. The primary cause is `provide_context` assumes its state as Regional or Emoji; otherwise it gets panicked with 'invalid state!' However: - if the chunk starts with the cursor position, it requires pre-context for `cat_before` (and that's necessary for some cases indeed). so provide_context should fill `cat_before` in such case. - if both `cat_before` and `cat_after` are RIS and the boundary is undecided by the chunk, it requires pre-context but it does not set its state to Regional. This is done by setting state within `handle_regional`. --- src/grapheme.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/grapheme.rs b/src/grapheme.rs index c07fc8c..69b96b3 100644 --- a/src/grapheme.rs +++ b/src/grapheme.rs @@ -360,7 +360,10 @@ impl GraphemeCursor { match self.state { GraphemeState::Regional => self.handle_regional(chunk, chunk_start), GraphemeState::Emoji => self.handle_emoji(chunk, chunk_start), - _ => panic!("invalid state") + _ => if self.cat_before.is_none() && self.offset == chunk.len() + chunk_start { + let ch = chunk.chars().rev().next().unwrap(); + self.cat_before = Some(gr::grapheme_category(ch)); + }, } } @@ -406,6 +409,7 @@ impl GraphemeCursor { return; } self.pre_context_offset = Some(chunk_start); + self.state = GraphemeState::Regional; } fn handle_emoji(&mut self, chunk: &str, chunk_start: usize) { @@ -428,6 +432,7 @@ impl GraphemeCursor { return; } self.pre_context_offset = Some(chunk_start); + self.state = GraphemeState::Emoji; } /// Determine whether the current cursor location is a grapheme cluster boundary. @@ -659,3 +664,21 @@ impl GraphemeCursor { } } } + +#[test] +fn test_grapheme_cursor_ris_precontext() { + let s = "\u{1f1fa}\u{1f1f8}\u{1f1fa}\u{1f1f8}\u{1f1fa}\u{1f1f8}"; + let mut c = GraphemeCursor::new(8, s.len(), true); + assert_eq!(c.is_boundary(&s[4..], 4), Err(GraphemeIncomplete::PreContext(4))); + c.provide_context(&s[..4], 0); + assert_eq!(c.is_boundary(&s[4..], 4), Ok(true)); +} + +#[test] +fn test_grapheme_cursor_chunk_start_require_precontext() { + let s = "\r\n"; + let mut c = GraphemeCursor::new(1, s.len(), true); + assert_eq!(c.is_boundary(&s[1..], 1), Err(GraphemeIncomplete::PreContext(1))); + c.provide_context(&s[..1], 0); + assert_eq!(c.is_boundary(&s[1..], 1), Ok(false)); +}