mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 16:54:01 +00:00
migrate add impl items to the new editing API
This commit is contained in:
parent
5dbbfda34a
commit
0840ec038b
@ -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<N: AstNode>(&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(),
|
||||
|
@ -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);
|
||||
});
|
||||
|
||||
|
@ -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<N: AstNode> AstEditor<N> {
|
||||
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<ast::RecordFieldList> {
|
||||
@ -179,39 +145,6 @@ impl AstEditor<ast::RecordFieldList> {
|
||||
}
|
||||
}
|
||||
|
||||
impl AstEditor<ast::ItemList> {
|
||||
pub fn append_items(&mut self, items: impl Iterator<Item = ast::ImplItem>) {
|
||||
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<SyntaxElement> {
|
||||
self.ast().syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
|
||||
}
|
||||
}
|
||||
|
||||
impl AstEditor<ast::TypeParam> {
|
||||
pub fn remove_bounds(&mut self) -> &mut Self {
|
||||
let colon = match self.ast.colon_token() {
|
||||
|
@ -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<Item = ast::ImplItem>) -> 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<SyntaxElement> {
|
||||
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<N: ast::AttrsOwner>(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<SmolStr> {
|
||||
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<N: AstNode>(
|
||||
parent: &N,
|
||||
|
Loading…
Reference in New Issue
Block a user