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/ast/edit.rs141
-rw-r--r--crates/ra_syntax/src/parsing/text_tree_sink.rs13
2 files changed, 121 insertions, 33 deletions
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index 2304e00cf..b69cae234 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -46,14 +46,46 @@ impl ast::FnDef {
46 } 46 }
47} 47}
48 48
49fn make_multiline<N>(node: N) -> N
50where
51 N: AstNode + Clone,
52{
53 let l_curly = match node.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) {
54 Some(it) => it,
55 None => return node,
56 };
57 let sibling = match l_curly.next_sibling_or_token() {
58 Some(it) => it,
59 None => return node,
60 };
61 let existing_ws = match sibling.as_token() {
62 None => None,
63 Some(tok) if tok.kind() != WHITESPACE => None,
64 Some(ws) => {
65 if ws.text().contains('\n') {
66 return node;
67 }
68 Some(ws.clone())
69 }
70 };
71
72 let indent = leading_indent(node.syntax()).unwrap_or_default();
73 let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
74 let to_insert = iter::once(ws.ws().into());
75 match existing_ws {
76 None => node.insert_children(InsertPosition::After(l_curly), to_insert),
77 Some(ws) => node.replace_children(single_node(ws), to_insert),
78 }
79}
80
49impl ast::ItemList { 81impl ast::ItemList {
50 #[must_use] 82 #[must_use]
51 pub fn append_items(&self, items: impl Iterator<Item = ast::ImplItem>) -> ast::ItemList { 83 pub fn append_items(&self, items: impl IntoIterator<Item = ast::ImplItem>) -> ast::ItemList {
52 let mut res = self.clone(); 84 let mut res = self.clone();
53 if !self.syntax().text().contains_char('\n') { 85 if !self.syntax().text().contains_char('\n') {
54 res = res.make_multiline(); 86 res = make_multiline(res);
55 } 87 }
56 items.for_each(|it| res = res.append_item(it)); 88 items.into_iter().for_each(|it| res = res.append_item(it));
57 res 89 res
58 } 90 }
59 91
@@ -81,35 +113,6 @@ impl ast::ItemList {
81 fn l_curly(&self) -> Option<SyntaxElement> { 113 fn l_curly(&self) -> Option<SyntaxElement> {
82 self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) 114 self.syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
83 } 115 }
84
85 fn make_multiline(&self) -> ast::ItemList {
86 let l_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) {
87 Some(it) => it,
88 None => return self.clone(),
89 };
90 let sibling = match l_curly.next_sibling_or_token() {
91 Some(it) => it,
92 None => return self.clone(),
93 };
94 let existing_ws = match sibling.as_token() {
95 None => None,
96 Some(tok) if tok.kind() != WHITESPACE => None,
97 Some(ws) => {
98 if ws.text().contains('\n') {
99 return self.clone();
100 }
101 Some(ws.clone())
102 }
103 };
104
105 let indent = leading_indent(self.syntax()).unwrap_or_default();
106 let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
107 let to_insert = iter::once(ws.ws().into());
108 match existing_ws {
109 None => self.insert_children(InsertPosition::After(l_curly), to_insert),
110 Some(ws) => self.replace_children(single_node(ws), to_insert),
111 }
112 }
113} 116}
114 117
115impl ast::RecordFieldList { 118impl ast::RecordFieldList {
@@ -334,6 +337,80 @@ impl ast::UseTree {
334 } 337 }
335} 338}
336 339
340impl ast::MatchArmList {
341 #[must_use]
342 pub fn append_arms(&self, items: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
343 let mut res = self.clone();
344 res = res.strip_if_only_whitespace();
345 if !res.syntax().text().contains_char('\n') {
346 res = make_multiline(res);
347 }
348 items.into_iter().for_each(|it| res = res.append_arm(it));
349 res
350 }
351
352 fn strip_if_only_whitespace(&self) -> ast::MatchArmList {
353 let mut iter = self.syntax().children_with_tokens().skip_while(|it| it.kind() != T!['{']);
354 iter.next(); // Eat the curly
355 let mut inner = iter.take_while(|it| it.kind() != T!['}']);
356 if !inner.clone().all(|it| it.kind() == WHITESPACE) {
357 return self.clone();
358 }
359 let start = match inner.next() {
360 Some(s) => s,
361 None => return self.clone(),
362 };
363 let end = match inner.last() {
364 Some(s) => s,
365 None => start.clone(),
366 };
367 self.replace_children(start..=end, &mut iter::empty())
368 }
369
370 #[must_use]
371 pub fn remove_placeholder(&self) -> ast::MatchArmList {
372 let placeholder =
373 self.arms().find(|arm| matches!(arm.pat(), Some(ast::Pat::PlaceholderPat(_))));
374 if let Some(placeholder) = placeholder {
375 self.remove_arm(&placeholder)
376 } else {
377 self.clone()
378 }
379 }
380
381 #[must_use]
382 fn remove_arm(&self, arm: &ast::MatchArm) -> ast::MatchArmList {
383 let start = arm.syntax().clone();
384 let end = if let Some(comma) = start
385 .siblings_with_tokens(Direction::Next)
386 .skip(1)
387 .skip_while(|it| it.kind().is_trivia())
388 .next()
389 .filter(|it| it.kind() == T![,])
390 {
391 comma
392 } else {
393 start.clone().into()
394 };
395 self.replace_children(start.into()..=end, None)
396 }
397
398 #[must_use]
399 pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList {
400 let r_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['}']) {
401 Some(t) => t,
402 None => return self.clone(),
403 };
404 let position = InsertPosition::Before(r_curly.into());
405 let arm_ws = tokens::WsBuilder::new(" ");
406 let match_indent = &leading_indent(self.syntax()).unwrap_or_default();
407 let match_ws = tokens::WsBuilder::new(&format!("\n{}", match_indent));
408 let to_insert: ArrayVec<[SyntaxElement; 3]> =
409 [arm_ws.ws().into(), item.syntax().clone().into(), match_ws.ws().into()].into();
410 self.insert_children(position, to_insert)
411 }
412}
413
337#[must_use] 414#[must_use]
338pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { 415pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
339 N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() 416 N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap()
diff --git a/crates/ra_syntax/src/parsing/text_tree_sink.rs b/crates/ra_syntax/src/parsing/text_tree_sink.rs
index dd202601d..87bb21cd9 100644
--- a/crates/ra_syntax/src/parsing/text_tree_sink.rs
+++ b/crates/ra_syntax/src/parsing/text_tree_sink.rs
@@ -149,10 +149,21 @@ fn n_attached_trivias<'a>(
149 MACRO_CALL | CONST_DEF | TYPE_ALIAS_DEF | STRUCT_DEF | ENUM_DEF | ENUM_VARIANT | FN_DEF 149 MACRO_CALL | CONST_DEF | TYPE_ALIAS_DEF | STRUCT_DEF | ENUM_DEF | ENUM_VARIANT | FN_DEF
150 | TRAIT_DEF | MODULE | RECORD_FIELD_DEF | STATIC_DEF => { 150 | TRAIT_DEF | MODULE | RECORD_FIELD_DEF | STATIC_DEF => {
151 let mut res = 0; 151 let mut res = 0;
152 for (i, (kind, text)) in trivias.enumerate() { 152 let mut trivias = trivias.enumerate().peekable();
153
154 while let Some((i, (kind, text))) = trivias.next() {
153 match kind { 155 match kind {
154 WHITESPACE => { 156 WHITESPACE => {
155 if text.contains("\n\n") { 157 if text.contains("\n\n") {
158 // we check whether the next token is a doc-comment
159 // and skip the whitespace in this case
160 if let Some((peek_kind, peek_text)) =
161 trivias.peek().map(|(_, pair)| pair)
162 {
163 if *peek_kind == COMMENT && peek_text.starts_with("///") {
164 continue;
165 }
166 }
156 break; 167 break;
157 } 168 }
158 } 169 }