aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_editor/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_editor/src')
-rw-r--r--crates/ra_editor/src/code_actions.rs7
-rw-r--r--crates/ra_editor/src/lib.rs90
-rw-r--r--crates/ra_editor/src/typing.rs27
3 files changed, 84 insertions, 40 deletions
diff --git a/crates/ra_editor/src/code_actions.rs b/crates/ra_editor/src/code_actions.rs
index 1d78cb7e8..7615f37a6 100644
--- a/crates/ra_editor/src/code_actions.rs
+++ b/crates/ra_editor/src/code_actions.rs
@@ -12,6 +12,7 @@ use crate::{find_node_at_offset, TextEdit, TextEditBuilder};
12 12
13#[derive(Debug)] 13#[derive(Debug)]
14pub struct LocalEdit { 14pub struct LocalEdit {
15 pub label: String,
15 pub edit: TextEdit, 16 pub edit: TextEdit,
16 pub cursor_position: Option<TextUnit>, 17 pub cursor_position: Option<TextUnit>,
17} 18}
@@ -30,6 +31,7 @@ pub fn flip_comma<'a>(
30 edit.replace(prev.range(), next.text().to_string()); 31 edit.replace(prev.range(), next.text().to_string());
31 edit.replace(next.range(), prev.text().to_string()); 32 edit.replace(next.range(), prev.text().to_string());
32 LocalEdit { 33 LocalEdit {
34 label: "flip comma".to_string(),
33 edit: edit.finish(), 35 edit: edit.finish(),
34 cursor_position: None, 36 cursor_position: None,
35 } 37 }
@@ -58,6 +60,7 @@ pub fn add_derive<'a>(
58 Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'), 60 Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'),
59 }; 61 };
60 LocalEdit { 62 LocalEdit {
63 label: "add `#[derive]`".to_string(),
61 edit: edit.finish(), 64 edit: edit.finish(),
62 cursor_position: Some(offset), 65 cursor_position: Some(offset),
63 } 66 }
@@ -109,6 +112,7 @@ pub fn add_impl<'a>(
109 buf.push_str("\n}"); 112 buf.push_str("\n}");
110 edit.insert(start_offset, buf); 113 edit.insert(start_offset, buf);
111 LocalEdit { 114 LocalEdit {
115 label: "add impl".to_string(),
112 edit: edit.finish(), 116 edit: edit.finish(),
113 cursor_position: Some(offset), 117 cursor_position: Some(offset),
114 } 118 }
@@ -148,6 +152,7 @@ pub fn introduce_variable<'a>(
148 } 152 }
149 let cursor_position = anchor_stmt.range().start() + TextUnit::of_str("let "); 153 let cursor_position = anchor_stmt.range().start() + TextUnit::of_str("let ");
150 LocalEdit { 154 LocalEdit {
155 label: "introduce variable".to_string(),
151 edit: edit.finish(), 156 edit: edit.finish(),
152 cursor_position: Some(cursor_position), 157 cursor_position: Some(cursor_position),
153 } 158 }
@@ -194,6 +199,7 @@ pub fn make_pub_crate<'a>(
194 || parent.children().any(|child| child.kind() == VISIBILITY) 199 || parent.children().any(|child| child.kind() == VISIBILITY)
195 { 200 {
196 return LocalEdit { 201 return LocalEdit {
202 label: "make pub crate".to_string(),
197 edit: edit.finish(), 203 edit: edit.finish(),
198 cursor_position: Some(offset), 204 cursor_position: Some(offset),
199 }; 205 };
@@ -201,6 +207,7 @@ pub fn make_pub_crate<'a>(
201 207
202 edit.insert(node_start, "pub(crate) ".to_string()); 208 edit.insert(node_start, "pub(crate) ".to_string());
203 LocalEdit { 209 LocalEdit {
210 label: "make pub crate".to_string(),
204 edit: edit.finish(), 211 edit: edit.finish(),
205 cursor_position: Some(node_start), 212 cursor_position: Some(node_start),
206 } 213 }
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs
index 619497f0b..d9b89155b 100644
--- a/crates/ra_editor/src/lib.rs
+++ b/crates/ra_editor/src/lib.rs
@@ -26,6 +26,7 @@ use ra_syntax::{
26 SyntaxKind::{self, *}, 26 SyntaxKind::{self, *},
27 SyntaxNodeRef, TextRange, TextUnit, 27 SyntaxNodeRef, TextRange, TextUnit,
28}; 28};
29use itertools::Itertools;
29 30
30#[derive(Debug)] 31#[derive(Debug)]
31pub struct HighlightedRange { 32pub struct HighlightedRange {
@@ -44,6 +45,7 @@ pub struct Diagnostic {
44 pub range: TextRange, 45 pub range: TextRange,
45 pub msg: String, 46 pub msg: String,
46 pub severity: Severity, 47 pub severity: Severity,
48 pub fix: Option<LocalEdit>,
47} 49}
48 50
49#[derive(Debug)] 51#[derive(Debug)]
@@ -113,6 +115,7 @@ pub fn diagnostics(file: &SourceFileNode) -> Vec<Diagnostic> {
113 range: location_to_range(err.location()), 115 range: location_to_range(err.location()),
114 msg: format!("Syntax Error: {}", err), 116 msg: format!("Syntax Error: {}", err),
115 severity: Severity::Error, 117 severity: Severity::Error,
118 fix: None,
116 }) 119 })
117 .collect(); 120 .collect();
118 121
@@ -126,11 +129,28 @@ fn check_unnecessary_braces_in_use_statement(file: &SourceFileNode) -> Vec<Diagn
126 let mut diagnostics = Vec::new(); 129 let mut diagnostics = Vec::new();
127 for node in file.syntax().descendants() { 130 for node in file.syntax().descendants() {
128 if let Some(use_tree_list) = ast::UseTreeList::cast(node) { 131 if let Some(use_tree_list) = ast::UseTreeList::cast(node) {
129 if use_tree_list.use_trees().count() <= 1 { 132 if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
133 let range = use_tree_list.syntax().range();
134 let edit = text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
135 single_use_tree,
136 )
137 .unwrap_or_else(|| {
138 let to_replace = single_use_tree.syntax().text().to_string();
139 let mut edit_builder = TextEditBuilder::new();
140 edit_builder.delete(range);
141 edit_builder.insert(range.start(), to_replace);
142 edit_builder.finish()
143 });
144
130 diagnostics.push(Diagnostic { 145 diagnostics.push(Diagnostic {
131 range: use_tree_list.syntax().range(), 146 range: range,
132 msg: format!("Unnecessary braces in use statement"), 147 msg: format!("Unnecessary braces in use statement"),
133 severity: Severity::WeakWarning, 148 severity: Severity::WeakWarning,
149 fix: Some(LocalEdit {
150 label: "Remove unnecessary braces".to_string(),
151 edit: edit,
152 cursor_position: None,
153 }),
134 }) 154 })
135 } 155 }
136 } 156 }
@@ -139,6 +159,28 @@ fn check_unnecessary_braces_in_use_statement(file: &SourceFileNode) -> Vec<Diagn
139 diagnostics 159 diagnostics
140} 160}
141 161
162fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
163 single_use_tree: ast::UseTree,
164) -> Option<TextEdit> {
165 let use_tree_list_node = single_use_tree.syntax().parent()?;
166 if single_use_tree
167 .path()?
168 .segment()?
169 .syntax()
170 .first_child()?
171 .kind()
172 == SyntaxKind::SELF_KW
173 {
174 let start = use_tree_list_node.prev_sibling()?.range().start();
175 let end = use_tree_list_node.range().end();
176 let range = TextRange::from_to(start, end);
177 let mut edit_builder = TextEditBuilder::new();
178 edit_builder.delete(range);
179 return Some(edit_builder.finish());
180 }
181 None
182}
183
142pub fn syntax_tree(file: &SourceFileNode) -> String { 184pub fn syntax_tree(file: &SourceFileNode) -> String {
143 ::ra_syntax::utils::dump_tree(file.syntax()) 185 ::ra_syntax::utils::dump_tree(file.syntax())
144} 186}
@@ -175,8 +217,9 @@ pub fn find_node_at_offset<'a, N: AstNode<'a>>(
175 217
176#[cfg(test)] 218#[cfg(test)]
177mod tests { 219mod tests {
220 use crate::test_utils::{add_cursor, assert_eq_dbg, assert_eq_text, extract_offset};
221
178 use super::*; 222 use super::*;
179 use crate::test_utils::{add_cursor, assert_eq_dbg, extract_offset, assert_eq_text};
180 223
181 #[test] 224 #[test]
182 fn test_highlighting() { 225 fn test_highlighting() {
@@ -240,22 +283,31 @@ fn test_foo() {}
240 283
241 #[test] 284 #[test]
242 fn test_check_unnecessary_braces_in_use_statement() { 285 fn test_check_unnecessary_braces_in_use_statement() {
243 let file = SourceFileNode::parse( 286 fn check_not_applicable(code: &str) {
244 r#" 287 let file = SourceFileNode::parse(code);
245use a; 288 let diagnostics = check_unnecessary_braces_in_use_statement(&file);
246use {b}; 289 assert!(diagnostics.is_empty());
247use a::{c}; 290 }
248use a::{c, d::e}; 291
249use a::{c, d::{e}}; 292 fn check_apply(before: &str, after: &str) {
250fn main() {} 293 let file = SourceFileNode::parse(before);
251"#, 294 let diagnostic = check_unnecessary_braces_in_use_statement(&file)
295 .pop()
296 .unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
297 let fix = diagnostic.fix.unwrap();
298 let actual = fix.edit.apply(&before);
299 assert_eq_text!(after, &actual);
300 }
301
302 check_not_applicable(
303 "
304 use a;
305 use a::{c, d::e};
306 ",
252 ); 307 );
253 let diagnostics = check_unnecessary_braces_in_use_statement(&file); 308 check_apply("use {b};", "use b;");
254 assert_eq_dbg( 309 check_apply("use a::{c};", "use a::c;");
255 r#"[Diagnostic { range: [12; 15), msg: "Unnecessary braces in use statement", severity: WeakWarning }, 310 check_apply("use a::{self};", "use a;");
256 Diagnostic { range: [24; 27), msg: "Unnecessary braces in use statement", severity: WeakWarning }, 311 check_apply("use a::{c, d::{e}};", "use a::{c, d::e};");
257 Diagnostic { range: [61; 64), msg: "Unnecessary braces in use statement", severity: WeakWarning }]"#,
258 &diagnostics,
259 )
260 } 312 }
261} 313}
diff --git a/crates/ra_editor/src/typing.rs b/crates/ra_editor/src/typing.rs
index 5e412bcfa..21d068a7b 100644
--- a/crates/ra_editor/src/typing.rs
+++ b/crates/ra_editor/src/typing.rs
@@ -9,6 +9,7 @@ use ra_syntax::{
9 SyntaxNodeRef, TextRange, TextUnit, 9 SyntaxNodeRef, TextRange, TextUnit,
10}; 10};
11use ra_text_edit::text_utils::contains_offset_nonstrict; 11use ra_text_edit::text_utils::contains_offset_nonstrict;
12use itertools::Itertools;
12 13
13use crate::{find_node_at_offset, TextEditBuilder, LocalEdit}; 14use crate::{find_node_at_offset, TextEditBuilder, LocalEdit};
14 15
@@ -19,6 +20,7 @@ pub fn join_lines(file: &SourceFileNode, range: TextRange) -> LocalEdit {
19 let pos = match text.find('\n') { 20 let pos = match text.find('\n') {
20 None => { 21 None => {
21 return LocalEdit { 22 return LocalEdit {
23 label: "join lines".to_string(),
22 edit: TextEditBuilder::new().finish(), 24 edit: TextEditBuilder::new().finish(),
23 cursor_position: None, 25 cursor_position: None,
24 }; 26 };
@@ -51,6 +53,7 @@ pub fn join_lines(file: &SourceFileNode, range: TextRange) -> LocalEdit {
51 } 53 }
52 54
53 LocalEdit { 55 LocalEdit {
56 label: "join lines".to_string(),
54 edit: edit.finish(), 57 edit: edit.finish(),
55 cursor_position: None, 58 cursor_position: None,
56 } 59 }
@@ -76,6 +79,7 @@ pub fn on_enter(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> {
76 let mut edit = TextEditBuilder::new(); 79 let mut edit = TextEditBuilder::new();
77 edit.insert(offset, inserted); 80 edit.insert(offset, inserted);
78 Some(LocalEdit { 81 Some(LocalEdit {
82 label: "on enter".to_string(),
79 edit: edit.finish(), 83 edit: edit.finish(),
80 cursor_position: Some(cursor_position), 84 cursor_position: Some(cursor_position),
81 }) 85 })
@@ -126,6 +130,7 @@ pub fn on_eq_typed(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit>
126 let mut edit = TextEditBuilder::new(); 130 let mut edit = TextEditBuilder::new();
127 edit.insert(offset, ";".to_string()); 131 edit.insert(offset, ";".to_string());
128 Some(LocalEdit { 132 Some(LocalEdit {
133 label: "add semicolon".to_string(),
129 edit: edit.finish(), 134 edit: edit.finish(),
130 cursor_position: None, 135 cursor_position: None,
131 }) 136 })
@@ -240,7 +245,7 @@ fn single_expr(block: ast::Block) -> Option<ast::Expr> {
240 245
241fn join_single_use_tree(edit: &mut TextEditBuilder, node: SyntaxNodeRef) -> Option<()> { 246fn join_single_use_tree(edit: &mut TextEditBuilder, node: SyntaxNodeRef) -> Option<()> {
242 let use_tree_list = ast::UseTreeList::cast(node.parent()?)?; 247 let use_tree_list = ast::UseTreeList::cast(node.parent()?)?;
243 let tree = single_use_tree(use_tree_list)?; 248 let (tree,) = use_tree_list.use_trees().collect_tuple()?;
244 edit.replace( 249 edit.replace(
245 use_tree_list.syntax().range(), 250 use_tree_list.syntax().range(),
246 tree.syntax().text().to_string(), 251 tree.syntax().text().to_string(),
@@ -248,26 +253,6 @@ fn join_single_use_tree(edit: &mut TextEditBuilder, node: SyntaxNodeRef) -> Opti
248 Some(()) 253 Some(())
249} 254}
250 255
251fn single_use_tree(tree_list: ast::UseTreeList) -> Option<ast::UseTree> {
252 let mut res = None;
253 for child in tree_list.syntax().children() {
254 if let Some(tree) = ast::UseTree::cast(child) {
255 if tree.syntax().text().contains('\n') {
256 return None;
257 }
258 if mem::replace(&mut res, Some(tree)).is_some() {
259 return None;
260 }
261 } else {
262 match child.kind() {
263 WHITESPACE | L_CURLY | R_CURLY | COMMA => (),
264 _ => return None,
265 }
266 }
267 }
268 res
269}
270
271fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str { 256fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str {
272 match left.kind() { 257 match left.kind() {
273 L_PAREN | L_BRACK => return "", 258 L_PAREN | L_BRACK => return "",