mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 13:25:09 +00:00
migrate add impl items to the new editing API
This commit is contained in:
parent
5dbbfda34a
commit
0840ec038b
4 changed files with 109 additions and 80 deletions
|
@ -2,7 +2,7 @@ use hir::db::HirDatabase;
|
||||||
use ra_db::FileRange;
|
use ra_db::FileRange;
|
||||||
use ra_fmt::{leading_indent, reindent};
|
use ra_fmt::{leading_indent, reindent};
|
||||||
use ra_syntax::{
|
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,
|
AstNode, SourceFile, SyntaxElement, SyntaxNode, SyntaxToken, TextRange, TextUnit,
|
||||||
TokenAtOffset,
|
TokenAtOffset,
|
||||||
};
|
};
|
||||||
|
@ -177,6 +177,12 @@ impl AssistBuilder {
|
||||||
&mut self.edit
|
&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 {
|
fn build(self) -> AssistAction {
|
||||||
AssistAction {
|
AssistAction {
|
||||||
edit: self.edit.finish(),
|
edit: self.edit.finish(),
|
||||||
|
|
|
@ -4,7 +4,7 @@ use ra_syntax::{
|
||||||
SmolStr,
|
SmolStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{ast_editor::AstEditor, Assist, AssistCtx, AssistId};
|
use crate::{Assist, AssistCtx, AssistId};
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum AddMissingImplMembersMode {
|
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()),
|
ast::ImplItem::FnDef(def) => edit::strip_attrs_and_docs(add_body(def).into()),
|
||||||
_ => edit::strip_attrs_and_docs(it),
|
_ => edit::strip_attrs_and_docs(it),
|
||||||
});
|
});
|
||||||
let mut ast_editor = AstEditor::new(impl_item_list);
|
let new_impl_item_list = impl_item_list.append_items(items);
|
||||||
|
let cursor_position = {
|
||||||
ast_editor.append_items(items);
|
let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap();
|
||||||
|
first_new_item.syntax().text_range().start()
|
||||||
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());
|
|
||||||
|
|
||||||
|
edit.replace_ast(impl_item_list, new_impl_item_list);
|
||||||
edit.set_cursor(cursor_position);
|
edit.set_cursor(cursor_position);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,7 @@ use ra_fmt::leading_indent;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
algo,
|
algo,
|
||||||
ast::{self, make::tokens, TypeBoundsOwner},
|
ast::{self, make::tokens, TypeBoundsOwner},
|
||||||
AstNode, Direction, InsertPosition, SyntaxElement,
|
AstNode, Direction, InsertPosition, SyntaxElement, T,
|
||||||
SyntaxKind::*,
|
|
||||||
T,
|
|
||||||
};
|
};
|
||||||
use ra_text_edit::TextEditBuilder;
|
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);
|
let new_syntax = algo::replace_children(self.ast().syntax(), to_delete, &mut to_insert);
|
||||||
N::cast(new_syntax).unwrap()
|
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> {
|
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> {
|
impl AstEditor<ast::TypeParam> {
|
||||||
pub fn remove_bounds(&mut self) -> &mut Self {
|
pub fn remove_bounds(&mut self) -> &mut Self {
|
||||||
let colon = match self.ast.colon_token() {
|
let colon = match self.ast.colon_token() {
|
||||||
|
|
|
@ -7,10 +7,14 @@ use arrayvec::ArrayVec;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
algo,
|
algo,
|
||||||
ast::{self, make, AstNode},
|
ast::{
|
||||||
InsertPosition, SyntaxElement,
|
self,
|
||||||
|
make::{self, tokens},
|
||||||
|
AstNode,
|
||||||
|
},
|
||||||
|
AstToken, InsertPosition, SmolStr, SyntaxElement,
|
||||||
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
|
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
|
||||||
SyntaxNode,
|
SyntaxNode, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl ast::FnDef {
|
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 {
|
pub fn strip_attrs_and_docs<N: ast::AttrsOwner>(node: N) -> N {
|
||||||
N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap()
|
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
|
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]
|
#[must_use]
|
||||||
fn insert_children<N: AstNode>(
|
fn insert_children<N: AstNode>(
|
||||||
parent: &N,
|
parent: &N,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue