diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-07-19 18:57:33 +0100 |
---|---|---|
committer | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-07-19 18:57:33 +0100 |
commit | fabd4c4304e387ed6bcc29b39d1593666c4cbec9 (patch) | |
tree | b18a0be7255bb070dd7e2ae3b5687bc5f35f0d41 /crates/ra_syntax/src | |
parent | d4ffbf2ae092b313b3c750adad398f3aa6fb209b (diff) | |
parent | 002529937075bd69d7f71483d798d6e4f43d1de9 (diff) |
Merge #1556
1556: sane indexing in text r=matklad a=matklad
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_syntax/src')
-rw-r--r-- | crates/ra_syntax/src/syntax_node.rs | 1 | ||||
-rw-r--r-- | crates/ra_syntax/src/syntax_text.rs | 115 |
2 files changed, 75 insertions, 41 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)] |
298 | pub struct SyntaxToken(pub(crate) rowan::cursor::SyntaxToken); | 298 | pub struct SyntaxToken(pub(crate) rowan::cursor::SyntaxToken); |
299 | 299 | ||
300 | //FIXME: always output text | ||
301 | impl fmt::Debug for SyntaxToken { | 300 | impl 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..d8adf782b 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 { |
@@ -72,18 +95,21 @@ impl<'a> SyntaxText<'a> { | |||
72 | self.range.is_empty() | 95 | self.range.is_empty() |
73 | } | 96 | } |
74 | 97 | ||
75 | /// NB, the offsets here are absolute, and this probably doesn't make sense! | ||
76 | pub fn slice(&self, range: impl ops::RangeBounds<TextUnit>) -> SyntaxText<'a> { | 98 | pub fn slice(&self, range: impl ops::RangeBounds<TextUnit>) -> SyntaxText<'a> { |
77 | let start = match range.start_bound() { | 99 | let start = match range.start_bound() { |
78 | Bound::Included(b) => *b, | 100 | Bound::Included(&b) => b, |
79 | Bound::Excluded(b) => *b + TextUnit::from(1u32), | 101 | Bound::Excluded(_) => panic!("utf-aware slicing can't work this way"), |
80 | Bound::Unbounded => self.range.start(), | 102 | Bound::Unbounded => 0.into(), |
81 | }; | 103 | }; |
82 | let end = match range.end_bound() { | 104 | let end = match range.end_bound() { |
83 | Bound::Included(b) => *b + TextUnit::from(1u32), | 105 | Bound::Included(_) => panic!("utf-aware slicing can't work this way"), |
84 | Bound::Excluded(b) => *b, | 106 | Bound::Excluded(&b) => b, |
85 | Bound::Unbounded => self.range.end(), | 107 | Bound::Unbounded => self.len(), |
86 | }; | 108 | }; |
109 | assert!(start <= end); | ||
110 | let len = end - start; | ||
111 | let start = self.range.start() + start; | ||
112 | let end = start + len; | ||
87 | assert!( | 113 | assert!( |
88 | start <= end, | 114 | start <= end, |
89 | "invalid slice, range: {:?}, slice: {:?}", | 115 | "invalid slice, range: {:?}, slice: {:?}", |
@@ -101,17 +127,25 @@ impl<'a> SyntaxText<'a> { | |||
101 | } | 127 | } |
102 | 128 | ||
103 | pub fn char_at(&self, offset: impl Into<TextUnit>) -> Option<char> { | 129 | pub fn char_at(&self, offset: impl Into<TextUnit>) -> Option<char> { |
104 | let mut start: TextUnit = 0.into(); | ||
105 | let offset = offset.into(); | 130 | let offset = offset.into(); |
106 | for chunk in self.chunks() { | 131 | let mut start: TextUnit = 0.into(); |
107 | let end = start + TextUnit::of_str(chunk.as_str()); | 132 | let res = self.try_for_each_chunk(|chunk| { |
133 | let end = start + TextUnit::of_str(chunk); | ||
108 | if start <= offset && offset < end { | 134 | if start <= offset && offset < end { |
109 | let off: usize = u32::from(offset - start) as usize; | 135 | let off: usize = u32::from(offset - start) as usize; |
110 | return Some(chunk[off..].chars().next().unwrap()); | 136 | return Err(chunk[off..].chars().next().unwrap()); |
111 | } | 137 | } |
112 | start = end; | 138 | start = end; |
113 | } | 139 | Ok(()) |
114 | None | 140 | }); |
141 | found(res) | ||
142 | } | ||
143 | } | ||
144 | |||
145 | fn found<T>(res: Result<(), T>) -> Option<T> { | ||
146 | match res { | ||
147 | Ok(()) => None, | ||
148 | Err(it) => Some(it), | ||
115 | } | 149 | } |
116 | } | 150 | } |
117 | 151 | ||
@@ -135,13 +169,14 @@ impl From<SyntaxText<'_>> for String { | |||
135 | 169 | ||
136 | impl PartialEq<str> for SyntaxText<'_> { | 170 | impl PartialEq<str> for SyntaxText<'_> { |
137 | fn eq(&self, mut rhs: &str) -> bool { | 171 | fn eq(&self, mut rhs: &str) -> bool { |
138 | for chunk in self.chunks() { | 172 | self.try_for_each_chunk(|chunk| { |
139 | if !rhs.starts_with(chunk.as_str()) { | 173 | if !rhs.starts_with(chunk) { |
140 | return false; | 174 | return Err(()); |
141 | } | 175 | } |
142 | rhs = &rhs[chunk.len()..]; | 176 | rhs = &rhs[chunk.len()..]; |
143 | } | 177 | Ok(()) |
144 | rhs.is_empty() | 178 | }) |
179 | .is_ok() | ||
145 | } | 180 | } |
146 | } | 181 | } |
147 | 182 | ||