mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-20 19:52:48 +00:00
move field list to ast/edit.rs
This commit is contained in:
parent
0840ec038b
commit
e010b144d5
@ -178,9 +178,7 @@ impl AssistBuilder {
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
|
||||
}
|
||||
|
||||
fn build(self) -> AssistAction {
|
||||
|
@ -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<N: AstNode> {
|
||||
original_ast: N,
|
||||
@ -25,9 +22,7 @@ impl<N: AstNode> AstEditor<N> {
|
||||
}
|
||||
|
||||
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<N: AstNode> AstEditor<N> {
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn insert_children(
|
||||
&self,
|
||||
position: InsertPosition<SyntaxElement>,
|
||||
mut to_insert: impl Iterator<Item = SyntaxElement>,
|
||||
) -> 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<N: AstNode> AstEditor<N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl AstEditor<ast::RecordFieldList> {
|
||||
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<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() {
|
||||
|
@ -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<Diagnostic>
|
||||
})
|
||||
})
|
||||
.on::<hir::diagnostics::MissingFields, _>(|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 {
|
||||
|
@ -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<T> {
|
||||
After(T),
|
||||
}
|
||||
|
||||
pub struct TreeDiff {
|
||||
replacements: FxHashMap<SyntaxElement, SyntaxElement>,
|
||||
}
|
||||
|
||||
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<T> {
|
||||
///
|
||||
/// 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<SyntaxElement, SyntaxElement> {
|
||||
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<SyntaxElement, SyntaxElement>,
|
||||
|
@ -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<SyntaxElement> {
|
||||
self.syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn strip_attrs_and_docs<N: ast::AttrsOwner>(node: N) -> N {
|
||||
N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user