diff options
31 files changed, 603 insertions, 183 deletions
diff --git a/Cargo.lock b/Cargo.lock index 5f88ad0c4..15c6a4c11 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -471,9 +471,9 @@ dependencies = [ | |||
471 | 471 | ||
472 | [[package]] | 472 | [[package]] |
473 | name = "inotify" | 473 | name = "inotify" |
474 | version = "0.7.0" | 474 | version = "0.7.1" |
475 | source = "registry+https://github.com/rust-lang/crates.io-index" | 475 | source = "registry+https://github.com/rust-lang/crates.io-index" |
476 | checksum = "24e40d6fd5d64e2082e0c796495c8ef5ad667a96d03e5aaa0becfd9d47bcbfb8" | 476 | checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" |
477 | dependencies = [ | 477 | dependencies = [ |
478 | "bitflags", | 478 | "bitflags", |
479 | "inotify-sys", | 479 | "inotify-sys", |
diff --git a/crates/ra_assists/src/handlers/introduce_named_lifetime.rs b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs index beb5b7366..28fcbc9ba 100644 --- a/crates/ra_assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs | |||
@@ -41,8 +41,6 @@ pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) - | |||
41 | if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::FnDef::cast) { | 41 | if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::FnDef::cast) { |
42 | generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range()) | 42 | generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range()) |
43 | } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::ImplDef::cast) { | 43 | } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::ImplDef::cast) { |
44 | // only allow naming the last anonymous lifetime | ||
45 | lifetime_token.next_token().filter(|tok| tok.kind() == SyntaxKind::R_ANGLE)?; | ||
46 | generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range()) | 44 | generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range()) |
47 | } else { | 45 | } else { |
48 | None | 46 | None |
@@ -191,6 +189,23 @@ mod tests { | |||
191 | } | 189 | } |
192 | 190 | ||
193 | #[test] | 191 | #[test] |
192 | fn test_impl_with_other_type_param() { | ||
193 | check_assist( | ||
194 | introduce_named_lifetime, | ||
195 | "impl<I> fmt::Display for SepByBuilder<'_<|>, I> | ||
196 | where | ||
197 | I: Iterator, | ||
198 | I::Item: fmt::Display, | ||
199 | {", | ||
200 | "impl<I, 'a> fmt::Display for SepByBuilder<'a, I> | ||
201 | where | ||
202 | I: Iterator, | ||
203 | I::Item: fmt::Display, | ||
204 | {", | ||
205 | ) | ||
206 | } | ||
207 | |||
208 | #[test] | ||
194 | fn test_example_case_cursor_before_tick() { | 209 | fn test_example_case_cursor_before_tick() { |
195 | check_assist( | 210 | check_assist( |
196 | introduce_named_lifetime, | 211 | introduce_named_lifetime, |
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index e7868268b..fcfa2788c 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs | |||
@@ -89,8 +89,7 @@ pub const DEFAULT_LRU_CAP: usize = 128; | |||
89 | pub trait FileLoader { | 89 | pub trait FileLoader { |
90 | /// Text of the file. | 90 | /// Text of the file. |
91 | fn file_text(&self, file_id: FileId) -> Arc<String>; | 91 | fn file_text(&self, file_id: FileId) -> Arc<String>; |
92 | fn resolve_relative_path(&self, anchor: FileId, relative_path: &RelativePath) | 92 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId>; |
93 | -> Option<FileId>; | ||
94 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>; | 93 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>; |
95 | 94 | ||
96 | fn resolve_extern_path( | 95 | fn resolve_extern_path( |
@@ -155,20 +154,21 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> { | |||
155 | fn file_text(&self, file_id: FileId) -> Arc<String> { | 154 | fn file_text(&self, file_id: FileId) -> Arc<String> { |
156 | SourceDatabaseExt::file_text(self.0, file_id) | 155 | SourceDatabaseExt::file_text(self.0, file_id) |
157 | } | 156 | } |
158 | fn resolve_relative_path( | 157 | /// Note that we intentionally accept a `&str` and not a `&Path` here. This |
159 | &self, | 158 | /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such, |
160 | anchor: FileId, | 159 | /// so the input is guaranteed to be utf-8 string. We might introduce |
161 | relative_path: &RelativePath, | 160 | /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we |
162 | ) -> Option<FileId> { | 161 | /// get by with a `&str` for the time being. |
163 | let path = { | 162 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { |
164 | let mut path = self.0.file_relative_path(anchor); | 163 | let rel_path = { |
165 | assert!(path.pop()); | 164 | let mut rel_path = self.0.file_relative_path(anchor); |
166 | path.push(relative_path); | 165 | assert!(rel_path.pop()); |
167 | path.normalize() | 166 | rel_path.push(path); |
167 | rel_path.normalize() | ||
168 | }; | 168 | }; |
169 | let source_root = self.0.file_source_root(anchor); | 169 | let source_root = self.0.file_source_root(anchor); |
170 | let source_root = self.0.source_root(source_root); | 170 | let source_root = self.0.source_root(source_root); |
171 | source_root.file_by_relative_path(&path) | 171 | source_root.file_by_relative_path(&rel_path) |
172 | } | 172 | } |
173 | 173 | ||
174 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | 174 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { |
diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs index 386c5cade..cede4a6fc 100644 --- a/crates/ra_hir_def/src/nameres/mod_resolution.rs +++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs | |||
@@ -61,7 +61,7 @@ impl ModDir { | |||
61 | }; | 61 | }; |
62 | 62 | ||
63 | for candidate in candidate_files.iter() { | 63 | for candidate in candidate_files.iter() { |
64 | if let Some(file_id) = db.resolve_relative_path(file_id, candidate) { | 64 | if let Some(file_id) = db.resolve_path(file_id, candidate.as_str()) { |
65 | let mut root_non_dir_owner = false; | 65 | let mut root_non_dir_owner = false; |
66 | let mut mod_path = RelativePathBuf::new(); | 66 | let mut mod_path = RelativePathBuf::new(); |
67 | if !(candidate.ends_with("mod.rs") || attr_path.is_some()) { | 67 | if !(candidate.ends_with("mod.rs") || attr_path.is_some()) { |
diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs index eb83dee79..e7a5182f0 100644 --- a/crates/ra_hir_def/src/test_db.rs +++ b/crates/ra_hir_def/src/test_db.rs | |||
@@ -58,12 +58,8 @@ impl FileLoader for TestDB { | |||
58 | fn file_text(&self, file_id: FileId) -> Arc<String> { | 58 | fn file_text(&self, file_id: FileId) -> Arc<String> { |
59 | FileLoaderDelegate(self).file_text(file_id) | 59 | FileLoaderDelegate(self).file_text(file_id) |
60 | } | 60 | } |
61 | fn resolve_relative_path( | 61 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { |
62 | &self, | 62 | FileLoaderDelegate(self).resolve_path(anchor, path) |
63 | anchor: FileId, | ||
64 | relative_path: &RelativePath, | ||
65 | ) -> Option<FileId> { | ||
66 | FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) | ||
67 | } | 63 | } |
68 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | 64 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { |
69 | FileLoaderDelegate(self).relevant_crates(file_id) | 65 | FileLoaderDelegate(self).relevant_crates(file_id) |
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index 3bce8f673..eec5fb8eb 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs | |||
@@ -8,7 +8,7 @@ use crate::{ | |||
8 | use crate::{quote, EagerMacroId, LazyMacroId, MacroCallId}; | 8 | use crate::{quote, EagerMacroId, LazyMacroId, MacroCallId}; |
9 | use either::Either; | 9 | use either::Either; |
10 | use mbe::parse_to_token_tree; | 10 | use mbe::parse_to_token_tree; |
11 | use ra_db::{FileId, RelativePath}; | 11 | use ra_db::FileId; |
12 | use ra_parser::FragmentKind; | 12 | use ra_parser::FragmentKind; |
13 | 13 | ||
14 | macro_rules! register_builtin { | 14 | macro_rules! register_builtin { |
@@ -297,7 +297,7 @@ fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Opti | |||
297 | let call_site = call_id.as_file().original_file(db); | 297 | let call_site = call_id.as_file().original_file(db); |
298 | 298 | ||
299 | // Handle trivial case | 299 | // Handle trivial case |
300 | if let Some(res) = db.resolve_relative_path(call_site, &RelativePath::new(&path)) { | 300 | if let Some(res) = db.resolve_path(call_site, path) { |
301 | // Prevent include itself | 301 | // Prevent include itself |
302 | return if res == call_site { None } else { Some(res) }; | 302 | return if res == call_site { None } else { Some(res) }; |
303 | } | 303 | } |
diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs index c1fb762de..765a2f6d1 100644 --- a/crates/ra_hir_expand/src/test_db.rs +++ b/crates/ra_hir_expand/src/test_db.rs | |||
@@ -41,12 +41,8 @@ impl FileLoader for TestDB { | |||
41 | fn file_text(&self, file_id: FileId) -> Arc<String> { | 41 | fn file_text(&self, file_id: FileId) -> Arc<String> { |
42 | FileLoaderDelegate(self).file_text(file_id) | 42 | FileLoaderDelegate(self).file_text(file_id) |
43 | } | 43 | } |
44 | fn resolve_relative_path( | 44 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { |
45 | &self, | 45 | FileLoaderDelegate(self).resolve_path(anchor, path) |
46 | anchor: FileId, | ||
47 | relative_path: &RelativePath, | ||
48 | ) -> Option<FileId> { | ||
49 | FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) | ||
50 | } | 46 | } |
51 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | 47 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { |
52 | FileLoaderDelegate(self).relevant_crates(file_id) | 48 | FileLoaderDelegate(self).relevant_crates(file_id) |
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index 8498d3d96..21a3bdfd1 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs | |||
@@ -72,12 +72,8 @@ impl FileLoader for TestDB { | |||
72 | fn file_text(&self, file_id: FileId) -> Arc<String> { | 72 | fn file_text(&self, file_id: FileId) -> Arc<String> { |
73 | FileLoaderDelegate(self).file_text(file_id) | 73 | FileLoaderDelegate(self).file_text(file_id) |
74 | } | 74 | } |
75 | fn resolve_relative_path( | 75 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { |
76 | &self, | 76 | FileLoaderDelegate(self).resolve_path(anchor, path) |
77 | anchor: FileId, | ||
78 | relative_path: &RelativePath, | ||
79 | ) -> Option<FileId> { | ||
80 | FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) | ||
81 | } | 77 | } |
82 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | 78 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { |
83 | FileLoaderDelegate(self).relevant_crates(file_id) | 79 | FileLoaderDelegate(self).relevant_crates(file_id) |
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 9636cd0d6..62df07459 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -13,14 +13,43 @@ use ra_ide_db::{ | |||
13 | use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; | 13 | use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; |
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, | 16 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav}, |
17 | FilePosition, RangeInfo, | 17 | FilePosition, NavigationTarget, RangeInfo, |
18 | }; | 18 | }; |
19 | 19 | ||
20 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
21 | pub struct HoverConfig { | ||
22 | pub implementations: bool, | ||
23 | } | ||
24 | |||
25 | impl Default for HoverConfig { | ||
26 | fn default() -> Self { | ||
27 | Self { implementations: true } | ||
28 | } | ||
29 | } | ||
30 | |||
31 | impl HoverConfig { | ||
32 | pub const NO_ACTIONS: Self = Self { implementations: false }; | ||
33 | |||
34 | pub fn any(&self) -> bool { | ||
35 | self.implementations | ||
36 | } | ||
37 | |||
38 | pub fn none(&self) -> bool { | ||
39 | !self.any() | ||
40 | } | ||
41 | } | ||
42 | |||
43 | #[derive(Debug, Clone)] | ||
44 | pub enum HoverAction { | ||
45 | Implementaion(FilePosition), | ||
46 | } | ||
47 | |||
20 | /// Contains the results when hovering over an item | 48 | /// Contains the results when hovering over an item |
21 | #[derive(Debug, Default)] | 49 | #[derive(Debug, Default)] |
22 | pub struct HoverResult { | 50 | pub struct HoverResult { |
23 | results: Vec<String>, | 51 | results: Vec<String>, |
52 | actions: Vec<HoverAction>, | ||
24 | } | 53 | } |
25 | 54 | ||
26 | impl HoverResult { | 55 | impl HoverResult { |
@@ -48,10 +77,20 @@ impl HoverResult { | |||
48 | &self.results | 77 | &self.results |
49 | } | 78 | } |
50 | 79 | ||
80 | pub fn actions(&self) -> &[HoverAction] { | ||
81 | &self.actions | ||
82 | } | ||
83 | |||
84 | pub fn push_action(&mut self, action: HoverAction) { | ||
85 | self.actions.push(action); | ||
86 | } | ||
87 | |||
51 | /// Returns the results converted into markup | 88 | /// Returns the results converted into markup |
52 | /// for displaying in a UI | 89 | /// for displaying in a UI |
90 | /// | ||
91 | /// Does not process actions! | ||
53 | pub fn to_markup(&self) -> String { | 92 | pub fn to_markup(&self) -> String { |
54 | self.results.join("\n\n---\n") | 93 | self.results.join("\n\n___\n") |
55 | } | 94 | } |
56 | } | 95 | } |
57 | 96 | ||
@@ -82,6 +121,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
82 | res.extend(hover_text_from_name_kind(db, name_kind)); | 121 | res.extend(hover_text_from_name_kind(db, name_kind)); |
83 | 122 | ||
84 | if !res.is_empty() { | 123 | if !res.is_empty() { |
124 | if let Some(action) = show_implementations_action(db, name_kind) { | ||
125 | res.push_action(action); | ||
126 | } | ||
127 | |||
85 | return Some(RangeInfo::new(range, res)); | 128 | return Some(RangeInfo::new(range, res)); |
86 | } | 129 | } |
87 | } | 130 | } |
@@ -112,6 +155,26 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
112 | Some(RangeInfo::new(range, res)) | 155 | Some(RangeInfo::new(range, res)) |
113 | } | 156 | } |
114 | 157 | ||
158 | fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | ||
159 | fn to_action(nav_target: NavigationTarget) -> HoverAction { | ||
160 | HoverAction::Implementaion(FilePosition { | ||
161 | file_id: nav_target.file_id(), | ||
162 | offset: nav_target.range().start(), | ||
163 | }) | ||
164 | } | ||
165 | |||
166 | match def { | ||
167 | Definition::ModuleDef(it) => match it { | ||
168 | ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.to_nav(db))), | ||
169 | ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.to_nav(db))), | ||
170 | ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.to_nav(db))), | ||
171 | ModuleDef::Trait(it) => Some(to_action(it.to_nav(db))), | ||
172 | _ => None, | ||
173 | }, | ||
174 | _ => None, | ||
175 | } | ||
176 | } | ||
177 | |||
115 | fn hover_text( | 178 | fn hover_text( |
116 | docs: Option<String>, | 179 | docs: Option<String>, |
117 | desc: Option<String>, | 180 | desc: Option<String>, |
@@ -228,6 +291,8 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | |||
228 | 291 | ||
229 | #[cfg(test)] | 292 | #[cfg(test)] |
230 | mod tests { | 293 | mod tests { |
294 | use super::*; | ||
295 | |||
231 | use ra_db::FileLoader; | 296 | use ra_db::FileLoader; |
232 | use ra_syntax::TextRange; | 297 | use ra_syntax::TextRange; |
233 | 298 | ||
@@ -241,7 +306,14 @@ mod tests { | |||
241 | s.map(trim_markup) | 306 | s.map(trim_markup) |
242 | } | 307 | } |
243 | 308 | ||
244 | fn check_hover_result(fixture: &str, expected: &[&str]) -> String { | 309 | fn assert_impl_action(action: &HoverAction, position: u32) { |
310 | let offset = match action { | ||
311 | HoverAction::Implementaion(pos) => pos.offset, | ||
312 | }; | ||
313 | assert_eq!(offset, position.into()); | ||
314 | } | ||
315 | |||
316 | fn check_hover_result(fixture: &str, expected: &[&str]) -> (String, Vec<HoverAction>) { | ||
245 | let (analysis, position) = analysis_and_position(fixture); | 317 | let (analysis, position) = analysis_and_position(fixture); |
246 | let hover = analysis.hover(position).unwrap().unwrap(); | 318 | let hover = analysis.hover(position).unwrap().unwrap(); |
247 | let mut results = Vec::from(hover.info.results()); | 319 | let mut results = Vec::from(hover.info.results()); |
@@ -256,7 +328,7 @@ mod tests { | |||
256 | assert_eq!(hover.info.len(), expected.len()); | 328 | assert_eq!(hover.info.len(), expected.len()); |
257 | 329 | ||
258 | let content = analysis.db.file_text(position.file_id); | 330 | let content = analysis.db.file_text(position.file_id); |
259 | content[hover.range].to_string() | 331 | (content[hover.range].to_string(), hover.info.actions().to_vec()) |
260 | } | 332 | } |
261 | 333 | ||
262 | fn check_hover_no_result(fixture: &str) { | 334 | fn check_hover_no_result(fixture: &str) { |
@@ -746,7 +818,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
746 | 818 | ||
747 | #[test] | 819 | #[test] |
748 | fn test_hover_through_macro() { | 820 | fn test_hover_through_macro() { |
749 | let hover_on = check_hover_result( | 821 | let (hover_on, _) = check_hover_result( |
750 | " | 822 | " |
751 | //- /lib.rs | 823 | //- /lib.rs |
752 | macro_rules! id { | 824 | macro_rules! id { |
@@ -767,7 +839,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
767 | 839 | ||
768 | #[test] | 840 | #[test] |
769 | fn test_hover_through_expr_in_macro() { | 841 | fn test_hover_through_expr_in_macro() { |
770 | let hover_on = check_hover_result( | 842 | let (hover_on, _) = check_hover_result( |
771 | " | 843 | " |
772 | //- /lib.rs | 844 | //- /lib.rs |
773 | macro_rules! id { | 845 | macro_rules! id { |
@@ -785,7 +857,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
785 | 857 | ||
786 | #[test] | 858 | #[test] |
787 | fn test_hover_through_expr_in_macro_recursive() { | 859 | fn test_hover_through_expr_in_macro_recursive() { |
788 | let hover_on = check_hover_result( | 860 | let (hover_on, _) = check_hover_result( |
789 | " | 861 | " |
790 | //- /lib.rs | 862 | //- /lib.rs |
791 | macro_rules! id_deep { | 863 | macro_rules! id_deep { |
@@ -806,7 +878,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
806 | 878 | ||
807 | #[test] | 879 | #[test] |
808 | fn test_hover_through_func_in_macro_recursive() { | 880 | fn test_hover_through_func_in_macro_recursive() { |
809 | let hover_on = check_hover_result( | 881 | let (hover_on, _) = check_hover_result( |
810 | " | 882 | " |
811 | //- /lib.rs | 883 | //- /lib.rs |
812 | macro_rules! id_deep { | 884 | macro_rules! id_deep { |
@@ -830,7 +902,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
830 | 902 | ||
831 | #[test] | 903 | #[test] |
832 | fn test_hover_through_literal_string_in_macro() { | 904 | fn test_hover_through_literal_string_in_macro() { |
833 | let hover_on = check_hover_result( | 905 | let (hover_on, _) = check_hover_result( |
834 | r#" | 906 | r#" |
835 | //- /lib.rs | 907 | //- /lib.rs |
836 | macro_rules! arr { | 908 | macro_rules! arr { |
@@ -849,7 +921,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
849 | 921 | ||
850 | #[test] | 922 | #[test] |
851 | fn test_hover_through_assert_macro() { | 923 | fn test_hover_through_assert_macro() { |
852 | let hover_on = check_hover_result( | 924 | let (hover_on, _) = check_hover_result( |
853 | r#" | 925 | r#" |
854 | //- /lib.rs | 926 | //- /lib.rs |
855 | #[rustc_builtin_macro] | 927 | #[rustc_builtin_macro] |
@@ -925,13 +997,14 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
925 | 997 | ||
926 | #[test] | 998 | #[test] |
927 | fn test_hover_trait_show_qualifiers() { | 999 | fn test_hover_trait_show_qualifiers() { |
928 | check_hover_result( | 1000 | let (_, actions) = check_hover_result( |
929 | " | 1001 | " |
930 | //- /lib.rs | 1002 | //- /lib.rs |
931 | unsafe trait foo<|>() {} | 1003 | unsafe trait foo<|>() {} |
932 | ", | 1004 | ", |
933 | &["unsafe trait foo"], | 1005 | &["unsafe trait foo"], |
934 | ); | 1006 | ); |
1007 | assert_impl_action(&actions[0], 13); | ||
935 | } | 1008 | } |
936 | 1009 | ||
937 | #[test] | 1010 | #[test] |
@@ -1052,4 +1125,55 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1052 | &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"], | 1125 | &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"], |
1053 | ); | 1126 | ); |
1054 | } | 1127 | } |
1128 | |||
1129 | #[test] | ||
1130 | fn test_hover_trait_has_impl_action() { | ||
1131 | let (_, actions) = check_hover_result( | ||
1132 | " | ||
1133 | //- /lib.rs | ||
1134 | trait foo<|>() {} | ||
1135 | ", | ||
1136 | &["trait foo"], | ||
1137 | ); | ||
1138 | assert_impl_action(&actions[0], 6); | ||
1139 | } | ||
1140 | |||
1141 | #[test] | ||
1142 | fn test_hover_struct_has_impl_action() { | ||
1143 | let (_, actions) = check_hover_result( | ||
1144 | " | ||
1145 | //- /lib.rs | ||
1146 | struct foo<|>() {} | ||
1147 | ", | ||
1148 | &["struct foo"], | ||
1149 | ); | ||
1150 | assert_impl_action(&actions[0], 7); | ||
1151 | } | ||
1152 | |||
1153 | #[test] | ||
1154 | fn test_hover_union_has_impl_action() { | ||
1155 | let (_, actions) = check_hover_result( | ||
1156 | " | ||
1157 | //- /lib.rs | ||
1158 | union foo<|>() {} | ||
1159 | ", | ||
1160 | &["union foo"], | ||
1161 | ); | ||
1162 | assert_impl_action(&actions[0], 6); | ||
1163 | } | ||
1164 | |||
1165 | #[test] | ||
1166 | fn test_hover_enum_has_impl_action() { | ||
1167 | let (_, actions) = check_hover_result( | ||
1168 | " | ||
1169 | //- /lib.rs | ||
1170 | enum foo<|>() { | ||
1171 | A, | ||
1172 | B | ||
1173 | } | ||
1174 | ", | ||
1175 | &["enum foo"], | ||
1176 | ); | ||
1177 | assert_impl_action(&actions[0], 5); | ||
1178 | } | ||
1055 | } | 1179 | } |
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 75bd3c96b..49366de98 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -149,11 +149,10 @@ fn get_param_name_hints( | |||
149 | ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(), | 149 | ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(), |
150 | _ => return None, | 150 | _ => return None, |
151 | }; | 151 | }; |
152 | let args_count = args.clone().count(); | ||
153 | 152 | ||
154 | let fn_signature = get_fn_signature(sema, &expr)?; | 153 | let fn_signature = get_fn_signature(sema, &expr)?; |
155 | let n_params_to_skip = | 154 | let n_params_to_skip = |
156 | if fn_signature.has_self_param && fn_signature.parameter_names.len() > args_count { | 155 | if fn_signature.has_self_param && matches!(&expr, ast::Expr::MethodCallExpr(_)) { |
157 | 1 | 156 | 1 |
158 | } else { | 157 | } else { |
159 | 0 | 158 | 0 |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 34c2d75fe..a56718d3f 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -66,7 +66,7 @@ pub use crate::{ | |||
66 | display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, | 66 | display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, |
67 | expand_macro::ExpandedMacro, | 67 | expand_macro::ExpandedMacro, |
68 | folding_ranges::{Fold, FoldKind}, | 68 | folding_ranges::{Fold, FoldKind}, |
69 | hover::HoverResult, | 69 | hover::{HoverAction, HoverConfig, HoverResult}, |
70 | inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, | 70 | inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, |
71 | references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, | 71 | references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, |
72 | runnables::{Runnable, RunnableKind, TestId}, | 72 | runnables::{Runnable, RunnableKind, TestId}, |
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 8b06cbfc5..1db60b87f 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs | |||
@@ -18,7 +18,7 @@ use ra_syntax::{ | |||
18 | use crate::RootDatabase; | 18 | use crate::RootDatabase; |
19 | 19 | ||
20 | // FIXME: a more precise name would probably be `Symbol`? | 20 | // FIXME: a more precise name would probably be `Symbol`? |
21 | #[derive(Debug, PartialEq, Eq)] | 21 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] |
22 | pub enum Definition { | 22 | pub enum Definition { |
23 | Macro(MacroDef), | 23 | Macro(MacroDef), |
24 | Field(Field), | 24 | Field(Field), |
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs index 1b74e6558..93d5891a0 100644 --- a/crates/ra_ide_db/src/lib.rs +++ b/crates/ra_ide_db/src/lib.rs | |||
@@ -57,12 +57,8 @@ impl FileLoader for RootDatabase { | |||
57 | fn file_text(&self, file_id: FileId) -> Arc<String> { | 57 | fn file_text(&self, file_id: FileId) -> Arc<String> { |
58 | FileLoaderDelegate(self).file_text(file_id) | 58 | FileLoaderDelegate(self).file_text(file_id) |
59 | } | 59 | } |
60 | fn resolve_relative_path( | 60 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { |
61 | &self, | 61 | FileLoaderDelegate(self).resolve_path(anchor, path) |
62 | anchor: FileId, | ||
63 | relative_path: &RelativePath, | ||
64 | ) -> Option<FileId> { | ||
65 | FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) | ||
66 | } | 62 | } |
67 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | 63 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { |
68 | FileLoaderDelegate(self).relevant_crates(file_id) | 64 | FileLoaderDelegate(self).relevant_crates(file_id) |
diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs index 67a924de5..9c14b954a 100644 --- a/crates/ra_parser/src/grammar/items.rs +++ b/crates/ra_parser/src/grammar/items.rs | |||
@@ -118,7 +118,17 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul | |||
118 | && p.at_contextual_kw("default") | 118 | && p.at_contextual_kw("default") |
119 | && (match p.nth(1) { | 119 | && (match p.nth(1) { |
120 | T![impl] => true, | 120 | T![impl] => true, |
121 | T![fn] | T![type] => { | 121 | T![unsafe] => { |
122 | // test default_unsafe_impl | ||
123 | // default unsafe impl Foo {} | ||
124 | if p.nth(2) == T![impl] { | ||
125 | p.bump_remap(T![default]); | ||
126 | p.bump(T![unsafe]); | ||
127 | has_mods = true; | ||
128 | } | ||
129 | false | ||
130 | } | ||
131 | T![fn] | T![type] | T![const] => { | ||
122 | if let ItemFlavor::Mod = flavor { | 132 | if let ItemFlavor::Mod = flavor { |
123 | true | 133 | true |
124 | } else { | 134 | } else { |
@@ -198,6 +208,9 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul | |||
198 | // default type T = Bar; | 208 | // default type T = Bar; |
199 | // default fn foo() {} | 209 | // default fn foo() {} |
200 | // } | 210 | // } |
211 | T![const] => { | ||
212 | consts::const_def(p, m); | ||
213 | } | ||
201 | 214 | ||
202 | // test unsafe_default_impl | 215 | // test unsafe_default_impl |
203 | // unsafe default impl Foo {} | 216 | // unsafe default impl Foo {} |
diff --git a/crates/ra_syntax/test_data/parser/err/0043_default_const.rast b/crates/ra_syntax/test_data/parser/err/0043_default_const.rast new file mode 100644 index 000000000..8eb583ef8 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0043_default_const.rast | |||
@@ -0,0 +1,40 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "trait" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] "T" | ||
7 | [email protected] " " | ||
8 | [email protected] | ||
9 | [email protected] "{" | ||
10 | [email protected] "\n " | ||
11 | [email protected] | ||
12 | [email protected] | ||
13 | [email protected] | ||
14 | [email protected] | ||
15 | [email protected] "default" | ||
16 | [email protected] " " | ||
17 | [email protected] | ||
18 | [email protected] "const" | ||
19 | [email protected] " " | ||
20 | [email protected] | ||
21 | [email protected] "f" | ||
22 | [email protected] ":" | ||
23 | [email protected] " " | ||
24 | [email protected] | ||
25 | [email protected] | ||
26 | [email protected] | ||
27 | [email protected] | ||
28 | [email protected] "u8" | ||
29 | [email protected] " " | ||
30 | [email protected] "=" | ||
31 | [email protected] " " | ||
32 | [email protected] | ||
33 | [email protected] "0" | ||
34 | [email protected] ";" | ||
35 | [email protected] "\n" | ||
36 | [email protected] "}" | ||
37 | [email protected] "\n" | ||
38 | error 19..19: expected BANG | ||
39 | error 19..19: expected `{`, `[`, `(` | ||
40 | error 19..19: expected SEMICOLON | ||
diff --git a/crates/ra_syntax/test_data/parser/err/0043_default_const.rs b/crates/ra_syntax/test_data/parser/err/0043_default_const.rs new file mode 100644 index 000000000..80f15474a --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0043_default_const.rs | |||
@@ -0,0 +1,3 @@ | |||
1 | trait T { | ||
2 | default const f: u8 = 0; | ||
3 | } | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast b/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast index 9be441110..53f7ebaf9 100644 --- a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast +++ b/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast | |||
@@ -17,23 +17,29 @@ [email protected] | |||
17 | [email protected] "{" | 17 | [email protected] "{" |
18 | [email protected] "}" | 18 | [email protected] "}" |
19 | [email protected] "\n" | 19 | [email protected] "\n" |
20 | ERROR@25..31 | 20 | CONST_DEF@25..46 |
21 | [email protected] "unsafe" | 21 | [email protected] "unsafe" |
22 | [email protected] " " | 22 | [email protected] " " |
23 | [email protected] | ||
24 | [email protected] "const" | 23 | [email protected] "const" |
25 | [email protected] " " | 24 | [email protected] " " |
26 | [email protected] "fn" | 25 | [email protected] |
26 | [email protected] "fn" | ||
27 | [email protected] " " | 27 | [email protected] " " |
28 | [email protected] | 28 | [email protected] |
29 | [email protected] "bar" | 29 | [email protected] |
30 | [email protected] | 30 | [email protected] |
31 | [email protected] "(" | 31 | [email protected] |
32 | [email protected] ")" | 32 | [email protected] "bar" |
33 | [email protected] " " | 33 | [email protected] |
34 | [email protected] | 34 | [email protected] "(" |
35 | [email protected] "{" | 35 | [email protected] ")" |
36 | [email protected] "}" | 36 | [email protected] " " |
37 | [email protected] | ||
38 | [email protected] "{" | ||
39 | [email protected] "}" | ||
37 | [email protected] "\n" | 40 | [email protected] "\n" |
38 | error 6..6: expected existential, fn, trait or impl | 41 | error 6..6: expected existential, fn, trait or impl |
39 | error 31..31: expected existential, fn, trait or impl | 42 | error 38..38: expected a name |
43 | error 40..40: expected COLON | ||
44 | error 46..46: expected SEMICOLON | ||
45 | error 47..47: expected an item | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast new file mode 100644 index 000000000..a9eda5668 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast | |||
@@ -0,0 +1,18 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "default" | ||
4 | [email protected] " " | ||
5 | [email protected] "unsafe" | ||
6 | [email protected] " " | ||
7 | [email protected] "impl" | ||
8 | [email protected] " " | ||
9 | [email protected] | ||
10 | [email protected] | ||
11 | [email protected] | ||
12 | [email protected] | ||
13 | [email protected] "Foo" | ||
14 | [email protected] " " | ||
15 | [email protected] | ||
16 | [email protected] "{" | ||
17 | [email protected] "}" | ||
18 | [email protected] "\n" | ||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs new file mode 100644 index 000000000..ba0998ff4 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs | |||
@@ -0,0 +1 @@ | |||
default unsafe impl Foo {} | |||
diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast new file mode 100644 index 000000000..dab0247ee --- /dev/null +++ b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast | |||
@@ -0,0 +1,44 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "impl" | ||
4 | [email protected] " " | ||
5 | [email protected] | ||
6 | [email protected] | ||
7 | [email protected] | ||
8 | [email protected] | ||
9 | [email protected] "T" | ||
10 | [email protected] " " | ||
11 | [email protected] "for" | ||
12 | [email protected] " " | ||
13 | [email protected] | ||
14 | [email protected] | ||
15 | [email protected] | ||
16 | [email protected] | ||
17 | [email protected] "Foo" | ||
18 | [email protected] " " | ||
19 | [email protected] | ||
20 | [email protected] "{" | ||
21 | [email protected] "\n " | ||
22 | [email protected] | ||
23 | [email protected] "default" | ||
24 | [email protected] " " | ||
25 | [email protected] "const" | ||
26 | [email protected] " " | ||
27 | [email protected] | ||
28 | [email protected] "f" | ||
29 | [email protected] ":" | ||
30 | [email protected] " " | ||
31 | [email protected] | ||
32 | [email protected] | ||
33 | [email protected] | ||
34 | [email protected] | ||
35 | [email protected] "u8" | ||
36 | [email protected] " " | ||
37 | [email protected] "=" | ||
38 | [email protected] " " | ||
39 | [email protected] | ||
40 | [email protected] "0" | ||
41 | [email protected] ";" | ||
42 | [email protected] "\n" | ||
43 | [email protected] "}" | ||
44 | [email protected] "\n" | ||
diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs new file mode 100644 index 000000000..dfb3b92dc --- /dev/null +++ b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs | |||
@@ -0,0 +1,3 @@ | |||
1 | impl T for Foo { | ||
2 | default const f: u8 = 0; | ||
3 | } | ||
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 23168c3ae..8d6efdbe8 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -11,7 +11,7 @@ use std::{ffi::OsString, path::PathBuf}; | |||
11 | 11 | ||
12 | use lsp_types::ClientCapabilities; | 12 | use lsp_types::ClientCapabilities; |
13 | use ra_flycheck::FlycheckConfig; | 13 | use ra_flycheck::FlycheckConfig; |
14 | use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; | 14 | use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig}; |
15 | use ra_project_model::{CargoConfig, JsonProject, ProjectManifest}; | 15 | use ra_project_model::{CargoConfig, JsonProject, ProjectManifest}; |
16 | use serde::Deserialize; | 16 | use serde::Deserialize; |
17 | 17 | ||
@@ -34,6 +34,7 @@ pub struct Config { | |||
34 | pub assist: AssistConfig, | 34 | pub assist: AssistConfig, |
35 | pub call_info_full: bool, | 35 | pub call_info_full: bool, |
36 | pub lens: LensConfig, | 36 | pub lens: LensConfig, |
37 | pub hover: HoverConfig, | ||
37 | 38 | ||
38 | pub with_sysroot: bool, | 39 | pub with_sysroot: bool, |
39 | pub linked_projects: Vec<LinkedProject>, | 40 | pub linked_projects: Vec<LinkedProject>, |
@@ -124,6 +125,7 @@ pub struct ClientCapsConfig { | |||
124 | pub work_done_progress: bool, | 125 | pub work_done_progress: bool, |
125 | pub code_action_group: bool, | 126 | pub code_action_group: bool, |
126 | pub resolve_code_action: bool, | 127 | pub resolve_code_action: bool, |
128 | pub hover_actions: bool, | ||
127 | } | 129 | } |
128 | 130 | ||
129 | impl Default for Config { | 131 | impl Default for Config { |
@@ -162,6 +164,7 @@ impl Default for Config { | |||
162 | assist: AssistConfig::default(), | 164 | assist: AssistConfig::default(), |
163 | call_info_full: true, | 165 | call_info_full: true, |
164 | lens: LensConfig::default(), | 166 | lens: LensConfig::default(), |
167 | hover: HoverConfig::default(), | ||
165 | linked_projects: Vec::new(), | 168 | linked_projects: Vec::new(), |
166 | } | 169 | } |
167 | } | 170 | } |
@@ -278,6 +281,14 @@ impl Config { | |||
278 | } | 281 | } |
279 | } | 282 | } |
280 | 283 | ||
284 | let mut use_hover_actions = false; | ||
285 | set(value, "/hoverActions/enable", &mut use_hover_actions); | ||
286 | if use_hover_actions { | ||
287 | set(value, "/hoverActions/implementations", &mut self.hover.implementations); | ||
288 | } else { | ||
289 | self.hover = HoverConfig::NO_ACTIONS; | ||
290 | } | ||
291 | |||
281 | log::info!("Config::update() = {:#?}", self); | 292 | log::info!("Config::update() = {:#?}", self); |
282 | 293 | ||
283 | fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { | 294 | fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { |
@@ -331,17 +342,15 @@ impl Config { | |||
331 | 342 | ||
332 | self.assist.allow_snippets(false); | 343 | self.assist.allow_snippets(false); |
333 | if let Some(experimental) = &caps.experimental { | 344 | if let Some(experimental) = &caps.experimental { |
334 | let snippet_text_edit = | 345 | let get_bool = |
335 | experimental.get("snippetTextEdit").and_then(|it| it.as_bool()) == Some(true); | 346 | |index: &str| experimental.get(index).and_then(|it| it.as_bool()) == Some(true); |
336 | self.assist.allow_snippets(snippet_text_edit); | ||
337 | 347 | ||
338 | let code_action_group = | 348 | let snippet_text_edit = get_bool("snippetTextEdit"); |
339 | experimental.get("codeActionGroup").and_then(|it| it.as_bool()) == Some(true); | 349 | self.assist.allow_snippets(snippet_text_edit); |
340 | self.client_caps.code_action_group = code_action_group; | ||
341 | 350 | ||
342 | let resolve_code_action = | 351 | self.client_caps.code_action_group = get_bool("codeActionGroup"); |
343 | experimental.get("resolveCodeAction").and_then(|it| it.as_bool()) == Some(true); | 352 | self.client_caps.resolve_code_action = get_bool("resolveCodeAction"); |
344 | self.client_caps.resolve_code_action = resolve_code_action; | 353 | self.client_caps.hover_actions = get_bool("hoverActions"); |
345 | } | 354 | } |
346 | } | 355 | } |
347 | } | 356 | } |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 3b957534d..1371f6cb4 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -260,3 +260,35 @@ pub struct SnippetTextEdit { | |||
260 | #[serde(skip_serializing_if = "Option::is_none")] | 260 | #[serde(skip_serializing_if = "Option::is_none")] |
261 | pub insert_text_format: Option<lsp_types::InsertTextFormat>, | 261 | pub insert_text_format: Option<lsp_types::InsertTextFormat>, |
262 | } | 262 | } |
263 | |||
264 | pub enum HoverRequest {} | ||
265 | |||
266 | impl Request for HoverRequest { | ||
267 | type Params = lsp_types::HoverParams; | ||
268 | type Result = Option<Hover>; | ||
269 | const METHOD: &'static str = "textDocument/hover"; | ||
270 | } | ||
271 | |||
272 | #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] | ||
273 | pub struct Hover { | ||
274 | #[serde(flatten)] | ||
275 | pub hover: lsp_types::Hover, | ||
276 | #[serde(skip_serializing_if = "Vec::is_empty")] | ||
277 | pub actions: Vec<CommandLinkGroup>, | ||
278 | } | ||
279 | |||
280 | #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] | ||
281 | pub struct CommandLinkGroup { | ||
282 | #[serde(skip_serializing_if = "Option::is_none")] | ||
283 | pub title: Option<String>, | ||
284 | pub commands: Vec<CommandLink>, | ||
285 | } | ||
286 | |||
287 | // LSP v3.15 Command does not have a `tooltip` field, vscode supports one. | ||
288 | #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] | ||
289 | pub struct CommandLink { | ||
290 | #[serde(flatten)] | ||
291 | pub command: lsp_types::Command, | ||
292 | #[serde(skip_serializing_if = "Option::is_none")] | ||
293 | pub tooltip: Option<String>, | ||
294 | } | ||
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index e60337b8e..752dbf145 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -510,6 +510,7 @@ fn on_request( | |||
510 | .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)? | 510 | .on::<lsp_ext::InlayHints>(handlers::handle_inlay_hints)? |
511 | .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)? | 511 | .on::<lsp_ext::CodeActionRequest>(handlers::handle_code_action)? |
512 | .on::<lsp_ext::ResolveCodeActionRequest>(handlers::handle_resolve_code_action)? | 512 | .on::<lsp_ext::ResolveCodeActionRequest>(handlers::handle_resolve_code_action)? |
513 | .on::<lsp_ext::HoverRequest>(handlers::handle_hover)? | ||
513 | .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)? | 514 | .on::<lsp_types::request::OnTypeFormatting>(handlers::handle_on_type_formatting)? |
514 | .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)? | 515 | .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)? |
515 | .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)? | 516 | .on::<lsp_types::request::WorkspaceSymbol>(handlers::handle_workspace_symbol)? |
@@ -521,7 +522,6 @@ fn on_request( | |||
521 | .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)? | 522 | .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)? |
522 | .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)? | 523 | .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)? |
523 | .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)? | 524 | .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)? |
524 | .on::<lsp_types::request::HoverRequest>(handlers::handle_hover)? | ||
525 | .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)? | 525 | .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)? |
526 | .on::<lsp_types::request::Rename>(handlers::handle_rename)? | 526 | .on::<lsp_types::request::Rename>(handlers::handle_rename)? |
527 | .on::<lsp_types::request::References>(handlers::handle_references)? | 527 | .on::<lsp_types::request::References>(handlers::handle_references)? |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 6acf80c58..3ff779702 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -12,13 +12,14 @@ use lsp_types::{ | |||
12 | CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, | 12 | CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, |
13 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, | 13 | CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, |
14 | CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight, | 14 | CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight, |
15 | DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, | 15 | DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location, MarkupContent, |
16 | MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, | 16 | MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensParams, |
17 | SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, | 17 | SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, |
18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, | 18 | TextDocumentIdentifier, Url, WorkspaceEdit, |
19 | }; | 19 | }; |
20 | use ra_ide::{ | 20 | use ra_ide::{ |
21 | FileId, FilePosition, FileRange, Query, RangeInfo, RunnableKind, SearchScope, TextEdit, | 21 | FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, RunnableKind, SearchScope, |
22 | TextEdit, | ||
22 | }; | 23 | }; |
23 | use ra_prof::profile; | 24 | use ra_prof::profile; |
24 | use ra_project_model::TargetKind; | 25 | use ra_project_model::TargetKind; |
@@ -537,7 +538,7 @@ pub fn handle_signature_help( | |||
537 | pub fn handle_hover( | 538 | pub fn handle_hover( |
538 | snap: GlobalStateSnapshot, | 539 | snap: GlobalStateSnapshot, |
539 | params: lsp_types::HoverParams, | 540 | params: lsp_types::HoverParams, |
540 | ) -> Result<Option<Hover>> { | 541 | ) -> Result<Option<lsp_ext::Hover>> { |
541 | let _p = profile("handle_hover"); | 542 | let _p = profile("handle_hover"); |
542 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; | 543 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; |
543 | let info = match snap.analysis().hover(position)? { | 544 | let info = match snap.analysis().hover(position)? { |
@@ -546,14 +547,18 @@ pub fn handle_hover( | |||
546 | }; | 547 | }; |
547 | let line_index = snap.analysis.file_line_index(position.file_id)?; | 548 | let line_index = snap.analysis.file_line_index(position.file_id)?; |
548 | let range = to_proto::range(&line_index, info.range); | 549 | let range = to_proto::range(&line_index, info.range); |
549 | let res = Hover { | 550 | let hover = lsp_ext::Hover { |
550 | contents: HoverContents::Markup(MarkupContent { | 551 | hover: lsp_types::Hover { |
551 | kind: MarkupKind::Markdown, | 552 | contents: HoverContents::Markup(MarkupContent { |
552 | value: crate::markdown::format_docs(&info.info.to_markup()), | 553 | kind: MarkupKind::Markdown, |
553 | }), | 554 | value: crate::markdown::format_docs(&info.info.to_markup()), |
554 | range: Some(range), | 555 | }), |
556 | range: Some(range), | ||
557 | }, | ||
558 | actions: prepare_hover_actions(&snap, info.info.actions()), | ||
555 | }; | 559 | }; |
556 | Ok(Some(res)) | 560 | |
561 | Ok(Some(hover)) | ||
557 | } | 562 | } |
558 | 563 | ||
559 | pub fn handle_prepare_rename( | 564 | pub fn handle_prepare_rename( |
@@ -924,24 +929,13 @@ pub fn handle_code_lens_resolve( | |||
924 | _ => vec![], | 929 | _ => vec![], |
925 | }; | 930 | }; |
926 | 931 | ||
927 | let title = if locations.len() == 1 { | 932 | let title = implementation_title(locations.len()); |
928 | "1 implementation".into() | 933 | let cmd = show_references_command( |
929 | } else { | ||
930 | format!("{} implementations", locations.len()) | ||
931 | }; | ||
932 | |||
933 | // We cannot use the 'editor.action.showReferences' command directly | ||
934 | // because that command requires vscode types which we convert in the handler | ||
935 | // on the client side. | ||
936 | let cmd = Command { | ||
937 | title, | 934 | title, |
938 | command: "rust-analyzer.showReferences".into(), | 935 | &lens_params.text_document_position_params.text_document.uri, |
939 | arguments: Some(vec![ | 936 | code_lens.range.start, |
940 | to_value(&lens_params.text_document_position_params.text_document.uri).unwrap(), | 937 | locations, |
941 | to_value(code_lens.range.start).unwrap(), | 938 | ); |
942 | to_value(locations).unwrap(), | ||
943 | ]), | ||
944 | }; | ||
945 | Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None }) | 939 | Ok(CodeLens { range: code_lens.range, command: Some(cmd), data: None }) |
946 | } | 940 | } |
947 | None => Ok(CodeLens { | 941 | None => Ok(CodeLens { |
@@ -1145,3 +1139,78 @@ pub fn handle_semantic_tokens_range( | |||
1145 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | 1139 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); |
1146 | Ok(Some(semantic_tokens.into())) | 1140 | Ok(Some(semantic_tokens.into())) |
1147 | } | 1141 | } |
1142 | |||
1143 | fn implementation_title(count: usize) -> String { | ||
1144 | if count == 1 { | ||
1145 | "1 implementation".into() | ||
1146 | } else { | ||
1147 | format!("{} implementations", count) | ||
1148 | } | ||
1149 | } | ||
1150 | |||
1151 | fn show_references_command( | ||
1152 | title: String, | ||
1153 | uri: &lsp_types::Url, | ||
1154 | position: lsp_types::Position, | ||
1155 | locations: Vec<lsp_types::Location>, | ||
1156 | ) -> Command { | ||
1157 | // We cannot use the 'editor.action.showReferences' command directly | ||
1158 | // because that command requires vscode types which we convert in the handler | ||
1159 | // on the client side. | ||
1160 | |||
1161 | Command { | ||
1162 | title, | ||
1163 | command: "rust-analyzer.showReferences".into(), | ||
1164 | arguments: Some(vec![ | ||
1165 | to_value(uri).unwrap(), | ||
1166 | to_value(position).unwrap(), | ||
1167 | to_value(locations).unwrap(), | ||
1168 | ]), | ||
1169 | } | ||
1170 | } | ||
1171 | |||
1172 | fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { | ||
1173 | lsp_ext::CommandLink { tooltip: Some(tooltip), command } | ||
1174 | } | ||
1175 | |||
1176 | fn show_impl_command_link( | ||
1177 | snap: &GlobalStateSnapshot, | ||
1178 | position: &FilePosition, | ||
1179 | ) -> Option<lsp_ext::CommandLinkGroup> { | ||
1180 | if snap.config.hover.implementations { | ||
1181 | if let Some(nav_data) = snap.analysis().goto_implementation(*position).unwrap_or(None) { | ||
1182 | let uri = to_proto::url(snap, position.file_id).ok()?; | ||
1183 | let line_index = snap.analysis().file_line_index(position.file_id).ok()?; | ||
1184 | let position = to_proto::position(&line_index, position.offset); | ||
1185 | let locations: Vec<_> = nav_data | ||
1186 | .info | ||
1187 | .iter() | ||
1188 | .filter_map(|it| to_proto::location(snap, it.file_range()).ok()) | ||
1189 | .collect(); | ||
1190 | let title = implementation_title(locations.len()); | ||
1191 | let command = show_references_command(title, &uri, position, locations); | ||
1192 | |||
1193 | return Some(lsp_ext::CommandLinkGroup { | ||
1194 | commands: vec![to_command_link(command, "Go to implementations".into())], | ||
1195 | ..Default::default() | ||
1196 | }); | ||
1197 | } | ||
1198 | } | ||
1199 | None | ||
1200 | } | ||
1201 | |||
1202 | fn prepare_hover_actions( | ||
1203 | snap: &GlobalStateSnapshot, | ||
1204 | actions: &[HoverAction], | ||
1205 | ) -> Vec<lsp_ext::CommandLinkGroup> { | ||
1206 | if snap.config.hover.none() || !snap.config.client_caps.hover_actions { | ||
1207 | return Vec::new(); | ||
1208 | } | ||
1209 | |||
1210 | actions | ||
1211 | .iter() | ||
1212 | .filter_map(|it| match it { | ||
1213 | HoverAction::Implementaion(position) => show_impl_command_link(snap, position), | ||
1214 | }) | ||
1215 | .collect() | ||
1216 | } | ||
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index ad3476310..0e2a83c6a 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs | |||
@@ -4,9 +4,7 @@ use std::{collections::HashMap, path::PathBuf, time::Instant}; | |||
4 | 4 | ||
5 | use lsp_types::{ | 5 | use lsp_types::{ |
6 | notification::DidOpenTextDocument, | 6 | notification::DidOpenTextDocument, |
7 | request::{ | 7 | request::{CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest}, |
8 | CodeActionRequest, Completion, Formatting, GotoDefinition, GotoTypeDefinition, HoverRequest, | ||
9 | }, | ||
10 | CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, | 8 | CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, |
11 | DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams, | 9 | DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams, |
12 | PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams, | 10 | PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams, |
@@ -507,6 +505,10 @@ fn main() { | |||
507 | println!("cargo:rerun-if-changed=build.rs"); | 505 | println!("cargo:rerun-if-changed=build.rs"); |
508 | } | 506 | } |
509 | //- src/main.rs | 507 | //- src/main.rs |
508 | #[rustc_builtin_macro] macro_rules! include {} | ||
509 | #[rustc_builtin_macro] macro_rules! concat {} | ||
510 | #[rustc_builtin_macro] macro_rules! env {} | ||
511 | |||
510 | include!(concat!(env!("OUT_DIR"), "/hello.rs")); | 512 | include!(concat!(env!("OUT_DIR"), "/hello.rs")); |
511 | 513 | ||
512 | #[cfg(atom_cfg)] | 514 | #[cfg(atom_cfg)] |
@@ -521,10 +523,8 @@ struct B; | |||
521 | fn main() { | 523 | fn main() { |
522 | let va = A; | 524 | let va = A; |
523 | let vb = B; | 525 | let vb = B; |
524 | message(); | 526 | let should_be_str = message(); |
525 | } | 527 | } |
526 | |||
527 | fn main() { message(); } | ||
528 | "###, | 528 | "###, |
529 | ) | 529 | ) |
530 | .with_config(|config| { | 530 | .with_config(|config| { |
@@ -532,54 +532,35 @@ fn main() { message(); } | |||
532 | }) | 532 | }) |
533 | .server(); | 533 | .server(); |
534 | server.wait_until_workspace_is_loaded(); | 534 | server.wait_until_workspace_is_loaded(); |
535 | let res = server.send_request::<GotoDefinition>(GotoDefinitionParams { | 535 | let res = server.send_request::<HoverRequest>(HoverParams { |
536 | text_document_position_params: TextDocumentPositionParams::new( | 536 | text_document_position_params: TextDocumentPositionParams::new( |
537 | server.doc_id("src/main.rs"), | 537 | server.doc_id("src/main.rs"), |
538 | Position::new(14, 8), | 538 | Position::new(18, 10), |
539 | ), | 539 | ), |
540 | work_done_progress_params: Default::default(), | 540 | work_done_progress_params: Default::default(), |
541 | partial_result_params: Default::default(), | ||
542 | }); | 541 | }); |
543 | assert!(format!("{}", res).contains("hello.rs")); | 542 | assert!(res.to_string().contains("&str")); |
544 | server.request::<GotoTypeDefinition>( | 543 | server.request::<GotoTypeDefinition>( |
545 | GotoDefinitionParams { | 544 | GotoDefinitionParams { |
546 | text_document_position_params: TextDocumentPositionParams::new( | 545 | text_document_position_params: TextDocumentPositionParams::new( |
547 | server.doc_id("src/main.rs"), | 546 | server.doc_id("src/main.rs"), |
548 | Position::new(12, 9), | 547 | Position::new(16, 9), |
549 | ), | 548 | ), |
550 | work_done_progress_params: Default::default(), | 549 | work_done_progress_params: Default::default(), |
551 | partial_result_params: Default::default(), | 550 | partial_result_params: Default::default(), |
552 | }, | 551 | }, |
553 | json!([{ | 552 | json!([{ |
554 | "originSelectionRange": { | 553 | "originSelectionRange": { |
555 | "end": { | 554 | "end": { "character": 10, "line": 16 }, |
556 | "character": 10, | 555 | "start": { "character": 8, "line": 16 } |
557 | "line": 12 | ||
558 | }, | ||
559 | "start": { | ||
560 | "character": 8, | ||
561 | "line": 12 | ||
562 | } | ||
563 | }, | 556 | }, |
564 | "targetRange": { | 557 | "targetRange": { |
565 | "end": { | 558 | "end": { "character": 9, "line": 7 }, |
566 | "character": 9, | 559 | "start": { "character": 0, "line": 6 } |
567 | "line": 3 | ||
568 | }, | ||
569 | "start": { | ||
570 | "character": 0, | ||
571 | "line": 2 | ||
572 | } | ||
573 | }, | 560 | }, |
574 | "targetSelectionRange": { | 561 | "targetSelectionRange": { |
575 | "end": { | 562 | "end": { "character": 8, "line": 7 }, |
576 | "character": 8, | 563 | "start": { "character": 7, "line": 7 } |
577 | "line": 3 | ||
578 | }, | ||
579 | "start": { | ||
580 | "character": 7, | ||
581 | "line": 3 | ||
582 | } | ||
583 | }, | 564 | }, |
584 | "targetUri": "file:///[..]src/main.rs" | 565 | "targetUri": "file:///[..]src/main.rs" |
585 | }]), | 566 | }]), |
@@ -588,41 +569,23 @@ fn main() { message(); } | |||
588 | GotoDefinitionParams { | 569 | GotoDefinitionParams { |
589 | text_document_position_params: TextDocumentPositionParams::new( | 570 | text_document_position_params: TextDocumentPositionParams::new( |
590 | server.doc_id("src/main.rs"), | 571 | server.doc_id("src/main.rs"), |
591 | Position::new(13, 9), | 572 | Position::new(17, 9), |
592 | ), | 573 | ), |
593 | work_done_progress_params: Default::default(), | 574 | work_done_progress_params: Default::default(), |
594 | partial_result_params: Default::default(), | 575 | partial_result_params: Default::default(), |
595 | }, | 576 | }, |
596 | json!([{ | 577 | json!([{ |
597 | "originSelectionRange": { | 578 | "originSelectionRange": { |
598 | "end": { | 579 | "end": { "character": 10, "line": 17 }, |
599 | "character": 10, | 580 | "start": { "character": 8, "line": 17 } |
600 | "line": 13 | ||
601 | }, | ||
602 | "start": { | ||
603 | "character": 8, | ||
604 | "line":13 | ||
605 | } | ||
606 | }, | 581 | }, |
607 | "targetRange": { | 582 | "targetRange": { |
608 | "end": { | 583 | "end": { "character": 9, "line": 11 }, |
609 | "character": 9, | 584 | "start": { "character": 0, "line":10 } |
610 | "line": 7 | ||
611 | }, | ||
612 | "start": { | ||
613 | "character": 0, | ||
614 | "line":6 | ||
615 | } | ||
616 | }, | 585 | }, |
617 | "targetSelectionRange": { | 586 | "targetSelectionRange": { |
618 | "end": { | 587 | "end": { "character": 8, "line": 11 }, |
619 | "character": 8, | 588 | "start": { "character": 7, "line": 11 } |
620 | "line": 7 | ||
621 | }, | ||
622 | "start": { | ||
623 | "character": 7, | ||
624 | "line": 7 | ||
625 | } | ||
626 | }, | 589 | }, |
627 | "targetUri": "file:///[..]src/main.rs" | 590 | "targetUri": "file:///[..]src/main.rs" |
628 | }]), | 591 | }]), |
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 7f7940d0b..a0847dad3 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -467,3 +467,41 @@ interface InlayHint { | |||
467 | label: string, | 467 | label: string, |
468 | } | 468 | } |
469 | ``` | 469 | ``` |
470 | |||
471 | ## Hover Actions | ||
472 | |||
473 | **Client Capability:** `{ "hoverActions": boolean }` | ||
474 | |||
475 | If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`: | ||
476 | |||
477 | ```typescript | ||
478 | interface Hover { | ||
479 | ... | ||
480 | actions?: CommandLinkGroup[]; | ||
481 | } | ||
482 | |||
483 | interface CommandLink extends Command { | ||
484 | /** | ||
485 | * A tooltip for the command, when represented in the UI. | ||
486 | */ | ||
487 | tooltip?: string; | ||
488 | } | ||
489 | |||
490 | interface CommandLinkGroup { | ||
491 | title?: string; | ||
492 | commands: CommandLink[]; | ||
493 | } | ||
494 | ``` | ||
495 | |||
496 | Such actions on the client side are appended to a hover bottom as command links: | ||
497 | ``` | ||
498 | +-----------------------------+ | ||
499 | | Hover content | | ||
500 | | | | ||
501 | +-----------------------------+ | ||
502 | | _Action1_ | _Action2_ | <- first group, no TITLE | ||
503 | +-----------------------------+ | ||
504 | | TITLE _Action1_ | _Action2_ | <- second group | ||
505 | +-----------------------------+ | ||
506 | ... | ||
507 | ``` \ No newline at end of file | ||
diff --git a/editors/code/package.json b/editors/code/package.json index 30ab7ba4a..b9c57db3b 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -462,17 +462,27 @@ | |||
462 | "default": true | 462 | "default": true |
463 | }, | 463 | }, |
464 | "rust-analyzer.lens.run": { | 464 | "rust-analyzer.lens.run": { |
465 | "markdownDescription": "Whether to show Run lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 465 | "markdownDescription": "Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", |
466 | "type": "boolean", | 466 | "type": "boolean", |
467 | "default": true | 467 | "default": true |
468 | }, | 468 | }, |
469 | "rust-analyzer.lens.debug": { | 469 | "rust-analyzer.lens.debug": { |
470 | "markdownDescription": "Whether to show Debug lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 470 | "markdownDescription": "Whether to show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", |
471 | "type": "boolean", | 471 | "type": "boolean", |
472 | "default": true | 472 | "default": true |
473 | }, | 473 | }, |
474 | "rust-analyzer.lens.implementations": { | 474 | "rust-analyzer.lens.implementations": { |
475 | "markdownDescription": "Whether to show Implementations lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 475 | "markdownDescription": "Whether to show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", |
476 | "type": "boolean", | ||
477 | "default": true | ||
478 | }, | ||
479 | "rust-analyzer.hoverActions.enable": { | ||
480 | "description": "Whether to show HoverActions in Rust files.", | ||
481 | "type": "boolean", | ||
482 | "default": true | ||
483 | }, | ||
484 | "rust-analyzer.hoverActions.implementations": { | ||
485 | "markdownDescription": "Whether to show `Implementations` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", | ||
476 | "type": "boolean", | 486 | "type": "boolean", |
477 | "default": true | 487 | "default": true |
478 | }, | 488 | }, |
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 40ad1e3cd..65ad573d8 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts | |||
@@ -7,6 +7,20 @@ import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.pr | |||
7 | import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; | 7 | import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; |
8 | import { assert } from './util'; | 8 | import { assert } from './util'; |
9 | 9 | ||
10 | function renderCommand(cmd: ra.CommandLink) { | ||
11 | return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`; | ||
12 | } | ||
13 | |||
14 | function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownString { | ||
15 | const text = actions.map(group => | ||
16 | (group.title ? (group.title + " ") : "") + group.commands.map(renderCommand).join(' | ') | ||
17 | ).join('___'); | ||
18 | |||
19 | const result = new vscode.MarkdownString(text); | ||
20 | result.isTrusted = true; | ||
21 | return result; | ||
22 | } | ||
23 | |||
10 | export function createClient(serverPath: string, cwd: string): lc.LanguageClient { | 24 | export function createClient(serverPath: string, cwd: string): lc.LanguageClient { |
11 | // '.' Is the fallback if no folder is open | 25 | // '.' Is the fallback if no folder is open |
12 | // TODO?: Workspace folders support Uri's (eg: file://test.txt). | 26 | // TODO?: Workspace folders support Uri's (eg: file://test.txt). |
@@ -35,6 +49,23 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient | |||
35 | if (res === undefined) throw new Error('busy'); | 49 | if (res === undefined) throw new Error('busy'); |
36 | return res; | 50 | return res; |
37 | }, | 51 | }, |
52 | async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, _next: lc.ProvideHoverSignature) { | ||
53 | return client.sendRequest(lc.HoverRequest.type, client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), token).then( | ||
54 | (result) => { | ||
55 | const hover = client.protocol2CodeConverter.asHover(result); | ||
56 | if (hover) { | ||
57 | const actions = (<any>result).actions; | ||
58 | if (actions) { | ||
59 | hover.contents.push(renderHoverActions(actions)); | ||
60 | } | ||
61 | } | ||
62 | return hover; | ||
63 | }, | ||
64 | (error) => { | ||
65 | client.logFailedRequest(lc.HoverRequest.type, error); | ||
66 | return Promise.resolve(null); | ||
67 | }); | ||
68 | }, | ||
38 | // Using custom handling of CodeActions where each code action is resloved lazily | 69 | // Using custom handling of CodeActions where each code action is resloved lazily |
39 | // That's why we are not waiting for any command or edits | 70 | // That's why we are not waiting for any command or edits |
40 | async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { | 71 | async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { |
@@ -129,6 +160,7 @@ class ExperimentalFeatures implements lc.StaticFeature { | |||
129 | caps.snippetTextEdit = true; | 160 | caps.snippetTextEdit = true; |
130 | caps.codeActionGroup = true; | 161 | caps.codeActionGroup = true; |
131 | caps.resolveCodeAction = true; | 162 | caps.resolveCodeAction = true; |
163 | caps.hoverActions = true; | ||
132 | capabilities.experimental = caps; | 164 | capabilities.experimental = caps; |
133 | } | 165 | } |
134 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { | 166 | initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index e8abf8284..d8f0037d4 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -16,10 +16,8 @@ export class Config { | |||
16 | "files", | 16 | "files", |
17 | "highlighting", | 17 | "highlighting", |
18 | "updates.channel", | 18 | "updates.channel", |
19 | "lens.enable", | 19 | "lens", // works as lens.* |
20 | "lens.run", | 20 | "hoverActions", // works as hoverActions.* |
21 | "lens.debug", | ||
22 | "lens.implementations", | ||
23 | ] | 21 | ] |
24 | .map(opt => `${this.rootSection}.${opt}`); | 22 | .map(opt => `${this.rootSection}.${opt}`); |
25 | 23 | ||
@@ -132,4 +130,11 @@ export class Config { | |||
132 | implementations: this.get<boolean>("lens.implementations"), | 130 | implementations: this.get<boolean>("lens.implementations"), |
133 | }; | 131 | }; |
134 | } | 132 | } |
133 | |||
134 | get hoverActions() { | ||
135 | return { | ||
136 | enable: this.get<boolean>("hoverActions.enable"), | ||
137 | implementations: this.get<boolean>("hoverActions.implementations"), | ||
138 | }; | ||
139 | } | ||
135 | } | 140 | } |
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 9793b926c..e16ea799c 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts | |||
@@ -90,3 +90,15 @@ export interface SsrParams { | |||
90 | parseOnly: boolean; | 90 | parseOnly: boolean; |
91 | } | 91 | } |
92 | export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr'); | 92 | export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>('experimental/ssr'); |
93 | |||
94 | export interface CommandLink extends lc.Command { | ||
95 | /** | ||
96 | * A tooltip for the command, when represented in the UI. | ||
97 | */ | ||
98 | tooltip?: string; | ||
99 | } | ||
100 | |||
101 | export interface CommandLinkGroup { | ||
102 | title?: string; | ||
103 | commands: CommandLink[]; | ||
104 | } | ||