diff options
-rw-r--r-- | crates/ra_hir/src/nameres/collector.rs | 67 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/raw.rs | 17 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests/mods.rs | 14 | ||||
-rw-r--r-- | crates/ra_mbe/src/subtree_source.rs | 62 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast.rs | 46 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/traits.rs | 12 |
6 files changed, 157 insertions, 61 deletions
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 06b732215..7da2dcdff 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs | |||
@@ -483,7 +483,7 @@ struct ModCollector<'a, D> { | |||
483 | module_id: CrateModuleId, | 483 | module_id: CrateModuleId, |
484 | file_id: HirFileId, | 484 | file_id: HirFileId, |
485 | raw_items: &'a raw::RawItems, | 485 | raw_items: &'a raw::RawItems, |
486 | parent_module: Option<&'a Name>, | 486 | parent_module: Option<ParentModule<'a>>, |
487 | } | 487 | } |
488 | 488 | ||
489 | impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>> | 489 | impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>> |
@@ -508,15 +508,16 @@ where | |||
508 | fn collect_module(&mut self, module: &raw::ModuleData) { | 508 | fn collect_module(&mut self, module: &raw::ModuleData) { |
509 | match module { | 509 | match module { |
510 | // inline module, just recurse | 510 | // inline module, just recurse |
511 | raw::ModuleData::Definition { name, items, ast_id } => { | 511 | raw::ModuleData::Definition { name, items, ast_id, attr_path } => { |
512 | let module_id = | 512 | let module_id = |
513 | self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None); | 513 | self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None); |
514 | let parent_module = ParentModule { name, attr_path: attr_path.as_ref() }; | ||
514 | ModCollector { | 515 | ModCollector { |
515 | def_collector: &mut *self.def_collector, | 516 | def_collector: &mut *self.def_collector, |
516 | module_id, | 517 | module_id, |
517 | file_id: self.file_id, | 518 | file_id: self.file_id, |
518 | raw_items: self.raw_items, | 519 | raw_items: self.raw_items, |
519 | parent_module: Some(name), | 520 | parent_module: Some(parent_module), |
520 | } | 521 | } |
521 | .collect(&*items); | 522 | .collect(&*items); |
522 | } | 523 | } |
@@ -530,7 +531,7 @@ where | |||
530 | name, | 531 | name, |
531 | is_root, | 532 | is_root, |
532 | attr_path.as_ref(), | 533 | attr_path.as_ref(), |
533 | self.parent_module, | 534 | self.parent_module.as_ref(), |
534 | ) { | 535 | ) { |
535 | Ok(file_id) => { | 536 | Ok(file_id) => { |
536 | let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id)); | 537 | let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id)); |
@@ -647,7 +648,7 @@ fn resolve_submodule( | |||
647 | name: &Name, | 648 | name: &Name, |
648 | is_root: bool, | 649 | is_root: bool, |
649 | attr_path: Option<&SmolStr>, | 650 | attr_path: Option<&SmolStr>, |
650 | parent_module: Option<&Name>, | 651 | parent_module: Option<&ParentModule>, |
651 | ) -> Result<FileId, RelativePathBuf> { | 652 | ) -> Result<FileId, RelativePathBuf> { |
652 | let file_id = file_id.original_file(db); | 653 | let file_id = file_id.original_file(db); |
653 | let source_root_id = db.file_source_root(file_id); | 654 | let source_root_id = db.file_source_root(file_id); |
@@ -657,20 +658,49 @@ fn resolve_submodule( | |||
657 | let mod_name = path.file_stem().unwrap_or("unknown"); | 658 | let mod_name = path.file_stem().unwrap_or("unknown"); |
658 | 659 | ||
659 | let resolve_mode = match (attr_path.filter(|p| !p.is_empty()), parent_module) { | 660 | let resolve_mode = match (attr_path.filter(|p| !p.is_empty()), parent_module) { |
660 | (Some(file_path), Some(parent_name)) => { | 661 | (Some(file_path), Some(parent_module)) => { |
661 | let file_path = normalize_attribute_path(file_path); | 662 | let file_path = normalize_attribute_path(file_path); |
662 | let path = dir_path.join(format!("{}/{}", parent_name, file_path)).normalize(); | 663 | match parent_module.attribute_path() { |
663 | ResolutionMode::InsideInlineModule(InsideInlineModuleMode::WithAttributePath(path)) | 664 | Some(parent_module_attr_path) => { |
665 | let path = dir_path | ||
666 | .join(format!( | ||
667 | "{}/{}", | ||
668 | normalize_attribute_path(parent_module_attr_path), | ||
669 | file_path | ||
670 | )) | ||
671 | .normalize(); | ||
672 | ResolutionMode::InlineModuleWithAttributePath( | ||
673 | InsideInlineModuleMode::WithAttributePath(path), | ||
674 | ) | ||
675 | } | ||
676 | None => { | ||
677 | let path = | ||
678 | dir_path.join(format!("{}/{}", parent_module.name, file_path)).normalize(); | ||
679 | ResolutionMode::InsideInlineModule(InsideInlineModuleMode::WithAttributePath( | ||
680 | path, | ||
681 | )) | ||
682 | } | ||
683 | } | ||
664 | } | 684 | } |
685 | (None, Some(parent_module)) => match parent_module.attribute_path() { | ||
686 | Some(parent_module_attr_path) => { | ||
687 | let path = dir_path.join(format!( | ||
688 | "{}/{}.rs", | ||
689 | normalize_attribute_path(parent_module_attr_path), | ||
690 | name | ||
691 | )); | ||
692 | ResolutionMode::InlineModuleWithAttributePath(InsideInlineModuleMode::File(path)) | ||
693 | } | ||
694 | None => { | ||
695 | let path = dir_path.join(format!("{}/{}.rs", parent_module.name, name)); | ||
696 | ResolutionMode::InsideInlineModule(InsideInlineModuleMode::File(path)) | ||
697 | } | ||
698 | }, | ||
665 | (Some(file_path), None) => { | 699 | (Some(file_path), None) => { |
666 | let file_path = normalize_attribute_path(file_path); | 700 | let file_path = normalize_attribute_path(file_path); |
667 | let path = dir_path.join(file_path.as_ref()).normalize(); | 701 | let path = dir_path.join(file_path.as_ref()).normalize(); |
668 | ResolutionMode::OutOfLine(OutOfLineMode::WithAttributePath(path)) | 702 | ResolutionMode::OutOfLine(OutOfLineMode::WithAttributePath(path)) |
669 | } | 703 | } |
670 | (None, Some(parent_name)) => { | ||
671 | let path = dir_path.join(format!("{}/{}.rs", parent_name, name)); | ||
672 | ResolutionMode::InsideInlineModule(InsideInlineModuleMode::File(path)) | ||
673 | } | ||
674 | _ => { | 704 | _ => { |
675 | let is_dir_owner = is_root || mod_name == "mod"; | 705 | let is_dir_owner = is_root || mod_name == "mod"; |
676 | if is_dir_owner { | 706 | if is_dir_owner { |
@@ -743,6 +773,7 @@ impl InsideInlineModuleMode { | |||
743 | enum ResolutionMode { | 773 | enum ResolutionMode { |
744 | OutOfLine(OutOfLineMode), | 774 | OutOfLine(OutOfLineMode), |
745 | InsideInlineModule(InsideInlineModuleMode), | 775 | InsideInlineModule(InsideInlineModuleMode), |
776 | InlineModuleWithAttributePath(InsideInlineModuleMode), | ||
746 | } | 777 | } |
747 | 778 | ||
748 | impl ResolutionMode { | 779 | impl ResolutionMode { |
@@ -752,6 +783,7 @@ impl ResolutionMode { | |||
752 | match self { | 783 | match self { |
753 | OutOfLine(mode) => mode.resolve(source_root), | 784 | OutOfLine(mode) => mode.resolve(source_root), |
754 | InsideInlineModule(mode) => mode.resolve(source_root), | 785 | InsideInlineModule(mode) => mode.resolve(source_root), |
786 | InlineModuleWithAttributePath(mode) => mode.resolve(source_root), | ||
755 | } | 787 | } |
756 | } | 788 | } |
757 | } | 789 | } |
@@ -773,6 +805,17 @@ fn resolve_find_result( | |||
773 | } | 805 | } |
774 | } | 806 | } |
775 | 807 | ||
808 | struct ParentModule<'a> { | ||
809 | name: &'a Name, | ||
810 | attr_path: Option<&'a SmolStr>, | ||
811 | } | ||
812 | |||
813 | impl<'a> ParentModule<'a> { | ||
814 | pub fn attribute_path(&self) -> Option<&SmolStr> { | ||
815 | self.attr_path.filter(|p| !p.is_empty()) | ||
816 | } | ||
817 | } | ||
818 | |||
776 | #[cfg(test)] | 819 | #[cfg(test)] |
777 | mod tests { | 820 | mod tests { |
778 | use ra_db::SourceDatabase; | 821 | use ra_db::SourceDatabase; |
diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 8517f3c43..584e15e29 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs | |||
@@ -130,8 +130,17 @@ impl_arena_id!(Module); | |||
130 | 130 | ||
131 | #[derive(Debug, PartialEq, Eq)] | 131 | #[derive(Debug, PartialEq, Eq)] |
132 | pub(super) enum ModuleData { | 132 | pub(super) enum ModuleData { |
133 | Declaration { name: Name, ast_id: FileAstId<ast::Module>, attr_path: Option<SmolStr> }, | 133 | Declaration { |
134 | Definition { name: Name, ast_id: FileAstId<ast::Module>, items: Vec<RawItem> }, | 134 | name: Name, |
135 | ast_id: FileAstId<ast::Module>, | ||
136 | attr_path: Option<SmolStr>, | ||
137 | }, | ||
138 | Definition { | ||
139 | name: Name, | ||
140 | ast_id: FileAstId<ast::Module>, | ||
141 | items: Vec<RawItem>, | ||
142 | attr_path: Option<SmolStr>, | ||
143 | }, | ||
135 | } | 144 | } |
136 | 145 | ||
137 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 146 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -256,9 +265,9 @@ impl RawItemsCollector { | |||
256 | None => return, | 265 | None => return, |
257 | }; | 266 | }; |
258 | 267 | ||
259 | let attr_path = extract_mod_path_attribute(&module); | ||
260 | let ast_id = self.source_ast_id_map.ast_id(&module); | 268 | let ast_id = self.source_ast_id_map.ast_id(&module); |
261 | if module.has_semi() { | 269 | if module.has_semi() { |
270 | let attr_path = extract_mod_path_attribute(&module); | ||
262 | let item = | 271 | let item = |
263 | self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id, attr_path }); | 272 | self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id, attr_path }); |
264 | self.push_item(current_module, RawItem::Module(item)); | 273 | self.push_item(current_module, RawItem::Module(item)); |
@@ -266,10 +275,12 @@ impl RawItemsCollector { | |||
266 | } | 275 | } |
267 | 276 | ||
268 | if let Some(item_list) = module.item_list() { | 277 | if let Some(item_list) = module.item_list() { |
278 | let attr_path = extract_mod_path_attribute(&module); | ||
269 | let item = self.raw_items.modules.alloc(ModuleData::Definition { | 279 | let item = self.raw_items.modules.alloc(ModuleData::Definition { |
270 | name, | 280 | name, |
271 | ast_id, | 281 | ast_id, |
272 | items: Vec::new(), | 282 | items: Vec::new(), |
283 | attr_path, | ||
273 | }); | 284 | }); |
274 | self.process_module(Some(item), item_list); | 285 | self.process_module(Some(item), item_list); |
275 | self.push_item(current_module, RawItem::Module(item)); | 286 | self.push_item(current_module, RawItem::Module(item)); |
diff --git a/crates/ra_hir/src/nameres/tests/mods.rs b/crates/ra_hir/src/nameres/tests/mods.rs index 382728149..6dd18df1a 100644 --- a/crates/ra_hir/src/nameres/tests/mods.rs +++ b/crates/ra_hir/src/nameres/tests/mods.rs | |||
@@ -336,9 +336,7 @@ fn module_resolution_explicit_path_mod_rs_with_win_separator() { | |||
336 | "###); | 336 | "###); |
337 | } | 337 | } |
338 | 338 | ||
339 | // FIXME: issue #1529. not support out-of-line modules inside inline. | ||
340 | #[test] | 339 | #[test] |
341 | #[ignore] | ||
342 | fn module_resolution_decl_inside_inline_module_with_path_attribute() { | 340 | fn module_resolution_decl_inside_inline_module_with_path_attribute() { |
343 | let map = def_map_with_crate_graph( | 341 | let map = def_map_with_crate_graph( |
344 | r###" | 342 | r###" |
@@ -397,9 +395,7 @@ fn module_resolution_decl_inside_inline_module() { | |||
397 | "###); | 395 | "###); |
398 | } | 396 | } |
399 | 397 | ||
400 | // FIXME: issue #1529. not support out-of-line modules inside inline. | ||
401 | #[test] | 398 | #[test] |
402 | #[ignore] | ||
403 | fn module_resolution_decl_inside_inline_module_2_with_path_attribute() { | 399 | fn module_resolution_decl_inside_inline_module_2_with_path_attribute() { |
404 | let map = def_map_with_crate_graph( | 400 | let map = def_map_with_crate_graph( |
405 | r###" | 401 | r###" |
@@ -429,9 +425,7 @@ fn module_resolution_decl_inside_inline_module_2_with_path_attribute() { | |||
429 | "###); | 425 | "###); |
430 | } | 426 | } |
431 | 427 | ||
432 | // FIXME: issue #1529. not support out-of-line modules inside inline. | ||
433 | #[test] | 428 | #[test] |
434 | #[ignore] | ||
435 | fn module_resolution_decl_inside_inline_module_3() { | 429 | fn module_resolution_decl_inside_inline_module_3() { |
436 | let map = def_map_with_crate_graph( | 430 | let map = def_map_with_crate_graph( |
437 | r###" | 431 | r###" |
@@ -462,9 +456,7 @@ fn module_resolution_decl_inside_inline_module_3() { | |||
462 | "###); | 456 | "###); |
463 | } | 457 | } |
464 | 458 | ||
465 | // FIXME: issue #1529. not support out-of-line modules inside inline. | ||
466 | #[test] | 459 | #[test] |
467 | #[ignore] | ||
468 | fn module_resolution_decl_inside_inline_module_empty_path() { | 460 | fn module_resolution_decl_inside_inline_module_empty_path() { |
469 | let map = def_map_with_crate_graph( | 461 | let map = def_map_with_crate_graph( |
470 | r###" | 462 | r###" |
@@ -475,7 +467,7 @@ fn module_resolution_decl_inside_inline_module_empty_path() { | |||
475 | mod bar; | 467 | mod bar; |
476 | } | 468 | } |
477 | 469 | ||
478 | //- /users.rs | 470 | //- /foo/users.rs |
479 | pub struct Baz; | 471 | pub struct Baz; |
480 | "###, | 472 | "###, |
481 | crate_graph! { | 473 | crate_graph! { |
@@ -520,9 +512,7 @@ fn module_resolution_decl_empty_path() { | |||
520 | "###); | 512 | "###); |
521 | } | 513 | } |
522 | 514 | ||
523 | // FIXME: issue #1529. not support out-of-line modules inside inline. | ||
524 | #[test] | 515 | #[test] |
525 | #[ignore] | ||
526 | fn module_resolution_decl_inside_inline_module_relative_path() { | 516 | fn module_resolution_decl_inside_inline_module_relative_path() { |
527 | let map = def_map_with_crate_graph( | 517 | let map = def_map_with_crate_graph( |
528 | r###" | 518 | r###" |
@@ -660,9 +650,7 @@ fn module_resolution_decl_inside_inline_module_in_non_crate_root() { | |||
660 | "###); | 650 | "###); |
661 | } | 651 | } |
662 | 652 | ||
663 | // FIXME: issue #1529. not support out-of-line modules inside inline. | ||
664 | #[test] | 653 | #[test] |
665 | #[ignore] | ||
666 | fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() { | 654 | fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() { |
667 | let map = def_map_with_crate_graph( | 655 | let map = def_map_with_crate_graph( |
668 | r###" | 656 | r###" |
diff --git a/crates/ra_mbe/src/subtree_source.rs b/crates/ra_mbe/src/subtree_source.rs index 6603ff34d..9d6d0133f 100644 --- a/crates/ra_mbe/src/subtree_source.rs +++ b/crates/ra_mbe/src/subtree_source.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use ra_parser::{Token, TokenSource}; | 1 | use ra_parser::{Token, TokenSource}; |
2 | use ra_syntax::{classify_literal, SmolStr, SyntaxKind, SyntaxKind::*, T}; | 2 | use ra_syntax::{classify_literal, SmolStr, SyntaxKind, SyntaxKind::*, T}; |
3 | use std::cell::{Cell, RefCell}; | 3 | use std::cell::{Cell, Ref, RefCell}; |
4 | use tt::buffer::{Cursor, TokenBuffer}; | 4 | use tt::buffer::{Cursor, TokenBuffer}; |
5 | 5 | ||
6 | #[derive(Debug, Clone, Eq, PartialEq)] | 6 | #[derive(Debug, Clone, Eq, PartialEq)] |
@@ -20,8 +20,8 @@ impl<'a> SubtreeTokenSource<'a> { | |||
20 | // Helper function used in test | 20 | // Helper function used in test |
21 | #[cfg(test)] | 21 | #[cfg(test)] |
22 | pub fn text(&self) -> SmolStr { | 22 | pub fn text(&self) -> SmolStr { |
23 | match self.get(self.curr.1) { | 23 | match *self.get(self.curr.1) { |
24 | Some(tt) => tt.text, | 24 | Some(ref tt) => tt.text.clone(), |
25 | _ => SmolStr::new(""), | 25 | _ => SmolStr::new(""), |
26 | } | 26 | } |
27 | } | 27 | } |
@@ -41,44 +41,46 @@ impl<'a> SubtreeTokenSource<'a> { | |||
41 | } | 41 | } |
42 | 42 | ||
43 | fn mk_token(&self, pos: usize) -> Token { | 43 | fn mk_token(&self, pos: usize) -> Token { |
44 | match self.get(pos) { | 44 | match *self.get(pos) { |
45 | Some(tt) => Token { kind: tt.kind, is_jointed_to_next: tt.is_joint_to_next }, | 45 | Some(ref tt) => Token { kind: tt.kind, is_jointed_to_next: tt.is_joint_to_next }, |
46 | None => Token { kind: EOF, is_jointed_to_next: false }, | 46 | None => Token { kind: EOF, is_jointed_to_next: false }, |
47 | } | 47 | } |
48 | } | 48 | } |
49 | 49 | ||
50 | fn get(&self, pos: usize) -> Option<TtToken> { | 50 | fn get(&self, pos: usize) -> Ref<Option<TtToken>> { |
51 | let mut cached = self.cached.borrow_mut(); | 51 | if pos < self.cached.borrow().len() { |
52 | if pos < cached.len() { | 52 | return Ref::map(self.cached.borrow(), |c| &c[pos]); |
53 | return cached[pos].clone(); | ||
54 | } | 53 | } |
55 | 54 | ||
56 | while pos >= cached.len() { | 55 | { |
57 | let cursor = self.cached_cursor.get(); | 56 | let mut cached = self.cached.borrow_mut(); |
58 | if cursor.eof() { | 57 | while pos >= cached.len() { |
59 | cached.push(None); | 58 | let cursor = self.cached_cursor.get(); |
60 | continue; | 59 | if cursor.eof() { |
61 | } | 60 | cached.push(None); |
62 | 61 | continue; | |
63 | match cursor.token_tree() { | ||
64 | Some(tt::TokenTree::Leaf(leaf)) => { | ||
65 | cached.push(Some(convert_leaf(&leaf))); | ||
66 | self.cached_cursor.set(cursor.bump()); | ||
67 | } | ||
68 | Some(tt::TokenTree::Subtree(subtree)) => { | ||
69 | self.cached_cursor.set(cursor.subtree().unwrap()); | ||
70 | cached.push(Some(convert_delim(subtree.delimiter, false))); | ||
71 | } | 62 | } |
72 | None => { | 63 | |
73 | if let Some(subtree) = cursor.end() { | 64 | match cursor.token_tree() { |
74 | cached.push(Some(convert_delim(subtree.delimiter, true))); | 65 | Some(tt::TokenTree::Leaf(leaf)) => { |
66 | cached.push(Some(convert_leaf(&leaf))); | ||
75 | self.cached_cursor.set(cursor.bump()); | 67 | self.cached_cursor.set(cursor.bump()); |
76 | } | 68 | } |
69 | Some(tt::TokenTree::Subtree(subtree)) => { | ||
70 | self.cached_cursor.set(cursor.subtree().unwrap()); | ||
71 | cached.push(Some(convert_delim(subtree.delimiter, false))); | ||
72 | } | ||
73 | None => { | ||
74 | if let Some(subtree) = cursor.end() { | ||
75 | cached.push(Some(convert_delim(subtree.delimiter, true))); | ||
76 | self.cached_cursor.set(cursor.bump()); | ||
77 | } | ||
78 | } | ||
77 | } | 79 | } |
78 | } | 80 | } |
79 | } | 81 | } |
80 | 82 | ||
81 | cached[pos].clone() | 83 | Ref::map(self.cached.borrow(), |c| &c[pos]) |
82 | } | 84 | } |
83 | } | 85 | } |
84 | 86 | ||
@@ -103,8 +105,8 @@ impl<'a> TokenSource for SubtreeTokenSource<'a> { | |||
103 | 105 | ||
104 | /// Is the current token a specified keyword? | 106 | /// Is the current token a specified keyword? |
105 | fn is_keyword(&self, kw: &str) -> bool { | 107 | fn is_keyword(&self, kw: &str) -> bool { |
106 | match self.get(self.curr.1) { | 108 | match *self.get(self.curr.1) { |
107 | Some(t) => t.text == *kw, | 109 | Some(ref t) => t.text == *kw, |
108 | _ => false, | 110 | _ => false, |
109 | } | 111 | } |
110 | } | 112 | } |
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index c5746d98d..6f0489617 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -139,6 +139,52 @@ fn test_doc_comment_preserves_newlines() { | |||
139 | } | 139 | } |
140 | 140 | ||
141 | #[test] | 141 | #[test] |
142 | fn test_doc_comment_single_line_block_strips_suffix() { | ||
143 | let file = SourceFile::parse( | ||
144 | r#" | ||
145 | /** this is mod foo*/ | ||
146 | mod foo {} | ||
147 | "#, | ||
148 | ) | ||
149 | .ok() | ||
150 | .unwrap(); | ||
151 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | ||
152 | assert_eq!("this is mod foo", module.doc_comment_text().unwrap()); | ||
153 | } | ||
154 | |||
155 | #[test] | ||
156 | fn test_doc_comment_single_line_block_strips_suffix_whitespace() { | ||
157 | let file = SourceFile::parse( | ||
158 | r#" | ||
159 | /** this is mod foo */ | ||
160 | mod foo {} | ||
161 | "#, | ||
162 | ) | ||
163 | .ok() | ||
164 | .unwrap(); | ||
165 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | ||
166 | assert_eq!("this is mod foo", module.doc_comment_text().unwrap()); | ||
167 | } | ||
168 | |||
169 | #[test] | ||
170 | fn test_doc_comment_multi_line_block_strips_suffix() { | ||
171 | let file = SourceFile::parse( | ||
172 | r#" | ||
173 | /** | ||
174 | this | ||
175 | is | ||
176 | mod foo | ||
177 | */ | ||
178 | mod foo {} | ||
179 | "#, | ||
180 | ) | ||
181 | .ok() | ||
182 | .unwrap(); | ||
183 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | ||
184 | assert_eq!(" this\n is\n mod foo", module.doc_comment_text().unwrap()); | ||
185 | } | ||
186 | |||
187 | #[test] | ||
142 | fn test_where_predicates() { | 188 | fn test_where_predicates() { |
143 | fn assert_bound(text: &str, bound: Option<TypeBound>) { | 189 | fn assert_bound(text: &str, bound: Option<TypeBound>) { |
144 | assert_eq!(text, bound.unwrap().syntax().text().to_string()); | 190 | assert_eq!(text, bound.unwrap().syntax().text().to_string()); |
diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs index 6ed1b5213..1b9a2b20c 100644 --- a/crates/ra_syntax/src/ast/traits.rs +++ b/crates/ra_syntax/src/ast/traits.rs | |||
@@ -115,8 +115,8 @@ pub trait DocCommentsOwner: AstNode { | |||
115 | } | 115 | } |
116 | 116 | ||
117 | /// Returns the textual content of a doc comment block as a single string. | 117 | /// Returns the textual content of a doc comment block as a single string. |
118 | /// That is, strips leading `///` (+ optional 1 character of whitespace) | 118 | /// That is, strips leading `///` (+ optional 1 character of whitespace), |
119 | /// and joins lines. | 119 | /// trailing `*/`, trailing whitespace and then joins the lines. |
120 | fn doc_comment_text(&self) -> Option<String> { | 120 | fn doc_comment_text(&self) -> Option<String> { |
121 | let mut has_comments = false; | 121 | let mut has_comments = false; |
122 | let docs = self | 122 | let docs = self |
@@ -136,7 +136,13 @@ pub trait DocCommentsOwner: AstNode { | |||
136 | prefix_len | 136 | prefix_len |
137 | }; | 137 | }; |
138 | 138 | ||
139 | line[pos..].to_owned() | 139 | let end = if comment.kind().shape.is_block() && line.ends_with("*/") { |
140 | line.len() - 2 | ||
141 | } else { | ||
142 | line.len() | ||
143 | }; | ||
144 | |||
145 | line[pos..end].trim_end().to_owned() | ||
140 | }) | 146 | }) |
141 | .join("\n"); | 147 | .join("\n"); |
142 | 148 | ||