From 8467ba8cdb565d865e14e2115cde98cd7692719d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 19 Jul 2019 20:29:59 +0300 Subject: flip syntax text to use internal iteration --- crates/ra_syntax/src/syntax_node.rs | 1 - crates/ra_syntax/src/syntax_text.rs | 98 ++++++++++++++++++++++++------------- 2 files changed, 65 insertions(+), 34 deletions(-) (limited to 'crates/ra_syntax/src') diff --git a/crates/ra_syntax/src/syntax_node.rs b/crates/ra_syntax/src/syntax_node.rs index 62e0967b7..8fe9e5b4e 100644 --- a/crates/ra_syntax/src/syntax_node.rs +++ b/crates/ra_syntax/src/syntax_node.rs @@ -297,7 +297,6 @@ fn to_green_element(element: SyntaxElement) -> rowan::GreenElement { #[derive(Clone, PartialEq, Eq, Hash)] pub struct SyntaxToken(pub(crate) rowan::cursor::SyntaxToken); -//FIXME: always output text impl fmt::Debug for SyntaxToken { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{:?}@{:?}", self.kind(), self.range())?; diff --git a/crates/ra_syntax/src/syntax_text.rs b/crates/ra_syntax/src/syntax_text.rs index 6902a04a2..6f15e3b35 100644 --- a/crates/ra_syntax/src/syntax_text.rs +++ b/crates/ra_syntax/src/syntax_text.rs @@ -16,26 +16,48 @@ impl<'a> SyntaxText<'a> { SyntaxText { node, range: node.range() } } - pub fn chunks(&self) -> impl Iterator { - let range = self.range; - self.node.descendants_with_tokens().filter_map(move |el| match el { - SyntaxElement::Token(t) => { - let text = t.text(); - let range = range.intersection(&t.range())?; - let res = if range == t.range() { - t.text().clone() - } else { - let range = range - t.range().start(); - text[range].into() - }; - Some(res) - } - SyntaxElement::Node(_) => None, + pub fn try_fold_chunks(&self, init: T, mut f: F) -> Result + where + F: FnMut(T, &str) -> Result, + { + self.node.descendants_with_tokens().try_fold(init, move |acc, element| { + let res = match element { + SyntaxElement::Token(token) => { + let range = match self.range.intersection(&token.range()) { + None => return Ok(acc), + Some(it) => it, + }; + let slice = if range == token.range() { + token.text() + } else { + let range = range - token.range().start(); + &token.text()[range] + }; + f(acc, slice)? + } + SyntaxElement::Node(_) => acc, + }; + Ok(res) }) } + pub fn try_for_each_chunk Result<(), E>, E>( + &self, + mut f: F, + ) -> Result<(), E> { + self.try_fold_chunks((), move |(), chunk| f(chunk)) + } + + pub fn for_each_chunk(&self, mut f: F) { + enum Void {} + match self.try_for_each_chunk(|chunk| Ok::<(), Void>(f(chunk))) { + Ok(()) => (), + Err(void) => match void {}, + } + } + pub fn push_to(&self, buf: &mut String) { - self.chunks().for_each(|it| buf.push_str(it.as_str())); + self.for_each_chunk(|chunk| buf.push_str(chunk)) } pub fn to_string(&self) -> String { @@ -49,19 +71,20 @@ impl<'a> SyntaxText<'a> { } pub fn contains(&self, c: char) -> bool { - self.chunks().any(|it| it.contains(c)) + self.try_for_each_chunk(|chunk| if chunk.contains(c) { Err(()) } else { Ok(()) }).is_err() } pub fn find(&self, c: char) -> Option { let mut acc: TextUnit = 0.into(); - for chunk in self.chunks() { + let res = self.try_for_each_chunk(|chunk| { if let Some(pos) = chunk.find(c) { let pos: TextUnit = (pos as u32).into(); - return Some(acc + pos); + return Err(acc + pos); } - acc += TextUnit::of_str(chunk.as_str()); - } - None + acc += TextUnit::of_str(chunk); + Ok(()) + }); + found(res) } pub fn len(&self) -> TextUnit { @@ -101,17 +124,25 @@ impl<'a> SyntaxText<'a> { } pub fn char_at(&self, offset: impl Into) -> Option { - let mut start: TextUnit = 0.into(); let offset = offset.into(); - for chunk in self.chunks() { - let end = start + TextUnit::of_str(chunk.as_str()); + let mut start: TextUnit = 0.into(); + let res = self.try_for_each_chunk(|chunk| { + let end = start + TextUnit::of_str(chunk); if start <= offset && offset < end { let off: usize = u32::from(offset - start) as usize; - return Some(chunk[off..].chars().next().unwrap()); + return Err(chunk[off..].chars().next().unwrap()); } start = end; - } - None + Ok(()) + }); + found(res) + } +} + +fn found(res: Result<(), T>) -> Option { + match res { + Ok(()) => None, + Err(it) => Some(it), } } @@ -135,13 +166,14 @@ impl From> for String { impl PartialEq for SyntaxText<'_> { fn eq(&self, mut rhs: &str) -> bool { - for chunk in self.chunks() { - if !rhs.starts_with(chunk.as_str()) { - return false; + self.try_for_each_chunk(|chunk| { + if !rhs.starts_with(chunk) { + return Err(()); } rhs = &rhs[chunk.len()..]; - } - rhs.is_empty() + Ok(()) + }) + .is_ok() } } -- cgit v1.2.3 From 002529937075bd69d7f71483d798d6e4f43d1de9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 19 Jul 2019 20:55:32 +0300 Subject: sane indexing in text --- crates/ra_syntax/src/syntax_text.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'crates/ra_syntax/src') diff --git a/crates/ra_syntax/src/syntax_text.rs b/crates/ra_syntax/src/syntax_text.rs index 6f15e3b35..d8adf782b 100644 --- a/crates/ra_syntax/src/syntax_text.rs +++ b/crates/ra_syntax/src/syntax_text.rs @@ -95,18 +95,21 @@ impl<'a> SyntaxText<'a> { self.range.is_empty() } - /// NB, the offsets here are absolute, and this probably doesn't make sense! pub fn slice(&self, range: impl ops::RangeBounds) -> SyntaxText<'a> { let start = match range.start_bound() { - Bound::Included(b) => *b, - Bound::Excluded(b) => *b + TextUnit::from(1u32), - Bound::Unbounded => self.range.start(), + Bound::Included(&b) => b, + Bound::Excluded(_) => panic!("utf-aware slicing can't work this way"), + Bound::Unbounded => 0.into(), }; let end = match range.end_bound() { - Bound::Included(b) => *b + TextUnit::from(1u32), - Bound::Excluded(b) => *b, - Bound::Unbounded => self.range.end(), + Bound::Included(_) => panic!("utf-aware slicing can't work this way"), + Bound::Excluded(&b) => b, + Bound::Unbounded => self.len(), }; + assert!(start <= end); + let len = end - start; + let start = self.range.start() + start; + let end = start + len; assert!( start <= end, "invalid slice, range: {:?}, slice: {:?}", -- cgit v1.2.3