diff options
Diffstat (limited to 'crates/ra_editor')
-rw-r--r-- | crates/ra_editor/src/code_actions.rs | 7 | ||||
-rw-r--r-- | crates/ra_editor/src/lib.rs | 90 | ||||
-rw-r--r-- | crates/ra_editor/src/typing.rs | 27 |
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)] |
14 | pub struct LocalEdit { | 14 | pub 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 | }; |
29 | use itertools::Itertools; | ||
29 | 30 | ||
30 | #[derive(Debug)] | 31 | #[derive(Debug)] |
31 | pub struct HighlightedRange { | 32 | pub 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 | ||
162 | fn 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 | |||
142 | pub fn syntax_tree(file: &SourceFileNode) -> String { | 184 | pub 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)] |
177 | mod tests { | 219 | mod 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); |
245 | use a; | 288 | let diagnostics = check_unnecessary_braces_in_use_statement(&file); |
246 | use {b}; | 289 | assert!(diagnostics.is_empty()); |
247 | use a::{c}; | 290 | } |
248 | use a::{c, d::e}; | 291 | |
249 | use a::{c, d::{e}}; | 292 | fn check_apply(before: &str, after: &str) { |
250 | fn 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 | }; |
11 | use ra_text_edit::text_utils::contains_offset_nonstrict; | 11 | use ra_text_edit::text_utils::contains_offset_nonstrict; |
12 | use itertools::Itertools; | ||
12 | 13 | ||
13 | use crate::{find_node_at_offset, TextEditBuilder, LocalEdit}; | 14 | use 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 | ||
241 | fn join_single_use_tree(edit: &mut TextEditBuilder, node: SyntaxNodeRef) -> Option<()> { | 246 | fn 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 | ||
251 | fn 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 | |||
271 | fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str { | 256 | fn 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 "", |