From 5dbbfda34ae423229487595fd0ae9e727ae42906 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 28 Sep 2019 19:50:16 +0300 Subject: simplify strip attrs --- crates/ra_syntax/src/ast/edit.rs | 21 ++++++++++++++++++++- crates/ra_syntax/src/ast/extensions.rs | 12 +++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) (limited to 'crates/ra_syntax/src/ast') diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index c65899812..7013cc9b5 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs @@ -1,14 +1,16 @@ //! This module contains functions for editing syntax trees. As the trees are //! immutable, all function here return a fresh copy of the tree, instead of //! doing an in-place modification. +use std::{iter, ops::RangeInclusive}; use arrayvec::ArrayVec; -use std::ops::RangeInclusive; use crate::{ algo, ast::{self, make, AstNode}, InsertPosition, SyntaxElement, + SyntaxKind::{ATTR, COMMENT, WHITESPACE}, + SyntaxNode, }; impl ast::FnDef { @@ -31,6 +33,23 @@ impl ast::FnDef { } } +pub fn strip_attrs_and_docs(node: N) -> N { + N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap() +} + +fn strip_attrs_and_docs_inner(mut node: SyntaxNode) -> SyntaxNode { + while let Some(start) = + node.children_with_tokens().find(|it| it.kind() == ATTR || it.kind() == COMMENT) + { + let end = match &start.next_sibling_or_token() { + Some(el) if el.kind() == WHITESPACE => el.clone(), + Some(_) | None => start.clone(), + }; + node = algo::replace_children(&node, RangeInclusive::new(start, end), &mut iter::empty()); + } + node +} + #[must_use] fn insert_children( parent: &N, diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index 0433edb84..8c5ece65d 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs @@ -4,7 +4,7 @@ use itertools::Itertools; use crate::{ - ast::{self, child_opt, children, AstNode, SyntaxNode}, + ast::{self, child_opt, children, AstChildren, AstNode, SyntaxNode}, SmolStr, SyntaxElement, SyntaxKind::*, SyntaxToken, T, @@ -203,6 +203,16 @@ impl ast::ImplBlock { } } +impl ast::AttrsOwner for ast::ImplItem { + fn attrs(&self) -> AstChildren { + match self { + ast::ImplItem::FnDef(it) => it.attrs(), + ast::ImplItem::TypeAliasDef(it) => it.attrs(), + ast::ImplItem::ConstDef(it) => it.attrs(), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum StructKind { Tuple(ast::TupleFieldDefList), -- cgit v1.2.3 From 0840ec038b2822a424acf238d8db5af569f99a21 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 28 Sep 2019 20:09:57 +0300 Subject: migrate add impl items to the new editing API --- crates/ra_syntax/src/ast/edit.rs | 97 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 3 deletions(-) (limited to 'crates/ra_syntax/src/ast') diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 7013cc9b5..2af6f573e 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs @@ -7,10 +7,14 @@ use arrayvec::ArrayVec; use crate::{ algo, - ast::{self, make, AstNode}, - InsertPosition, SyntaxElement, + ast::{ + self, + make::{self, tokens}, + AstNode, + }, + AstToken, InsertPosition, SmolStr, SyntaxElement, SyntaxKind::{ATTR, COMMENT, WHITESPACE}, - SyntaxNode, + SyntaxNode, T, }; impl ast::FnDef { @@ -33,6 +37,74 @@ impl ast::FnDef { } } +impl ast::ItemList { + #[must_use] + pub fn append_items(&self, items: impl Iterator) -> ast::ItemList { + let mut res = self.clone(); + if !self.syntax().text().contains_char('\n') { + res = res.make_multiline(); + } + items.for_each(|it| res = res.append_item(it)); + res + } + + #[must_use] + pub fn append_item(&self, item: ast::ImplItem) -> ast::ItemList { + let (indent, position) = match self.impl_items().last() { + Some(it) => ( + leading_indent(it.syntax()).unwrap_or_default().to_string(), + InsertPosition::After(it.syntax().clone().into()), + ), + None => match self.l_curly() { + Some(it) => ( + " ".to_string() + &leading_indent(self.syntax()).unwrap_or_default(), + InsertPosition::After(it), + ), + None => return self.clone(), + }, + }; + let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); + let to_insert: ArrayVec<[SyntaxElement; 2]> = + [ws.ws().into(), item.syntax().clone().into()].into(); + insert_children(self, position, to_insert.into_iter()) + } + + fn l_curly(&self) -> Option { + self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) + } + + fn make_multiline(&self) -> ast::ItemList { + let l_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) { + Some(it) => it, + None => return self.clone(), + }; + let sibling = match l_curly.next_sibling_or_token() { + Some(it) => it, + None => return self.clone(), + }; + let existing_ws = match sibling.as_token() { + None => None, + Some(tok) if tok.kind() != WHITESPACE => None, + Some(ws) => { + if ws.text().contains('\n') { + return self.clone(); + } + Some(ws.clone()) + } + }; + + let indent = leading_indent(self.syntax()).unwrap_or("".into()); + let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); + let to_insert = iter::once(ws.ws().into()); + match existing_ws { + None => insert_children(self, InsertPosition::After(l_curly), to_insert), + Some(ws) => { + replace_children(self, RangeInclusive::new(ws.clone().into(), ws.into()), to_insert) + } + } + } +} + pub fn strip_attrs_and_docs(node: N) -> N { N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap() } @@ -50,6 +122,25 @@ fn strip_attrs_and_docs_inner(mut node: SyntaxNode) -> SyntaxNode { node } +// Note this is copy-pasted from fmt. It seems like fmt should be a separate +// crate, but basic tree building should be this crate. However, tree building +// might want to call into fmt... +fn leading_indent(node: &SyntaxNode) -> Option { + let prev_tokens = std::iter::successors(node.first_token(), |token| token.prev_token()); + for token in prev_tokens { + if let Some(ws) = ast::Whitespace::cast(token.clone()) { + let ws_text = ws.text(); + if let Some(pos) = ws_text.rfind('\n') { + return Some(ws_text[pos + 1..].into()); + } + } + if token.text().contains('\n') { + break; + } + } + None +} + #[must_use] fn insert_children( parent: &N, -- cgit v1.2.3 From e010b144d5abcbd0947d0490123ef693a6a17c78 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 30 Sep 2019 09:27:26 +0300 Subject: move field list to ast/edit.rs --- crates/ra_syntax/src/ast/edit.rs | 82 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) (limited to 'crates/ra_syntax/src/ast') diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 2af6f573e..6e64c0675 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs @@ -12,7 +12,7 @@ use crate::{ make::{self, tokens}, AstNode, }, - AstToken, InsertPosition, SmolStr, SyntaxElement, + AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind::{ATTR, COMMENT, WHITESPACE}, SyntaxNode, T, }; @@ -105,6 +105,86 @@ impl ast::ItemList { } } +impl ast::RecordFieldList { + #[must_use] + pub fn append_field(&self, field: &ast::RecordField) -> ast::RecordFieldList { + self.insert_field(InsertPosition::Last, field) + } + + #[must_use] + pub fn insert_field( + &self, + position: InsertPosition<&'_ ast::RecordField>, + field: &ast::RecordField, + ) -> ast::RecordFieldList { + let is_multiline = self.syntax().text().contains_char('\n'); + let ws; + let space = if is_multiline { + ws = tokens::WsBuilder::new(&format!( + "\n{} ", + leading_indent(self.syntax()).unwrap_or("".into()) + )); + ws.ws() + } else { + tokens::single_space() + }; + + let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new(); + to_insert.push(space.into()); + to_insert.push(field.syntax().clone().into()); + to_insert.push(tokens::comma().into()); + + macro_rules! after_l_curly { + () => {{ + let anchor = match self.l_curly() { + Some(it) => it, + None => return self.clone(), + }; + InsertPosition::After(anchor) + }}; + } + + macro_rules! after_field { + ($anchor:expr) => { + if let Some(comma) = $anchor + .syntax() + .siblings_with_tokens(Direction::Next) + .find(|it| it.kind() == T![,]) + { + InsertPosition::After(comma) + } else { + to_insert.insert(0, tokens::comma().into()); + InsertPosition::After($anchor.syntax().clone().into()) + } + }; + }; + + let position = match position { + InsertPosition::First => after_l_curly!(), + InsertPosition::Last => { + if !is_multiline { + // don't insert comma before curly + to_insert.pop(); + } + match self.fields().last() { + Some(it) => after_field!(it), + None => after_l_curly!(), + } + } + InsertPosition::Before(anchor) => { + InsertPosition::Before(anchor.syntax().clone().into()) + } + InsertPosition::After(anchor) => after_field!(anchor), + }; + + insert_children(self, position, to_insert.iter().cloned()) + } + + fn l_curly(&self) -> Option { + self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) + } +} + pub fn strip_attrs_and_docs(node: N) -> N { N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap() } -- cgit v1.2.3 From 054c53aeb9a9e29d1c06fa183da263037aa62572 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 30 Sep 2019 09:56:20 +0300 Subject: move remove bounds to ast/edit.rs --- crates/ra_syntax/src/ast/edit.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'crates/ra_syntax/src/ast') diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 6e64c0675..9d0fd1383 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs @@ -10,7 +10,7 @@ use crate::{ ast::{ self, make::{self, tokens}, - AstNode, + AstNode, TypeBoundsOwner, }, AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind::{ATTR, COMMENT, WHITESPACE}, @@ -185,6 +185,20 @@ impl ast::RecordFieldList { } } +impl ast::TypeParam { + pub fn remove_bounds(&self) -> ast::TypeParam { + let colon = match self.colon_token() { + Some(it) => it, + None => return self.clone(), + }; + let end = match self.type_bound_list() { + Some(it) => it.syntax().clone().into(), + None => colon.clone().into(), + }; + replace_children(self, RangeInclusive::new(colon.into(), end), iter::empty()) + } +} + pub fn strip_attrs_and_docs(node: N) -> N { N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap() } -- cgit v1.2.3 From 05ca252fb51bbbf60433bdd3af55ce14bbd66bfd Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 30 Sep 2019 10:05:12 +0300 Subject: remove ast_editor.rs --- crates/ra_syntax/src/ast/edit.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'crates/ra_syntax/src/ast') diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 9d0fd1383..d0857d88b 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs @@ -4,6 +4,7 @@ use std::{iter, ops::RangeInclusive}; use arrayvec::ArrayVec; +use rustc_hash::FxHashMap; use crate::{ algo, @@ -216,6 +217,17 @@ fn strip_attrs_and_docs_inner(mut node: SyntaxNode) -> SyntaxNode { node } +pub fn replace_descendants( + parent: &N, + replacement_map: impl Iterator, +) -> N { + let map = replacement_map + .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into())) + .collect::>(); + let new_syntax = algo::replace_descendants(parent.syntax(), &map); + N::cast(new_syntax).unwrap() +} + // Note this is copy-pasted from fmt. It seems like fmt should be a separate // crate, but basic tree building should be this crate. However, tree building // might want to call into fmt... -- cgit v1.2.3 From 4acadbdca61e77368061a0c53125e164912ab5d5 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 30 Sep 2019 10:08:28 +0300 Subject: cleanup editor --- crates/ra_syntax/src/ast/edit.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'crates/ra_syntax/src/ast') diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index d0857d88b..03f3b5fbb 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs @@ -187,6 +187,7 @@ impl ast::RecordFieldList { } impl ast::TypeParam { + #[must_use] pub fn remove_bounds(&self) -> ast::TypeParam { let colon = match self.colon_token() { Some(it) => it, @@ -200,7 +201,8 @@ impl ast::TypeParam { } } -pub fn strip_attrs_and_docs(node: N) -> N { +#[must_use] +pub fn strip_attrs_and_docs(node: &N) -> N { N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap() } @@ -217,6 +219,7 @@ fn strip_attrs_and_docs_inner(mut node: SyntaxNode) -> SyntaxNode { node } +#[must_use] pub fn replace_descendants( parent: &N, replacement_map: impl Iterator, -- cgit v1.2.3 From 71efdaa6364142b359c59659ec10f35a1e53b5d2 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Mon, 30 Sep 2019 04:44:33 +0800 Subject: Parse correct AttrInput --- crates/ra_syntax/src/ast/generated.rs | 44 +++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'crates/ra_syntax/src/ast') diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index dc1f8c82c..408449fd6 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -166,11 +166,55 @@ impl AstNode for Attr { } } impl Attr { + pub fn path(&self) -> Option { + AstChildren::new(&self.syntax).next() + } + pub fn input(&self) -> Option { + AstChildren::new(&self.syntax).next() + } pub fn value(&self) -> Option { AstChildren::new(&self.syntax).next() } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum AttrInput { + Literal(Literal), + TokenTree(TokenTree), +} +impl From for AttrInput { + fn from(node: Literal) -> AttrInput { + AttrInput::Literal(node) + } +} +impl From for AttrInput { + fn from(node: TokenTree) -> AttrInput { + AttrInput::TokenTree(node) + } +} +impl AstNode for AttrInput { + fn can_cast(kind: SyntaxKind) -> bool { + match kind { + LITERAL | TOKEN_TREE => true, + _ => false, + } + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + LITERAL => AttrInput::Literal(Literal { syntax }), + TOKEN_TREE => AttrInput::TokenTree(TokenTree { syntax }), + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + AttrInput::Literal(it) => &it.syntax, + AttrInput::TokenTree(it) => &it.syntax, + } + } +} +impl AttrInput {} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AwaitExpr { pub(crate) syntax: SyntaxNode, } -- cgit v1.2.3 From 5a4b4f507e9b90bfe41b451763868cba0a70c392 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Mon, 30 Sep 2019 05:15:03 +0800 Subject: Fix API of Attr --- crates/ra_syntax/src/ast/extensions.rs | 66 +++++++++++++--------------------- crates/ra_syntax/src/ast/generated.rs | 3 -- crates/ra_syntax/src/ast/traits.rs | 2 +- 3 files changed, 25 insertions(+), 46 deletions(-) (limited to 'crates/ra_syntax/src/ast') diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index 8c5ece65d..a7b886457 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs @@ -1,10 +1,8 @@ //! Various extension methods to ast Nodes, which are hard to code-generate. //! Extensions for various expressions live in a sibling `expr_extensions` module. -use itertools::Itertools; - use crate::{ - ast::{self, child_opt, children, AstChildren, AstNode, SyntaxNode}, + ast::{self, child_opt, children, AstChildren, AstNode, AttrInput, SyntaxNode}, SmolStr, SyntaxElement, SyntaxKind::*, SyntaxToken, T, @@ -39,12 +37,7 @@ fn text_of_first_token(node: &SyntaxNode) -> &SmolStr { impl ast::Attr { pub fn is_inner(&self) -> bool { - let tt = match self.value() { - None => return false, - Some(tt) => tt, - }; - - let prev = match tt.syntax().prev_sibling() { + let prev = match self.syntax().prev_sibling() { None => return false, Some(prev) => prev, }; @@ -52,48 +45,37 @@ impl ast::Attr { prev.kind() == T![!] } - pub fn as_atom(&self) -> Option { - let tt = self.value()?; - let (_bra, attr, _ket) = tt.syntax().children_with_tokens().collect_tuple()?; - if attr.kind() == IDENT { - Some(attr.as_token()?.text().clone()) - } else { - None + pub fn as_simple_atom(&self) -> Option { + match self.input() { + None => self.simple_name(), + Some(_) => None, } } - pub fn as_call(&self) -> Option<(SmolStr, ast::TokenTree)> { - let tt = self.value()?; - let (_bra, attr, args, _ket) = tt.syntax().children_with_tokens().collect_tuple()?; - let args = ast::TokenTree::cast(args.as_node()?.clone())?; - if attr.kind() == IDENT { - Some((attr.as_token()?.text().clone(), args)) - } else { - None + pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> { + match self.input() { + Some(AttrInput::TokenTree(tt)) => Some((self.simple_name()?, tt)), + _ => None, } } - pub fn as_named(&self) -> Option { - let tt = self.value()?; - let attr = tt.syntax().children_with_tokens().nth(1)?; - if attr.kind() == IDENT { - Some(attr.as_token()?.text().clone()) - } else { - None + pub fn as_simple_key_value(&self) -> Option<(SmolStr, SmolStr)> { + match self.input() { + Some(AttrInput::Literal(lit)) => { + let key = self.simple_name()?; + // FIXME: escape? raw string? + let value = lit.syntax().first_token()?.text().trim_matches('"').into(); + Some((key, value)) + } + _ => None, } } - pub fn as_key_value(&self) -> Option<(SmolStr, SmolStr)> { - let tt = self.value()?; - let tt_node = tt.syntax(); - let attr = tt_node.children_with_tokens().nth(1)?; - if attr.kind() == IDENT { - let key = attr.as_token()?.text().clone(); - let val_node = tt_node.children_with_tokens().find(|t| t.kind() == STRING)?; - let val = val_node.as_token()?.text().trim_start_matches('"').trim_end_matches('"'); - Some((key, SmolStr::new(val))) - } else { - None + pub fn simple_name(&self) -> Option { + let path = self.path()?; + match (path.segment(), path.qualifier()) { + (Some(segment), None) => Some(segment.syntax().first_token()?.text().clone()), + _ => None, } } } diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 408449fd6..aaf03ce3f 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -172,9 +172,6 @@ impl Attr { pub fn input(&self) -> Option { AstChildren::new(&self.syntax).next() } - pub fn value(&self) -> Option { - AstChildren::new(&self.syntax).next() - } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum AttrInput { diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs index c3e676d4c..f275a4955 100644 --- a/crates/ra_syntax/src/ast/traits.rs +++ b/crates/ra_syntax/src/ast/traits.rs @@ -99,7 +99,7 @@ pub trait AttrsOwner: AstNode { children(self) } fn has_atom_attr(&self, atom: &str) -> bool { - self.attrs().filter_map(|x| x.as_atom()).any(|x| x == atom) + self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom) } } -- cgit v1.2.3 From f7e12559cb26b59a9a2ecee4deecaf6fe9100d16 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Mon, 30 Sep 2019 16:09:02 +0800 Subject: Fixes --- crates/ra_syntax/src/ast/extensions.rs | 9 --------- 1 file changed, 9 deletions(-) (limited to 'crates/ra_syntax/src/ast') diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index a7b886457..cefc00402 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs @@ -36,15 +36,6 @@ fn text_of_first_token(node: &SyntaxNode) -> &SmolStr { } impl ast::Attr { - pub fn is_inner(&self) -> bool { - let prev = match self.syntax().prev_sibling() { - None => return false, - Some(prev) => prev, - }; - - prev.kind() == T![!] - } - pub fn as_simple_atom(&self) -> Option { match self.input() { None => self.simple_name(), -- cgit v1.2.3