diff options
34 files changed, 355 insertions, 233 deletions
diff --git a/Cargo.lock b/Cargo.lock index 15ccf4146..ee04cdce8 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -1375,9 +1375,9 @@ checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" | |||
1375 | 1375 | ||
1376 | [[package]] | 1376 | [[package]] |
1377 | name = "salsa" | 1377 | name = "salsa" |
1378 | version = "0.16.1" | 1378 | version = "0.17.0-pre.1" |
1379 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1379 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1380 | checksum = "4b84d9f96071f3f3be0dc818eae3327625d8ebc95b58da37d6850724f31d3403" | 1380 | checksum = "58038261ea8cd5a7730c4d8c97a22063d7c7eb1c2809e55c3c15f0a5903e5582" |
1381 | dependencies = [ | 1381 | dependencies = [ |
1382 | "crossbeam-utils", | 1382 | "crossbeam-utils", |
1383 | "indexmap", | 1383 | "indexmap", |
@@ -1392,9 +1392,9 @@ dependencies = [ | |||
1392 | 1392 | ||
1393 | [[package]] | 1393 | [[package]] |
1394 | name = "salsa-macros" | 1394 | name = "salsa-macros" |
1395 | version = "0.16.0" | 1395 | version = "0.17.0-pre.1" |
1396 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1396 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1397 | checksum = "cd3904a4ba0a9d0211816177fd34b04c7095443f8cdacd11175064fe541c8fe2" | 1397 | checksum = "2e2fc060627fa5d44bffac98f6089b9497779e2deccc26687f60adc2638e32fb" |
1398 | dependencies = [ | 1398 | dependencies = [ |
1399 | "heck", | 1399 | "heck", |
1400 | "proc-macro2", | 1400 | "proc-macro2", |
diff --git a/crates/base_db/Cargo.toml b/crates/base_db/Cargo.toml index 1724d2f85..69173ad1f 100644 --- a/crates/base_db/Cargo.toml +++ b/crates/base_db/Cargo.toml | |||
@@ -10,7 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | salsa = "0.16.0" | 13 | salsa = "0.17.0-pre.1" |
14 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
15 | 15 | ||
16 | syntax = { path = "../syntax", version = "0.0.0" } | 16 | syntax = { path = "../syntax", version = "0.0.0" } |
diff --git a/crates/base_db/src/cancellation.rs b/crates/base_db/src/cancellation.rs deleted file mode 100644 index 7420a1976..000000000 --- a/crates/base_db/src/cancellation.rs +++ /dev/null | |||
@@ -1,48 +0,0 @@ | |||
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 quickly 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 interfere with | ||
15 | //! any background processing (this bit is handled by salsa, see the | ||
16 | //! `BaseDatabase::check_canceled` method). | ||
17 | |||
18 | /// An "error" signifying that the operation was canceled. | ||
19 | #[derive(Clone, PartialEq, Eq, Hash)] | ||
20 | pub struct Canceled { | ||
21 | _private: (), | ||
22 | } | ||
23 | |||
24 | impl Canceled { | ||
25 | pub(crate) fn new() -> Canceled { | ||
26 | Canceled { _private: () } | ||
27 | } | ||
28 | |||
29 | pub fn throw() -> ! { | ||
30 | // We use resume and not panic here to avoid running the panic | ||
31 | // hook (that is, to avoid collecting and printing backtrace). | ||
32 | std::panic::resume_unwind(Box::new(Canceled::new())) | ||
33 | } | ||
34 | } | ||
35 | |||
36 | impl std::fmt::Display for Canceled { | ||
37 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
38 | fmt.write_str("canceled") | ||
39 | } | ||
40 | } | ||
41 | |||
42 | impl std::fmt::Debug for Canceled { | ||
43 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
44 | write!(fmt, "Canceled") | ||
45 | } | ||
46 | } | ||
47 | |||
48 | impl std::error::Error for Canceled {} | ||
diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index 980a0ed98..62bf2a4b2 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | //! base_db defines basic database traits. The concrete DB is defined by ide. | 1 | //! base_db defines basic database traits. The concrete DB is defined by ide. |
2 | mod cancellation; | ||
3 | mod input; | 2 | mod input; |
4 | mod change; | 3 | mod change; |
5 | pub mod fixture; | 4 | pub mod fixture; |
@@ -10,14 +9,13 @@ use rustc_hash::FxHashSet; | |||
10 | use syntax::{ast, Parse, SourceFile, TextRange, TextSize}; | 9 | use syntax::{ast, Parse, SourceFile, TextRange, TextSize}; |
11 | 10 | ||
12 | pub use crate::{ | 11 | pub use crate::{ |
13 | cancellation::Canceled, | ||
14 | change::Change, | 12 | change::Change, |
15 | input::{ | 13 | input::{ |
16 | CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, | 14 | CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, |
17 | ProcMacro, ProcMacroExpander, ProcMacroId, ProcMacroKind, SourceRoot, SourceRootId, | 15 | ProcMacro, ProcMacroExpander, ProcMacroId, ProcMacroKind, SourceRoot, SourceRootId, |
18 | }, | 16 | }, |
19 | }; | 17 | }; |
20 | pub use salsa; | 18 | pub use salsa::{self, Cancelled}; |
21 | pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, FileId, VfsPath}; | 19 | pub use vfs::{file_set::FileSet, AnchoredPath, AnchoredPathBuf, FileId, VfsPath}; |
22 | 20 | ||
23 | #[macro_export] | 21 | #[macro_export] |
@@ -38,45 +36,6 @@ pub trait Upcast<T: ?Sized> { | |||
38 | fn upcast(&self) -> &T; | 36 | fn upcast(&self) -> &T; |
39 | } | 37 | } |
40 | 38 | ||
41 | pub trait CheckCanceled { | ||
42 | /// Aborts current query if there are pending changes. | ||
43 | /// | ||
44 | /// rust-analyzer needs to be able to answer semantic questions about the | ||
45 | /// code while the code is being modified. A common problem is that a | ||
46 | /// long-running query is being calculated when a new change arrives. | ||
47 | /// | ||
48 | /// We can't just apply the change immediately: this will cause the pending | ||
49 | /// query to see inconsistent state (it will observe an absence of | ||
50 | /// repeatable read). So what we do is we **cancel** all pending queries | ||
51 | /// before applying the change. | ||
52 | /// | ||
53 | /// We implement cancellation by panicking with a special value and catching | ||
54 | /// it on the API boundary. Salsa explicitly supports this use-case. | ||
55 | fn check_canceled(&self); | ||
56 | |||
57 | fn catch_canceled<F, T>(&self, f: F) -> Result<T, Canceled> | ||
58 | where | ||
59 | Self: Sized + panic::RefUnwindSafe, | ||
60 | F: FnOnce(&Self) -> T + panic::UnwindSafe, | ||
61 | { | ||
62 | // Uncomment to debug missing cancellations. | ||
63 | // let _span = profile::heartbeat_span(); | ||
64 | panic::catch_unwind(|| f(self)).map_err(|err| match err.downcast::<Canceled>() { | ||
65 | Ok(canceled) => *canceled, | ||
66 | Err(payload) => panic::resume_unwind(payload), | ||
67 | }) | ||
68 | } | ||
69 | } | ||
70 | |||
71 | impl<T: salsa::Database> CheckCanceled for T { | ||
72 | fn check_canceled(&self) { | ||
73 | // profile::heartbeat(); | ||
74 | if self.salsa_runtime().is_current_revision_canceled() { | ||
75 | Canceled::throw() | ||
76 | } | ||
77 | } | ||
78 | } | ||
79 | |||
80 | #[derive(Clone, Copy, Debug)] | 39 | #[derive(Clone, Copy, Debug)] |
81 | pub struct FilePosition { | 40 | pub struct FilePosition { |
82 | pub file_id: FileId, | 41 | pub file_id: FileId, |
@@ -101,7 +60,7 @@ pub trait FileLoader { | |||
101 | /// Database which stores all significant input facts: source code and project | 60 | /// Database which stores all significant input facts: source code and project |
102 | /// model. Everything else in rust-analyzer is derived from these queries. | 61 | /// model. Everything else in rust-analyzer is derived from these queries. |
103 | #[salsa::query_group(SourceDatabaseStorage)] | 62 | #[salsa::query_group(SourceDatabaseStorage)] |
104 | pub trait SourceDatabase: CheckCanceled + FileLoader + std::fmt::Debug { | 63 | pub trait SourceDatabase: FileLoader + std::fmt::Debug { |
105 | // Parses the file into the syntax tree. | 64 | // Parses the file into the syntax tree. |
106 | #[salsa::invoke(parse_query)] | 65 | #[salsa::invoke(parse_query)] |
107 | fn parse(&self, file_id: FileId) -> Parse<ast::SourceFile>; | 66 | fn parse(&self, file_id: FileId) -> Parse<ast::SourceFile>; |
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 22ec7c6ac..2cdbd172a 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs | |||
@@ -227,3 +227,27 @@ impl Diagnostic for MacroError { | |||
227 | true | 227 | true |
228 | } | 228 | } |
229 | } | 229 | } |
230 | |||
231 | #[derive(Debug)] | ||
232 | pub struct UnimplementedBuiltinMacro { | ||
233 | pub file: HirFileId, | ||
234 | pub node: SyntaxNodePtr, | ||
235 | } | ||
236 | |||
237 | impl Diagnostic for UnimplementedBuiltinMacro { | ||
238 | fn code(&self) -> DiagnosticCode { | ||
239 | DiagnosticCode("unimplemented-builtin-macro") | ||
240 | } | ||
241 | |||
242 | fn message(&self) -> String { | ||
243 | "unimplemented built-in macro".to_string() | ||
244 | } | ||
245 | |||
246 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
247 | InFile::new(self.file, self.node.clone()) | ||
248 | } | ||
249 | |||
250 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
251 | self | ||
252 | } | ||
253 | } | ||
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 975ae4869..d3ef29db4 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -36,8 +36,8 @@ use std::{iter, sync::Arc}; | |||
36 | use arrayvec::ArrayVec; | 36 | use arrayvec::ArrayVec; |
37 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; | 37 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; |
38 | use diagnostics::{ | 38 | use diagnostics::{ |
39 | InactiveCode, MacroError, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, | 39 | InactiveCode, MacroError, UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, |
40 | UnresolvedModule, UnresolvedProcMacro, | 40 | UnresolvedMacroCall, UnresolvedModule, UnresolvedProcMacro, |
41 | }; | 41 | }; |
42 | use either::Either; | 42 | use either::Either; |
43 | use hir_def::{ | 43 | use hir_def::{ |
@@ -565,6 +565,14 @@ impl Module { | |||
565 | }; | 565 | }; |
566 | sink.push(MacroError { file, node: ast, message: message.clone() }); | 566 | sink.push(MacroError { file, node: ast, message: message.clone() }); |
567 | } | 567 | } |
568 | |||
569 | DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { | ||
570 | let node = ast.to_node(db.upcast()); | ||
571 | // Must have a name, otherwise we wouldn't emit it. | ||
572 | let name = node.name().expect("unimplemented builtin macro with no name"); | ||
573 | let ptr = SyntaxNodePtr::from(AstPtr::new(&name)); | ||
574 | sink.push(UnimplementedBuiltinMacro { file: ast.file_id, node: ptr }); | ||
575 | } | ||
568 | } | 576 | } |
569 | } | 577 | } |
570 | for decl in self.declarations(db) { | 578 | for decl in self.declarations(db) { |
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 8d3c43d08..c7f2c02e4 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -361,7 +361,7 @@ impl<'db> SemanticsImpl<'db> { | |||
361 | let sa = self.analyze(&parent); | 361 | let sa = self.analyze(&parent); |
362 | 362 | ||
363 | let token = successors(Some(InFile::new(sa.file_id, token)), |token| { | 363 | let token = successors(Some(InFile::new(sa.file_id, token)), |token| { |
364 | self.db.check_canceled(); | 364 | self.db.unwind_if_cancelled(); |
365 | let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; | 365 | let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; |
366 | let tt = macro_call.token_tree()?; | 366 | let tt = macro_call.token_tree()?; |
367 | if !tt.syntax().text_range().contains_range(token.value.text_range()) { | 367 | if !tt.syntax().text_range().contains_range(token.value.text_range()) { |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index d9d6c91a8..7f9fdb379 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -351,7 +351,7 @@ impl DefCollector<'_> { | |||
351 | let mut i = 0; | 351 | let mut i = 0; |
352 | 'outer: loop { | 352 | 'outer: loop { |
353 | loop { | 353 | loop { |
354 | self.db.check_canceled(); | 354 | self.db.unwind_if_cancelled(); |
355 | loop { | 355 | loop { |
356 | if self.resolve_imports() == ReachedFixedPoint::Yes { | 356 | if self.resolve_imports() == ReachedFixedPoint::Yes { |
357 | break; | 357 | break; |
@@ -836,7 +836,7 @@ impl DefCollector<'_> { | |||
836 | vis: Visibility, | 836 | vis: Visibility, |
837 | import_type: ImportType, | 837 | import_type: ImportType, |
838 | ) { | 838 | ) { |
839 | self.db.check_canceled(); | 839 | self.db.unwind_if_cancelled(); |
840 | self.update_recursive(module_id, resolutions, vis, import_type, 0) | 840 | self.update_recursive(module_id, resolutions, vis, import_type, 0) |
841 | } | 841 | } |
842 | 842 | ||
@@ -1679,14 +1679,22 @@ impl ModCollector<'_, '_> { | |||
1679 | None => &mac.name, | 1679 | None => &mac.name, |
1680 | }; | 1680 | }; |
1681 | let krate = self.def_collector.def_map.krate; | 1681 | let krate = self.def_collector.def_map.krate; |
1682 | if let Some(macro_id) = find_builtin_macro(name, krate, ast_id) { | 1682 | match find_builtin_macro(name, krate, ast_id) { |
1683 | self.def_collector.define_macro_rules( | 1683 | Some(macro_id) => { |
1684 | self.module_id, | 1684 | self.def_collector.define_macro_rules( |
1685 | mac.name.clone(), | 1685 | self.module_id, |
1686 | macro_id, | 1686 | mac.name.clone(), |
1687 | is_export, | 1687 | macro_id, |
1688 | ); | 1688 | is_export, |
1689 | return; | 1689 | ); |
1690 | return; | ||
1691 | } | ||
1692 | None => { | ||
1693 | self.def_collector | ||
1694 | .def_map | ||
1695 | .diagnostics | ||
1696 | .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, ast_id)); | ||
1697 | } | ||
1690 | } | 1698 | } |
1691 | } | 1699 | } |
1692 | 1700 | ||
@@ -1715,15 +1723,23 @@ impl ModCollector<'_, '_> { | |||
1715 | let macro_id = find_builtin_macro(&mac.name, krate, ast_id) | 1723 | let macro_id = find_builtin_macro(&mac.name, krate, ast_id) |
1716 | .or_else(|| find_builtin_derive(&mac.name, krate, ast_id)); | 1724 | .or_else(|| find_builtin_derive(&mac.name, krate, ast_id)); |
1717 | 1725 | ||
1718 | if let Some(macro_id) = macro_id { | 1726 | match macro_id { |
1719 | self.def_collector.define_macro_def( | 1727 | Some(macro_id) => { |
1720 | self.module_id, | 1728 | self.def_collector.define_macro_def( |
1721 | mac.name.clone(), | 1729 | self.module_id, |
1722 | macro_id, | 1730 | mac.name.clone(), |
1723 | &self.item_tree[mac.visibility], | 1731 | macro_id, |
1724 | ); | 1732 | &self.item_tree[mac.visibility], |
1733 | ); | ||
1734 | return; | ||
1735 | } | ||
1736 | None => { | ||
1737 | self.def_collector | ||
1738 | .def_map | ||
1739 | .diagnostics | ||
1740 | .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, ast_id)); | ||
1741 | } | ||
1725 | } | 1742 | } |
1726 | return; | ||
1727 | } | 1743 | } |
1728 | 1744 | ||
1729 | // Case 2: normal `macro` | 1745 | // Case 2: normal `macro` |
diff --git a/crates/hir_def/src/nameres/diagnostics.rs b/crates/hir_def/src/nameres/diagnostics.rs index 57c36c3c6..95061f601 100644 --- a/crates/hir_def/src/nameres/diagnostics.rs +++ b/crates/hir_def/src/nameres/diagnostics.rs | |||
@@ -27,6 +27,8 @@ pub enum DefDiagnosticKind { | |||
27 | UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath }, | 27 | UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath }, |
28 | 28 | ||
29 | MacroError { ast: MacroCallKind, message: String }, | 29 | MacroError { ast: MacroCallKind, message: String }, |
30 | |||
31 | UnimplementedBuiltinMacro { ast: AstId<ast::Macro> }, | ||
30 | } | 32 | } |
31 | 33 | ||
32 | #[derive(Debug, PartialEq, Eq)] | 34 | #[derive(Debug, PartialEq, Eq)] |
@@ -93,4 +95,11 @@ impl DefDiagnostic { | |||
93 | ) -> Self { | 95 | ) -> Self { |
94 | Self { in_module: container, kind: DefDiagnosticKind::UnresolvedMacroCall { ast, path } } | 96 | Self { in_module: container, kind: DefDiagnosticKind::UnresolvedMacroCall { ast, path } } |
95 | } | 97 | } |
98 | |||
99 | pub(super) fn unimplemented_builtin_macro( | ||
100 | container: LocalModuleId, | ||
101 | ast: AstId<ast::Macro>, | ||
102 | ) -> Self { | ||
103 | Self { in_module: container, kind: DefDiagnosticKind::UnimplementedBuiltinMacro { ast } } | ||
104 | } | ||
96 | } | 105 | } |
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index a9c1e13e2..e840fe5e8 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs | |||
@@ -298,6 +298,13 @@ impl TestDB { | |||
298 | DefDiagnosticKind::MacroError { ast, message } => { | 298 | DefDiagnosticKind::MacroError { ast, message } => { |
299 | (ast.to_node(self.upcast()), message.as_str()) | 299 | (ast.to_node(self.upcast()), message.as_str()) |
300 | } | 300 | } |
301 | DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { | ||
302 | let node = ast.to_node(self.upcast()); | ||
303 | ( | ||
304 | InFile::new(ast.file_id, node.syntax().clone()), | ||
305 | "UnimplementedBuiltinMacro", | ||
306 | ) | ||
307 | } | ||
301 | }; | 308 | }; |
302 | 309 | ||
303 | let frange = node.as_ref().original_file_range(self); | 310 | let frange = node.as_ref().original_file_range(self); |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 97507305c..41ef45326 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -119,7 +119,7 @@ impl<'a> InferenceContext<'a> { | |||
119 | } | 119 | } |
120 | 120 | ||
121 | fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { | 121 | fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { |
122 | self.db.check_canceled(); | 122 | self.db.unwind_if_cancelled(); |
123 | 123 | ||
124 | let body = Arc::clone(&self.body); // avoid borrow checker problem | 124 | let body = Arc::clone(&self.body); // avoid borrow checker problem |
125 | let ty = match &body[tgt_expr] { | 125 | let ty = match &body[tgt_expr] { |
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs index 294cb531c..f589b314b 100644 --- a/crates/hir_ty/src/traits.rs +++ b/crates/hir_ty/src/traits.rs | |||
@@ -112,7 +112,7 @@ fn solve( | |||
112 | let fuel = std::cell::Cell::new(CHALK_SOLVER_FUEL); | 112 | let fuel = std::cell::Cell::new(CHALK_SOLVER_FUEL); |
113 | 113 | ||
114 | let should_continue = || { | 114 | let should_continue = || { |
115 | context.db.check_canceled(); | 115 | db.unwind_if_cancelled(); |
116 | let remaining = fuel.get(); | 116 | let remaining = fuel.get(); |
117 | fuel.set(remaining - 1); | 117 | fuel.set(remaining - 1); |
118 | if remaining == 0 { | 118 | if remaining == 0 { |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 6cf5810fa..d5c954b8b 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -182,6 +182,11 @@ pub(crate) fn diagnostics( | |||
182 | res.borrow_mut() | 182 | res.borrow_mut() |
183 | .push(Diagnostic::error(display_range, d.message()).with_code(Some(d.code()))); | 183 | .push(Diagnostic::error(display_range, d.message()).with_code(Some(d.code()))); |
184 | }) | 184 | }) |
185 | .on::<hir::diagnostics::UnimplementedBuiltinMacro, _>(|d| { | ||
186 | let display_range = sema.diagnostics_display_range(d.display_source()).range; | ||
187 | res.borrow_mut() | ||
188 | .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code()))); | ||
189 | }) | ||
185 | // Only collect experimental diagnostics when they're enabled. | 190 | // Only collect experimental diagnostics when they're enabled. |
186 | .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) | 191 | .filter(|diag| !(diag.is_experimental() && config.disable_experimental)) |
187 | .filter(|diag| !config.disabled.contains(diag.code().as_str())); | 192 | .filter(|diag| !config.disabled.contains(diag.code().as_str())); |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index ff2a54117..97c9e5d2b 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -58,7 +58,7 @@ use cfg::CfgOptions; | |||
58 | 58 | ||
59 | use ide_db::base_db::{ | 59 | use ide_db::base_db::{ |
60 | salsa::{self, ParallelDatabase}, | 60 | salsa::{self, ParallelDatabase}, |
61 | CheckCanceled, Env, FileLoader, FileSet, SourceDatabase, VfsPath, | 61 | Env, FileLoader, FileSet, SourceDatabase, VfsPath, |
62 | }; | 62 | }; |
63 | use ide_db::{ | 63 | use ide_db::{ |
64 | symbol_index::{self, FileSymbol}, | 64 | symbol_index::{self, FileSymbol}, |
@@ -98,7 +98,7 @@ pub use ide_completion::{ | |||
98 | }; | 98 | }; |
99 | pub use ide_db::{ | 99 | pub use ide_db::{ |
100 | base_db::{ | 100 | base_db::{ |
101 | Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, | 101 | Cancelled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, |
102 | SourceRoot, SourceRootId, | 102 | SourceRoot, SourceRootId, |
103 | }, | 103 | }, |
104 | call_info::CallInfo, | 104 | call_info::CallInfo, |
@@ -113,7 +113,7 @@ pub use ide_ssr::SsrError; | |||
113 | pub use syntax::{TextRange, TextSize}; | 113 | pub use syntax::{TextRange, TextSize}; |
114 | pub use text_edit::{Indel, TextEdit}; | 114 | pub use text_edit::{Indel, TextEdit}; |
115 | 115 | ||
116 | pub type Cancelable<T> = Result<T, Canceled>; | 116 | pub type Cancellable<T> = Result<T, Cancelled>; |
117 | 117 | ||
118 | /// Info associated with a text range. | 118 | /// Info associated with a text range. |
119 | #[derive(Debug)] | 119 | #[derive(Debug)] |
@@ -227,11 +227,11 @@ impl Analysis { | |||
227 | } | 227 | } |
228 | 228 | ||
229 | /// Debug info about the current state of the analysis. | 229 | /// Debug info about the current state of the analysis. |
230 | pub fn status(&self, file_id: Option<FileId>) -> Cancelable<String> { | 230 | pub fn status(&self, file_id: Option<FileId>) -> Cancellable<String> { |
231 | self.with_db(|db| status::status(&*db, file_id)) | 231 | self.with_db(|db| status::status(&*db, file_id)) |
232 | } | 232 | } |
233 | 233 | ||
234 | pub fn prime_caches<F>(&self, cb: F) -> Cancelable<()> | 234 | pub fn prime_caches<F>(&self, cb: F) -> Cancellable<()> |
235 | where | 235 | where |
236 | F: Fn(PrimeCachesProgress) + Sync + std::panic::UnwindSafe, | 236 | F: Fn(PrimeCachesProgress) + Sync + std::panic::UnwindSafe, |
237 | { | 237 | { |
@@ -239,35 +239,35 @@ impl Analysis { | |||
239 | } | 239 | } |
240 | 240 | ||
241 | /// Gets the text of the source file. | 241 | /// Gets the text of the source file. |
242 | pub fn file_text(&self, file_id: FileId) -> Cancelable<Arc<String>> { | 242 | pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<String>> { |
243 | self.with_db(|db| db.file_text(file_id)) | 243 | self.with_db(|db| db.file_text(file_id)) |
244 | } | 244 | } |
245 | 245 | ||
246 | /// Gets the syntax tree of the file. | 246 | /// Gets the syntax tree of the file. |
247 | pub fn parse(&self, file_id: FileId) -> Cancelable<SourceFile> { | 247 | pub fn parse(&self, file_id: FileId) -> Cancellable<SourceFile> { |
248 | self.with_db(|db| db.parse(file_id).tree()) | 248 | self.with_db(|db| db.parse(file_id).tree()) |
249 | } | 249 | } |
250 | 250 | ||
251 | /// Returns true if this file belongs to an immutable library. | 251 | /// Returns true if this file belongs to an immutable library. |
252 | pub fn is_library_file(&self, file_id: FileId) -> Cancelable<bool> { | 252 | pub fn is_library_file(&self, file_id: FileId) -> Cancellable<bool> { |
253 | use ide_db::base_db::SourceDatabaseExt; | 253 | use ide_db::base_db::SourceDatabaseExt; |
254 | self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library) | 254 | self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library) |
255 | } | 255 | } |
256 | 256 | ||
257 | /// Gets the file's `LineIndex`: data structure to convert between absolute | 257 | /// Gets the file's `LineIndex`: data structure to convert between absolute |
258 | /// offsets and line/column representation. | 258 | /// offsets and line/column representation. |
259 | pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> { | 259 | pub fn file_line_index(&self, file_id: FileId) -> Cancellable<Arc<LineIndex>> { |
260 | self.with_db(|db| db.line_index(file_id)) | 260 | self.with_db(|db| db.line_index(file_id)) |
261 | } | 261 | } |
262 | 262 | ||
263 | /// Selects the next syntactic nodes encompassing the range. | 263 | /// Selects the next syntactic nodes encompassing the range. |
264 | pub fn extend_selection(&self, frange: FileRange) -> Cancelable<TextRange> { | 264 | pub fn extend_selection(&self, frange: FileRange) -> Cancellable<TextRange> { |
265 | self.with_db(|db| extend_selection::extend_selection(db, frange)) | 265 | self.with_db(|db| extend_selection::extend_selection(db, frange)) |
266 | } | 266 | } |
267 | 267 | ||
268 | /// Returns position of the matching brace (all types of braces are | 268 | /// Returns position of the matching brace (all types of braces are |
269 | /// supported). | 269 | /// supported). |
270 | pub fn matching_brace(&self, position: FilePosition) -> Cancelable<Option<TextSize>> { | 270 | pub fn matching_brace(&self, position: FilePosition) -> Cancellable<Option<TextSize>> { |
271 | self.with_db(|db| { | 271 | self.with_db(|db| { |
272 | let parse = db.parse(position.file_id); | 272 | let parse = db.parse(position.file_id); |
273 | let file = parse.tree(); | 273 | let file = parse.tree(); |
@@ -281,30 +281,30 @@ impl Analysis { | |||
281 | &self, | 281 | &self, |
282 | file_id: FileId, | 282 | file_id: FileId, |
283 | text_range: Option<TextRange>, | 283 | text_range: Option<TextRange>, |
284 | ) -> Cancelable<String> { | 284 | ) -> Cancellable<String> { |
285 | self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) | 285 | self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) |
286 | } | 286 | } |
287 | 287 | ||
288 | pub fn view_hir(&self, position: FilePosition) -> Cancelable<String> { | 288 | pub fn view_hir(&self, position: FilePosition) -> Cancellable<String> { |
289 | self.with_db(|db| view_hir::view_hir(&db, position)) | 289 | self.with_db(|db| view_hir::view_hir(&db, position)) |
290 | } | 290 | } |
291 | 291 | ||
292 | pub fn view_item_tree(&self, file_id: FileId) -> Cancelable<String> { | 292 | pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> { |
293 | self.with_db(|db| view_item_tree::view_item_tree(&db, file_id)) | 293 | self.with_db(|db| view_item_tree::view_item_tree(&db, file_id)) |
294 | } | 294 | } |
295 | 295 | ||
296 | /// Renders the crate graph to GraphViz "dot" syntax. | 296 | /// Renders the crate graph to GraphViz "dot" syntax. |
297 | pub fn view_crate_graph(&self) -> Cancelable<Result<String, String>> { | 297 | pub fn view_crate_graph(&self) -> Cancellable<Result<String, String>> { |
298 | self.with_db(|db| view_crate_graph::view_crate_graph(&db)) | 298 | self.with_db(|db| view_crate_graph::view_crate_graph(&db)) |
299 | } | 299 | } |
300 | 300 | ||
301 | pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> { | 301 | pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> { |
302 | self.with_db(|db| expand_macro::expand_macro(db, position)) | 302 | self.with_db(|db| expand_macro::expand_macro(db, position)) |
303 | } | 303 | } |
304 | 304 | ||
305 | /// Returns an edit to remove all newlines in the range, cleaning up minor | 305 | /// Returns an edit to remove all newlines in the range, cleaning up minor |
306 | /// stuff like trailing commas. | 306 | /// stuff like trailing commas. |
307 | pub fn join_lines(&self, frange: FileRange) -> Cancelable<TextEdit> { | 307 | pub fn join_lines(&self, frange: FileRange) -> Cancellable<TextEdit> { |
308 | self.with_db(|db| { | 308 | self.with_db(|db| { |
309 | let parse = db.parse(frange.file_id); | 309 | let parse = db.parse(frange.file_id); |
310 | join_lines::join_lines(&parse.tree(), frange.range) | 310 | join_lines::join_lines(&parse.tree(), frange.range) |
@@ -314,7 +314,7 @@ impl Analysis { | |||
314 | /// Returns an edit which should be applied when opening a new line, fixing | 314 | /// Returns an edit which should be applied when opening a new line, fixing |
315 | /// up minor stuff like continuing the comment. | 315 | /// up minor stuff like continuing the comment. |
316 | /// The edit will be a snippet (with `$0`). | 316 | /// The edit will be a snippet (with `$0`). |
317 | pub fn on_enter(&self, position: FilePosition) -> Cancelable<Option<TextEdit>> { | 317 | pub fn on_enter(&self, position: FilePosition) -> Cancellable<Option<TextEdit>> { |
318 | self.with_db(|db| typing::on_enter(&db, position)) | 318 | self.with_db(|db| typing::on_enter(&db, position)) |
319 | } | 319 | } |
320 | 320 | ||
@@ -326,7 +326,7 @@ impl Analysis { | |||
326 | &self, | 326 | &self, |
327 | position: FilePosition, | 327 | position: FilePosition, |
328 | char_typed: char, | 328 | char_typed: char, |
329 | ) -> Cancelable<Option<SourceChange>> { | 329 | ) -> Cancellable<Option<SourceChange>> { |
330 | // Fast path to not even parse the file. | 330 | // Fast path to not even parse the file. |
331 | if !typing::TRIGGER_CHARS.contains(char_typed) { | 331 | if !typing::TRIGGER_CHARS.contains(char_typed) { |
332 | return Ok(None); | 332 | return Ok(None); |
@@ -336,7 +336,7 @@ impl Analysis { | |||
336 | 336 | ||
337 | /// Returns a tree representation of symbols in the file. Useful to draw a | 337 | /// Returns a tree representation of symbols in the file. Useful to draw a |
338 | /// file outline. | 338 | /// file outline. |
339 | pub fn file_structure(&self, file_id: FileId) -> Cancelable<Vec<StructureNode>> { | 339 | pub fn file_structure(&self, file_id: FileId) -> Cancellable<Vec<StructureNode>> { |
340 | self.with_db(|db| file_structure::file_structure(&db.parse(file_id).tree())) | 340 | self.with_db(|db| file_structure::file_structure(&db.parse(file_id).tree())) |
341 | } | 341 | } |
342 | 342 | ||
@@ -345,17 +345,17 @@ impl Analysis { | |||
345 | &self, | 345 | &self, |
346 | file_id: FileId, | 346 | file_id: FileId, |
347 | config: &InlayHintsConfig, | 347 | config: &InlayHintsConfig, |
348 | ) -> Cancelable<Vec<InlayHint>> { | 348 | ) -> Cancellable<Vec<InlayHint>> { |
349 | self.with_db(|db| inlay_hints::inlay_hints(db, file_id, config)) | 349 | self.with_db(|db| inlay_hints::inlay_hints(db, file_id, config)) |
350 | } | 350 | } |
351 | 351 | ||
352 | /// Returns the set of folding ranges. | 352 | /// Returns the set of folding ranges. |
353 | pub fn folding_ranges(&self, file_id: FileId) -> Cancelable<Vec<Fold>> { | 353 | pub fn folding_ranges(&self, file_id: FileId) -> Cancellable<Vec<Fold>> { |
354 | self.with_db(|db| folding_ranges::folding_ranges(&db.parse(file_id).tree())) | 354 | self.with_db(|db| folding_ranges::folding_ranges(&db.parse(file_id).tree())) |
355 | } | 355 | } |
356 | 356 | ||
357 | /// Fuzzy searches for a symbol. | 357 | /// Fuzzy searches for a symbol. |
358 | pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> { | 358 | pub fn symbol_search(&self, query: Query) -> Cancellable<Vec<NavigationTarget>> { |
359 | self.with_db(|db| { | 359 | self.with_db(|db| { |
360 | symbol_index::world_symbols(db, query) | 360 | symbol_index::world_symbols(db, query) |
361 | .into_iter() | 361 | .into_iter() |
@@ -368,7 +368,7 @@ impl Analysis { | |||
368 | pub fn goto_definition( | 368 | pub fn goto_definition( |
369 | &self, | 369 | &self, |
370 | position: FilePosition, | 370 | position: FilePosition, |
371 | ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { | 371 | ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> { |
372 | self.with_db(|db| goto_definition::goto_definition(db, position)) | 372 | self.with_db(|db| goto_definition::goto_definition(db, position)) |
373 | } | 373 | } |
374 | 374 | ||
@@ -376,7 +376,7 @@ impl Analysis { | |||
376 | pub fn goto_implementation( | 376 | pub fn goto_implementation( |
377 | &self, | 377 | &self, |
378 | position: FilePosition, | 378 | position: FilePosition, |
379 | ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { | 379 | ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> { |
380 | self.with_db(|db| goto_implementation::goto_implementation(db, position)) | 380 | self.with_db(|db| goto_implementation::goto_implementation(db, position)) |
381 | } | 381 | } |
382 | 382 | ||
@@ -384,7 +384,7 @@ impl Analysis { | |||
384 | pub fn goto_type_definition( | 384 | pub fn goto_type_definition( |
385 | &self, | 385 | &self, |
386 | position: FilePosition, | 386 | position: FilePosition, |
387 | ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { | 387 | ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> { |
388 | self.with_db(|db| goto_type_definition::goto_type_definition(db, position)) | 388 | self.with_db(|db| goto_type_definition::goto_type_definition(db, position)) |
389 | } | 389 | } |
390 | 390 | ||
@@ -393,12 +393,12 @@ impl Analysis { | |||
393 | &self, | 393 | &self, |
394 | position: FilePosition, | 394 | position: FilePosition, |
395 | search_scope: Option<SearchScope>, | 395 | search_scope: Option<SearchScope>, |
396 | ) -> Cancelable<Option<ReferenceSearchResult>> { | 396 | ) -> Cancellable<Option<ReferenceSearchResult>> { |
397 | self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope)) | 397 | self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope)) |
398 | } | 398 | } |
399 | 399 | ||
400 | /// Finds all methods and free functions for the file. Does not return tests! | 400 | /// Finds all methods and free functions for the file. Does not return tests! |
401 | pub fn find_all_methods(&self, file_id: FileId) -> Cancelable<Vec<FileRange>> { | 401 | pub fn find_all_methods(&self, file_id: FileId) -> Cancellable<Vec<FileRange>> { |
402 | self.with_db(|db| fn_references::find_all_methods(db, file_id)) | 402 | self.with_db(|db| fn_references::find_all_methods(db, file_id)) |
403 | } | 403 | } |
404 | 404 | ||
@@ -408,7 +408,7 @@ impl Analysis { | |||
408 | position: FilePosition, | 408 | position: FilePosition, |
409 | links_in_hover: bool, | 409 | links_in_hover: bool, |
410 | markdown: bool, | 410 | markdown: bool, |
411 | ) -> Cancelable<Option<RangeInfo<HoverResult>>> { | 411 | ) -> Cancellable<Option<RangeInfo<HoverResult>>> { |
412 | self.with_db(|db| hover::hover(db, position, links_in_hover, markdown)) | 412 | self.with_db(|db| hover::hover(db, position, links_in_hover, markdown)) |
413 | } | 413 | } |
414 | 414 | ||
@@ -416,12 +416,12 @@ impl Analysis { | |||
416 | pub fn external_docs( | 416 | pub fn external_docs( |
417 | &self, | 417 | &self, |
418 | position: FilePosition, | 418 | position: FilePosition, |
419 | ) -> Cancelable<Option<doc_links::DocumentationLink>> { | 419 | ) -> Cancellable<Option<doc_links::DocumentationLink>> { |
420 | self.with_db(|db| doc_links::external_docs(db, &position)) | 420 | self.with_db(|db| doc_links::external_docs(db, &position)) |
421 | } | 421 | } |
422 | 422 | ||
423 | /// Computes parameter information for the given call expression. | 423 | /// Computes parameter information for the given call expression. |
424 | pub fn call_info(&self, position: FilePosition) -> Cancelable<Option<CallInfo>> { | 424 | pub fn call_info(&self, position: FilePosition) -> Cancellable<Option<CallInfo>> { |
425 | self.with_db(|db| ide_db::call_info::call_info(db, position)) | 425 | self.with_db(|db| ide_db::call_info::call_info(db, position)) |
426 | } | 426 | } |
427 | 427 | ||
@@ -429,42 +429,42 @@ impl Analysis { | |||
429 | pub fn call_hierarchy( | 429 | pub fn call_hierarchy( |
430 | &self, | 430 | &self, |
431 | position: FilePosition, | 431 | position: FilePosition, |
432 | ) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> { | 432 | ) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> { |
433 | self.with_db(|db| call_hierarchy::call_hierarchy(db, position)) | 433 | self.with_db(|db| call_hierarchy::call_hierarchy(db, position)) |
434 | } | 434 | } |
435 | 435 | ||
436 | /// Computes incoming calls for the given file position. | 436 | /// Computes incoming calls for the given file position. |
437 | pub fn incoming_calls(&self, position: FilePosition) -> Cancelable<Option<Vec<CallItem>>> { | 437 | pub fn incoming_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> { |
438 | self.with_db(|db| call_hierarchy::incoming_calls(db, position)) | 438 | self.with_db(|db| call_hierarchy::incoming_calls(db, position)) |
439 | } | 439 | } |
440 | 440 | ||
441 | /// Computes incoming calls for the given file position. | 441 | /// Computes incoming calls for the given file position. |
442 | pub fn outgoing_calls(&self, position: FilePosition) -> Cancelable<Option<Vec<CallItem>>> { | 442 | pub fn outgoing_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> { |
443 | self.with_db(|db| call_hierarchy::outgoing_calls(db, position)) | 443 | self.with_db(|db| call_hierarchy::outgoing_calls(db, position)) |
444 | } | 444 | } |
445 | 445 | ||
446 | /// Returns a `mod name;` declaration which created the current module. | 446 | /// Returns a `mod name;` declaration which created the current module. |
447 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { | 447 | pub fn parent_module(&self, position: FilePosition) -> Cancellable<Vec<NavigationTarget>> { |
448 | self.with_db(|db| parent_module::parent_module(db, position)) | 448 | self.with_db(|db| parent_module::parent_module(db, position)) |
449 | } | 449 | } |
450 | 450 | ||
451 | /// Returns crates this file belongs too. | 451 | /// Returns crates this file belongs too. |
452 | pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { | 452 | pub fn crate_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> { |
453 | self.with_db(|db| parent_module::crate_for(db, file_id)) | 453 | self.with_db(|db| parent_module::crate_for(db, file_id)) |
454 | } | 454 | } |
455 | 455 | ||
456 | /// Returns the edition of the given crate. | 456 | /// Returns the edition of the given crate. |
457 | pub fn crate_edition(&self, crate_id: CrateId) -> Cancelable<Edition> { | 457 | pub fn crate_edition(&self, crate_id: CrateId) -> Cancellable<Edition> { |
458 | self.with_db(|db| db.crate_graph()[crate_id].edition) | 458 | self.with_db(|db| db.crate_graph()[crate_id].edition) |
459 | } | 459 | } |
460 | 460 | ||
461 | /// Returns the root file of the given crate. | 461 | /// Returns the root file of the given crate. |
462 | pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { | 462 | pub fn crate_root(&self, crate_id: CrateId) -> Cancellable<FileId> { |
463 | self.with_db(|db| db.crate_graph()[crate_id].root_file_id) | 463 | self.with_db(|db| db.crate_graph()[crate_id].root_file_id) |
464 | } | 464 | } |
465 | 465 | ||
466 | /// Returns the set of possible targets to run for the current file. | 466 | /// Returns the set of possible targets to run for the current file. |
467 | pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> { | 467 | pub fn runnables(&self, file_id: FileId) -> Cancellable<Vec<Runnable>> { |
468 | self.with_db(|db| runnables::runnables(db, file_id)) | 468 | self.with_db(|db| runnables::runnables(db, file_id)) |
469 | } | 469 | } |
470 | 470 | ||
@@ -473,24 +473,24 @@ impl Analysis { | |||
473 | &self, | 473 | &self, |
474 | position: FilePosition, | 474 | position: FilePosition, |
475 | search_scope: Option<SearchScope>, | 475 | search_scope: Option<SearchScope>, |
476 | ) -> Cancelable<Vec<Runnable>> { | 476 | ) -> Cancellable<Vec<Runnable>> { |
477 | self.with_db(|db| runnables::related_tests(db, position, search_scope)) | 477 | self.with_db(|db| runnables::related_tests(db, position, search_scope)) |
478 | } | 478 | } |
479 | 479 | ||
480 | /// Computes syntax highlighting for the given file | 480 | /// Computes syntax highlighting for the given file |
481 | pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HlRange>> { | 481 | pub fn highlight(&self, file_id: FileId) -> Cancellable<Vec<HlRange>> { |
482 | self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false)) | 482 | self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false)) |
483 | } | 483 | } |
484 | 484 | ||
485 | /// Computes syntax highlighting for the given file range. | 485 | /// Computes syntax highlighting for the given file range. |
486 | pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HlRange>> { | 486 | pub fn highlight_range(&self, frange: FileRange) -> Cancellable<Vec<HlRange>> { |
487 | self.with_db(|db| { | 487 | self.with_db(|db| { |
488 | syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false) | 488 | syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false) |
489 | }) | 489 | }) |
490 | } | 490 | } |
491 | 491 | ||
492 | /// Computes syntax highlighting for the given file. | 492 | /// Computes syntax highlighting for the given file. |
493 | pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancelable<String> { | 493 | pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancellable<String> { |
494 | self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow)) | 494 | self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow)) |
495 | } | 495 | } |
496 | 496 | ||
@@ -499,7 +499,7 @@ impl Analysis { | |||
499 | &self, | 499 | &self, |
500 | config: &CompletionConfig, | 500 | config: &CompletionConfig, |
501 | position: FilePosition, | 501 | position: FilePosition, |
502 | ) -> Cancelable<Option<Vec<CompletionItem>>> { | 502 | ) -> Cancellable<Option<Vec<CompletionItem>>> { |
503 | self.with_db(|db| ide_completion::completions(db, config, position).map(Into::into)) | 503 | self.with_db(|db| ide_completion::completions(db, config, position).map(Into::into)) |
504 | } | 504 | } |
505 | 505 | ||
@@ -510,7 +510,7 @@ impl Analysis { | |||
510 | position: FilePosition, | 510 | position: FilePosition, |
511 | full_import_path: &str, | 511 | full_import_path: &str, |
512 | imported_name: String, | 512 | imported_name: String, |
513 | ) -> Cancelable<Vec<TextEdit>> { | 513 | ) -> Cancellable<Vec<TextEdit>> { |
514 | Ok(self | 514 | Ok(self |
515 | .with_db(|db| { | 515 | .with_db(|db| { |
516 | ide_completion::resolve_completion_edits( | 516 | ide_completion::resolve_completion_edits( |
@@ -533,7 +533,7 @@ impl Analysis { | |||
533 | config: &AssistConfig, | 533 | config: &AssistConfig, |
534 | resolve: AssistResolveStrategy, | 534 | resolve: AssistResolveStrategy, |
535 | frange: FileRange, | 535 | frange: FileRange, |
536 | ) -> Cancelable<Vec<Assist>> { | 536 | ) -> Cancellable<Vec<Assist>> { |
537 | self.with_db(|db| { | 537 | self.with_db(|db| { |
538 | let ssr_assists = ssr::ssr_assists(db, &resolve, frange); | 538 | let ssr_assists = ssr::ssr_assists(db, &resolve, frange); |
539 | let mut acc = Assist::get(db, config, resolve, frange); | 539 | let mut acc = Assist::get(db, config, resolve, frange); |
@@ -548,7 +548,7 @@ impl Analysis { | |||
548 | config: &DiagnosticsConfig, | 548 | config: &DiagnosticsConfig, |
549 | resolve: AssistResolveStrategy, | 549 | resolve: AssistResolveStrategy, |
550 | file_id: FileId, | 550 | file_id: FileId, |
551 | ) -> Cancelable<Vec<Diagnostic>> { | 551 | ) -> Cancellable<Vec<Diagnostic>> { |
552 | self.with_db(|db| diagnostics::diagnostics(db, config, &resolve, file_id)) | 552 | self.with_db(|db| diagnostics::diagnostics(db, config, &resolve, file_id)) |
553 | } | 553 | } |
554 | 554 | ||
@@ -559,7 +559,7 @@ impl Analysis { | |||
559 | diagnostics_config: &DiagnosticsConfig, | 559 | diagnostics_config: &DiagnosticsConfig, |
560 | resolve: AssistResolveStrategy, | 560 | resolve: AssistResolveStrategy, |
561 | frange: FileRange, | 561 | frange: FileRange, |
562 | ) -> Cancelable<Vec<Assist>> { | 562 | ) -> Cancellable<Vec<Assist>> { |
563 | let include_fixes = match &assist_config.allowed { | 563 | let include_fixes = match &assist_config.allowed { |
564 | Some(it) => it.iter().any(|&it| it == AssistKind::None || it == AssistKind::QuickFix), | 564 | Some(it) => it.iter().any(|&it| it == AssistKind::None || it == AssistKind::QuickFix), |
565 | None => true, | 565 | None => true, |
@@ -591,14 +591,14 @@ impl Analysis { | |||
591 | &self, | 591 | &self, |
592 | position: FilePosition, | 592 | position: FilePosition, |
593 | new_name: &str, | 593 | new_name: &str, |
594 | ) -> Cancelable<Result<SourceChange, RenameError>> { | 594 | ) -> Cancellable<Result<SourceChange, RenameError>> { |
595 | self.with_db(|db| references::rename::rename(db, position, new_name)) | 595 | self.with_db(|db| references::rename::rename(db, position, new_name)) |
596 | } | 596 | } |
597 | 597 | ||
598 | pub fn prepare_rename( | 598 | pub fn prepare_rename( |
599 | &self, | 599 | &self, |
600 | position: FilePosition, | 600 | position: FilePosition, |
601 | ) -> Cancelable<Result<RangeInfo<()>, RenameError>> { | 601 | ) -> Cancellable<Result<RangeInfo<()>, RenameError>> { |
602 | self.with_db(|db| references::rename::prepare_rename(db, position)) | 602 | self.with_db(|db| references::rename::prepare_rename(db, position)) |
603 | } | 603 | } |
604 | 604 | ||
@@ -606,7 +606,7 @@ impl Analysis { | |||
606 | &self, | 606 | &self, |
607 | file_id: FileId, | 607 | file_id: FileId, |
608 | new_name_stem: &str, | 608 | new_name_stem: &str, |
609 | ) -> Cancelable<Option<SourceChange>> { | 609 | ) -> Cancellable<Option<SourceChange>> { |
610 | self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem)) | 610 | self.with_db(|db| references::rename::will_rename_file(db, file_id, new_name_stem)) |
611 | } | 611 | } |
612 | 612 | ||
@@ -616,7 +616,7 @@ impl Analysis { | |||
616 | parse_only: bool, | 616 | parse_only: bool, |
617 | resolve_context: FilePosition, | 617 | resolve_context: FilePosition, |
618 | selections: Vec<FileRange>, | 618 | selections: Vec<FileRange>, |
619 | ) -> Cancelable<Result<SourceChange, SsrError>> { | 619 | ) -> Cancellable<Result<SourceChange, SsrError>> { |
620 | self.with_db(|db| { | 620 | self.with_db(|db| { |
621 | let rule: ide_ssr::SsrRule = query.parse()?; | 621 | let rule: ide_ssr::SsrRule = query.parse()?; |
622 | let mut match_finder = | 622 | let mut match_finder = |
@@ -631,11 +631,11 @@ impl Analysis { | |||
631 | &self, | 631 | &self, |
632 | file_id: FileId, | 632 | file_id: FileId, |
633 | config: AnnotationConfig, | 633 | config: AnnotationConfig, |
634 | ) -> Cancelable<Vec<Annotation>> { | 634 | ) -> Cancellable<Vec<Annotation>> { |
635 | self.with_db(|db| annotations::annotations(db, file_id, config)) | 635 | self.with_db(|db| annotations::annotations(db, file_id, config)) |
636 | } | 636 | } |
637 | 637 | ||
638 | pub fn resolve_annotation(&self, annotation: Annotation) -> Cancelable<Annotation> { | 638 | pub fn resolve_annotation(&self, annotation: Annotation) -> Cancellable<Annotation> { |
639 | self.with_db(|db| annotations::resolve_annotation(db, annotation)) | 639 | self.with_db(|db| annotations::resolve_annotation(db, annotation)) |
640 | } | 640 | } |
641 | 641 | ||
@@ -643,16 +643,28 @@ impl Analysis { | |||
643 | &self, | 643 | &self, |
644 | range: FileRange, | 644 | range: FileRange, |
645 | direction: Direction, | 645 | direction: Direction, |
646 | ) -> Cancelable<Option<TextEdit>> { | 646 | ) -> Cancellable<Option<TextEdit>> { |
647 | self.with_db(|db| move_item::move_item(db, range, direction)) | 647 | self.with_db(|db| move_item::move_item(db, range, direction)) |
648 | } | 648 | } |
649 | 649 | ||
650 | /// Performs an operation on that may be Canceled. | 650 | /// Performs an operation on the database that may be canceled. |
651 | fn with_db<F, T>(&self, f: F) -> Cancelable<T> | 651 | /// |
652 | /// rust-analyzer needs to be able to answer semantic questions about the | ||
653 | /// code while the code is being modified. A common problem is that a | ||
654 | /// long-running query is being calculated when a new change arrives. | ||
655 | /// | ||
656 | /// We can't just apply the change immediately: this will cause the pending | ||
657 | /// query to see inconsistent state (it will observe an absence of | ||
658 | /// repeatable read). So what we do is we **cancel** all pending queries | ||
659 | /// before applying the change. | ||
660 | /// | ||
661 | /// Salsa implements cancelation by unwinding with a special value and | ||
662 | /// catching it on the API boundary. | ||
663 | fn with_db<F, T>(&self, f: F) -> Cancellable<T> | ||
652 | where | 664 | where |
653 | F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, | 665 | F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, |
654 | { | 666 | { |
655 | self.db.catch_canceled(f) | 667 | Cancelled::catch(|| f(&self.db)) |
656 | } | 668 | } |
657 | } | 669 | } |
658 | 670 | ||
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs index 151bf3783..ffdcdc930 100644 --- a/crates/ide_completion/src/completions.rs +++ b/crates/ide_completion/src/completions.rs | |||
@@ -69,18 +69,25 @@ impl Completions { | |||
69 | items.into_iter().for_each(|item| self.add(item.into())) | 69 | items.into_iter().for_each(|item| self.add(item.into())) |
70 | } | 70 | } |
71 | 71 | ||
72 | pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &hir::Type) { | 72 | pub(crate) fn add_field( |
73 | let item = render_field(RenderContext::new(ctx), field, ty); | 73 | &mut self, |
74 | ctx: &CompletionContext, | ||
75 | receiver: Option<hir::Name>, | ||
76 | field: hir::Field, | ||
77 | ty: &hir::Type, | ||
78 | ) { | ||
79 | let item = render_field(RenderContext::new(ctx), receiver, field, ty); | ||
74 | self.add(item); | 80 | self.add(item); |
75 | } | 81 | } |
76 | 82 | ||
77 | pub(crate) fn add_tuple_field( | 83 | pub(crate) fn add_tuple_field( |
78 | &mut self, | 84 | &mut self, |
79 | ctx: &CompletionContext, | 85 | ctx: &CompletionContext, |
86 | receiver: Option<hir::Name>, | ||
80 | field: usize, | 87 | field: usize, |
81 | ty: &hir::Type, | 88 | ty: &hir::Type, |
82 | ) { | 89 | ) { |
83 | let item = render_tuple_field(RenderContext::new(ctx), field, ty); | 90 | let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty); |
84 | self.add(item); | 91 | self.add(item); |
85 | } | 92 | } |
86 | 93 | ||
@@ -132,9 +139,11 @@ impl Completions { | |||
132 | &mut self, | 139 | &mut self, |
133 | ctx: &CompletionContext, | 140 | ctx: &CompletionContext, |
134 | func: hir::Function, | 141 | func: hir::Function, |
142 | receiver: Option<hir::Name>, | ||
135 | local_name: Option<hir::Name>, | 143 | local_name: Option<hir::Name>, |
136 | ) { | 144 | ) { |
137 | if let Some(item) = render_method(RenderContext::new(ctx), None, local_name, func) { | 145 | if let Some(item) = render_method(RenderContext::new(ctx), None, receiver, local_name, func) |
146 | { | ||
138 | self.add(item) | 147 | self.add(item) |
139 | } | 148 | } |
140 | } | 149 | } |
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index fd9738743..302c9ccbd 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! Completes references after dot (fields and method calls). | 1 | //! Completes references after dot (fields and method calls). |
2 | 2 | ||
3 | use hir::{HasVisibility, Type}; | 3 | use either::Either; |
4 | use hir::{HasVisibility, ScopeDef}; | ||
4 | use rustc_hash::FxHashSet; | 5 | use rustc_hash::FxHashSet; |
5 | 6 | ||
6 | use crate::{context::CompletionContext, Completions}; | 7 | use crate::{context::CompletionContext, Completions}; |
@@ -9,7 +10,7 @@ use crate::{context::CompletionContext, Completions}; | |||
9 | pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | 10 | pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { |
10 | let dot_receiver = match &ctx.dot_receiver { | 11 | let dot_receiver = match &ctx.dot_receiver { |
11 | Some(expr) => expr, | 12 | Some(expr) => expr, |
12 | _ => return, | 13 | _ => return complete_undotted_self(acc, ctx), |
13 | }; | 14 | }; |
14 | 15 | ||
15 | let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { | 16 | let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { |
@@ -20,12 +21,43 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
20 | if ctx.is_call { | 21 | if ctx.is_call { |
21 | cov_mark::hit!(test_no_struct_field_completion_for_method_call); | 22 | cov_mark::hit!(test_no_struct_field_completion_for_method_call); |
22 | } else { | 23 | } else { |
23 | complete_fields(acc, ctx, &receiver_ty); | 24 | complete_fields(ctx, &receiver_ty, |field, ty| match field { |
25 | Either::Left(field) => acc.add_field(ctx, None, field, &ty), | ||
26 | Either::Right(tuple_idx) => acc.add_tuple_field(ctx, None, tuple_idx, &ty), | ||
27 | }); | ||
24 | } | 28 | } |
25 | complete_methods(acc, ctx, &receiver_ty); | 29 | complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None)); |
30 | } | ||
31 | |||
32 | fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) { | ||
33 | if !ctx.is_trivial_path || !ctx.config.enable_self_on_the_fly { | ||
34 | return; | ||
35 | } | ||
36 | ctx.scope.process_all_names(&mut |name, def| { | ||
37 | if let ScopeDef::Local(local) = &def { | ||
38 | if local.is_self(ctx.db) { | ||
39 | let ty = local.ty(ctx.db); | ||
40 | complete_fields(ctx, &ty, |field, ty| match field { | ||
41 | either::Either::Left(field) => { | ||
42 | acc.add_field(ctx, Some(name.clone()), field, &ty) | ||
43 | } | ||
44 | either::Either::Right(tuple_idx) => { | ||
45 | acc.add_tuple_field(ctx, Some(name.clone()), tuple_idx, &ty) | ||
46 | } | ||
47 | }); | ||
48 | complete_methods(ctx, &ty, |func| { | ||
49 | acc.add_method(ctx, func, Some(name.clone()), None) | ||
50 | }); | ||
51 | } | ||
52 | } | ||
53 | }); | ||
26 | } | 54 | } |
27 | 55 | ||
28 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 56 | fn complete_fields( |
57 | ctx: &CompletionContext, | ||
58 | receiver: &hir::Type, | ||
59 | mut f: impl FnMut(Either<hir::Field, usize>, hir::Type), | ||
60 | ) { | ||
29 | for receiver in receiver.autoderef(ctx.db) { | 61 | for receiver in receiver.autoderef(ctx.db) { |
30 | for (field, ty) in receiver.fields(ctx.db) { | 62 | for (field, ty) in receiver.fields(ctx.db) { |
31 | if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { | 63 | if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { |
@@ -33,16 +65,20 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty | |||
33 | // field is editable, we should show the completion | 65 | // field is editable, we should show the completion |
34 | continue; | 66 | continue; |
35 | } | 67 | } |
36 | acc.add_field(ctx, field, &ty); | 68 | f(Either::Left(field), ty); |
37 | } | 69 | } |
38 | for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { | 70 | for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { |
39 | // FIXME: Handle visibility | 71 | // FIXME: Handle visibility |
40 | acc.add_tuple_field(ctx, i, &ty); | 72 | f(Either::Right(i), ty); |
41 | } | 73 | } |
42 | } | 74 | } |
43 | } | 75 | } |
44 | 76 | ||
45 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { | 77 | fn complete_methods( |
78 | ctx: &CompletionContext, | ||
79 | receiver: &hir::Type, | ||
80 | mut f: impl FnMut(hir::Function), | ||
81 | ) { | ||
46 | if let Some(krate) = ctx.krate { | 82 | if let Some(krate) = ctx.krate { |
47 | let mut seen_methods = FxHashSet::default(); | 83 | let mut seen_methods = FxHashSet::default(); |
48 | let traits_in_scope = ctx.scope.traits_in_scope(); | 84 | let traits_in_scope = ctx.scope.traits_in_scope(); |
@@ -51,7 +87,7 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T | |||
51 | && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m)) | 87 | && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m)) |
52 | && seen_methods.insert(func.name(ctx.db)) | 88 | && seen_methods.insert(func.name(ctx.db)) |
53 | { | 89 | { |
54 | acc.add_method(ctx, func, None); | 90 | f(func); |
55 | } | 91 | } |
56 | None::<()> | 92 | None::<()> |
57 | }); | 93 | }); |
@@ -484,4 +520,34 @@ impl S { | |||
484 | "#]], | 520 | "#]], |
485 | ); | 521 | ); |
486 | } | 522 | } |
523 | |||
524 | #[test] | ||
525 | fn completes_bare_fields_and_methods_in_methods() { | ||
526 | check( | ||
527 | r#" | ||
528 | struct Foo { field: i32 } | ||
529 | |||
530 | impl Foo { fn foo(&self) { $0 } }"#, | ||
531 | expect![[r#" | ||
532 | lc self &Foo | ||
533 | sp Self | ||
534 | st Foo | ||
535 | fd self.field i32 | ||
536 | me self.foo() fn(&self) | ||
537 | "#]], | ||
538 | ); | ||
539 | check( | ||
540 | r#" | ||
541 | struct Foo(i32); | ||
542 | |||
543 | impl Foo { fn foo(&mut self) { $0 } }"#, | ||
544 | expect![[r#" | ||
545 | lc self &mut Foo | ||
546 | sp Self | ||
547 | st Foo | ||
548 | fd self.0 i32 | ||
549 | me self.foo() fn(&mut self) | ||
550 | "#]], | ||
551 | ); | ||
552 | } | ||
487 | } | 553 | } |
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs index 227c08d01..0ac47cdbe 100644 --- a/crates/ide_completion/src/completions/record.rs +++ b/crates/ide_completion/src/completions/record.rs | |||
@@ -39,7 +39,7 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> | |||
39 | }; | 39 | }; |
40 | 40 | ||
41 | for (field, ty) in missing_fields { | 41 | for (field, ty) in missing_fields { |
42 | acc.add_field(ctx, field, &ty); | 42 | acc.add_field(ctx, None, field, &ty); |
43 | } | 43 | } |
44 | 44 | ||
45 | Some(()) | 45 | Some(()) |
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index 9db8516d0..20188a7dd 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -11,6 +11,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
11 | if ctx.is_path_disallowed() || ctx.expects_item() { | 11 | if ctx.is_path_disallowed() || ctx.expects_item() { |
12 | return; | 12 | return; |
13 | } | 13 | } |
14 | |||
14 | if ctx.expects_assoc_item() { | 15 | if ctx.expects_assoc_item() { |
15 | ctx.scope.process_all_names(&mut |name, def| { | 16 | ctx.scope.process_all_names(&mut |name, def| { |
16 | if let ScopeDef::MacroDef(macro_def) = def { | 17 | if let ScopeDef::MacroDef(macro_def) = def { |
@@ -32,6 +33,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
32 | }); | 33 | }); |
33 | return; | 34 | return; |
34 | } | 35 | } |
36 | |||
35 | if let Some(hir::Adt::Enum(e)) = | 37 | if let Some(hir::Adt::Enum(e)) = |
36 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) | 38 | ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) |
37 | { | 39 | { |
diff --git a/crates/ide_completion/src/config.rs b/crates/ide_completion/src/config.rs index d70ed6c1c..c300ce887 100644 --- a/crates/ide_completion/src/config.rs +++ b/crates/ide_completion/src/config.rs | |||
@@ -10,6 +10,7 @@ use ide_db::helpers::{insert_use::InsertUseConfig, SnippetCap}; | |||
10 | pub struct CompletionConfig { | 10 | pub struct CompletionConfig { |
11 | pub enable_postfix_completions: bool, | 11 | pub enable_postfix_completions: bool, |
12 | pub enable_imports_on_the_fly: bool, | 12 | pub enable_imports_on_the_fly: bool, |
13 | pub enable_self_on_the_fly: bool, | ||
13 | pub add_call_parenthesis: bool, | 14 | pub add_call_parenthesis: bool, |
14 | pub add_call_argument_snippets: bool, | 15 | pub add_call_argument_snippets: bool, |
15 | pub snippet_cap: Option<SnippetCap>, | 16 | pub snippet_cap: Option<SnippetCap>, |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 425dd0247..a49a60711 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -25,18 +25,20 @@ use crate::{ | |||
25 | 25 | ||
26 | pub(crate) fn render_field<'a>( | 26 | pub(crate) fn render_field<'a>( |
27 | ctx: RenderContext<'a>, | 27 | ctx: RenderContext<'a>, |
28 | receiver: Option<hir::Name>, | ||
28 | field: hir::Field, | 29 | field: hir::Field, |
29 | ty: &hir::Type, | 30 | ty: &hir::Type, |
30 | ) -> CompletionItem { | 31 | ) -> CompletionItem { |
31 | Render::new(ctx).render_field(field, ty) | 32 | Render::new(ctx).render_field(receiver, field, ty) |
32 | } | 33 | } |
33 | 34 | ||
34 | pub(crate) fn render_tuple_field<'a>( | 35 | pub(crate) fn render_tuple_field<'a>( |
35 | ctx: RenderContext<'a>, | 36 | ctx: RenderContext<'a>, |
37 | receiver: Option<hir::Name>, | ||
36 | field: usize, | 38 | field: usize, |
37 | ty: &hir::Type, | 39 | ty: &hir::Type, |
38 | ) -> CompletionItem { | 40 | ) -> CompletionItem { |
39 | Render::new(ctx).render_tuple_field(field, ty) | 41 | Render::new(ctx).render_tuple_field(receiver, field, ty) |
40 | } | 42 | } |
41 | 43 | ||
42 | pub(crate) fn render_resolution<'a>( | 44 | pub(crate) fn render_resolution<'a>( |
@@ -126,11 +128,19 @@ impl<'a> Render<'a> { | |||
126 | Render { ctx } | 128 | Render { ctx } |
127 | } | 129 | } |
128 | 130 | ||
129 | fn render_field(&self, field: hir::Field, ty: &hir::Type) -> CompletionItem { | 131 | fn render_field( |
132 | &self, | ||
133 | receiver: Option<hir::Name>, | ||
134 | field: hir::Field, | ||
135 | ty: &hir::Type, | ||
136 | ) -> CompletionItem { | ||
130 | let is_deprecated = self.ctx.is_deprecated(field); | 137 | let is_deprecated = self.ctx.is_deprecated(field); |
131 | let name = field.name(self.ctx.db()).to_string(); | 138 | let name = field.name(self.ctx.db()).to_string(); |
132 | let mut item = | 139 | let mut item = CompletionItem::new( |
133 | CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name.clone()); | 140 | CompletionKind::Reference, |
141 | self.ctx.source_range(), | ||
142 | receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name)), | ||
143 | ); | ||
134 | item.kind(SymbolKind::Field) | 144 | item.kind(SymbolKind::Field) |
135 | .detail(ty.display(self.ctx.db()).to_string()) | 145 | .detail(ty.display(self.ctx.db()).to_string()) |
136 | .set_documentation(field.docs(self.ctx.db())) | 146 | .set_documentation(field.docs(self.ctx.db())) |
@@ -151,11 +161,17 @@ impl<'a> Render<'a> { | |||
151 | item.build() | 161 | item.build() |
152 | } | 162 | } |
153 | 163 | ||
154 | fn render_tuple_field(&self, field: usize, ty: &hir::Type) -> CompletionItem { | 164 | fn render_tuple_field( |
165 | &self, | ||
166 | receiver: Option<hir::Name>, | ||
167 | field: usize, | ||
168 | ty: &hir::Type, | ||
169 | ) -> CompletionItem { | ||
155 | let mut item = CompletionItem::new( | 170 | let mut item = CompletionItem::new( |
156 | CompletionKind::Reference, | 171 | CompletionKind::Reference, |
157 | self.ctx.source_range(), | 172 | self.ctx.source_range(), |
158 | field.to_string(), | 173 | receiver |
174 | .map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)), | ||
159 | ); | 175 | ); |
160 | 176 | ||
161 | item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string()); | 177 | item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string()); |
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs index 63bd66926..3ec77ca0f 100644 --- a/crates/ide_completion/src/render/function.rs +++ b/crates/ide_completion/src/render/function.rs | |||
@@ -20,23 +20,25 @@ pub(crate) fn render_fn<'a>( | |||
20 | fn_: hir::Function, | 20 | fn_: hir::Function, |
21 | ) -> Option<CompletionItem> { | 21 | ) -> Option<CompletionItem> { |
22 | let _p = profile::span("render_fn"); | 22 | let _p = profile::span("render_fn"); |
23 | Some(FunctionRender::new(ctx, local_name, fn_, false)?.render(import_to_add)) | 23 | Some(FunctionRender::new(ctx, None, local_name, fn_, false)?.render(import_to_add)) |
24 | } | 24 | } |
25 | 25 | ||
26 | pub(crate) fn render_method<'a>( | 26 | pub(crate) fn render_method<'a>( |
27 | ctx: RenderContext<'a>, | 27 | ctx: RenderContext<'a>, |
28 | import_to_add: Option<ImportEdit>, | 28 | import_to_add: Option<ImportEdit>, |
29 | receiver: Option<hir::Name>, | ||
29 | local_name: Option<hir::Name>, | 30 | local_name: Option<hir::Name>, |
30 | fn_: hir::Function, | 31 | fn_: hir::Function, |
31 | ) -> Option<CompletionItem> { | 32 | ) -> Option<CompletionItem> { |
32 | let _p = profile::span("render_method"); | 33 | let _p = profile::span("render_method"); |
33 | Some(FunctionRender::new(ctx, local_name, fn_, true)?.render(import_to_add)) | 34 | Some(FunctionRender::new(ctx, receiver, local_name, fn_, true)?.render(import_to_add)) |
34 | } | 35 | } |
35 | 36 | ||
36 | #[derive(Debug)] | 37 | #[derive(Debug)] |
37 | struct FunctionRender<'a> { | 38 | struct FunctionRender<'a> { |
38 | ctx: RenderContext<'a>, | 39 | ctx: RenderContext<'a>, |
39 | name: String, | 40 | name: String, |
41 | receiver: Option<hir::Name>, | ||
40 | func: hir::Function, | 42 | func: hir::Function, |
41 | ast_node: Fn, | 43 | ast_node: Fn, |
42 | is_method: bool, | 44 | is_method: bool, |
@@ -45,6 +47,7 @@ struct FunctionRender<'a> { | |||
45 | impl<'a> FunctionRender<'a> { | 47 | impl<'a> FunctionRender<'a> { |
46 | fn new( | 48 | fn new( |
47 | ctx: RenderContext<'a>, | 49 | ctx: RenderContext<'a>, |
50 | receiver: Option<hir::Name>, | ||
48 | local_name: Option<hir::Name>, | 51 | local_name: Option<hir::Name>, |
49 | fn_: hir::Function, | 52 | fn_: hir::Function, |
50 | is_method: bool, | 53 | is_method: bool, |
@@ -52,11 +55,14 @@ impl<'a> FunctionRender<'a> { | |||
52 | let name = local_name.unwrap_or_else(|| fn_.name(ctx.db())).to_string(); | 55 | let name = local_name.unwrap_or_else(|| fn_.name(ctx.db())).to_string(); |
53 | let ast_node = fn_.source(ctx.db())?.value; | 56 | let ast_node = fn_.source(ctx.db())?.value; |
54 | 57 | ||
55 | Some(FunctionRender { ctx, name, func: fn_, ast_node, is_method }) | 58 | Some(FunctionRender { ctx, name, receiver, func: fn_, ast_node, is_method }) |
56 | } | 59 | } |
57 | 60 | ||
58 | fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { | 61 | fn render(mut self, import_to_add: Option<ImportEdit>) -> CompletionItem { |
59 | let params = self.params(); | 62 | let params = self.params(); |
63 | if let Some(receiver) = &self.receiver { | ||
64 | self.name = format!("{}.{}", receiver, &self.name) | ||
65 | } | ||
60 | let mut item = CompletionItem::new( | 66 | let mut item = CompletionItem::new( |
61 | CompletionKind::Reference, | 67 | CompletionKind::Reference, |
62 | self.ctx.source_range(), | 68 | self.ctx.source_range(), |
@@ -148,7 +154,7 @@ impl<'a> FunctionRender<'a> { | |||
148 | }; | 154 | }; |
149 | 155 | ||
150 | let mut params_pats = Vec::new(); | 156 | let mut params_pats = Vec::new(); |
151 | let params_ty = if self.ctx.completion.dot_receiver.is_some() { | 157 | let params_ty = if self.ctx.completion.dot_receiver.is_some() || self.receiver.is_some() { |
152 | self.func.method_params(self.ctx.db()).unwrap_or_default() | 158 | self.func.method_params(self.ctx.db()).unwrap_or_default() |
153 | } else { | 159 | } else { |
154 | if let Some(s) = ast_params.self_param() { | 160 | if let Some(s) = ast_params.self_param() { |
@@ -255,6 +261,26 @@ fn bar(s: &S) { | |||
255 | } | 261 | } |
256 | "#, | 262 | "#, |
257 | ); | 263 | ); |
264 | |||
265 | check_edit( | ||
266 | "self.foo", | ||
267 | r#" | ||
268 | struct S {} | ||
269 | impl S { | ||
270 | fn foo(&self, x: i32) { | ||
271 | $0 | ||
272 | } | ||
273 | } | ||
274 | "#, | ||
275 | r#" | ||
276 | struct S {} | ||
277 | impl S { | ||
278 | fn foo(&self, x: i32) { | ||
279 | self.foo(${1:x})$0 | ||
280 | } | ||
281 | } | ||
282 | "#, | ||
283 | ); | ||
258 | } | 284 | } |
259 | 285 | ||
260 | #[test] | 286 | #[test] |
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs index 93c7c872c..b0a4b2026 100644 --- a/crates/ide_completion/src/test_utils.rs +++ b/crates/ide_completion/src/test_utils.rs | |||
@@ -19,6 +19,7 @@ use crate::{item::CompletionKind, CompletionConfig, CompletionItem}; | |||
19 | pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { | 19 | pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { |
20 | enable_postfix_completions: true, | 20 | enable_postfix_completions: true, |
21 | enable_imports_on_the_fly: true, | 21 | enable_imports_on_the_fly: true, |
22 | enable_self_on_the_fly: true, | ||
22 | add_call_parenthesis: true, | 23 | add_call_parenthesis: true, |
23 | add_call_argument_snippets: true, | 24 | add_call_argument_snippets: true, |
24 | snippet_cap: SnippetCap::new(true), | 25 | snippet_cap: SnippetCap::new(true), |
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 88ee4a87d..1f900aef4 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs | |||
@@ -19,8 +19,7 @@ use std::{fmt, sync::Arc}; | |||
19 | 19 | ||
20 | use base_db::{ | 20 | use base_db::{ |
21 | salsa::{self, Durability}, | 21 | salsa::{self, Durability}, |
22 | AnchoredPath, Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, | 22 | AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, |
23 | SourceDatabase, Upcast, | ||
24 | }; | 23 | }; |
25 | use hir::db::{AstDatabase, DefDatabase, HirDatabase}; | 24 | use hir::db::{AstDatabase, DefDatabase, HirDatabase}; |
26 | use rustc_hash::FxHashSet; | 25 | use rustc_hash::FxHashSet; |
@@ -80,20 +79,7 @@ impl FileLoader for RootDatabase { | |||
80 | } | 79 | } |
81 | } | 80 | } |
82 | 81 | ||
83 | impl salsa::Database for RootDatabase { | 82 | impl salsa::Database for RootDatabase {} |
84 | fn on_propagated_panic(&self) -> ! { | ||
85 | Canceled::throw() | ||
86 | } | ||
87 | fn salsa_event(&self, event: salsa::Event) { | ||
88 | match event.kind { | ||
89 | salsa::EventKind::DidValidateMemoizedValue { .. } | ||
90 | | salsa::EventKind::WillExecute { .. } => { | ||
91 | self.check_canceled(); | ||
92 | } | ||
93 | _ => (), | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | 83 | ||
98 | impl Default for RootDatabase { | 84 | impl Default for RootDatabase { |
99 | fn default() -> RootDatabase { | 85 | fn default() -> RootDatabase { |
@@ -126,7 +112,7 @@ impl salsa::ParallelDatabase for RootDatabase { | |||
126 | } | 112 | } |
127 | 113 | ||
128 | #[salsa::query_group(LineIndexDatabaseStorage)] | 114 | #[salsa::query_group(LineIndexDatabaseStorage)] |
129 | pub trait LineIndexDatabase: base_db::SourceDatabase + CheckCanceled { | 115 | pub trait LineIndexDatabase: base_db::SourceDatabase { |
130 | fn line_index(&self, file_id: FileId) -> Arc<LineIndex>; | 116 | fn line_index(&self, file_id: FileId) -> Arc<LineIndex>; |
131 | } | 117 | } |
132 | 118 | ||
diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs index 0f5c4abc4..5c372a7e5 100644 --- a/crates/ide_db/src/symbol_index.rs +++ b/crates/ide_db/src/symbol_index.rs | |||
@@ -127,7 +127,7 @@ fn library_symbols(db: &dyn SymbolsDatabase) -> Arc<FxHashMap<SourceRootId, Symb | |||
127 | } | 127 | } |
128 | 128 | ||
129 | fn file_symbols(db: &dyn SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> { | 129 | fn file_symbols(db: &dyn SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> { |
130 | db.check_canceled(); | 130 | db.unwind_if_cancelled(); |
131 | let parse = db.parse(file_id); | 131 | let parse = db.parse(file_id); |
132 | 132 | ||
133 | let symbols = source_file_to_file_symbols(&parse.tree(), file_id); | 133 | let symbols = source_file_to_file_symbols(&parse.tree(), file_id); |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index a67b0bb25..ae78fd4f6 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -100,6 +100,9 @@ config_data! { | |||
100 | /// Toggles the additional completions that automatically add imports when completed. | 100 | /// Toggles the additional completions that automatically add imports when completed. |
101 | /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. | 101 | /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. |
102 | completion_autoimport_enable: bool = "true", | 102 | completion_autoimport_enable: bool = "true", |
103 | /// Toggles the additional completions that automatically show method calls and field accesses | ||
104 | /// with `self` prefixed to them when inside a method. | ||
105 | completion_autoself_enable: bool = "true", | ||
103 | 106 | ||
104 | /// Whether to show native rust-analyzer diagnostics. | 107 | /// Whether to show native rust-analyzer diagnostics. |
105 | diagnostics_enable: bool = "true", | 108 | diagnostics_enable: bool = "true", |
@@ -666,6 +669,7 @@ impl Config { | |||
666 | enable_postfix_completions: self.data.completion_postfix_enable, | 669 | enable_postfix_completions: self.data.completion_postfix_enable, |
667 | enable_imports_on_the_fly: self.data.completion_autoimport_enable | 670 | enable_imports_on_the_fly: self.data.completion_autoimport_enable |
668 | && completion_item_edit_resolve(&self.caps), | 671 | && completion_item_edit_resolve(&self.caps), |
672 | enable_self_on_the_fly: self.data.completion_autoself_enable, | ||
669 | add_call_parenthesis: self.data.completion_addCallParenthesis, | 673 | add_call_parenthesis: self.data.completion_addCallParenthesis, |
670 | add_call_argument_snippets: self.data.completion_addCallArgumentSnippets, | 674 | add_call_argument_snippets: self.data.completion_addCallArgumentSnippets, |
671 | insert_use: self.insert_use_config(), | 675 | insert_use: self.insert_use_config(), |
diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index baf2199d9..2011a4132 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs | |||
@@ -5,7 +5,7 @@ use serde::{de::DeserializeOwned, Serialize}; | |||
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | global_state::{GlobalState, GlobalStateSnapshot}, | 7 | global_state::{GlobalState, GlobalStateSnapshot}, |
8 | lsp_utils::is_canceled, | 8 | lsp_utils::is_cancelled, |
9 | main_loop::Task, | 9 | main_loop::Task, |
10 | LspError, Result, | 10 | LspError, Result, |
11 | }; | 11 | }; |
@@ -132,7 +132,7 @@ where | |||
132 | Err(e) => match e.downcast::<LspError>() { | 132 | Err(e) => match e.downcast::<LspError>() { |
133 | Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message), | 133 | Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message), |
134 | Err(e) => { | 134 | Err(e) => { |
135 | if is_canceled(&*e) { | 135 | if is_cancelled(&*e) { |
136 | lsp_server::Response::new_err( | 136 | lsp_server::Response::new_err( |
137 | id, | 137 | id, |
138 | lsp_server::ErrorCode::ContentModified as i32, | 138 | lsp_server::ErrorCode::ContentModified as i32, |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 6a36d29d4..ea9dbf7fc 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -7,7 +7,7 @@ use std::{sync::Arc, time::Instant}; | |||
7 | 7 | ||
8 | use crossbeam_channel::{unbounded, Receiver, Sender}; | 8 | use crossbeam_channel::{unbounded, Receiver, Sender}; |
9 | use flycheck::FlycheckHandle; | 9 | use flycheck::FlycheckHandle; |
10 | use ide::{Analysis, AnalysisHost, Cancelable, Change, FileId}; | 10 | use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId}; |
11 | use ide_db::base_db::{CrateId, VfsPath}; | 11 | use ide_db::base_db::{CrateId, VfsPath}; |
12 | use lsp_types::{SemanticTokens, Url}; | 12 | use lsp_types::{SemanticTokens, Url}; |
13 | use parking_lot::{Mutex, RwLock}; | 13 | use parking_lot::{Mutex, RwLock}; |
@@ -280,7 +280,7 @@ impl GlobalStateSnapshot { | |||
280 | file_id_to_url(&self.vfs.read().0, id) | 280 | file_id_to_url(&self.vfs.read().0, id) |
281 | } | 281 | } |
282 | 282 | ||
283 | pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancelable<LineIndex> { | 283 | pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> { |
284 | let endings = self.vfs.read().1[&file_id]; | 284 | let endings = self.vfs.read().1[&file_id]; |
285 | let index = self.analysis.file_line_index(file_id)?; | 285 | let index = self.analysis.file_line_index(file_id)?; |
286 | let res = LineIndex { index, endings, encoding: self.config.offset_encoding() }; | 286 | let res = LineIndex { index, endings, encoding: self.config.offset_encoding() }; |
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index 781073fe5..ec36a5f5c 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs | |||
@@ -132,6 +132,7 @@ fn integrated_completion_benchmark() { | |||
132 | let config = CompletionConfig { | 132 | let config = CompletionConfig { |
133 | enable_postfix_completions: true, | 133 | enable_postfix_completions: true, |
134 | enable_imports_on_the_fly: true, | 134 | enable_imports_on_the_fly: true, |
135 | enable_self_on_the_fly: true, | ||
135 | add_call_parenthesis: true, | 136 | add_call_parenthesis: true, |
136 | add_call_argument_snippets: true, | 137 | add_call_argument_snippets: true, |
137 | snippet_cap: SnippetCap::new(true), | 138 | snippet_cap: SnippetCap::new(true), |
@@ -166,6 +167,7 @@ fn integrated_completion_benchmark() { | |||
166 | let config = CompletionConfig { | 167 | let config = CompletionConfig { |
167 | enable_postfix_completions: true, | 168 | enable_postfix_completions: true, |
168 | enable_imports_on_the_fly: true, | 169 | enable_imports_on_the_fly: true, |
170 | enable_self_on_the_fly: true, | ||
169 | add_call_parenthesis: true, | 171 | add_call_parenthesis: true, |
170 | add_call_argument_snippets: true, | 172 | add_call_argument_snippets: true, |
171 | snippet_cap: SnippetCap::new(true), | 173 | snippet_cap: SnippetCap::new(true), |
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index 73c4193e8..8000b5490 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Utilities for LSP-related boilerplate code. | 1 | //! Utilities for LSP-related boilerplate code. |
2 | use std::{error::Error, ops::Range, sync::Arc}; | 2 | use std::{error::Error, ops::Range, sync::Arc}; |
3 | 3 | ||
4 | use ide_db::base_db::Canceled; | 4 | use ide_db::base_db::Cancelled; |
5 | use lsp_server::Notification; | 5 | use lsp_server::Notification; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
@@ -10,8 +10,8 @@ use crate::{ | |||
10 | line_index::{LineEndings, LineIndex, OffsetEncoding}, | 10 | line_index::{LineEndings, LineIndex, OffsetEncoding}, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { | 13 | pub(crate) fn is_cancelled(e: &(dyn Error + 'static)) -> bool { |
14 | e.downcast_ref::<Canceled>().is_some() | 14 | e.downcast_ref::<Cancelled>().is_some() |
15 | } | 15 | } |
16 | 16 | ||
17 | pub(crate) fn notification_is<N: lsp_types::notification::Notification>( | 17 | pub(crate) fn notification_is<N: lsp_types::notification::Notification>( |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 008758ea0..31d8ea9e7 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -22,7 +22,7 @@ use crate::{ | |||
22 | from_proto, | 22 | from_proto, |
23 | global_state::{file_id_to_url, url_to_file_id, GlobalState}, | 23 | global_state::{file_id_to_url, url_to_file_id, GlobalState}, |
24 | handlers, lsp_ext, | 24 | handlers, lsp_ext, |
25 | lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress}, | 25 | lsp_utils::{apply_document_changes, is_cancelled, notification_is, Progress}, |
26 | reload::{BuildDataProgress, ProjectWorkspaceProgress}, | 26 | reload::{BuildDataProgress, ProjectWorkspaceProgress}, |
27 | Result, | 27 | Result, |
28 | }; | 28 | }; |
@@ -752,7 +752,7 @@ impl GlobalState { | |||
752 | .filter_map(|file_id| { | 752 | .filter_map(|file_id| { |
753 | handlers::publish_diagnostics(&snapshot, file_id) | 753 | handlers::publish_diagnostics(&snapshot, file_id) |
754 | .map_err(|err| { | 754 | .map_err(|err| { |
755 | if !is_canceled(&*err) { | 755 | if !is_cancelled(&*err) { |
756 | log::error!("failed to compute diagnostics: {:?}", err); | 756 | log::error!("failed to compute diagnostics: {:?}", err); |
757 | } | 757 | } |
758 | () | 758 | () |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index f5c8535a2..7428a3043 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -6,7 +6,7 @@ use std::{ | |||
6 | }; | 6 | }; |
7 | 7 | ||
8 | use ide::{ | 8 | use ide::{ |
9 | Annotation, AnnotationKind, Assist, AssistKind, CallInfo, Cancelable, CompletionItem, | 9 | Annotation, AnnotationKind, Assist, AssistKind, CallInfo, Cancellable, CompletionItem, |
10 | CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, | 10 | CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, |
11 | Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, | 11 | Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, |
12 | InlayKind, InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, | 12 | InlayKind, InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, |
@@ -727,7 +727,7 @@ pub(crate) fn snippet_text_document_edit( | |||
727 | pub(crate) fn snippet_text_document_ops( | 727 | pub(crate) fn snippet_text_document_ops( |
728 | snap: &GlobalStateSnapshot, | 728 | snap: &GlobalStateSnapshot, |
729 | file_system_edit: FileSystemEdit, | 729 | file_system_edit: FileSystemEdit, |
730 | ) -> Cancelable<Vec<lsp_ext::SnippetDocumentChangeOperation>> { | 730 | ) -> Cancellable<Vec<lsp_ext::SnippetDocumentChangeOperation>> { |
731 | let mut ops = Vec::new(); | 731 | let mut ops = Vec::new(); |
732 | match file_system_edit { | 732 | match file_system_edit { |
733 | FileSystemEdit::CreateFile { dst, initial_contents } => { | 733 | FileSystemEdit::CreateFile { dst, initial_contents } => { |
@@ -757,7 +757,7 @@ pub(crate) fn snippet_text_document_ops( | |||
757 | let new_uri = snap.anchored_path(&dst); | 757 | let new_uri = snap.anchored_path(&dst); |
758 | let mut rename_file = | 758 | let mut rename_file = |
759 | lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None }; | 759 | lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None }; |
760 | if snap.analysis.is_library_file(src) == Ok(true) | 760 | if snap.analysis.is_library_file(src).ok() == Some(true) |
761 | && snap.config.change_annotation_support() | 761 | && snap.config.change_annotation_support() |
762 | { | 762 | { |
763 | rename_file.annotation_id = Some(outside_workspace_annotation_id()) | 763 | rename_file.annotation_id = Some(outside_workspace_annotation_id()) |
@@ -1178,6 +1178,7 @@ mod tests { | |||
1178 | &ide::CompletionConfig { | 1178 | &ide::CompletionConfig { |
1179 | enable_postfix_completions: true, | 1179 | enable_postfix_completions: true, |
1180 | enable_imports_on_the_fly: true, | 1180 | enable_imports_on_the_fly: true, |
1181 | enable_self_on_the_fly: true, | ||
1181 | add_call_parenthesis: true, | 1182 | add_call_parenthesis: true, |
1182 | add_call_argument_snippets: true, | 1183 | add_call_argument_snippets: true, |
1183 | snippet_cap: SnippetCap::new(true), | 1184 | snippet_cap: SnippetCap::new(true), |
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index 39edf9e19..2624069a5 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md | |||
@@ -447,3 +447,8 @@ This is cheap enough to enable in production. | |||
447 | 447 | ||
448 | Similarly, we save live object counting (`RA_COUNT=1`). | 448 | Similarly, we save live object counting (`RA_COUNT=1`). |
449 | It is not cheap enough to enable in prod, and this is a bug which should be fixed. | 449 | It is not cheap enough to enable in prod, and this is a bug which should be fixed. |
450 | |||
451 | ### Configurability | ||
452 | |||
453 | rust-analyzer strives to be as configurable as possible while offering reasonable defaults where no configuration exists yet. | ||
454 | There will always be features that some people find more annoying than helpful, so giving the users the ability to tweak or disable these is a big part of offering a good user experience. | ||
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 4a5782a57..dbd9a3503 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc | |||
@@ -136,6 +136,12 @@ Whether to show postfix snippets like `dbg`, `if`, `not`, etc. | |||
136 | Toggles the additional completions that automatically add imports when completed. | 136 | Toggles the additional completions that automatically add imports when completed. |
137 | Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. | 137 | Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. |
138 | -- | 138 | -- |
139 | [[rust-analyzer.completion.autoself.enable]]rust-analyzer.completion.autoself.enable (default: `true`):: | ||
140 | + | ||
141 | -- | ||
142 | Toggles the additional completions that automatically show method calls and field accesses | ||
143 | with `self` prefixed to them when inside a method. | ||
144 | -- | ||
139 | [[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`):: | 145 | [[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`):: |
140 | + | 146 | + |
141 | -- | 147 | -- |
diff --git a/editors/code/package.json b/editors/code/package.json index 5b80cc1f9..42a06e137 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -572,6 +572,11 @@ | |||
572 | "default": true, | 572 | "default": true, |
573 | "type": "boolean" | 573 | "type": "boolean" |
574 | }, | 574 | }, |
575 | "rust-analyzer.completion.autoself.enable": { | ||
576 | "markdownDescription": "Toggles the additional completions that automatically show method calls and field accesses\nwith `self` prefixed to them when inside a method.", | ||
577 | "default": true, | ||
578 | "type": "boolean" | ||
579 | }, | ||
575 | "rust-analyzer.diagnostics.enable": { | 580 | "rust-analyzer.diagnostics.enable": { |
576 | "markdownDescription": "Whether to show native rust-analyzer diagnostics.", | 581 | "markdownDescription": "Whether to show native rust-analyzer diagnostics.", |
577 | "default": true, | 582 | "default": true, |