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 --- .../src/assists/add_missing_impl_members.rs | 12 +++--------- crates/ra_assists/src/ast_editor.rs | 17 ----------------- crates/ra_syntax/src/ast.rs | 2 +- crates/ra_syntax/src/ast/edit.rs | 21 ++++++++++++++++++++- crates/ra_syntax/src/ast/extensions.rs | 12 +++++++++++- 5 files changed, 35 insertions(+), 29 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/assists/add_missing_impl_members.rs b/crates/ra_assists/src/assists/add_missing_impl_members.rs index 682455bce..3fce4a5b7 100644 --- a/crates/ra_assists/src/assists/add_missing_impl_members.rs +++ b/crates/ra_assists/src/assists/add_missing_impl_members.rs @@ -1,6 +1,6 @@ use hir::{db::HirDatabase, HasSource}; use ra_syntax::{ - ast::{self, make, AstNode, NameOwner}, + ast::{self, edit, make, AstNode, NameOwner}, SmolStr, }; @@ -76,8 +76,8 @@ fn add_missing_impl_members_inner( ctx.add_action(AssistId(assist_id), label, |edit| { let n_existing_items = impl_item_list.impl_items().count(); let items = missing_items.into_iter().map(|it| match it { - ast::ImplItem::FnDef(def) => strip_docstring(add_body(def).into()), - _ => strip_docstring(it), + ast::ImplItem::FnDef(def) => edit::strip_attrs_and_docs(add_body(def).into()), + _ => edit::strip_attrs_and_docs(it), }); let mut ast_editor = AstEditor::new(impl_item_list); @@ -93,12 +93,6 @@ fn add_missing_impl_members_inner( ctx.build() } -fn strip_docstring(item: ast::ImplItem) -> ast::ImplItem { - let mut ast_editor = AstEditor::new(item); - ast_editor.strip_attrs_and_docs(); - ast_editor.ast().to_owned() -} - fn add_body(fn_def: ast::FnDef) -> ast::FnDef { if fn_def.body().is_none() { fn_def.with_body(make::block_from_expr(make::expr_unimplemented())) diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs index 72c8c478a..60b8923e1 100644 --- a/crates/ra_assists/src/ast_editor.rs +++ b/crates/ra_assists/src/ast_editor.rs @@ -212,23 +212,6 @@ impl AstEditor { } } -impl AstEditor { - pub fn strip_attrs_and_docs(&mut self) { - while let Some(start) = self - .ast() - .syntax() - .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(), - }; - self.ast = self.replace_children(RangeInclusive::new(start, end), iter::empty()); - } - } -} - impl AstEditor { pub fn remove_bounds(&mut self) -> &mut Self { let colon = match self.ast.colon_token() { diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index fdffd8cb1..1b2ce921a 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -5,7 +5,7 @@ mod traits; mod tokens; mod extensions; mod expr_extensions; -mod edit; +pub mod edit; pub mod make; use std::marker::PhantomData; 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_assists/src/assist_ctx.rs | 8 +- .../src/assists/add_missing_impl_members.rs | 15 ++-- crates/ra_assists/src/ast_editor.rs | 69 +-------------- crates/ra_syntax/src/ast/edit.rs | 97 +++++++++++++++++++++- 4 files changed, 109 insertions(+), 80 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index c45262efa..cbe12e908 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -2,7 +2,7 @@ use hir::db::HirDatabase; use ra_db::FileRange; use ra_fmt::{leading_indent, reindent}; use ra_syntax::{ - algo::{find_covering_element, find_node_at_offset}, + algo::{self, find_covering_element, find_node_at_offset}, AstNode, SourceFile, SyntaxElement, SyntaxNode, SyntaxToken, TextRange, TextUnit, TokenAtOffset, }; @@ -177,6 +177,12 @@ impl AssistBuilder { &mut self.edit } + pub(crate) fn replace_ast(&mut self, old: N, new: N) { + for (from, to) in algo::diff(old.syntax(), new.syntax()) { + self.edit.replace(from.text_range(), to.to_string()) + } + } + fn build(self) -> AssistAction { AssistAction { edit: self.edit.finish(), diff --git a/crates/ra_assists/src/assists/add_missing_impl_members.rs b/crates/ra_assists/src/assists/add_missing_impl_members.rs index 3fce4a5b7..c2e3eb06b 100644 --- a/crates/ra_assists/src/assists/add_missing_impl_members.rs +++ b/crates/ra_assists/src/assists/add_missing_impl_members.rs @@ -4,7 +4,7 @@ use ra_syntax::{ SmolStr, }; -use crate::{ast_editor::AstEditor, Assist, AssistCtx, AssistId}; +use crate::{Assist, AssistCtx, AssistId}; #[derive(PartialEq)] enum AddMissingImplMembersMode { @@ -79,14 +79,13 @@ fn add_missing_impl_members_inner( ast::ImplItem::FnDef(def) => edit::strip_attrs_and_docs(add_body(def).into()), _ => edit::strip_attrs_and_docs(it), }); - let mut ast_editor = AstEditor::new(impl_item_list); - - ast_editor.append_items(items); - - let first_new_item = ast_editor.ast().impl_items().nth(n_existing_items).unwrap(); - let cursor_position = first_new_item.syntax().text_range().start(); - ast_editor.into_text_edit(edit.text_edit_builder()); + let new_impl_item_list = impl_item_list.append_items(items); + let cursor_position = { + let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap(); + first_new_item.syntax().text_range().start() + }; + edit.replace_ast(impl_item_list, new_impl_item_list); edit.set_cursor(cursor_position); }); diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs index 60b8923e1..262e2fcf4 100644 --- a/crates/ra_assists/src/ast_editor.rs +++ b/crates/ra_assists/src/ast_editor.rs @@ -7,9 +7,7 @@ use ra_fmt::leading_indent; use ra_syntax::{ algo, ast::{self, make::tokens, TypeBoundsOwner}, - AstNode, Direction, InsertPosition, SyntaxElement, - SyntaxKind::*, - T, + AstNode, Direction, InsertPosition, SyntaxElement, T, }; use ra_text_edit::TextEditBuilder; @@ -67,38 +65,6 @@ impl AstEditor { let new_syntax = algo::replace_children(self.ast().syntax(), to_delete, &mut to_insert); N::cast(new_syntax).unwrap() } - - fn do_make_multiline(&mut self) { - let l_curly = - match self.ast().syntax().children_with_tokens().find(|it| it.kind() == T!['{']) { - Some(it) => it, - None => return, - }; - let sibling = match l_curly.next_sibling_or_token() { - Some(it) => it, - None => return, - }; - let existing_ws = match sibling.as_token() { - None => None, - Some(tok) if tok.kind() != WHITESPACE => None, - Some(ws) => { - if ws.text().contains('\n') { - return; - } - Some(ws.clone()) - } - }; - - let indent = leading_indent(self.ast().syntax()).unwrap_or("".into()); - let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); - let to_insert = iter::once(ws.ws().into()); - self.ast = match existing_ws { - None => self.insert_children(InsertPosition::After(l_curly), to_insert), - Some(ws) => { - self.replace_children(RangeInclusive::new(ws.clone().into(), ws.into()), to_insert) - } - }; - } } impl AstEditor { @@ -179,39 +145,6 @@ impl AstEditor { } } -impl AstEditor { - pub fn append_items(&mut self, items: impl Iterator) { - if !self.ast().syntax().text().contains_char('\n') { - self.do_make_multiline(); - } - items.for_each(|it| self.append_item(it)); - } - - pub fn append_item(&mut self, item: ast::ImplItem) { - let (indent, position) = match self.ast().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.ast().syntax()).unwrap_or_default(), - InsertPosition::After(it), - ), - None => return, - }, - }; - let ws = tokens::WsBuilder::new(&format!("\n{}", indent)); - let to_insert: ArrayVec<[SyntaxElement; 2]> = - [ws.ws().into(), item.syntax().clone().into()].into(); - self.ast = self.insert_children(position, to_insert.into_iter()); - } - - fn l_curly(&self) -> Option { - self.ast().syntax().children_with_tokens().find(|it| it.kind() == T!['{']) - } -} - impl AstEditor { pub fn remove_bounds(&mut self) -> &mut Self { let colon = match self.ast.colon_token() { 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_assists/src/assist_ctx.rs | 4 +- crates/ra_assists/src/ast_editor.rs | 101 ++--------------------------------- crates/ra_ide_api/src/diagnostics.rs | 10 ++-- crates/ra_syntax/src/algo.rs | 17 +++++- crates/ra_syntax/src/ast/edit.rs | 82 +++++++++++++++++++++++++++- 5 files changed, 106 insertions(+), 108 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index cbe12e908..5f564be0b 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -178,9 +178,7 @@ impl AssistBuilder { } pub(crate) fn replace_ast(&mut self, old: N, new: N) { - for (from, to) in algo::diff(old.syntax(), new.syntax()) { - self.edit.replace(from.text_range(), to.to_string()) - } + algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) } fn build(self) -> AssistAction { diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs index 262e2fcf4..54849b7b0 100644 --- a/crates/ra_assists/src/ast_editor.rs +++ b/crates/ra_assists/src/ast_editor.rs @@ -1,15 +1,12 @@ use std::{iter, ops::RangeInclusive}; -use arrayvec::ArrayVec; -use rustc_hash::FxHashMap; - -use ra_fmt::leading_indent; use ra_syntax::{ algo, - ast::{self, make::tokens, TypeBoundsOwner}, - AstNode, Direction, InsertPosition, SyntaxElement, T, + ast::{self, TypeBoundsOwner}, + AstNode, SyntaxElement, }; use ra_text_edit::TextEditBuilder; +use rustc_hash::FxHashMap; pub struct AstEditor { original_ast: N, @@ -25,9 +22,7 @@ impl AstEditor { } pub fn into_text_edit(self, builder: &mut TextEditBuilder) { - for (from, to) in algo::diff(&self.original_ast.syntax(), self.ast().syntax()) { - builder.replace(from.text_range(), to.to_string()) - } + algo::diff(&self.original_ast.syntax(), self.ast().syntax()).into_text_edit(builder) } pub fn ast(&self) -> &N { @@ -46,16 +41,6 @@ impl AstEditor { self } - #[must_use] - fn insert_children( - &self, - position: InsertPosition, - mut to_insert: impl Iterator, - ) -> N { - let new_syntax = algo::insert_children(self.ast().syntax(), position, &mut to_insert); - N::cast(new_syntax).unwrap() - } - #[must_use] fn replace_children( &self, @@ -67,84 +52,6 @@ impl AstEditor { } } -impl AstEditor { - pub fn append_field(&mut self, field: &ast::RecordField) { - self.insert_field(InsertPosition::Last, field) - } - - pub fn insert_field( - &mut self, - position: InsertPosition<&'_ ast::RecordField>, - field: &ast::RecordField, - ) { - let is_multiline = self.ast().syntax().text().contains_char('\n'); - let ws; - let space = if is_multiline { - ws = tokens::WsBuilder::new(&format!( - "\n{} ", - leading_indent(self.ast().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, - }; - 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.ast().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), - }; - - self.ast = self.insert_children(position, to_insert.iter().cloned()); - } - - fn l_curly(&self) -> Option { - self.ast().syntax().children_with_tokens().find(|it| it.kind() == T!['{']) - } -} - impl AstEditor { pub fn remove_bounds(&mut self) -> &mut Self { let colon = match self.ast.colon_token() { diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 144bc0a70..4fa07e3dc 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs @@ -2,10 +2,10 @@ use std::cell::RefCell; use hir::diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSink}; use itertools::Itertools; -use ra_assists::ast_editor::AstEditor; use ra_db::SourceDatabase; use ra_prof::profile; use ra_syntax::{ + algo, ast::{self, make, AstNode}, Location, SyntaxNode, TextRange, T, }; @@ -56,15 +56,15 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec }) }) .on::(|d| { - let node = d.ast(db); - let mut ast_editor = AstEditor::new(node); + let mut field_list = d.ast(db); for f in d.missed_fields.iter() { let field = make::record_field(make::name_ref(&f.to_string()), Some(make::expr_unit())); - ast_editor.append_field(&field); + field_list = field_list.append_field(&field); } let mut builder = TextEditBuilder::default(); - ast_editor.into_text_edit(&mut builder); + algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); + let fix = SourceChange::source_file_edit_from("fill struct fields", file_id, builder.finish()); res.borrow_mut().push(Diagnostic { diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index 46680a08f..f33d2ad4e 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs @@ -3,6 +3,7 @@ pub mod visit; use std::ops::RangeInclusive; use itertools::Itertools; +use ra_text_edit::TextEditBuilder; use rustc_hash::FxHashMap; use crate::{ @@ -63,6 +64,18 @@ pub enum InsertPosition { After(T), } +pub struct TreeDiff { + replacements: FxHashMap, +} + +impl TreeDiff { + pub fn into_text_edit(&self, builder: &mut TextEditBuilder) { + for (from, to) in self.replacements.iter() { + builder.replace(from.text_range(), to.to_string()) + } + } +} + /// Finds minimal the diff, which, applied to `from`, will result in `to`. /// /// Specifically, returns a map whose keys are descendants of `from` and values @@ -70,12 +83,12 @@ pub enum InsertPosition { /// /// A trivial solution is a singletom map `{ from: to }`, but this function /// tries to find a more fine-grained diff. -pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> FxHashMap { +pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { let mut buf = FxHashMap::default(); // FIXME: this is both horrible inefficient and gives larger than // necessary diff. I bet there's a cool algorithm to diff trees properly. go(&mut buf, from.clone().into(), to.clone().into()); - return buf; + return TreeDiff { replacements: buf }; fn go( buf: &mut FxHashMap, 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_assists/src/assists/move_bounds.rs | 3 +-- crates/ra_assists/src/ast_editor.rs | 15 --------------- crates/ra_syntax/src/ast/edit.rs | 16 +++++++++++++++- 3 files changed, 16 insertions(+), 18 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/assists/move_bounds.rs b/crates/ra_assists/src/assists/move_bounds.rs index fd4bdc55c..1d27832a3 100644 --- a/crates/ra_assists/src/assists/move_bounds.rs +++ b/crates/ra_assists/src/assists/move_bounds.rs @@ -39,8 +39,7 @@ pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx) .type_params() .filter(|it| it.type_bound_list().is_some()) .map(|type_param| { - let without_bounds = - AstEditor::new(type_param.clone()).remove_bounds().ast().clone(); + let without_bounds = type_param.remove_bounds(); (type_param, without_bounds) }); diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs index 54849b7b0..69abf28a1 100644 --- a/crates/ra_assists/src/ast_editor.rs +++ b/crates/ra_assists/src/ast_editor.rs @@ -51,18 +51,3 @@ impl AstEditor { N::cast(new_syntax).unwrap() } } - -impl AstEditor { - pub fn remove_bounds(&mut self) -> &mut Self { - let colon = match self.ast.colon_token() { - Some(it) => it, - None => return self, - }; - let end = match self.ast.type_bound_list() { - Some(it) => it.syntax().clone().into(), - None => colon.clone().into(), - }; - self.ast = self.replace_children(RangeInclusive::new(colon.into(), end), iter::empty()); - self - } -} 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_assists/src/assists/move_bounds.rs | 9 +++-- crates/ra_assists/src/ast_editor.rs | 53 ---------------------------- crates/ra_assists/src/lib.rs | 1 - crates/ra_syntax/src/ast/edit.rs | 12 +++++++ 4 files changed, 16 insertions(+), 59 deletions(-) delete mode 100644 crates/ra_assists/src/ast_editor.rs (limited to 'crates') diff --git a/crates/ra_assists/src/assists/move_bounds.rs b/crates/ra_assists/src/assists/move_bounds.rs index 1d27832a3..39ff51233 100644 --- a/crates/ra_assists/src/assists/move_bounds.rs +++ b/crates/ra_assists/src/assists/move_bounds.rs @@ -1,11 +1,11 @@ use hir::db::HirDatabase; use ra_syntax::{ - ast::{self, make, AstNode, NameOwner, TypeBoundsOwner}, + ast::{self, edit, make, AstNode, NameOwner, TypeBoundsOwner}, SyntaxElement, SyntaxKind::*, }; -use crate::{ast_editor::AstEditor, Assist, AssistCtx, AssistId}; +use crate::{Assist, AssistCtx, AssistId}; pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx) -> Option { let type_param_list = ctx.node_at_offset::()?; @@ -43,9 +43,8 @@ pub(crate) fn move_bounds_to_where_clause(mut ctx: AssistCtx) (type_param, without_bounds) }); - let mut ast_editor = AstEditor::new(type_param_list.clone()); - ast_editor.replace_descendants(new_params); - ast_editor.into_text_edit(edit.text_edit_builder()); + let new_type_param_list = edit::replace_descendants(&type_param_list, new_params); + edit.replace_ast(type_param_list.clone(), new_type_param_list); let where_clause = { let predicates = type_param_list.type_params().filter_map(build_predicate); diff --git a/crates/ra_assists/src/ast_editor.rs b/crates/ra_assists/src/ast_editor.rs deleted file mode 100644 index 69abf28a1..000000000 --- a/crates/ra_assists/src/ast_editor.rs +++ /dev/null @@ -1,53 +0,0 @@ -use std::{iter, ops::RangeInclusive}; - -use ra_syntax::{ - algo, - ast::{self, TypeBoundsOwner}, - AstNode, SyntaxElement, -}; -use ra_text_edit::TextEditBuilder; -use rustc_hash::FxHashMap; - -pub struct AstEditor { - original_ast: N, - ast: N, -} - -impl AstEditor { - pub fn new(node: N) -> AstEditor - where - N: Clone, - { - AstEditor { original_ast: node.clone(), ast: node } - } - - pub fn into_text_edit(self, builder: &mut TextEditBuilder) { - algo::diff(&self.original_ast.syntax(), self.ast().syntax()).into_text_edit(builder) - } - - pub fn ast(&self) -> &N { - &self.ast - } - - pub fn replace_descendants( - &mut self, - replacement_map: impl Iterator, - ) -> &mut Self { - let map = replacement_map - .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into())) - .collect::>(); - let new_syntax = algo::replace_descendants(self.ast.syntax(), &map); - self.ast = N::cast(new_syntax).unwrap(); - self - } - - #[must_use] - fn replace_children( - &self, - to_delete: RangeInclusive, - mut to_insert: impl Iterator, - ) -> N { - let new_syntax = algo::replace_children(self.ast().syntax(), to_delete, &mut to_insert); - N::cast(new_syntax).unwrap() - } -} diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 3ca3320f7..91b2a1dce 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -7,7 +7,6 @@ mod assist_ctx; mod marks; -pub mod ast_editor; use hir::db::HirDatabase; use itertools::Itertools; 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_assists/src/assists/add_missing_impl_members.rs | 11 +++++++---- crates/ra_syntax/src/ast/edit.rs | 5 ++++- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'crates') diff --git a/crates/ra_assists/src/assists/add_missing_impl_members.rs b/crates/ra_assists/src/assists/add_missing_impl_members.rs index c2e3eb06b..6fd1c3753 100644 --- a/crates/ra_assists/src/assists/add_missing_impl_members.rs +++ b/crates/ra_assists/src/assists/add_missing_impl_members.rs @@ -75,10 +75,13 @@ fn add_missing_impl_members_inner( ctx.add_action(AssistId(assist_id), label, |edit| { let n_existing_items = impl_item_list.impl_items().count(); - let items = missing_items.into_iter().map(|it| match it { - ast::ImplItem::FnDef(def) => edit::strip_attrs_and_docs(add_body(def).into()), - _ => edit::strip_attrs_and_docs(it), - }); + let items = missing_items + .into_iter() + .map(|it| match it { + ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), + _ => it, + }) + .map(|it| edit::strip_attrs_and_docs(&it)); let new_impl_item_list = impl_item_list.append_items(items); let cursor_position = { let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap(); 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