diff options
-rw-r--r-- | crates/ra_assists/src/remove_dbg.rs | 7 | ||||
-rw-r--r-- | crates/ra_hir/src/ids.rs | 11 | ||||
-rw-r--r-- | crates/ra_ide_api/src/display/structure.rs | 6 | ||||
-rw-r--r-- | crates/ra_syntax/src/syntax_node.rs | 1 | ||||
-rw-r--r-- | crates/ra_syntax/src/syntax_text.rs | 115 |
5 files changed, 91 insertions, 49 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<impl HirDatabase>) -> Option<Assist> | |||
36 | 36 | ||
37 | let macro_content = { | 37 | let macro_content = { |
38 | let macro_args = macro_call.token_tree()?.syntax().clone(); | 38 | let macro_args = macro_call.token_tree()?.syntax().clone(); |
39 | let range = macro_args.range(); | ||
40 | let start = range.start() + TextUnit::of_char('('); | ||
41 | let end = range.end() - TextUnit::of_char(')'); | ||
42 | 39 | ||
43 | macro_args.text().slice(start..end).to_string() | 40 | let text = macro_args.text(); |
41 | let without_parens = TextUnit::of_char('(')..text.len() - TextUnit::of_char(')'); | ||
42 | text.slice(without_parens).to_string() | ||
44 | }; | 43 | }; |
45 | 44 | ||
46 | ctx.add_action(AssistId("remove_dbg"), "remove dbg!()", |edit| { | 45 | ctx.add_action(AssistId("remove_dbg"), "remove dbg!()", |edit| { |
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 { | |||
362 | pub fn debug_dump(self, db: &impl AstDatabase) -> String { | 362 | pub fn debug_dump(self, db: &impl AstDatabase) -> String { |
363 | let loc = self.loc(db); | 363 | let loc = self.loc(db); |
364 | let node = loc.ast_id.to_node(db); | 364 | let node = loc.ast_id.to_node(db); |
365 | let syntax_str = node.syntax().text().chunks().collect::<Vec<_>>().join(" "); | 365 | let syntax_str = { |
366 | let mut res = String::new(); | ||
367 | node.syntax().text().for_each_chunk(|chunk| { | ||
368 | if !res.is_empty() { | ||
369 | res.push(' ') | ||
370 | } | ||
371 | res.push_str(chunk) | ||
372 | }); | ||
373 | res | ||
374 | }; | ||
366 | 375 | ||
367 | // dump the file name | 376 | // dump the file name |
368 | let file_id: HirFileId = self.loc(db).ast_id.file_id(); | 377 | 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<StructureNode> { | |||
83 | 83 | ||
84 | fn collapse_ws(node: &SyntaxNode, output: &mut String) { | 84 | fn collapse_ws(node: &SyntaxNode, output: &mut String) { |
85 | let mut can_insert_ws = false; | 85 | let mut can_insert_ws = false; |
86 | for chunk in node.text().chunks() { | 86 | node.text().for_each_chunk(|chunk| { |
87 | for line in chunk.lines() { | 87 | for line in chunk.lines() { |
88 | let line = line.trim(); | 88 | let line = line.trim(); |
89 | if line.is_empty() { | 89 | if line.is_empty() { |
90 | if can_insert_ws { | 90 | if can_insert_ws { |
91 | output.push_str(" "); | 91 | output.push(' '); |
92 | can_insert_ws = false; | 92 | can_insert_ws = false; |
93 | } | 93 | } |
94 | } else { | 94 | } else { |
@@ -96,7 +96,7 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> { | |||
96 | can_insert_ws = true; | 96 | can_insert_ws = true; |
97 | } | 97 | } |
98 | } | 98 | } |
99 | } | 99 | }) |
100 | } | 100 | } |
101 | 101 | ||
102 | visitor() | 102 | 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 { | |||
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 | ||