diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/ast_transform.rs | 17 | ||||
-rw-r--r-- | crates/assists/src/handlers/add_missing_impl_members.rs | 25 | ||||
-rw-r--r-- | crates/assists/src/handlers/auto_import.rs | 7 | ||||
-rw-r--r-- | crates/assists/src/handlers/extract_struct_from_enum_variant.rs | 8 | ||||
-rw-r--r-- | crates/assists/src/handlers/fill_match_arms.rs | 4 | ||||
-rw-r--r-- | crates/assists/src/utils.rs | 21 | ||||
-rw-r--r-- | crates/hir/src/lib.rs | 7 | ||||
-rw-r--r-- | crates/hir_def/src/find_path.rs | 2 | ||||
-rw-r--r-- | crates/hir_def/src/import_map.rs | 36 | ||||
-rw-r--r-- | crates/hir_def/src/path.rs | 22 | ||||
-rw-r--r-- | crates/hir_ty/src/display.rs | 29 | ||||
-rw-r--r-- | crates/ide/src/hover.rs | 51 | ||||
-rw-r--r-- | crates/ide/src/inlay_hints.rs | 34 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ide/src/markdown_remove.rs | 23 | ||||
-rw-r--r-- | crates/ide/src/typing/on_enter.rs | 29 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 6 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 6 | ||||
-rw-r--r-- | crates/syntax/src/ptr.rs | 2 |
19 files changed, 258 insertions, 75 deletions
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs index 4307e0191..ac72f3f02 100644 --- a/crates/assists/src/ast_transform.rs +++ b/crates/assists/src/ast_transform.rs | |||
@@ -1,13 +1,14 @@ | |||
1 | //! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. | 1 | //! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. |
2 | use rustc_hash::FxHashMap; | ||
3 | |||
4 | use hir::{HirDisplay, PathResolution, SemanticsScope}; | 2 | use hir::{HirDisplay, PathResolution, SemanticsScope}; |
3 | use rustc_hash::FxHashMap; | ||
5 | use syntax::{ | 4 | use syntax::{ |
6 | algo::SyntaxRewriter, | 5 | algo::SyntaxRewriter, |
7 | ast::{self, AstNode}, | 6 | ast::{self, AstNode}, |
8 | SyntaxNode, | 7 | SyntaxNode, |
9 | }; | 8 | }; |
10 | 9 | ||
10 | use crate::utils::mod_path_to_ast; | ||
11 | |||
11 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { | 12 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { |
12 | SyntaxRewriter::from_fn(|element| match element { | 13 | SyntaxRewriter::from_fn(|element| match element { |
13 | syntax::SyntaxElement::Node(n) => { | 14 | syntax::SyntaxElement::Node(n) => { |
@@ -189,7 +190,7 @@ impl<'a> AstTransform<'a> for QualifyPaths<'a> { | |||
189 | match resolution { | 190 | match resolution { |
190 | PathResolution::Def(def) => { | 191 | PathResolution::Def(def) => { |
191 | let found_path = from.find_use_path(self.source_scope.db.upcast(), def)?; | 192 | let found_path = from.find_use_path(self.source_scope.db.upcast(), def)?; |
192 | let mut path = path_to_ast(found_path); | 193 | let mut path = mod_path_to_ast(&found_path); |
193 | 194 | ||
194 | let type_args = p | 195 | let type_args = p |
195 | .segment() | 196 | .segment() |
@@ -210,13 +211,3 @@ impl<'a> AstTransform<'a> for QualifyPaths<'a> { | |||
210 | } | 211 | } |
211 | } | 212 | } |
212 | } | 213 | } |
213 | |||
214 | pub(crate) fn path_to_ast(path: hir::ModPath) -> ast::Path { | ||
215 | let parse = ast::SourceFile::parse(&path.to_string()); | ||
216 | parse | ||
217 | .tree() | ||
218 | .syntax() | ||
219 | .descendants() | ||
220 | .find_map(ast::Path::cast) | ||
221 | .unwrap_or_else(|| panic!("failed to parse path {:?}, `{}`", path, path)) | ||
222 | } | ||
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs index 51b5a2eb0..4c400f287 100644 --- a/crates/assists/src/handlers/add_missing_impl_members.rs +++ b/crates/assists/src/handlers/add_missing_impl_members.rs | |||
@@ -820,4 +820,29 @@ impl Tr for () { | |||
820 | }"#, | 820 | }"#, |
821 | ) | 821 | ) |
822 | } | 822 | } |
823 | |||
824 | #[test] | ||
825 | fn weird_path() { | ||
826 | check_assist( | ||
827 | add_missing_impl_members, | ||
828 | r#" | ||
829 | trait Test { | ||
830 | fn foo(&self, x: crate) | ||
831 | } | ||
832 | impl Test for () { | ||
833 | <|> | ||
834 | } | ||
835 | "#, | ||
836 | r#" | ||
837 | trait Test { | ||
838 | fn foo(&self, x: crate) | ||
839 | } | ||
840 | impl Test for () { | ||
841 | fn foo(&self, x: crate) { | ||
842 | ${0:todo!()} | ||
843 | } | ||
844 | } | ||
845 | "#, | ||
846 | ) | ||
847 | } | ||
823 | } | 848 | } |
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs index 357ff6392..d3ee98e5f 100644 --- a/crates/assists/src/handlers/auto_import.rs +++ b/crates/assists/src/handlers/auto_import.rs | |||
@@ -13,7 +13,10 @@ use syntax::{ | |||
13 | SyntaxNode, | 13 | SyntaxNode, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | use crate::{utils::insert_use, AssistContext, AssistId, AssistKind, Assists, GroupLabel}; | 16 | use crate::{ |
17 | utils::insert_use, utils::mod_path_to_ast, AssistContext, AssistId, AssistKind, Assists, | ||
18 | GroupLabel, | ||
19 | }; | ||
17 | 20 | ||
18 | // Assist: auto_import | 21 | // Assist: auto_import |
19 | // | 22 | // |
@@ -54,7 +57,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
54 | range, | 57 | range, |
55 | |builder| { | 58 | |builder| { |
56 | let new_syntax = | 59 | let new_syntax = |
57 | insert_use(&scope, import.to_ast_path(), ctx.config.insert_use.merge); | 60 | insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use.merge); |
58 | builder.replace(syntax.text_range(), new_syntax.to_string()) | 61 | builder.replace(syntax.text_range(), new_syntax.to_string()) |
59 | }, | 62 | }, |
60 | ); | 63 | ); |
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index f5f03ef36..7f4f80b23 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -10,9 +10,10 @@ use syntax::{ | |||
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{ |
13 | assist_context::AssistBuilder, utils::insert_use, AssistContext, AssistId, AssistKind, Assists, | 13 | assist_context::AssistBuilder, |
14 | utils::{insert_use, mod_path_to_ast, ImportScope}, | ||
15 | AssistContext, AssistId, AssistKind, Assists, | ||
14 | }; | 16 | }; |
15 | use insert_use::ImportScope; | ||
16 | 17 | ||
17 | // Assist: extract_struct_from_enum_variant | 18 | // Assist: extract_struct_from_enum_variant |
18 | // | 19 | // |
@@ -111,7 +112,8 @@ fn insert_import( | |||
111 | let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?; | 112 | let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?; |
112 | let syntax = scope.as_syntax_node(); | 113 | let syntax = scope.as_syntax_node(); |
113 | 114 | ||
114 | let new_syntax = insert_use(&scope, mod_path.to_ast_path(), ctx.config.insert_use.merge); | 115 | let new_syntax = |
116 | insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); | ||
115 | // FIXME: this will currently panic as multiple imports will have overlapping text ranges | 117 | // FIXME: this will currently panic as multiple imports will have overlapping text ranges |
116 | builder.replace(syntax.text_range(), new_syntax.to_string()) | 118 | builder.replace(syntax.text_range(), new_syntax.to_string()) |
117 | } | 119 | } |
diff --git a/crates/assists/src/handlers/fill_match_arms.rs b/crates/assists/src/handlers/fill_match_arms.rs index 3d9bdb2bf..676f5ad92 100644 --- a/crates/assists/src/handlers/fill_match_arms.rs +++ b/crates/assists/src/handlers/fill_match_arms.rs | |||
@@ -7,7 +7,7 @@ use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; | |||
7 | use test_utils::mark; | 7 | use test_utils::mark; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | utils::{render_snippet, Cursor, FamousDefs}, | 10 | utils::{mod_path_to_ast, render_snippet, Cursor, FamousDefs}, |
11 | AssistContext, AssistId, AssistKind, Assists, | 11 | AssistContext, AssistId, AssistKind, Assists, |
12 | }; | 12 | }; |
13 | 13 | ||
@@ -192,7 +192,7 @@ fn resolve_tuple_of_enum_def( | |||
192 | } | 192 | } |
193 | 193 | ||
194 | fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { | 194 | fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { |
195 | let path = crate::ast_transform::path_to_ast(module.find_use_path(db, ModuleDef::from(var))?); | 195 | let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?); |
196 | 196 | ||
197 | // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though | 197 | // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though |
198 | let pat: ast::Pat = match var.source(db).value.kind() { | 198 | let pat: ast::Pat = match var.source(db).value.kind() { |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index b0511ceb6..eb69c49a4 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -19,6 +19,27 @@ use crate::assist_config::SnippetCap; | |||
19 | pub use insert_use::MergeBehaviour; | 19 | pub use insert_use::MergeBehaviour; |
20 | pub(crate) use insert_use::{insert_use, ImportScope}; | 20 | pub(crate) use insert_use::{insert_use, ImportScope}; |
21 | 21 | ||
22 | pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { | ||
23 | let mut segments = Vec::new(); | ||
24 | let mut is_abs = false; | ||
25 | match path.kind { | ||
26 | hir::PathKind::Plain => {} | ||
27 | hir::PathKind::Super(0) => segments.push(make::path_segment_self()), | ||
28 | hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())), | ||
29 | hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => { | ||
30 | segments.push(make::path_segment_crate()) | ||
31 | } | ||
32 | hir::PathKind::Abs => is_abs = true, | ||
33 | } | ||
34 | |||
35 | segments.extend( | ||
36 | path.segments | ||
37 | .iter() | ||
38 | .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))), | ||
39 | ); | ||
40 | make::path_from_segments(segments, is_abs) | ||
41 | } | ||
42 | |||
22 | pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { | 43 | pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { |
23 | extract_trivial_expression(&block) | 44 | extract_trivial_expression(&block) |
24 | .filter(|expr| !expr.syntax().text().contains_char('\n')) | 45 | .filter(|expr| !expr.syntax().text().contains_char('\n')) |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 87084fa13..171118d98 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -51,7 +51,7 @@ pub use hir_def::{ | |||
51 | find_path::PrefixKind, | 51 | find_path::PrefixKind, |
52 | item_scope::ItemInNs, | 52 | item_scope::ItemInNs, |
53 | nameres::ModuleSource, | 53 | nameres::ModuleSource, |
54 | path::ModPath, | 54 | path::{ModPath, PathKind}, |
55 | type_ref::{Mutability, TypeRef}, | 55 | type_ref::{Mutability, TypeRef}, |
56 | }; | 56 | }; |
57 | pub use hir_expand::{ | 57 | pub use hir_expand::{ |
@@ -63,7 +63,4 @@ pub use hir_ty::display::HirDisplay; | |||
63 | // These are negative re-exports: pub using these names is forbidden, they | 63 | // These are negative re-exports: pub using these names is forbidden, they |
64 | // should remain private to hir internals. | 64 | // should remain private to hir internals. |
65 | #[allow(unused)] | 65 | #[allow(unused)] |
66 | use { | 66 | use {hir_def::path::Path, hir_expand::hygiene::Hygiene}; |
67 | hir_def::path::{Path, PathKind}, | ||
68 | hir_expand::hygiene::Hygiene, | ||
69 | }; | ||
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs index 9106ed45f..02613c4c4 100644 --- a/crates/hir_def/src/find_path.rs +++ b/crates/hir_def/src/find_path.rs | |||
@@ -222,6 +222,7 @@ fn find_path_inner( | |||
222 | best_path_len - 1, | 222 | best_path_len - 1, |
223 | prefixed, | 223 | prefixed, |
224 | )?; | 224 | )?; |
225 | mark::hit!(partially_imported); | ||
225 | path.segments.push(info.path.segments.last().unwrap().clone()); | 226 | path.segments.push(info.path.segments.last().unwrap().clone()); |
226 | Some(path) | 227 | Some(path) |
227 | }) | 228 | }) |
@@ -515,6 +516,7 @@ mod tests { | |||
515 | 516 | ||
516 | #[test] | 517 | #[test] |
517 | fn partially_imported() { | 518 | fn partially_imported() { |
519 | mark::check!(partially_imported); | ||
518 | // Tests that short paths are used even for external items, when parts of the path are | 520 | // Tests that short paths are used even for external items, when parts of the path are |
519 | // already in scope. | 521 | // already in scope. |
520 | let code = r#" | 522 | let code = r#" |
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs index 44bfe1593..028cae2e7 100644 --- a/crates/hir_def/src/import_map.rs +++ b/crates/hir_def/src/import_map.rs | |||
@@ -4,17 +4,16 @@ use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc}; | |||
4 | 4 | ||
5 | use base_db::CrateId; | 5 | use base_db::CrateId; |
6 | use fst::{self, Streamer}; | 6 | use fst::{self, Streamer}; |
7 | use hir_expand::name::Name; | ||
7 | use indexmap::{map::Entry, IndexMap}; | 8 | use indexmap::{map::Entry, IndexMap}; |
9 | use itertools::Itertools; | ||
8 | use rustc_hash::{FxHashMap, FxHasher}; | 10 | use rustc_hash::{FxHashMap, FxHasher}; |
9 | use smallvec::SmallVec; | 11 | use smallvec::SmallVec; |
10 | use syntax::SmolStr; | 12 | use syntax::SmolStr; |
11 | 13 | ||
12 | use crate::{ | 14 | use crate::{ |
13 | db::DefDatabase, | 15 | db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, |
14 | item_scope::ItemInNs, | 16 | ModuleId, TraitId, |
15 | path::{ModPath, PathKind}, | ||
16 | visibility::Visibility, | ||
17 | AssocItemId, ModuleDefId, ModuleId, TraitId, | ||
18 | }; | 17 | }; |
19 | 18 | ||
20 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; | 19 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; |
@@ -23,11 +22,28 @@ type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; | |||
23 | #[derive(Debug, Clone, Eq, PartialEq)] | 22 | #[derive(Debug, Clone, Eq, PartialEq)] |
24 | pub struct ImportInfo { | 23 | pub struct ImportInfo { |
25 | /// A path that can be used to import the item, relative to the crate's root. | 24 | /// A path that can be used to import the item, relative to the crate's root. |
26 | pub path: ModPath, | 25 | pub path: ImportPath, |
27 | /// The module containing this item. | 26 | /// The module containing this item. |
28 | pub container: ModuleId, | 27 | pub container: ModuleId, |
29 | } | 28 | } |
30 | 29 | ||
30 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
31 | pub struct ImportPath { | ||
32 | pub segments: Vec<Name>, | ||
33 | } | ||
34 | |||
35 | impl fmt::Display for ImportPath { | ||
36 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
37 | fmt::Display::fmt(&self.segments.iter().format("::"), f) | ||
38 | } | ||
39 | } | ||
40 | |||
41 | impl ImportPath { | ||
42 | fn len(&self) -> usize { | ||
43 | self.segments.len() | ||
44 | } | ||
45 | } | ||
46 | |||
31 | /// A map from publicly exported items to the path needed to import/name them from a downstream | 47 | /// A map from publicly exported items to the path needed to import/name them from a downstream |
32 | /// crate. | 48 | /// crate. |
33 | /// | 49 | /// |
@@ -61,7 +77,7 @@ impl ImportMap { | |||
61 | let mut import_map = Self::default(); | 77 | let mut import_map = Self::default(); |
62 | 78 | ||
63 | // We look only into modules that are public(ly reexported), starting with the crate root. | 79 | // We look only into modules that are public(ly reexported), starting with the crate root. |
64 | let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; | 80 | let empty = ImportPath { segments: vec![] }; |
65 | let root = ModuleId { krate, local_id: def_map.root }; | 81 | let root = ModuleId { krate, local_id: def_map.root }; |
66 | let mut worklist = vec![(root, empty)]; | 82 | let mut worklist = vec![(root, empty)]; |
67 | while let Some((module, mod_path)) = worklist.pop() { | 83 | while let Some((module, mod_path)) = worklist.pop() { |
@@ -152,8 +168,8 @@ impl ImportMap { | |||
152 | } | 168 | } |
153 | 169 | ||
154 | /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. | 170 | /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. |
155 | pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> { | 171 | pub fn path_of(&self, item: ItemInNs) -> Option<&ImportPath> { |
156 | Some(&self.map.get(&item)?.path) | 172 | self.import_info_for(item).map(|it| &it.path) |
157 | } | 173 | } |
158 | 174 | ||
159 | pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { | 175 | pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { |
@@ -197,7 +213,7 @@ impl fmt::Debug for ImportMap { | |||
197 | } | 213 | } |
198 | } | 214 | } |
199 | 215 | ||
200 | fn fst_path(path: &ModPath) -> String { | 216 | fn fst_path(path: &ImportPath) -> String { |
201 | let mut s = path.to_string(); | 217 | let mut s = path.to_string(); |
202 | s.make_ascii_lowercase(); | 218 | s.make_ascii_lowercase(); |
203 | s | 219 | s |
diff --git a/crates/hir_def/src/path.rs b/crates/hir_def/src/path.rs index 209b18e78..5b8c1e449 100644 --- a/crates/hir_def/src/path.rs +++ b/crates/hir_def/src/path.rs | |||
@@ -13,7 +13,7 @@ use hir_expand::{ | |||
13 | hygiene::Hygiene, | 13 | hygiene::Hygiene, |
14 | name::{AsName, Name}, | 14 | name::{AsName, Name}, |
15 | }; | 15 | }; |
16 | use syntax::ast::{self, make}; | 16 | use syntax::ast::{self}; |
17 | 17 | ||
18 | use crate::{ | 18 | use crate::{ |
19 | type_ref::{TypeBound, TypeRef}, | 19 | type_ref::{TypeBound, TypeRef}, |
@@ -100,26 +100,6 @@ impl ModPath { | |||
100 | } | 100 | } |
101 | self.segments.first() | 101 | self.segments.first() |
102 | } | 102 | } |
103 | |||
104 | pub fn to_ast_path(&self) -> ast::Path { | ||
105 | let mut segments = Vec::new(); | ||
106 | let mut is_abs = false; | ||
107 | match self.kind { | ||
108 | PathKind::Plain => {} | ||
109 | PathKind::Super(0) => segments.push(make::path_segment_self()), | ||
110 | PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())), | ||
111 | PathKind::Crate => segments.push(make::path_segment_crate()), | ||
112 | PathKind::Abs => is_abs = true, | ||
113 | PathKind::DollarCrate(_) => (), | ||
114 | } | ||
115 | |||
116 | segments.extend( | ||
117 | self.segments | ||
118 | .iter() | ||
119 | .map(|segment| make::path_segment(make::name_ref(&segment.to_string()))), | ||
120 | ); | ||
121 | make::path_from_segments(segments, is_abs) | ||
122 | } | ||
123 | } | 103 | } |
124 | 104 | ||
125 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 105 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index f389c5a4b..d2e151f25 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs | |||
@@ -221,7 +221,16 @@ impl HirDisplay for ApplicationTy { | |||
221 | } | 221 | } |
222 | TypeCtor::RawPtr(m) => { | 222 | TypeCtor::RawPtr(m) => { |
223 | let t = self.parameters.as_single(); | 223 | let t = self.parameters.as_single(); |
224 | write!(f, "*{}{}", m.as_keyword_for_ptr(), t.display(f.db))?; | 224 | let ty_display = t.display(f.db); |
225 | |||
226 | write!(f, "*{}", m.as_keyword_for_ptr())?; | ||
227 | if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) { | ||
228 | write!(f, "(")?; | ||
229 | write!(f, "{}", ty_display)?; | ||
230 | write!(f, ")")?; | ||
231 | } else { | ||
232 | write!(f, "{}", ty_display)?; | ||
233 | } | ||
225 | } | 234 | } |
226 | TypeCtor::Ref(m) => { | 235 | TypeCtor::Ref(m) => { |
227 | let t = self.parameters.as_single(); | 236 | let t = self.parameters.as_single(); |
@@ -230,7 +239,15 @@ impl HirDisplay for ApplicationTy { | |||
230 | } else { | 239 | } else { |
231 | t.display(f.db) | 240 | t.display(f.db) |
232 | }; | 241 | }; |
233 | write!(f, "&{}{}", m.as_keyword_for_ref(), ty_display)?; | 242 | |
243 | write!(f, "&{}", m.as_keyword_for_ref())?; | ||
244 | if matches!(t, Ty::Dyn(predicates) if predicates.len() > 1) { | ||
245 | write!(f, "(")?; | ||
246 | write!(f, "{}", ty_display)?; | ||
247 | write!(f, ")")?; | ||
248 | } else { | ||
249 | write!(f, "{}", ty_display)?; | ||
250 | } | ||
234 | } | 251 | } |
235 | TypeCtor::Never => write!(f, "!")?, | 252 | TypeCtor::Never => write!(f, "!")?, |
236 | TypeCtor::Tuple { .. } => { | 253 | TypeCtor::Tuple { .. } => { |
@@ -636,14 +653,14 @@ impl HirDisplay for GenericPredicate { | |||
636 | 653 | ||
637 | impl HirDisplay for Obligation { | 654 | impl HirDisplay for Obligation { |
638 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | 655 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
639 | Ok(match self { | 656 | match self { |
640 | Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db))?, | 657 | Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)), |
641 | Obligation::Projection(proj) => write!( | 658 | Obligation::Projection(proj) => write!( |
642 | f, | 659 | f, |
643 | "Normalize({} => {})", | 660 | "Normalize({} => {})", |
644 | proj.projection_ty.display(f.db), | 661 | proj.projection_ty.display(f.db), |
645 | proj.ty.display(f.db) | 662 | proj.ty.display(f.db) |
646 | )?, | 663 | ), |
647 | }) | 664 | } |
648 | } | 665 | } |
649 | } | 666 | } |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 4521d72cc..53265488e 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -15,6 +15,7 @@ use test_utils::mark; | |||
15 | use crate::{ | 15 | use crate::{ |
16 | display::{macro_label, ShortLabel, ToNav, TryToNav}, | 16 | display::{macro_label, ShortLabel, ToNav, TryToNav}, |
17 | link_rewrite::{remove_links, rewrite_links}, | 17 | link_rewrite::{remove_links, rewrite_links}, |
18 | markdown_remove::remove_markdown, | ||
18 | markup::Markup, | 19 | markup::Markup, |
19 | runnables::runnable, | 20 | runnables::runnable, |
20 | FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, | 21 | FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, |
@@ -27,6 +28,7 @@ pub struct HoverConfig { | |||
27 | pub debug: bool, | 28 | pub debug: bool, |
28 | pub goto_type_def: bool, | 29 | pub goto_type_def: bool, |
29 | pub links_in_hover: bool, | 30 | pub links_in_hover: bool, |
31 | pub markdown: bool, | ||
30 | } | 32 | } |
31 | 33 | ||
32 | impl Default for HoverConfig { | 34 | impl Default for HoverConfig { |
@@ -37,6 +39,7 @@ impl Default for HoverConfig { | |||
37 | debug: true, | 39 | debug: true, |
38 | goto_type_def: true, | 40 | goto_type_def: true, |
39 | links_in_hover: true, | 41 | links_in_hover: true, |
42 | markdown: true, | ||
40 | } | 43 | } |
41 | } | 44 | } |
42 | } | 45 | } |
@@ -48,6 +51,7 @@ impl HoverConfig { | |||
48 | debug: false, | 51 | debug: false, |
49 | goto_type_def: false, | 52 | goto_type_def: false, |
50 | links_in_hover: true, | 53 | links_in_hover: true, |
54 | markdown: true, | ||
51 | }; | 55 | }; |
52 | 56 | ||
53 | pub fn any(&self) -> bool { | 57 | pub fn any(&self) -> bool { |
@@ -91,6 +95,7 @@ pub(crate) fn hover( | |||
91 | db: &RootDatabase, | 95 | db: &RootDatabase, |
92 | position: FilePosition, | 96 | position: FilePosition, |
93 | links_in_hover: bool, | 97 | links_in_hover: bool, |
98 | markdown: bool, | ||
94 | ) -> Option<RangeInfo<HoverResult>> { | 99 | ) -> Option<RangeInfo<HoverResult>> { |
95 | let sema = Semantics::new(db); | 100 | let sema = Semantics::new(db); |
96 | let file = sema.parse(position.file_id).syntax().clone(); | 101 | let file = sema.parse(position.file_id).syntax().clone(); |
@@ -109,7 +114,9 @@ pub(crate) fn hover( | |||
109 | }; | 114 | }; |
110 | if let Some(definition) = definition { | 115 | if let Some(definition) = definition { |
111 | if let Some(markup) = hover_for_definition(db, definition) { | 116 | if let Some(markup) = hover_for_definition(db, definition) { |
112 | let markup = if links_in_hover { | 117 | let markup = if !markdown { |
118 | remove_markdown(&markup.as_str()) | ||
119 | } else if links_in_hover { | ||
113 | rewrite_links(db, &markup.as_str(), &definition) | 120 | rewrite_links(db, &markup.as_str(), &definition) |
114 | } else { | 121 | } else { |
115 | remove_links(&markup.as_str()) | 122 | remove_links(&markup.as_str()) |
@@ -147,7 +154,11 @@ pub(crate) fn hover( | |||
147 | } | 154 | } |
148 | }; | 155 | }; |
149 | 156 | ||
150 | res.markup = Markup::fenced_block(&ty.display(db)); | 157 | res.markup = if markdown { |
158 | Markup::fenced_block(&ty.display(db)) | ||
159 | } else { | ||
160 | ty.display(db).to_string().into() | ||
161 | }; | ||
151 | let range = sema.original_range(&node).range; | 162 | let range = sema.original_range(&node).range; |
152 | Some(RangeInfo::new(range, res)) | 163 | Some(RangeInfo::new(range, res)) |
153 | } | 164 | } |
@@ -383,12 +394,12 @@ mod tests { | |||
383 | 394 | ||
384 | fn check_hover_no_result(ra_fixture: &str) { | 395 | fn check_hover_no_result(ra_fixture: &str) { |
385 | let (analysis, position) = fixture::position(ra_fixture); | 396 | let (analysis, position) = fixture::position(ra_fixture); |
386 | assert!(analysis.hover(position, true).unwrap().is_none()); | 397 | assert!(analysis.hover(position, true, true).unwrap().is_none()); |
387 | } | 398 | } |
388 | 399 | ||
389 | fn check(ra_fixture: &str, expect: Expect) { | 400 | fn check(ra_fixture: &str, expect: Expect) { |
390 | let (analysis, position) = fixture::position(ra_fixture); | 401 | let (analysis, position) = fixture::position(ra_fixture); |
391 | let hover = analysis.hover(position, true).unwrap().unwrap(); | 402 | let hover = analysis.hover(position, true, true).unwrap().unwrap(); |
392 | 403 | ||
393 | let content = analysis.db.file_text(position.file_id); | 404 | let content = analysis.db.file_text(position.file_id); |
394 | let hovered_element = &content[hover.range]; | 405 | let hovered_element = &content[hover.range]; |
@@ -399,7 +410,18 @@ mod tests { | |||
399 | 410 | ||
400 | fn check_hover_no_links(ra_fixture: &str, expect: Expect) { | 411 | fn check_hover_no_links(ra_fixture: &str, expect: Expect) { |
401 | let (analysis, position) = fixture::position(ra_fixture); | 412 | let (analysis, position) = fixture::position(ra_fixture); |
402 | let hover = analysis.hover(position, false).unwrap().unwrap(); | 413 | let hover = analysis.hover(position, false, true).unwrap().unwrap(); |
414 | |||
415 | let content = analysis.db.file_text(position.file_id); | ||
416 | let hovered_element = &content[hover.range]; | ||
417 | |||
418 | let actual = format!("*{}*\n{}\n", hovered_element, hover.info.markup); | ||
419 | expect.assert_eq(&actual) | ||
420 | } | ||
421 | |||
422 | fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { | ||
423 | let (analysis, position) = fixture::position(ra_fixture); | ||
424 | let hover = analysis.hover(position, true, false).unwrap().unwrap(); | ||
403 | 425 | ||
404 | let content = analysis.db.file_text(position.file_id); | 426 | let content = analysis.db.file_text(position.file_id); |
405 | let hovered_element = &content[hover.range]; | 427 | let hovered_element = &content[hover.range]; |
@@ -410,7 +432,7 @@ mod tests { | |||
410 | 432 | ||
411 | fn check_actions(ra_fixture: &str, expect: Expect) { | 433 | fn check_actions(ra_fixture: &str, expect: Expect) { |
412 | let (analysis, position) = fixture::position(ra_fixture); | 434 | let (analysis, position) = fixture::position(ra_fixture); |
413 | let hover = analysis.hover(position, true).unwrap().unwrap(); | 435 | let hover = analysis.hover(position, true, true).unwrap().unwrap(); |
414 | expect.assert_debug_eq(&hover.info.actions) | 436 | expect.assert_debug_eq(&hover.info.actions) |
415 | } | 437 | } |
416 | 438 | ||
@@ -434,6 +456,23 @@ fn main() { | |||
434 | } | 456 | } |
435 | 457 | ||
436 | #[test] | 458 | #[test] |
459 | fn hover_remove_markdown_if_configured() { | ||
460 | check_hover_no_markdown( | ||
461 | r#" | ||
462 | pub fn foo() -> u32 { 1 } | ||
463 | |||
464 | fn main() { | ||
465 | let foo_test = foo()<|>; | ||
466 | } | ||
467 | "#, | ||
468 | expect![[r#" | ||
469 | *foo()* | ||
470 | u32 | ||
471 | "#]], | ||
472 | ); | ||
473 | } | ||
474 | |||
475 | #[test] | ||
437 | fn hover_shows_long_type_of_an_expression() { | 476 | fn hover_shows_long_type_of_an_expression() { |
438 | check( | 477 | check( |
439 | r#" | 478 | r#" |
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 1d7e8de56..3a4dc6a84 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs | |||
@@ -1026,4 +1026,38 @@ mod collections { | |||
1026 | "#, | 1026 | "#, |
1027 | ); | 1027 | ); |
1028 | } | 1028 | } |
1029 | |||
1030 | #[test] | ||
1031 | fn multi_dyn_trait_bounds() { | ||
1032 | check_with_config( | ||
1033 | InlayHintsConfig { | ||
1034 | type_hints: true, | ||
1035 | parameter_hints: false, | ||
1036 | chaining_hints: false, | ||
1037 | max_length: None, | ||
1038 | }, | ||
1039 | r#" | ||
1040 | //- /main.rs crate:main | ||
1041 | pub struct Vec<T> {} | ||
1042 | |||
1043 | impl<T> Vec<T> { | ||
1044 | pub fn new() -> Self { Vec {} } | ||
1045 | } | ||
1046 | |||
1047 | pub struct Box<T> {} | ||
1048 | |||
1049 | trait Display {} | ||
1050 | trait Sync {} | ||
1051 | |||
1052 | fn main() { | ||
1053 | let _v = Vec::<Box<&(dyn Display + Sync)>>::new(); | ||
1054 | //^^ Vec<Box<&(dyn Display + Sync)>> | ||
1055 | let _v = Vec::<Box<*const (dyn Display + Sync)>>::new(); | ||
1056 | //^^ Vec<Box<*const (dyn Display + Sync)>> | ||
1057 | let _v = Vec::<Box<dyn Display + Sync>>::new(); | ||
1058 | //^^ Vec<Box<dyn Display + Sync>> | ||
1059 | } | ||
1060 | "#, | ||
1061 | ); | ||
1062 | } | ||
1029 | } | 1063 | } |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 1aa673cf8..57f3581b6 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -46,6 +46,7 @@ mod syntax_highlighting; | |||
46 | mod syntax_tree; | 46 | mod syntax_tree; |
47 | mod typing; | 47 | mod typing; |
48 | mod link_rewrite; | 48 | mod link_rewrite; |
49 | mod markdown_remove; | ||
49 | 50 | ||
50 | use std::sync::Arc; | 51 | use std::sync::Arc; |
51 | 52 | ||
@@ -376,8 +377,9 @@ impl Analysis { | |||
376 | &self, | 377 | &self, |
377 | position: FilePosition, | 378 | position: FilePosition, |
378 | links_in_hover: bool, | 379 | links_in_hover: bool, |
380 | markdown: bool, | ||
379 | ) -> Cancelable<Option<RangeInfo<HoverResult>>> { | 381 | ) -> Cancelable<Option<RangeInfo<HoverResult>>> { |
380 | self.with_db(|db| hover::hover(db, position, links_in_hover)) | 382 | self.with_db(|db| hover::hover(db, position, links_in_hover, markdown)) |
381 | } | 383 | } |
382 | 384 | ||
383 | /// Computes parameter information for the given call expression. | 385 | /// Computes parameter information for the given call expression. |
diff --git a/crates/ide/src/markdown_remove.rs b/crates/ide/src/markdown_remove.rs new file mode 100644 index 000000000..02ad39dfb --- /dev/null +++ b/crates/ide/src/markdown_remove.rs | |||
@@ -0,0 +1,23 @@ | |||
1 | //! Removes markdown from strings. | ||
2 | |||
3 | use pulldown_cmark::{Event, Parser, Tag}; | ||
4 | |||
5 | /// Removes all markdown, keeping the text and code blocks | ||
6 | /// | ||
7 | /// Currently limited in styling, i.e. no ascii tables or lists | ||
8 | pub fn remove_markdown(markdown: &str) -> String { | ||
9 | let mut out = String::new(); | ||
10 | let parser = Parser::new(markdown); | ||
11 | |||
12 | for event in parser { | ||
13 | match event { | ||
14 | Event::Text(text) | Event::Code(text) => out.push_str(&text), | ||
15 | Event::SoftBreak | Event::HardBreak | Event::Rule | Event::End(Tag::CodeBlock(_)) => { | ||
16 | out.push('\n') | ||
17 | } | ||
18 | _ => {} | ||
19 | } | ||
20 | } | ||
21 | |||
22 | out | ||
23 | } | ||
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs index a0dc4b9df..98adef1d6 100644 --- a/crates/ide/src/typing/on_enter.rs +++ b/crates/ide/src/typing/on_enter.rs | |||
@@ -51,12 +51,12 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text | |||
51 | return None; | 51 | return None; |
52 | } | 52 | } |
53 | 53 | ||
54 | let mut remove_last_space = false; | 54 | let mut remove_trailing_whitespace = false; |
55 | // Continuing single-line non-doc comments (like this one :) ) is annoying | 55 | // Continuing single-line non-doc comments (like this one :) ) is annoying |
56 | if prefix == "//" && comment_range.end() == position.offset { | 56 | if prefix == "//" && comment_range.end() == position.offset { |
57 | if comment.text().ends_with(' ') { | 57 | if comment.text().ends_with(' ') { |
58 | mark::hit!(continues_end_of_line_comment_with_space); | 58 | mark::hit!(continues_end_of_line_comment_with_space); |
59 | remove_last_space = true; | 59 | remove_trailing_whitespace = true; |
60 | } else if !followed_by_comment(&comment) { | 60 | } else if !followed_by_comment(&comment) { |
61 | return None; | 61 | return None; |
62 | } | 62 | } |
@@ -64,8 +64,10 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text | |||
64 | 64 | ||
65 | let indent = node_indent(&file, comment.syntax())?; | 65 | let indent = node_indent(&file, comment.syntax())?; |
66 | let inserted = format!("\n{}{} $0", indent, prefix); | 66 | let inserted = format!("\n{}{} $0", indent, prefix); |
67 | let delete = if remove_last_space { | 67 | let delete = if remove_trailing_whitespace { |
68 | TextRange::new(position.offset - TextSize::of(' '), position.offset) | 68 | let trimmed_len = comment.text().trim_end().len() as u32; |
69 | let trailing_whitespace_len = comment.text().len() as u32 - trimmed_len; | ||
70 | TextRange::new(position.offset - TextSize::from(trailing_whitespace_len), position.offset) | ||
69 | } else { | 71 | } else { |
70 | TextRange::empty(position.offset) | 72 | TextRange::empty(position.offset) |
71 | }; | 73 | }; |
@@ -253,4 +255,23 @@ fn main() { | |||
253 | "#, | 255 | "#, |
254 | ); | 256 | ); |
255 | } | 257 | } |
258 | |||
259 | #[test] | ||
260 | fn trims_all_trailing_whitespace() { | ||
261 | do_check( | ||
262 | " | ||
263 | fn main() { | ||
264 | // Fix me \t\t <|> | ||
265 | let x = 1 + 1; | ||
266 | } | ||
267 | ", | ||
268 | " | ||
269 | fn main() { | ||
270 | // Fix me | ||
271 | // $0 | ||
272 | let x = 1 + 1; | ||
273 | } | ||
274 | ", | ||
275 | ); | ||
276 | } | ||
256 | } | 277 | } |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index dcbc11c14..1b9b24698 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -15,7 +15,7 @@ use ide::{ | |||
15 | AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig, | 15 | AssistConfig, CompletionConfig, DiagnosticsConfig, HoverConfig, InlayHintsConfig, |
16 | MergeBehaviour, | 16 | MergeBehaviour, |
17 | }; | 17 | }; |
18 | use lsp_types::ClientCapabilities; | 18 | use lsp_types::{ClientCapabilities, MarkupKind}; |
19 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; | 19 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; |
20 | use rustc_hash::FxHashSet; | 20 | use rustc_hash::FxHashSet; |
21 | use serde::Deserialize; | 21 | use serde::Deserialize; |
@@ -333,6 +333,7 @@ impl Config { | |||
333 | debug: data.hoverActions_enable && data.hoverActions_debug, | 333 | debug: data.hoverActions_enable && data.hoverActions_debug, |
334 | goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef, | 334 | goto_type_def: data.hoverActions_enable && data.hoverActions_gotoTypeDef, |
335 | links_in_hover: data.hoverActions_linksInHover, | 335 | links_in_hover: data.hoverActions_linksInHover, |
336 | markdown: true, | ||
336 | }; | 337 | }; |
337 | 338 | ||
338 | log::info!("Config::update() = {:#?}", self); | 339 | log::info!("Config::update() = {:#?}", self); |
@@ -340,6 +341,9 @@ impl Config { | |||
340 | 341 | ||
341 | pub fn update_caps(&mut self, caps: &ClientCapabilities) { | 342 | pub fn update_caps(&mut self, caps: &ClientCapabilities) { |
342 | if let Some(doc_caps) = caps.text_document.as_ref() { | 343 | if let Some(doc_caps) = caps.text_document.as_ref() { |
344 | if let Some(value) = doc_caps.hover.as_ref().and_then(|it| it.content_format.as_ref()) { | ||
345 | self.hover.markdown = value.contains(&MarkupKind::Markdown) | ||
346 | } | ||
343 | if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) { | 347 | if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) { |
344 | self.client_caps.location_link = value; | 348 | self.client_caps.location_link = value; |
345 | } | 349 | } |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index e970abb7c..468655f9c 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -618,7 +618,11 @@ pub(crate) fn handle_hover( | |||
618 | ) -> Result<Option<lsp_ext::Hover>> { | 618 | ) -> Result<Option<lsp_ext::Hover>> { |
619 | let _p = profile::span("handle_hover"); | 619 | let _p = profile::span("handle_hover"); |
620 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; | 620 | let position = from_proto::file_position(&snap, params.text_document_position_params)?; |
621 | let info = match snap.analysis.hover(position, snap.config.hover.links_in_hover)? { | 621 | let info = match snap.analysis.hover( |
622 | position, | ||
623 | snap.config.hover.links_in_hover, | ||
624 | snap.config.hover.markdown, | ||
625 | )? { | ||
622 | None => return Ok(None), | 626 | None => return Ok(None), |
623 | Some(info) => info, | 627 | Some(info) => info, |
624 | }; | 628 | }; |
diff --git a/crates/syntax/src/ptr.rs b/crates/syntax/src/ptr.rs index ca7957747..d3fb7a5d9 100644 --- a/crates/syntax/src/ptr.rs +++ b/crates/syntax/src/ptr.rs | |||
@@ -12,6 +12,8 @@ use crate::{AstNode, SyntaxKind, SyntaxNode, TextRange}; | |||
12 | /// specific node across reparses of the same file. | 12 | /// specific node across reparses of the same file. |
13 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 13 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
14 | pub struct SyntaxNodePtr { | 14 | pub struct SyntaxNodePtr { |
15 | // Don't expose this field further. At some point, we might want to replace | ||
16 | // range with node id. | ||
15 | pub(crate) range: TextRange, | 17 | pub(crate) range: TextRange, |
16 | kind: SyntaxKind, | 18 | kind: SyntaxKind, |
17 | } | 19 | } |