aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src')
-rw-r--r--crates/ra_syntax/src/syntax_node.rs1
-rw-r--r--crates/ra_syntax/src/syntax_text.rs98
2 files changed, 65 insertions, 34 deletions
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 {
297#[derive(Clone, PartialEq, Eq, Hash)] 297#[derive(Clone, PartialEq, Eq, Hash)]
298pub struct SyntaxToken(pub(crate) rowan::cursor::SyntaxToken); 298pub struct SyntaxToken(pub(crate) rowan::cursor::SyntaxToken);
299 299
300//FIXME: always output text
301impl fmt::Debug for SyntaxToken { 300impl fmt::Debug for SyntaxToken {
302 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 301 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
303 write!(fmt, "{:?}@{:?}", self.kind(), self.range())?; 302 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> {
16 SyntaxText { node, range: node.range() } 16 SyntaxText { node, range: node.range() }
17 } 17 }
18 18
19 pub fn chunks(&self) -> impl Iterator<Item = SmolStr> { 19 pub fn try_fold_chunks<T, F, E>(&self, init: T, mut f: F) -> Result<T, E>
20 let range = self.range; 20 where
21 self.node.descendants_with_tokens().filter_map(move |el| match el { 21 F: FnMut(T, &str) -> Result<T, E>,
22 SyntaxElement::Token(t) => { 22 {
23 let text = t.text(); 23 self.node.descendants_with_tokens().try_fold(init, move |acc, element| {
24 let range = range.intersection(&t.range())?; 24 let res = match element {
25 let res = if range == t.range() { 25 SyntaxElement::Token(token) => {
26 t.text().clone() 26 let range = match self.range.intersection(&token.range()) {
27 } else { 27 None => return Ok(acc),
28 let range = range - t.range().start(); 28 Some(it) => it,
29 text[range].into() 29 };
30 }; 30 let slice = if range == token.range() {
31 Some(res) 31 token.text()
32 } 32 } else {
33 SyntaxElement::Node(_) => None, 33 let range = range - token.range().start();
34 &token.text()[range]
35 };
36 f(acc, slice)?
37 }
38 SyntaxElement::Node(_) => acc,
39 };
40 Ok(res)
34 }) 41 })
35 } 42 }
36 43
44 pub fn try_for_each_chunk<F: FnMut(&str) -> Result<(), E>, E>(
45 &self,
46 mut f: F,
47 ) -> Result<(), E> {
48 self.try_fold_chunks((), move |(), chunk| f(chunk))
49 }
50
51 pub fn for_each_chunk<F: FnMut(&str)>(&self, mut f: F) {
52 enum Void {}
53 match self.try_for_each_chunk(|chunk| Ok::<(), Void>(f(chunk))) {
54 Ok(()) => (),
55 Err(void) => match void {},
56 }
57 }
58
37 pub fn push_to(&self, buf: &mut String) { 59 pub fn push_to(&self, buf: &mut String) {
38 self.chunks().for_each(|it| buf.push_str(it.as_str())); 60 self.for_each_chunk(|chunk| buf.push_str(chunk))
39 } 61 }
40 62
41 pub fn to_string(&self) -> String { 63 pub fn to_string(&self) -> String {
@@ -49,19 +71,20 @@ impl<'a> SyntaxText<'a> {
49 } 71 }
50 72
51 pub fn contains(&self, c: char) -> bool { 73 pub fn contains(&self, c: char) -> bool {
52 self.chunks().any(|it| it.contains(c)) 74 self.try_for_each_chunk(|chunk| if chunk.contains(c) { Err(()) } else { Ok(()) }).is_err()
53 } 75 }
54 76
55 pub fn find(&self, c: char) -> Option<TextUnit> { 77 pub fn find(&self, c: char) -> Option<TextUnit> {
56 let mut acc: TextUnit = 0.into(); 78 let mut acc: TextUnit = 0.into();
57 for chunk in self.chunks() { 79 let res = self.try_for_each_chunk(|chunk| {
58 if let Some(pos) = chunk.find(c) { 80 if let Some(pos) = chunk.find(c) {
59 let pos: TextUnit = (pos as u32).into(); 81 let pos: TextUnit = (pos as u32).into();
60 return Some(acc + pos); 82 return Err(acc + pos);
61 } 83 }
62 acc += TextUnit::of_str(chunk.as_str()); 84 acc += TextUnit::of_str(chunk);
63 } 85 Ok(())
64 None 86 });
87 found(res)
65 } 88 }
66 89
67 pub fn len(&self) -> TextUnit { 90 pub fn len(&self) -> TextUnit {
@@ -101,17 +124,25 @@ impl<'a> SyntaxText<'a> {
101 } 124 }
102 125
103 pub fn char_at(&self, offset: impl Into<TextUnit>) -> Option<char> { 126 pub fn char_at(&self, offset: impl Into<TextUnit>) -> Option<char> {
104 let mut start: TextUnit = 0.into();
105 let offset = offset.into(); 127 let offset = offset.into();
106 for chunk in self.chunks() { 128 let mut start: TextUnit = 0.into();
107 let end = start + TextUnit::of_str(chunk.as_str()); 129 let res = self.try_for_each_chunk(|chunk| {
130 let end = start + TextUnit::of_str(chunk);
108 if start <= offset && offset < end { 131 if start <= offset && offset < end {
109 let off: usize = u32::from(offset - start) as usize; 132 let off: usize = u32::from(offset - start) as usize;
110 return Some(chunk[off..].chars().next().unwrap()); 133 return Err(chunk[off..].chars().next().unwrap());
111 } 134 }
112 start = end; 135 start = end;
113 } 136 Ok(())
114 None 137 });
138 found(res)
139 }
140}
141
142fn found<T>(res: Result<(), T>) -> Option<T> {
143 match res {
144 Ok(()) => None,
145 Err(it) => Some(it),
115 } 146 }
116} 147}
117 148
@@ -135,13 +166,14 @@ impl From<SyntaxText<'_>> for String {
135 166
136impl PartialEq<str> for SyntaxText<'_> { 167impl PartialEq<str> for SyntaxText<'_> {
137 fn eq(&self, mut rhs: &str) -> bool { 168 fn eq(&self, mut rhs: &str) -> bool {
138 for chunk in self.chunks() { 169 self.try_for_each_chunk(|chunk| {
139 if !rhs.starts_with(chunk.as_str()) { 170 if !rhs.starts_with(chunk) {
140 return false; 171 return Err(());
141 } 172 }
142 rhs = &rhs[chunk.len()..]; 173 rhs = &rhs[chunk.len()..];
143 } 174 Ok(())
144 rhs.is_empty() 175 })
176 .is_ok()
145 } 177 }
146} 178}
147 179