aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/ast_transform.rs17
-rw-r--r--crates/assists/src/handlers/add_missing_impl_members.rs25
-rw-r--r--crates/assists/src/handlers/auto_import.rs7
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs8
-rw-r--r--crates/assists/src/handlers/fill_match_arms.rs4
-rw-r--r--crates/assists/src/utils.rs21
-rw-r--r--crates/hir/src/lib.rs7
-rw-r--r--crates/hir_def/src/find_path.rs2
-rw-r--r--crates/hir_def/src/import_map.rs36
-rw-r--r--crates/hir_def/src/path.rs22
-rw-r--r--crates/hir_ty/src/display.rs29
-rw-r--r--crates/ide/src/hover.rs51
-rw-r--r--crates/ide/src/inlay_hints.rs34
-rw-r--r--crates/ide/src/lib.rs4
-rw-r--r--crates/ide/src/markdown_remove.rs23
-rw-r--r--crates/ide/src/typing/on_enter.rs29
-rw-r--r--crates/rust-analyzer/src/config.rs6
-rw-r--r--crates/rust-analyzer/src/handlers.rs6
-rw-r--r--crates/syntax/src/ptr.rs2
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.
2use rustc_hash::FxHashMap;
3
4use hir::{HirDisplay, PathResolution, SemanticsScope}; 2use hir::{HirDisplay, PathResolution, SemanticsScope};
3use rustc_hash::FxHashMap;
5use syntax::{ 4use syntax::{
6 algo::SyntaxRewriter, 5 algo::SyntaxRewriter,
7 ast::{self, AstNode}, 6 ast::{self, AstNode},
8 SyntaxNode, 7 SyntaxNode,
9}; 8};
10 9
10use crate::utils::mod_path_to_ast;
11
11pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { 12pub 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
214pub(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#"
829trait Test {
830 fn foo(&self, x: crate)
831}
832impl Test for () {
833 <|>
834}
835"#,
836 r#"
837trait Test {
838 fn foo(&self, x: crate)
839}
840impl 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
16use crate::{utils::insert_use, AssistContext, AssistId, AssistKind, Assists, GroupLabel}; 16use 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
12use crate::{ 12use 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};
15use 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};
7use test_utils::mark; 7use test_utils::mark;
8 8
9use crate::{ 9use 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
194fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { 194fn 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;
19pub use insert_use::MergeBehaviour; 19pub use insert_use::MergeBehaviour;
20pub(crate) use insert_use::{insert_use, ImportScope}; 20pub(crate) use insert_use::{insert_use, ImportScope};
21 21
22pub 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
22pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { 43pub(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};
57pub use hir_expand::{ 57pub 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)]
66use { 66use {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
5use base_db::CrateId; 5use base_db::CrateId;
6use fst::{self, Streamer}; 6use fst::{self, Streamer};
7use hir_expand::name::Name;
7use indexmap::{map::Entry, IndexMap}; 8use indexmap::{map::Entry, IndexMap};
9use itertools::Itertools;
8use rustc_hash::{FxHashMap, FxHasher}; 10use rustc_hash::{FxHashMap, FxHasher};
9use smallvec::SmallVec; 11use smallvec::SmallVec;
10use syntax::SmolStr; 12use syntax::SmolStr;
11 13
12use crate::{ 14use 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
20type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; 19type 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)]
24pub struct ImportInfo { 23pub 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)]
31pub struct ImportPath {
32 pub segments: Vec<Name>,
33}
34
35impl 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
41impl 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
200fn fst_path(path: &ModPath) -> String { 216fn 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};
16use syntax::ast::{self, make}; 16use syntax::ast::{self};
17 17
18use crate::{ 18use 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
637impl HirDisplay for Obligation { 654impl 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;
15use crate::{ 15use 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
32impl Default for HoverConfig { 34impl 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#"
462pub fn foo() -> u32 { 1 }
463
464fn 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
1041pub struct Vec<T> {}
1042
1043impl<T> Vec<T> {
1044 pub fn new() -> Self { Vec {} }
1045}
1046
1047pub struct Box<T> {}
1048
1049trait Display {}
1050trait Sync {}
1051
1052fn 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;
46mod syntax_tree; 46mod syntax_tree;
47mod typing; 47mod typing;
48mod link_rewrite; 48mod link_rewrite;
49mod markdown_remove;
49 50
50use std::sync::Arc; 51use 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
3use 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
8pub 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 "
263fn main() {
264 // Fix me \t\t <|>
265 let x = 1 + 1;
266}
267",
268 "
269fn 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};
18use lsp_types::ClientCapabilities; 18use lsp_types::{ClientCapabilities, MarkupKind};
19use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; 19use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest};
20use rustc_hash::FxHashSet; 20use rustc_hash::FxHashSet;
21use serde::Deserialize; 21use 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)]
14pub struct SyntaxNodePtr { 14pub 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}