diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 61 | ||||
-rw-r--r-- | crates/ra_analysis/src/lib.rs | 5 | ||||
-rw-r--r-- | crates/ra_db/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_db/src/cancelation.rs | 85 | ||||
-rw-r--r-- | crates/ra_db/src/lib.rs | 18 | ||||
-rw-r--r-- | crates/ra_editor/src/code_actions.rs | 7 | ||||
-rw-r--r-- | crates/ra_editor/src/lib.rs | 58 | ||||
-rw-r--r-- | crates/ra_editor/src/typing.rs | 29 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 2 | ||||
-rw-r--r-- | crates/ra_lsp_server/tests/heavy_tests/main.rs | 4 | ||||
-rw-r--r-- | crates/tools/src/lib.rs | 2 |
12 files changed, 194 insertions, 79 deletions
diff --git a/Cargo.lock b/Cargo.lock index 51cf1825d..69134b434 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -668,6 +668,7 @@ dependencies = [ | |||
668 | name = "ra_db" | 668 | name = "ra_db" |
669 | version = "0.1.0" | 669 | version = "0.1.0" |
670 | dependencies = [ | 670 | dependencies = [ |
671 | "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", | ||
671 | "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", | 672 | "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", |
672 | "ra_editor 0.1.0", | 673 | "ra_editor 0.1.0", |
673 | "ra_syntax 0.1.0", | 674 | "ra_syntax 0.1.0", |
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index a547c5a20..38a5c1a7d 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -3,31 +3,32 @@ use std::{ | |||
3 | sync::Arc, | 3 | sync::Arc, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use ra_editor::{self, find_node_at_offset, FileSymbol, LineIndex, LocalEdit, Severity}; | ||
7 | use ra_syntax::{ | ||
8 | ast::{self, ArgListOwner, Expr, NameOwner, FnDef}, | ||
9 | algo::find_covering_node, | ||
10 | AstNode, SourceFileNode, | ||
11 | SyntaxKind::*, | ||
12 | SyntaxNodeRef, TextRange, TextUnit, | ||
13 | }; | ||
14 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; | ||
15 | use rayon::prelude::*; | 6 | use rayon::prelude::*; |
16 | use salsa::{Database, ParallelDatabase}; | 7 | use salsa::{Database, ParallelDatabase}; |
8 | |||
17 | use hir::{ | 9 | use hir::{ |
18 | self, | 10 | self, |
19 | source_binder, | ||
20 | FnSignatureInfo, | 11 | FnSignatureInfo, |
21 | Problem, | 12 | Problem, |
13 | source_binder, | ||
14 | }; | ||
15 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; | ||
16 | use ra_editor::{self, FileSymbol, find_node_at_offset, LineIndex, LocalEdit, Severity}; | ||
17 | use ra_syntax::{ | ||
18 | algo::find_covering_node, | ||
19 | ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, | ||
20 | AstNode, SourceFileNode, | ||
21 | SyntaxKind::*, | ||
22 | SyntaxNodeRef, TextRange, TextUnit, | ||
22 | }; | 23 | }; |
23 | 24 | ||
24 | use crate::{ | 25 | use crate::{ |
25 | completion::{completions, CompletionItem}, | 26 | AnalysisChange, |
26 | db, | 27 | Cancelable, |
27 | symbol_index::{SymbolIndex, SymbolsDatabase, LibrarySymbolsQuery}, | 28 | completion::{CompletionItem, completions}, |
28 | AnalysisChange, RootChange, Cancelable, CrateId, Diagnostic, FileId, | 29 | CrateId, db, Diagnostic, FileId, FilePosition, FileSystemEdit, |
29 | FileSystemEdit, FilePosition, Query, SourceChange, SourceFileEdit, | 30 | Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit, |
30 | ReferenceResolution, | 31 | symbol_index::{LibrarySymbolsQuery, SymbolIndex, SymbolsDatabase}, |
31 | }; | 32 | }; |
32 | 33 | ||
33 | #[derive(Debug, Default)] | 34 | #[derive(Debug, Default)] |
@@ -366,7 +367,7 @@ impl AnalysisImpl { | |||
366 | range: d.range, | 367 | range: d.range, |
367 | message: d.msg, | 368 | message: d.msg, |
368 | severity: d.severity, | 369 | severity: d.severity, |
369 | fix: None, | 370 | fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)), |
370 | }) | 371 | }) |
371 | .collect::<Vec<_>>(); | 372 | .collect::<Vec<_>>(); |
372 | if let Some(m) = source_binder::module_from_file_id(&*self.db, file_id)? { | 373 | if let Some(m) = source_binder::module_from_file_id(&*self.db, file_id)? { |
@@ -425,25 +426,15 @@ impl AnalysisImpl { | |||
425 | let file = self.file_syntax(file_id); | 426 | let file = self.file_syntax(file_id); |
426 | let offset = range.start(); | 427 | let offset = range.start(); |
427 | let actions = vec![ | 428 | let actions = vec![ |
428 | ( | 429 | ra_editor::flip_comma(&file, offset).map(|f| f()), |
429 | "flip comma", | 430 | ra_editor::add_derive(&file, offset).map(|f| f()), |
430 | ra_editor::flip_comma(&file, offset).map(|f| f()), | 431 | ra_editor::add_impl(&file, offset).map(|f| f()), |
431 | ), | 432 | ra_editor::make_pub_crate(&file, offset).map(|f| f()), |
432 | ( | 433 | ra_editor::introduce_variable(&file, range).map(|f| f()), |
433 | "add `#[derive]`", | ||
434 | ra_editor::add_derive(&file, offset).map(|f| f()), | ||
435 | ), | ||
436 | ("add impl", ra_editor::add_impl(&file, offset).map(|f| f())), | ||
437 | ( | ||
438 | "introduce variable", | ||
439 | ra_editor::introduce_variable(&file, range).map(|f| f()), | ||
440 | ), | ||
441 | ]; | 434 | ]; |
442 | actions | 435 | actions |
443 | .into_iter() | 436 | .into_iter() |
444 | .filter_map(|(name, local_edit)| { | 437 | .filter_map(|local_edit| Some(SourceChange::from_local_edit(file_id, local_edit?))) |
445 | Some(SourceChange::from_local_edit(file_id, name, local_edit?)) | ||
446 | }) | ||
447 | .collect() | 438 | .collect() |
448 | } | 439 | } |
449 | 440 | ||
@@ -541,13 +532,13 @@ impl AnalysisImpl { | |||
541 | } | 532 | } |
542 | 533 | ||
543 | impl SourceChange { | 534 | impl SourceChange { |
544 | pub(crate) fn from_local_edit(file_id: FileId, label: &str, edit: LocalEdit) -> SourceChange { | 535 | pub(crate) fn from_local_edit(file_id: FileId, edit: LocalEdit) -> SourceChange { |
545 | let file_edit = SourceFileEdit { | 536 | let file_edit = SourceFileEdit { |
546 | file_id, | 537 | file_id, |
547 | edit: edit.edit, | 538 | edit: edit.edit, |
548 | }; | 539 | }; |
549 | SourceChange { | 540 | SourceChange { |
550 | label: label.to_string(), | 541 | label: edit.label, |
551 | source_file_edits: vec![file_edit], | 542 | source_file_edits: vec![file_edit], |
552 | file_system_edits: vec![], | 543 | file_system_edits: vec![], |
553 | cursor_position: edit | 544 | cursor_position: edit |
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index a029f66b4..476d1b438 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -288,19 +288,18 @@ impl Analysis { | |||
288 | } | 288 | } |
289 | pub fn join_lines(&self, file_id: FileId, range: TextRange) -> SourceChange { | 289 | pub fn join_lines(&self, file_id: FileId, range: TextRange) -> SourceChange { |
290 | let file = self.imp.file_syntax(file_id); | 290 | let file = self.imp.file_syntax(file_id); |
291 | SourceChange::from_local_edit(file_id, "join lines", ra_editor::join_lines(&file, range)) | 291 | SourceChange::from_local_edit(file_id, ra_editor::join_lines(&file, range)) |
292 | } | 292 | } |
293 | pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> { | 293 | pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> { |
294 | let file = self.imp.file_syntax(position.file_id); | 294 | let file = self.imp.file_syntax(position.file_id); |
295 | let edit = ra_editor::on_enter(&file, position.offset)?; | 295 | let edit = ra_editor::on_enter(&file, position.offset)?; |
296 | let res = SourceChange::from_local_edit(position.file_id, "on enter", edit); | 296 | let res = SourceChange::from_local_edit(position.file_id, edit); |
297 | Some(res) | 297 | Some(res) |
298 | } | 298 | } |
299 | pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> { | 299 | pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> { |
300 | let file = self.imp.file_syntax(position.file_id); | 300 | let file = self.imp.file_syntax(position.file_id); |
301 | Some(SourceChange::from_local_edit( | 301 | Some(SourceChange::from_local_edit( |
302 | position.file_id, | 302 | position.file_id, |
303 | "add semicolon", | ||
304 | ra_editor::on_eq_typed(&file, position.offset)?, | 303 | ra_editor::on_eq_typed(&file, position.offset)?, |
305 | )) | 304 | )) |
306 | } | 305 | } |
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml index f316c0ab2..4be32b5f3 100644 --- a/crates/ra_db/Cargo.toml +++ b/crates/ra_db/Cargo.toml | |||
@@ -5,6 +5,7 @@ version = "0.1.0" | |||
5 | authors = ["Aleksey Kladov <[email protected]>"] | 5 | authors = ["Aleksey Kladov <[email protected]>"] |
6 | 6 | ||
7 | [dependencies] | 7 | [dependencies] |
8 | backtrace = "0.3.1" | ||
8 | relative-path = "0.4.0" | 9 | relative-path = "0.4.0" |
9 | salsa = "0.8.0" | 10 | salsa = "0.8.0" |
10 | rustc-hash = "1.0" | 11 | rustc-hash = "1.0" |
diff --git a/crates/ra_db/src/cancelation.rs b/crates/ra_db/src/cancelation.rs new file mode 100644 index 000000000..73444b015 --- /dev/null +++ b/crates/ra_db/src/cancelation.rs | |||
@@ -0,0 +1,85 @@ | |||
1 | //! Utility types to support cancellation. | ||
2 | //! | ||
3 | //! In a typical IDE use-case, requests and modification happen concurrently, as | ||
4 | //! in the following scenario: | ||
5 | //! | ||
6 | //! * user types a character, | ||
7 | //! * a syntax highlighting process is started | ||
8 | //! * user types next character, while syntax highlighting *is still in | ||
9 | //! progress*. | ||
10 | //! | ||
11 | //! In this situation, we want to react to modification as quckly as possible. | ||
12 | //! At the same time, in-progress results are not very interesting, because they | ||
13 | //! are invalidated by the edit anyway. So, we first cancel all in-flight | ||
14 | //! requests, and then apply modification knowing that it won't intrfere with | ||
15 | //! any background processing (this bit is handled by salsa, see | ||
16 | //! `BaseDatabase::check_canceled` method). | ||
17 | |||
18 | use std::{ | ||
19 | cmp, | ||
20 | hash::{Hash, Hasher}, | ||
21 | sync::Arc, | ||
22 | }; | ||
23 | |||
24 | use backtrace::Backtrace; | ||
25 | use parking_lot::Mutex; | ||
26 | |||
27 | /// An "error" signifing that the operation was canceled. | ||
28 | #[derive(Clone)] | ||
29 | pub struct Canceled { | ||
30 | backtrace: Arc<Mutex<Backtrace>>, | ||
31 | } | ||
32 | |||
33 | pub type Cancelable<T> = Result<T, Canceled>; | ||
34 | |||
35 | impl Canceled { | ||
36 | pub(crate) fn new() -> Canceled { | ||
37 | let bt = Backtrace::new_unresolved(); | ||
38 | Canceled { | ||
39 | backtrace: Arc::new(Mutex::new(bt)), | ||
40 | } | ||
41 | } | ||
42 | } | ||
43 | |||
44 | impl std::fmt::Display for Canceled { | ||
45 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
46 | fmt.write_str("canceled") | ||
47 | } | ||
48 | } | ||
49 | |||
50 | impl std::fmt::Debug for Canceled { | ||
51 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
52 | let mut bt = self.backtrace.lock(); | ||
53 | let bt: &mut Backtrace = &mut *bt; | ||
54 | bt.resolve(); | ||
55 | write!(fmt, "canceled at:\n{:?}", bt) | ||
56 | } | ||
57 | } | ||
58 | |||
59 | impl std::error::Error for Canceled {} | ||
60 | |||
61 | impl PartialEq for Canceled { | ||
62 | fn eq(&self, _: &Canceled) -> bool { | ||
63 | true | ||
64 | } | ||
65 | } | ||
66 | |||
67 | impl Eq for Canceled {} | ||
68 | |||
69 | impl Hash for Canceled { | ||
70 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
71 | ().hash(hasher) | ||
72 | } | ||
73 | } | ||
74 | |||
75 | impl cmp::Ord for Canceled { | ||
76 | fn cmp(&self, _: &Canceled) -> cmp::Ordering { | ||
77 | cmp::Ordering::Equal | ||
78 | } | ||
79 | } | ||
80 | |||
81 | impl cmp::PartialOrd for Canceled { | ||
82 | fn partial_cmp(&self, other: &Canceled) -> Option<cmp::Ordering> { | ||
83 | Some(self.cmp(other)) | ||
84 | } | ||
85 | } | ||
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index 78f2cbf12..1f7c9187b 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs | |||
@@ -1,27 +1,17 @@ | |||
1 | //! ra_db defines basic database traits. Concrete DB is defined by ra_analysis. | 1 | //! ra_db defines basic database traits. Concrete DB is defined by ra_analysis. |
2 | mod cancelation; | ||
2 | mod syntax_ptr; | 3 | mod syntax_ptr; |
3 | mod input; | 4 | mod input; |
4 | mod loc2id; | 5 | mod loc2id; |
5 | pub mod mock; | 6 | pub mod mock; |
6 | 7 | ||
7 | use std::sync::Arc; | 8 | use std::sync::Arc; |
9 | |||
8 | use ra_editor::LineIndex; | 10 | use ra_editor::LineIndex; |
9 | use ra_syntax::{TextUnit, SourceFileNode}; | 11 | use ra_syntax::{TextUnit, SourceFileNode}; |
10 | 12 | ||
11 | #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
12 | pub struct Canceled; | ||
13 | |||
14 | pub type Cancelable<T> = Result<T, Canceled>; | ||
15 | |||
16 | impl std::fmt::Display for Canceled { | ||
17 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
18 | fmt.write_str("canceled") | ||
19 | } | ||
20 | } | ||
21 | |||
22 | impl std::error::Error for Canceled {} | ||
23 | |||
24 | pub use crate::{ | 13 | pub use crate::{ |
14 | cancelation::{Canceled, Cancelable}, | ||
25 | syntax_ptr::LocalSyntaxPtr, | 15 | syntax_ptr::LocalSyntaxPtr, |
26 | input::{ | 16 | input::{ |
27 | FilesDatabase, FileId, CrateId, SourceRoot, SourceRootId, CrateGraph, | 17 | FilesDatabase, FileId, CrateId, SourceRoot, SourceRootId, CrateGraph, |
@@ -48,7 +38,7 @@ macro_rules! impl_numeric_id { | |||
48 | pub trait BaseDatabase: salsa::Database { | 38 | pub trait BaseDatabase: salsa::Database { |
49 | fn check_canceled(&self) -> Cancelable<()> { | 39 | fn check_canceled(&self) -> Cancelable<()> { |
50 | if self.salsa_runtime().is_current_revision_canceled() { | 40 | if self.salsa_runtime().is_current_revision_canceled() { |
51 | Err(Canceled) | 41 | Err(Canceled::new()) |
52 | } else { | 42 | } else { |
53 | Ok(()) | 43 | Ok(()) |
54 | } | 44 | } |
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 7a689b0f2..dcf8be5a7 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs | |||
@@ -42,6 +42,7 @@ pub struct Diagnostic { | |||
42 | pub range: TextRange, | 42 | pub range: TextRange, |
43 | pub msg: String, | 43 | pub msg: String, |
44 | pub severity: Severity, | 44 | pub severity: Severity, |
45 | pub fix: Option<LocalEdit>, | ||
45 | } | 46 | } |
46 | 47 | ||
47 | #[derive(Debug)] | 48 | #[derive(Debug)] |
@@ -111,6 +112,7 @@ pub fn diagnostics(file: &SourceFileNode) -> Vec<Diagnostic> { | |||
111 | range: location_to_range(err.location()), | 112 | range: location_to_range(err.location()), |
112 | msg: format!("Syntax Error: {}", err), | 113 | msg: format!("Syntax Error: {}", err), |
113 | severity: Severity::Error, | 114 | severity: Severity::Error, |
115 | fix: None, | ||
114 | }) | 116 | }) |
115 | .collect(); | 117 | .collect(); |
116 | 118 | ||
@@ -124,11 +126,30 @@ fn check_unnecessary_braces_in_use_statement(file: &SourceFileNode) -> Vec<Diagn | |||
124 | let mut diagnostics = Vec::new(); | 126 | let mut diagnostics = Vec::new(); |
125 | for node in file.syntax().descendants() { | 127 | for node in file.syntax().descendants() { |
126 | if let Some(use_tree_list) = ast::UseTreeList::cast(node) { | 128 | if let Some(use_tree_list) = ast::UseTreeList::cast(node) { |
127 | if use_tree_list.use_trees().count() <= 1 { | 129 | if use_tree_list.use_trees().count() == 1 { |
130 | let range = use_tree_list.syntax().range(); | ||
131 | // use_tree_list always has one child, so we use unwrap directly here. | ||
132 | let single_use_tree: ast::UseTree = use_tree_list.use_trees().next().unwrap(); | ||
133 | let edit = text_edit_for_remove_unnecessary_braces_with_self_in_use_statement( | ||
134 | single_use_tree, | ||
135 | ) | ||
136 | .unwrap_or_else(|| { | ||
137 | let to_replace = single_use_tree.syntax().text().to_string(); | ||
138 | let mut edit_builder = TextEditBuilder::new(); | ||
139 | edit_builder.delete(range); | ||
140 | edit_builder.insert(range.start(), to_replace); | ||
141 | edit_builder.finish() | ||
142 | }); | ||
143 | |||
128 | diagnostics.push(Diagnostic { | 144 | diagnostics.push(Diagnostic { |
129 | range: use_tree_list.syntax().range(), | 145 | range: range, |
130 | msg: format!("Unnecessary braces in use statement"), | 146 | msg: format!("Unnecessary braces in use statement"), |
131 | severity: Severity::WeakWarning, | 147 | severity: Severity::WeakWarning, |
148 | fix: Some(LocalEdit { | ||
149 | label: "Remove unnecessary braces".to_string(), | ||
150 | edit: edit, | ||
151 | cursor_position: None, | ||
152 | }), | ||
132 | }) | 153 | }) |
133 | } | 154 | } |
134 | } | 155 | } |
@@ -137,6 +158,28 @@ fn check_unnecessary_braces_in_use_statement(file: &SourceFileNode) -> Vec<Diagn | |||
137 | diagnostics | 158 | diagnostics |
138 | } | 159 | } |
139 | 160 | ||
161 | fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement( | ||
162 | single_use_tree: ast::UseTree, | ||
163 | ) -> Option<TextEdit> { | ||
164 | let use_tree_list_node = single_use_tree.syntax().parent()?; | ||
165 | if single_use_tree | ||
166 | .path()? | ||
167 | .segment()? | ||
168 | .syntax() | ||
169 | .first_child()? | ||
170 | .kind() | ||
171 | == SyntaxKind::SELF_KW | ||
172 | { | ||
173 | let start = use_tree_list_node.prev_sibling()?.range().start(); | ||
174 | let end = use_tree_list_node.range().end(); | ||
175 | let range = TextRange::from_to(start, end); | ||
176 | let mut edit_builder = TextEditBuilder::new(); | ||
177 | edit_builder.delete(range); | ||
178 | return Some(edit_builder.finish()); | ||
179 | } | ||
180 | None | ||
181 | } | ||
182 | |||
140 | pub fn syntax_tree(file: &SourceFileNode) -> String { | 183 | pub fn syntax_tree(file: &SourceFileNode) -> String { |
141 | ::ra_syntax::utils::dump_tree(file.syntax()) | 184 | ::ra_syntax::utils::dump_tree(file.syntax()) |
142 | } | 185 | } |
@@ -173,8 +216,9 @@ pub fn find_node_at_offset<'a, N: AstNode<'a>>( | |||
173 | 216 | ||
174 | #[cfg(test)] | 217 | #[cfg(test)] |
175 | mod tests { | 218 | mod tests { |
219 | use crate::test_utils::{add_cursor, assert_eq_dbg, assert_eq_text, extract_offset}; | ||
220 | |||
176 | use super::*; | 221 | use super::*; |
177 | use crate::test_utils::{add_cursor, assert_eq_dbg, extract_offset, assert_eq_text}; | ||
178 | 222 | ||
179 | #[test] | 223 | #[test] |
180 | fn test_highlighting() { | 224 | fn test_highlighting() { |
@@ -243,6 +287,7 @@ fn test_foo() {} | |||
243 | use a; | 287 | use a; |
244 | use {b}; | 288 | use {b}; |
245 | use a::{c}; | 289 | use a::{c}; |
290 | use a::{self}; | ||
246 | use a::{c, d::e}; | 291 | use a::{c, d::e}; |
247 | use a::{c, d::{e}}; | 292 | use a::{c, d::{e}}; |
248 | fn main() {} | 293 | fn main() {} |
@@ -250,9 +295,10 @@ fn main() {} | |||
250 | ); | 295 | ); |
251 | let diagnostics = check_unnecessary_braces_in_use_statement(&file); | 296 | let diagnostics = check_unnecessary_braces_in_use_statement(&file); |
252 | assert_eq_dbg( | 297 | assert_eq_dbg( |
253 | r#"[Diagnostic { range: [12; 15), msg: "Unnecessary braces in use statement", severity: WeakWarning }, | 298 | r#"[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: None }) }, |
254 | Diagnostic { range: [24; 27), msg: "Unnecessary braces in use statement", severity: WeakWarning }, | 299 | 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: None }) }, |
255 | Diagnostic { range: [61; 64), msg: "Unnecessary braces in use statement", severity: WeakWarning }]"#, | 300 | Diagnostic { range: [36; 42), msg: "Unnecessary braces in use statement", severity: WeakWarning, fix: Some(LocalEdit { label: "Remove unnecessary braces", edit: TextEdit { atoms: [AtomTextEdit { delete: [34; 42), insert: "" }] }, cursor_position: None }) }, |
301 | Diagnostic { range: [76; 79), msg: "Unnecessary braces in use statement", severity: WeakWarning, fix: Some(LocalEdit { label: "Remove unnecessary braces", edit: TextEdit { atoms: [AtomTextEdit { delete: [76; 76), insert: "e" }, AtomTextEdit { delete: [76; 79), insert: "" }] }, cursor_position: None }) }]"#, | ||
256 | &diagnostics, | 302 | &diagnostics, |
257 | ) | 303 | ) |
258 | } | 304 | } |
diff --git a/crates/ra_editor/src/typing.rs b/crates/ra_editor/src/typing.rs index 5e412bcfa..f0d8dc7bb 100644 --- a/crates/ra_editor/src/typing.rs +++ b/crates/ra_editor/src/typing.rs | |||
@@ -8,7 +8,9 @@ use ra_syntax::{ | |||
8 | SyntaxKind::*, | 8 | SyntaxKind::*, |
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::{ |
12 | contains_offset_nonstrict | ||
13 | }; | ||
12 | 14 | ||
13 | use crate::{find_node_at_offset, TextEditBuilder, LocalEdit}; | 15 | use crate::{find_node_at_offset, TextEditBuilder, LocalEdit}; |
14 | 16 | ||
@@ -19,6 +21,7 @@ pub fn join_lines(file: &SourceFileNode, range: TextRange) -> LocalEdit { | |||
19 | let pos = match text.find('\n') { | 21 | let pos = match text.find('\n') { |
20 | None => { | 22 | None => { |
21 | return LocalEdit { | 23 | return LocalEdit { |
24 | label: "join lines".to_string(), | ||
22 | edit: TextEditBuilder::new().finish(), | 25 | edit: TextEditBuilder::new().finish(), |
23 | cursor_position: None, | 26 | cursor_position: None, |
24 | }; | 27 | }; |
@@ -51,6 +54,7 @@ pub fn join_lines(file: &SourceFileNode, range: TextRange) -> LocalEdit { | |||
51 | } | 54 | } |
52 | 55 | ||
53 | LocalEdit { | 56 | LocalEdit { |
57 | label: "join lines".to_string(), | ||
54 | edit: edit.finish(), | 58 | edit: edit.finish(), |
55 | cursor_position: None, | 59 | cursor_position: None, |
56 | } | 60 | } |
@@ -76,6 +80,7 @@ pub fn on_enter(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> { | |||
76 | let mut edit = TextEditBuilder::new(); | 80 | let mut edit = TextEditBuilder::new(); |
77 | edit.insert(offset, inserted); | 81 | edit.insert(offset, inserted); |
78 | Some(LocalEdit { | 82 | Some(LocalEdit { |
83 | label: "on enter".to_string(), | ||
79 | edit: edit.finish(), | 84 | edit: edit.finish(), |
80 | cursor_position: Some(cursor_position), | 85 | cursor_position: Some(cursor_position), |
81 | }) | 86 | }) |
@@ -126,6 +131,7 @@ pub fn on_eq_typed(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> | |||
126 | let mut edit = TextEditBuilder::new(); | 131 | let mut edit = TextEditBuilder::new(); |
127 | edit.insert(offset, ";".to_string()); | 132 | edit.insert(offset, ";".to_string()); |
128 | Some(LocalEdit { | 133 | Some(LocalEdit { |
134 | label: "add semicolon".to_string(), | ||
129 | edit: edit.finish(), | 135 | edit: edit.finish(), |
130 | cursor_position: None, | 136 | cursor_position: None, |
131 | }) | 137 | }) |
@@ -249,23 +255,12 @@ fn join_single_use_tree(edit: &mut TextEditBuilder, node: SyntaxNodeRef) -> Opti | |||
249 | } | 255 | } |
250 | 256 | ||
251 | fn single_use_tree(tree_list: ast::UseTreeList) -> Option<ast::UseTree> { | 257 | fn single_use_tree(tree_list: ast::UseTreeList) -> Option<ast::UseTree> { |
252 | let mut res = None; | 258 | let sub_use_trees = tree_list.use_trees().count(); |
253 | for child in tree_list.syntax().children() { | 259 | if sub_use_trees != 1 { |
254 | if let Some(tree) = ast::UseTree::cast(child) { | 260 | return None; |
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 | } | 261 | } |
268 | res | 262 | |
263 | tree_list.use_trees().next() | ||
269 | } | 264 | } |
270 | 265 | ||
271 | fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str { | 266 | fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str { |
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 60f13267c..1edb9fae4 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -427,7 +427,7 @@ impl<'a> PoolDispatcher<'a> { | |||
427 | RawResponse::err( | 427 | RawResponse::err( |
428 | id, | 428 | id, |
429 | ErrorCode::ContentModified as i32, | 429 | ErrorCode::ContentModified as i32, |
430 | format!("content modified: {}", e), | 430 | format!("content modified: {:?}", e), |
431 | ) | 431 | ) |
432 | } else { | 432 | } else { |
433 | RawResponse::err( | 433 | RawResponse::err( |
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index 029a55d40..1f5cc5e8b 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs | |||
@@ -141,7 +141,7 @@ fn main() {} | |||
141 | server.request::<CodeActionRequest>( | 141 | server.request::<CodeActionRequest>( |
142 | CodeActionParams { | 142 | CodeActionParams { |
143 | text_document: server.doc_id("src/lib.rs"), | 143 | text_document: server.doc_id("src/lib.rs"), |
144 | range: Range::new(Position::new(0, 0), Position::new(0, 7)), | 144 | range: Range::new(Position::new(0, 4), Position::new(0, 7)), |
145 | context: empty_context(), | 145 | context: empty_context(), |
146 | }, | 146 | }, |
147 | json!([ | 147 | json!([ |
@@ -168,7 +168,7 @@ fn main() {} | |||
168 | server.request::<CodeActionRequest>( | 168 | server.request::<CodeActionRequest>( |
169 | CodeActionParams { | 169 | CodeActionParams { |
170 | text_document: server.doc_id("src/lib.rs"), | 170 | text_document: server.doc_id("src/lib.rs"), |
171 | range: Range::new(Position::new(2, 0), Position::new(2, 7)), | 171 | range: Range::new(Position::new(2, 4), Position::new(2, 7)), |
172 | context: empty_context(), | 172 | context: empty_context(), |
173 | }, | 173 | }, |
174 | json!([]), | 174 | json!([]), |
diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs index 2795afe0b..6f96b8120 100644 --- a/crates/tools/src/lib.rs +++ b/crates/tools/src/lib.rs | |||
@@ -29,7 +29,7 @@ pub fn collect_tests(s: &str) -> Vec<(usize, Test)> { | |||
29 | let prefix = "// "; | 29 | let prefix = "// "; |
30 | let comment_blocks = s | 30 | let comment_blocks = s |
31 | .lines() | 31 | .lines() |
32 | .map(str::trim_left) | 32 | .map(str::trim_start) |
33 | .enumerate() | 33 | .enumerate() |
34 | .group_by(|(_idx, line)| line.starts_with(prefix)); | 34 | .group_by(|(_idx, line)| line.starts_with(prefix)); |
35 | 35 | ||