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_hir/src/ids.rs | 11 +++- crates/ra_ide_api/src/display/structure.rs | 6 +- crates/ra_syntax/src/syntax_node.rs | 1 - crates/ra_syntax/src/syntax_text.rs | 98 ++++++++++++++++++++---------- 4 files changed, 78 insertions(+), 38 deletions(-) diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 05a18eb56..ec756f2c3 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -362,7 +362,16 @@ impl MacroCallId { pub fn debug_dump(self, db: &impl AstDatabase) -> String { let loc = self.loc(db); let node = loc.ast_id.to_node(db); - let syntax_str = node.syntax().text().chunks().collect::>().join(" "); + let syntax_str = { + let mut res = String::new(); + node.syntax().text().for_each_chunk(|chunk| { + if !res.is_empty() { + res.push(' ') + } + res.push_str(chunk) + }); + res + }; // dump the file name let file_id: HirFileId = self.loc(db).ast_id.file_id(); diff --git a/crates/ra_ide_api/src/display/structure.rs b/crates/ra_ide_api/src/display/structure.rs index 2e183d2f6..0b1a8b6e6 100644 --- a/crates/ra_ide_api/src/display/structure.rs +++ b/crates/ra_ide_api/src/display/structure.rs @@ -83,12 +83,12 @@ fn structure_node(node: &SyntaxNode) -> Option { fn collapse_ws(node: &SyntaxNode, output: &mut String) { let mut can_insert_ws = false; - for chunk in node.text().chunks() { + node.text().for_each_chunk(|chunk| { for line in chunk.lines() { let line = line.trim(); if line.is_empty() { if can_insert_ws { - output.push_str(" "); + output.push(' '); can_insert_ws = false; } } else { @@ -96,7 +96,7 @@ fn structure_node(node: &SyntaxNode) -> Option { can_insert_ws = true; } } - } + }) } visitor() 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_assists/src/remove_dbg.rs | 7 +++---- crates/ra_syntax/src/syntax_text.rs | 17 ++++++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/crates/ra_assists/src/remove_dbg.rs b/crates/ra_assists/src/remove_dbg.rs index c330bc827..5657ee4b8 100644 --- a/crates/ra_assists/src/remove_dbg.rs +++ b/crates/ra_assists/src/remove_dbg.rs @@ -36,11 +36,10 @@ pub(crate) fn remove_dbg(mut ctx: AssistCtx) -> Option let macro_content = { let macro_args = macro_call.token_tree()?.syntax().clone(); - let range = macro_args.range(); - let start = range.start() + TextUnit::of_char('('); - let end = range.end() - TextUnit::of_char(')'); - macro_args.text().slice(start..end).to_string() + let text = macro_args.text(); + let without_parens = TextUnit::of_char('(')..text.len() - TextUnit::of_char(')'); + text.slice(without_parens).to_string() }; ctx.add_action(AssistId("remove_dbg"), "remove dbg!()", |edit| { 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