aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax')
-rw-r--r--crates/ra_syntax/src/algo.rs52
-rw-r--r--crates/ra_syntax/src/ast/edit.rs11
-rw-r--r--crates/ra_syntax/src/ast/make.rs11
-rw-r--r--crates/ra_syntax/src/ast/tokens.rs110
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.txt41
6 files changed, 171 insertions, 55 deletions
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs
index 21fca99a6..b87c7084e 100644
--- a/crates/ra_syntax/src/algo.rs
+++ b/crates/ra_syntax/src/algo.rs
@@ -4,10 +4,11 @@ use std::ops::RangeInclusive;
4 4
5use itertools::Itertools; 5use itertools::Itertools;
6use ra_text_edit::TextEditBuilder; 6use ra_text_edit::TextEditBuilder;
7use rustc_hash::FxHashMap; 7use rustc_hash::{FxHashMap, FxHashSet};
8 8
9use crate::{ 9use crate::{
10 AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, TextRange, TextUnit, 10 AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken,
11 TextRange, TextUnit,
11}; 12};
12 13
13/// Returns ancestors of the node at the offset, sorted by length. This should 14/// Returns ancestors of the node at the offset, sorted by length. This should
@@ -37,6 +38,17 @@ pub fn find_node_at_offset<N: AstNode>(syntax: &SyntaxNode, offset: TextUnit) ->
37 ancestors_at_offset(syntax, offset).find_map(N::cast) 38 ancestors_at_offset(syntax, offset).find_map(N::cast)
38} 39}
39 40
41/// Skip to next non `trivia` token
42pub fn skip_trivia_token(mut token: SyntaxToken, direction: Direction) -> Option<SyntaxToken> {
43 while token.kind().is_trivia() {
44 token = match direction {
45 Direction::Next => token.next_token()?,
46 Direction::Prev => token.prev_token()?,
47 }
48 }
49 Some(token)
50}
51
40/// Finds the first sibling in the given direction which is not `trivia` 52/// Finds the first sibling in the given direction which is not `trivia`
41pub fn non_trivia_sibling(element: SyntaxElement, direction: Direction) -> Option<SyntaxElement> { 53pub fn non_trivia_sibling(element: SyntaxElement, direction: Direction) -> Option<SyntaxElement> {
42 return match element { 54 return match element {
@@ -56,6 +68,11 @@ pub fn find_covering_element(root: &SyntaxNode, range: TextRange) -> SyntaxEleme
56 root.covering_element(range) 68 root.covering_element(range)
57} 69}
58 70
71pub fn least_common_ancestor(u: &SyntaxNode, v: &SyntaxNode) -> Option<SyntaxNode> {
72 let u_ancestors = u.ancestors().collect::<FxHashSet<SyntaxNode>>();
73 v.ancestors().find(|it| u_ancestors.contains(it))
74}
75
59#[derive(Debug, PartialEq, Eq, Clone, Copy)] 76#[derive(Debug, PartialEq, Eq, Clone, Copy)]
60pub enum InsertPosition<T> { 77pub enum InsertPosition<T> {
61 First, 78 First,
@@ -127,6 +144,15 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff {
127pub fn insert_children( 144pub fn insert_children(
128 parent: &SyntaxNode, 145 parent: &SyntaxNode,
129 position: InsertPosition<SyntaxElement>, 146 position: InsertPosition<SyntaxElement>,
147 to_insert: impl IntoIterator<Item = SyntaxElement>,
148) -> SyntaxNode {
149 let mut to_insert = to_insert.into_iter();
150 _insert_children(parent, position, &mut to_insert)
151}
152
153fn _insert_children(
154 parent: &SyntaxNode,
155 position: InsertPosition<SyntaxElement>,
130 to_insert: &mut dyn Iterator<Item = SyntaxElement>, 156 to_insert: &mut dyn Iterator<Item = SyntaxElement>,
131) -> SyntaxNode { 157) -> SyntaxNode {
132 let mut delta = TextUnit::default(); 158 let mut delta = TextUnit::default();
@@ -161,6 +187,15 @@ pub fn insert_children(
161pub fn replace_children( 187pub fn replace_children(
162 parent: &SyntaxNode, 188 parent: &SyntaxNode,
163 to_delete: RangeInclusive<SyntaxElement>, 189 to_delete: RangeInclusive<SyntaxElement>,
190 to_insert: impl IntoIterator<Item = SyntaxElement>,
191) -> SyntaxNode {
192 let mut to_insert = to_insert.into_iter();
193 _replace_children(parent, to_delete, &mut to_insert)
194}
195
196fn _replace_children(
197 parent: &SyntaxNode,
198 to_delete: RangeInclusive<SyntaxElement>,
164 to_insert: &mut dyn Iterator<Item = SyntaxElement>, 199 to_insert: &mut dyn Iterator<Item = SyntaxElement>,
165) -> SyntaxNode { 200) -> SyntaxNode {
166 let start = position_of_child(parent, to_delete.start().clone()); 201 let start = position_of_child(parent, to_delete.start().clone());
@@ -185,14 +220,21 @@ pub fn replace_children(
185/// to create a type-safe abstraction on top of it instead. 220/// to create a type-safe abstraction on top of it instead.
186pub fn replace_descendants( 221pub fn replace_descendants(
187 parent: &SyntaxNode, 222 parent: &SyntaxNode,
188 map: &impl Fn(&SyntaxElement) -> Option<SyntaxElement>, 223 map: impl Fn(&SyntaxElement) -> Option<SyntaxElement>,
224) -> SyntaxNode {
225 _replace_descendants(parent, &map)
226}
227
228fn _replace_descendants(
229 parent: &SyntaxNode,
230 map: &dyn Fn(&SyntaxElement) -> Option<SyntaxElement>,
189) -> SyntaxNode { 231) -> SyntaxNode {
190 // FIXME: this could be made much faster. 232 // FIXME: this could be made much faster.
191 let new_children = parent.children_with_tokens().map(|it| go(map, it)).collect::<Vec<_>>(); 233 let new_children = parent.children_with_tokens().map(|it| go(map, it)).collect::<Vec<_>>();
192 return with_children(parent, new_children); 234 return with_children(parent, new_children);
193 235
194 fn go( 236 fn go(
195 map: &impl Fn(&SyntaxElement) -> Option<SyntaxElement>, 237 map: &dyn Fn(&SyntaxElement) -> Option<SyntaxElement>,
196 element: SyntaxElement, 238 element: SyntaxElement,
197 ) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { 239 ) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> {
198 if let Some(replacement) = map(&element) { 240 if let Some(replacement) = map(&element) {
@@ -204,7 +246,7 @@ pub fn replace_descendants(
204 match element { 246 match element {
205 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), 247 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()),
206 NodeOrToken::Node(it) => { 248 NodeOrToken::Node(it) => {
207 NodeOrToken::Node(replace_descendants(&it, map).green().clone()) 249 NodeOrToken::Node(_replace_descendants(&it, map).green().clone())
208 } 250 }
209 } 251 }
210 } 252 }
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index 0e78d8b63..d2630e9e9 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -276,7 +276,7 @@ pub fn replace_descendants<N: AstNode, D: AstNode>(
276 .into_iter() 276 .into_iter()
277 .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into())) 277 .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into()))
278 .collect::<FxHashMap<SyntaxElement, _>>(); 278 .collect::<FxHashMap<SyntaxElement, _>>();
279 let new_syntax = algo::replace_descendants(parent.syntax(), &|n| map.get(n).cloned()); 279 let new_syntax = algo::replace_descendants(parent.syntax(), |n| map.get(n).cloned());
280 N::cast(new_syntax).unwrap() 280 N::cast(new_syntax).unwrap()
281} 281}
282 282
@@ -331,7 +331,7 @@ impl IndentLevel {
331 ) 331 )
332 }) 332 })
333 .collect(); 333 .collect();
334 algo::replace_descendants(&node, &|n| replacements.get(n).cloned()) 334 algo::replace_descendants(&node, |n| replacements.get(n).cloned())
335 } 335 }
336 336
337 pub fn decrease_indent<N: AstNode>(self, node: N) -> N { 337 pub fn decrease_indent<N: AstNode>(self, node: N) -> N {
@@ -359,7 +359,7 @@ impl IndentLevel {
359 ) 359 )
360 }) 360 })
361 .collect(); 361 .collect();
362 algo::replace_descendants(&node, &|n| replacements.get(n).cloned()) 362 algo::replace_descendants(&node, |n| replacements.get(n).cloned())
363 } 363 }
364} 364}
365 365
@@ -389,7 +389,7 @@ fn insert_children<N: AstNode>(
389 position: InsertPosition<SyntaxElement>, 389 position: InsertPosition<SyntaxElement>,
390 to_insert: impl IntoIterator<Item = SyntaxElement>, 390 to_insert: impl IntoIterator<Item = SyntaxElement>,
391) -> N { 391) -> N {
392 let new_syntax = algo::insert_children(parent.syntax(), position, &mut to_insert.into_iter()); 392 let new_syntax = algo::insert_children(parent.syntax(), position, to_insert);
393 N::cast(new_syntax).unwrap() 393 N::cast(new_syntax).unwrap()
394} 394}
395 395
@@ -404,8 +404,7 @@ fn replace_children<N: AstNode>(
404 to_replace: RangeInclusive<SyntaxElement>, 404 to_replace: RangeInclusive<SyntaxElement>,
405 to_insert: impl IntoIterator<Item = SyntaxElement>, 405 to_insert: impl IntoIterator<Item = SyntaxElement>,
406) -> N { 406) -> N {
407 let new_syntax = 407 let new_syntax = algo::replace_children(parent.syntax(), to_replace, to_insert);
408 algo::replace_children(parent.syntax(), to_replace, &mut to_insert.into_iter());
409 N::cast(new_syntax).unwrap() 408 N::cast(new_syntax).unwrap()
410} 409}
411 410
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index 7c20fcc10..3f11b747f 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -12,11 +12,14 @@ pub fn name_ref(text: &str) -> ast::NameRef {
12 ast_from_text(&format!("fn f() {{ {}; }}", text)) 12 ast_from_text(&format!("fn f() {{ {}; }}", text))
13} 13}
14 14
15pub fn path_from_name_ref(name_ref: ast::NameRef) -> ast::Path { 15pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
16 path_from_text(&name_ref.syntax().to_string()) 16 ast_from_text(&format!("use {};", name_ref.syntax()))
17} 17}
18pub fn path_qualified(qual: ast::Path, name_ref: ast::NameRef) -> ast::Path { 18pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path {
19 path_from_text(&format!("{}::{}", qual.syntax(), name_ref.syntax())) 19 path_from_text(&format!("use {}", segment.syntax()))
20}
21pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path {
22 path_from_text(&format!("{}::{}", qual.syntax(), segment.syntax()))
20} 23}
21fn path_from_text(text: &str) -> ast::Path { 24fn path_from_text(text: &str) -> ast::Path {
22 ast_from_text(text) 25 ast_from_text(text)
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs
index ed8661faf..1a51b8d3b 100644
--- a/crates/ra_syntax/src/ast/tokens.rs
+++ b/crates/ra_syntax/src/ast/tokens.rs
@@ -110,6 +110,64 @@ impl Whitespace {
110 } 110 }
111} 111}
112 112
113pub struct QuoteOffsets {
114 pub quotes: [TextRange; 2],
115 pub contents: TextRange,
116}
117
118impl QuoteOffsets {
119 fn new(literal: &str) -> Option<QuoteOffsets> {
120 let left_quote = literal.find('"')?;
121 let right_quote = literal.rfind('"')?;
122 if left_quote == right_quote {
123 // `literal` only contains one quote
124 return None;
125 }
126
127 let start = TextUnit::from(0);
128 let left_quote = TextUnit::from_usize(left_quote) + TextUnit::of_char('"');
129 let right_quote = TextUnit::from_usize(right_quote);
130 let end = TextUnit::of_str(literal);
131
132 let res = QuoteOffsets {
133 quotes: [TextRange::from_to(start, left_quote), TextRange::from_to(right_quote, end)],
134 contents: TextRange::from_to(left_quote, right_quote),
135 };
136 Some(res)
137 }
138}
139
140pub trait HasQuotes: AstToken {
141 fn quote_offsets(&self) -> Option<QuoteOffsets> {
142 let text = self.text().as_str();
143 let offsets = QuoteOffsets::new(text)?;
144 let o = self.syntax().text_range().start();
145 let offsets = QuoteOffsets {
146 quotes: [offsets.quotes[0] + o, offsets.quotes[1] + o],
147 contents: offsets.contents + o,
148 };
149 Some(offsets)
150 }
151 fn open_quote_text_range(&self) -> Option<TextRange> {
152 self.quote_offsets().map(|it| it.quotes[0])
153 }
154
155 fn close_quote_text_range(&self) -> Option<TextRange> {
156 self.quote_offsets().map(|it| it.quotes[1])
157 }
158
159 fn text_range_between_quotes(&self) -> Option<TextRange> {
160 self.quote_offsets().map(|it| it.contents)
161 }
162}
163
164impl HasQuotes for String {}
165impl HasQuotes for RawString {}
166
167pub trait HasStringValue: HasQuotes {
168 fn value(&self) -> Option<std::string::String>;
169}
170
113pub struct String(SyntaxToken); 171pub struct String(SyntaxToken);
114 172
115impl AstToken for String { 173impl AstToken for String {
@@ -124,21 +182,16 @@ impl AstToken for String {
124 } 182 }
125} 183}
126 184
127impl String { 185impl HasStringValue for String {
128 pub fn value(&self) -> Option<std::string::String> { 186 fn value(&self) -> Option<std::string::String> {
129 let text = self.text().as_str(); 187 let text = self.text().as_str();
130 let usual_string_range = find_usual_string_range(text)?; 188 let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
131 let start_of_inside = usual_string_range.start().to_usize() + 1;
132 let end_of_inside = usual_string_range.end().to_usize();
133 let inside_str = &text[start_of_inside..end_of_inside];
134 189
135 let mut buf = std::string::String::with_capacity(inside_str.len()); 190 let mut buf = std::string::String::with_capacity(text.len());
136 let mut has_error = false; 191 let mut has_error = false;
137 rustc_lexer::unescape::unescape_str(inside_str, &mut |_, unescaped_char| { 192 rustc_lexer::unescape::unescape_str(text, &mut |_, unescaped_char| match unescaped_char {
138 match unescaped_char { 193 Ok(c) => buf.push(c),
139 Ok(c) => buf.push(c), 194 Err(_) => has_error = true,
140 Err(_) => has_error = true,
141 }
142 }); 195 });
143 196
144 if has_error { 197 if has_error {
@@ -162,27 +215,18 @@ impl AstToken for RawString {
162 } 215 }
163} 216}
164 217
165impl RawString { 218impl HasStringValue for RawString {
166 pub fn value(&self) -> Option<std::string::String> { 219 fn value(&self) -> Option<std::string::String> {
167 let text = self.text().as_str(); 220 let text = self.text().as_str();
168 let usual_string_range = find_usual_string_range(text)?; 221 let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
169 let start_of_inside = usual_string_range.start().to_usize() + 1; 222 Some(text.to_string())
170 let end_of_inside = usual_string_range.end().to_usize(); 223 }
171 let inside_str = &text[start_of_inside..end_of_inside]; 224}
172 Some(inside_str.to_string()) 225
173 } 226impl RawString {
174} 227 pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> {
175 228 let contents_range = self.text_range_between_quotes()?;
176fn find_usual_string_range(s: &str) -> Option<TextRange> { 229 assert!(range.is_subrange(&TextRange::offset_len(0.into(), contents_range.len())));
177 let left_quote = s.find('"')?; 230 Some(range + contents_range.start())
178 let right_quote = s.rfind('"')?;
179 if left_quote == right_quote {
180 // `s` only contains one quote
181 None
182 } else {
183 Some(TextRange::from_to(
184 TextUnit::from(left_quote as u32),
185 TextUnit::from(right_quote as u32),
186 ))
187 } 231 }
188} 232}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.rs b/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.rs
index 86803fe08..fa8ee49a2 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.rs
@@ -5,4 +5,5 @@ fn foo() {
5 let d: i32 = 92; 5 let d: i32 = 92;
6 let e: !; 6 let e: !;
7 let _: ! = {}; 7 let _: ! = {};
8 let f = #[attr]||{};
8} 9}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.txt b/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.txt
index 97166cfd5..17739dfbd 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.txt
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0130_let_stmt.txt
@@ -1,5 +1,5 @@
1SOURCE_FILE@[0; 110) 1SOURCE_FILE@[0; 135)
2 FN_DEF@[0; 109) 2 FN_DEF@[0; 134)
3 FN_KW@[0; 2) "fn" 3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " " 4 WHITESPACE@[2; 3) " "
5 NAME@[3; 6) 5 NAME@[3; 6)
@@ -8,8 +8,8 @@ SOURCE_FILE@[0; 110)
8 L_PAREN@[6; 7) "(" 8 L_PAREN@[6; 7) "("
9 R_PAREN@[7; 8) ")" 9 R_PAREN@[7; 8) ")"
10 WHITESPACE@[8; 9) " " 10 WHITESPACE@[8; 9) " "
11 BLOCK_EXPR@[9; 109) 11 BLOCK_EXPR@[9; 134)
12 BLOCK@[9; 109) 12 BLOCK@[9; 134)
13 L_CURLY@[9; 10) "{" 13 L_CURLY@[9; 10) "{"
14 WHITESPACE@[10; 15) "\n " 14 WHITESPACE@[10; 15) "\n "
15 LET_STMT@[15; 21) 15 LET_STMT@[15; 21)
@@ -97,6 +97,33 @@ SOURCE_FILE@[0; 110)
97 L_CURLY@[104; 105) "{" 97 L_CURLY@[104; 105) "{"
98 R_CURLY@[105; 106) "}" 98 R_CURLY@[105; 106) "}"
99 SEMI@[106; 107) ";" 99 SEMI@[106; 107) ";"
100 WHITESPACE@[107; 108) "\n" 100 WHITESPACE@[107; 112) "\n "
101 R_CURLY@[108; 109) "}" 101 LET_STMT@[112; 132)
102 WHITESPACE@[109; 110) "\n" 102 LET_KW@[112; 115) "let"
103 WHITESPACE@[115; 116) " "
104 BIND_PAT@[116; 117)
105 NAME@[116; 117)
106 IDENT@[116; 117) "f"
107 WHITESPACE@[117; 118) " "
108 EQ@[118; 119) "="
109 WHITESPACE@[119; 120) " "
110 LAMBDA_EXPR@[120; 131)
111 ATTR@[120; 127)
112 POUND@[120; 121) "#"
113 L_BRACK@[121; 122) "["
114 PATH@[122; 126)
115 PATH_SEGMENT@[122; 126)
116 NAME_REF@[122; 126)
117 IDENT@[122; 126) "attr"
118 R_BRACK@[126; 127) "]"
119 PARAM_LIST@[127; 129)
120 PIPE@[127; 128) "|"
121 PIPE@[128; 129) "|"
122 BLOCK_EXPR@[129; 131)
123 BLOCK@[129; 131)
124 L_CURLY@[129; 130) "{"
125 R_CURLY@[130; 131) "}"
126 SEMI@[131; 132) ";"
127 WHITESPACE@[132; 133) "\n"
128 R_CURLY@[133; 134) "}"
129 WHITESPACE@[134; 135) "\n"