add fix for removing unnecessary braces in use statements

This commit is contained in:
gfreezy 2018-12-26 00:45:13 +08:00
parent 5fb426cb9e
commit 72eb9de747
5 changed files with 69 additions and 62 deletions

View file

@ -3,31 +3,32 @@ use std::{
sync::Arc,
};
use ra_editor::{self, find_node_at_offset, FileSymbol, LineIndex, LocalEdit, Severity};
use rayon::prelude::*;
use salsa::{Database, ParallelDatabase};
use hir::{
self,
FnSignatureInfo,
Problem,
source_binder,
};
use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase};
use ra_editor::{self, FileSymbol, find_node_at_offset, LineIndex, LocalEdit, Severity};
use ra_syntax::{
ast::{self, ArgListOwner, Expr, NameOwner, FnDef},
algo::find_covering_node,
ast::{self, ArgListOwner, Expr, FnDef, NameOwner},
AstNode, SourceFileNode,
SyntaxKind::*,
SyntaxNodeRef, TextRange, TextUnit,
};
use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase};
use rayon::prelude::*;
use salsa::{Database, ParallelDatabase};
use hir::{
self,
source_binder,
FnSignatureInfo,
Problem,
};
use crate::{
completion::{completions, CompletionItem},
db,
symbol_index::{SymbolIndex, SymbolsDatabase, LibrarySymbolsQuery},
AnalysisChange, RootChange, Cancelable, CrateId, Diagnostic, FileId,
FileSystemEdit, FilePosition, Query, SourceChange, SourceFileEdit,
ReferenceResolution,
AnalysisChange,
Cancelable,
completion::{CompletionItem, completions},
CrateId, db, Diagnostic, FileId, FilePosition, FileSystemEdit,
Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit,
symbol_index::{LibrarySymbolsQuery, SymbolIndex, SymbolsDatabase},
};
#[derive(Debug, Default)]
@ -366,7 +367,7 @@ impl AnalysisImpl {
range: d.range,
message: d.msg,
severity: d.severity,
fix: None,
fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)),
})
.collect::<Vec<_>>();
if let Some(m) = source_binder::module_from_file_id(&*self.db, file_id)? {
@ -425,25 +426,14 @@ impl AnalysisImpl {
let file = self.file_syntax(file_id);
let offset = range.start();
let actions = vec![
(
"flip comma",
ra_editor::flip_comma(&file, offset).map(|f| f()),
),
(
"add `#[derive]`",
ra_editor::add_derive(&file, offset).map(|f| f()),
),
("add impl", ra_editor::add_impl(&file, offset).map(|f| f())),
(
"introduce variable",
ra_editor::introduce_variable(&file, range).map(|f| f()),
),
ra_editor::flip_comma(&file, offset).map(|f| f()),
ra_editor::add_derive(&file, offset).map(|f| f()),
ra_editor::add_impl(&file, offset).map(|f| f()),
ra_editor::introduce_variable(&file, range).map(|f| f()),
];
actions
.into_iter()
.filter_map(|(name, local_edit)| {
Some(SourceChange::from_local_edit(file_id, name, local_edit?))
})
.filter_map(|local_edit| Some(SourceChange::from_local_edit(file_id, local_edit?)))
.collect()
}
@ -541,13 +531,13 @@ impl AnalysisImpl {
}
impl SourceChange {
pub(crate) fn from_local_edit(file_id: FileId, label: &str, edit: LocalEdit) -> SourceChange {
pub(crate) fn from_local_edit(file_id: FileId, edit: LocalEdit) -> SourceChange {
let file_edit = SourceFileEdit {
file_id,
edit: edit.edit,
};
SourceChange {
label: label.to_string(),
label: edit.label,
source_file_edits: vec![file_edit],
file_system_edits: vec![],
cursor_position: edit

View file

@ -288,19 +288,18 @@ impl Analysis {
}
pub fn join_lines(&self, file_id: FileId, range: TextRange) -> SourceChange {
let file = self.imp.file_syntax(file_id);
SourceChange::from_local_edit(file_id, "join lines", ra_editor::join_lines(&file, range))
SourceChange::from_local_edit(file_id, ra_editor::join_lines(&file, range))
}
pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> {
let file = self.imp.file_syntax(position.file_id);
let edit = ra_editor::on_enter(&file, position.offset)?;
let res = SourceChange::from_local_edit(position.file_id, "on enter", edit);
let res = SourceChange::from_local_edit(position.file_id, edit);
Some(res)
}
pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> {
let file = self.imp.file_syntax(position.file_id);
Some(SourceChange::from_local_edit(
position.file_id,
"add semicolon",
ra_editor::on_eq_typed(&file, position.offset)?,
))
}

View file

@ -12,6 +12,7 @@ use crate::{find_node_at_offset, TextEdit, TextEditBuilder};
#[derive(Debug)]
pub struct LocalEdit {
pub label: String,
pub edit: TextEdit,
pub cursor_position: Option<TextUnit>,
}
@ -30,6 +31,7 @@ pub fn flip_comma<'a>(
edit.replace(prev.range(), next.text().to_string());
edit.replace(next.range(), prev.text().to_string());
LocalEdit {
label: "flip comma".to_string(),
edit: edit.finish(),
cursor_position: None,
}
@ -58,6 +60,7 @@ pub fn add_derive<'a>(
Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'),
};
LocalEdit {
label: "add `#[derive]`".to_string(),
edit: edit.finish(),
cursor_position: Some(offset),
}
@ -109,6 +112,7 @@ pub fn add_impl<'a>(
buf.push_str("\n}");
edit.insert(start_offset, buf);
LocalEdit {
label: "add impl".to_string(),
edit: edit.finish(),
cursor_position: Some(offset),
}
@ -148,6 +152,7 @@ pub fn introduce_variable<'a>(
}
let cursor_position = anchor_stmt.range().start() + TextUnit::of_str("let ");
LocalEdit {
label: "introduce variable".to_string(),
edit: edit.finish(),
cursor_position: Some(cursor_position),
}
@ -194,6 +199,7 @@ pub fn make_pub_crate<'a>(
|| parent.children().any(|child| child.kind() == VISIBILITY)
{
return LocalEdit {
label: "make pub crate".to_string(),
edit: edit.finish(),
cursor_position: Some(offset),
};
@ -201,6 +207,7 @@ pub fn make_pub_crate<'a>(
edit.insert(node_start, "pub(crate) ".to_string());
LocalEdit {
label: "make pub crate".to_string(),
edit: edit.finish(),
cursor_position: Some(node_start),
}

View file

@ -42,6 +42,7 @@ pub struct Diagnostic {
pub range: TextRange,
pub msg: String,
pub severity: Severity,
pub fix: Option<LocalEdit>,
}
#[derive(Debug)]
@ -111,6 +112,7 @@ pub fn diagnostics(file: &SourceFileNode) -> Vec<Diagnostic> {
range: location_to_range(err.location()),
msg: format!("Syntax Error: {}", err),
severity: Severity::Error,
fix: None,
})
.collect();
@ -124,11 +126,27 @@ fn check_unnecessary_braces_in_use_statement(file: &SourceFileNode) -> Vec<Diagn
let mut diagnostics = Vec::new();
for node in file.syntax().descendants() {
if let Some(use_tree_list) = ast::UseTreeList::cast(node) {
if use_tree_list.use_trees().count() <= 1 {
if use_tree_list.use_trees().count() == 1 {
let range = use_tree_list.syntax().range();
// use_tree_list always has one child, so we use unwrap directly here.
let to_replace = typing::single_use_tree(use_tree_list)
.unwrap()
.syntax()
.text()
.to_string();
let mut edit_builder = TextEditBuilder::new();
edit_builder.delete(range);
edit_builder.insert(range.start(), to_replace);
diagnostics.push(Diagnostic {
range: use_tree_list.syntax().range(),
range: range,
msg: format!("Unnecessary braces in use statement"),
severity: Severity::WeakWarning,
fix: Some(LocalEdit {
label: "Remove unnecessary braces".to_string(),
edit: edit_builder.finish(),
cursor_position: None,
}),
})
}
}
@ -250,9 +268,7 @@ fn main() {}
);
let diagnostics = check_unnecessary_braces_in_use_statement(&file);
assert_eq_dbg(
r#"[Diagnostic { range: [12; 15), msg: "Unnecessary braces in use statement", severity: WeakWarning },
Diagnostic { range: [24; 27), msg: "Unnecessary braces in use statement", severity: WeakWarning },
Diagnostic { range: [61; 64), msg: "Unnecessary braces in use statement", severity: WeakWarning }]"#,
"[Diagnostic { range: [12; 15), msg: \"Unnecessary braces in use statement\", severity: WeakWarning, fix: Some(LocalEdit { label: \"Remove unnecessary braces\", edit: TextEdit { atoms: [AtomTextEdit { delete: [12; 12), insert: \"b\" }, AtomTextEdit { delete: [12; 15), insert: \"\" }] }, cursor_position: Some(12) }) }, Diagnostic { range: [24; 27), msg: \"Unnecessary braces in use statement\", severity: WeakWarning, fix: Some(LocalEdit { label: \"Remove unnecessary braces\", edit: TextEdit { atoms: [AtomTextEdit { delete: [24; 24), insert: \"c\" }, AtomTextEdit { delete: [24; 27), insert: \"\" }] }, cursor_position: Some(24) }) }, Diagnostic { range: [61; 64), msg: \"Unnecessary braces in use statement\", severity: WeakWarning, fix: Some(LocalEdit { label: \"Remove unnecessary braces\", edit: TextEdit { atoms: [AtomTextEdit { delete: [61; 61), insert: \"e\" }, AtomTextEdit { delete: [61; 64), insert: \"\" }] }, cursor_position: Some(61) }) }]",
&diagnostics,
)
}

View file

@ -8,7 +8,9 @@ use ra_syntax::{
SyntaxKind::*,
SyntaxNodeRef, TextRange, TextUnit,
};
use ra_text_edit::text_utils::contains_offset_nonstrict;
use ra_text_edit::text_utils::{
contains_offset_nonstrict
};
use crate::{find_node_at_offset, TextEditBuilder, LocalEdit};
@ -19,6 +21,7 @@ pub fn join_lines(file: &SourceFileNode, range: TextRange) -> LocalEdit {
let pos = match text.find('\n') {
None => {
return LocalEdit {
label: "join lines".to_string(),
edit: TextEditBuilder::new().finish(),
cursor_position: None,
};
@ -51,6 +54,7 @@ pub fn join_lines(file: &SourceFileNode, range: TextRange) -> LocalEdit {
}
LocalEdit {
label: "join lines".to_string(),
edit: edit.finish(),
cursor_position: None,
}
@ -76,6 +80,7 @@ pub fn on_enter(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> {
let mut edit = TextEditBuilder::new();
edit.insert(offset, inserted);
Some(LocalEdit {
label: "on enter".to_string(),
edit: edit.finish(),
cursor_position: Some(cursor_position),
})
@ -126,6 +131,7 @@ pub fn on_eq_typed(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit>
let mut edit = TextEditBuilder::new();
edit.insert(offset, ";".to_string());
Some(LocalEdit {
label: "add semicolon".to_string(),
edit: edit.finish(),
cursor_position: None,
})
@ -248,24 +254,13 @@ fn join_single_use_tree(edit: &mut TextEditBuilder, node: SyntaxNodeRef) -> Opti
Some(())
}
fn single_use_tree(tree_list: ast::UseTreeList) -> Option<ast::UseTree> {
let mut res = None;
for child in tree_list.syntax().children() {
if let Some(tree) = ast::UseTree::cast(child) {
if tree.syntax().text().contains('\n') {
return None;
}
if mem::replace(&mut res, Some(tree)).is_some() {
return None;
}
} else {
match child.kind() {
WHITESPACE | L_CURLY | R_CURLY | COMMA => (),
_ => return None,
}
}
pub(crate) fn single_use_tree(tree_list: ast::UseTreeList) -> Option<ast::UseTree> {
let sub_use_trees = tree_list.use_trees().count();
if sub_use_trees != 1 {
return None;
}
res
tree_list.use_trees().next()
}
fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str {