From e7e896170a4cccefc5ac4744bb837329ae5d0d36 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 4 May 2019 15:00:16 +0800 Subject: Convert doc comment to attr --- crates/ra_mbe/src/syntax_bridge.rs | 73 ++++++++++++++++++++++++++++++++++++-- crates/ra_mbe/src/tests.rs | 25 +++++++++++++ 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index 3521b382a..8b4098c4a 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs @@ -118,6 +118,69 @@ impl TokenMap { } } +/// Returns the textual content of a doc comment block as a quoted string +/// That is, strips leading `///` (or `/**`, etc) +/// and strips the ending `*/` +/// And then quote the string, which is needed to convert to `tt::Literal` +fn doc_comment_text(comment: &ast::Comment) -> SmolStr { + use ast::AstToken; + + let prefix_len = comment.prefix().len(); + let mut text = &comment.text()[prefix_len..]; + + // Remove ending "*/" + if comment.kind().shape == ast::CommentShape::Block { + text = &text[0..text.len() - 2]; + } + + // Quote the string + // Note that `tt::Literal` expect an escaped string + let text = format!("{:?}", text); + text.into() +} + +fn convert_doc_comment<'a>(token: &ra_syntax::SyntaxToken<'a>) -> Option> { + use ast::AstToken; + let comment = ast::Comment::cast(*token)?; + let doc = comment.kind().doc?; + + // Make `doc="\" Comments\"" + let mut meta_tkns = Vec::new(); + meta_tkns.push(mk_ident("doc")); + meta_tkns.push(mk_punct('=')); + meta_tkns.push(mk_doc_literal(&comment)); + + // Make `#![]` + let mut token_trees = Vec::new(); + token_trees.push(mk_punct('#')); + if let ast::CommentPlacement::Inner = doc { + token_trees.push(mk_punct('!')); + } + token_trees.push(tt::TokenTree::from(tt::Subtree::from( + tt::Subtree { delimiter: tt::Delimiter::Bracket, token_trees: meta_tkns }.into(), + ))); + + return Some(token_trees); + + // Helper functions + fn mk_ident(s: &str) -> tt::TokenTree { + tt::TokenTree::from(tt::Leaf::from(tt::Ident { + text: s.into(), + id: tt::TokenId::unspecified(), + })) + } + + fn mk_punct(c: char) -> tt::TokenTree { + tt::TokenTree::from(tt::Leaf::from(tt::Punct { char: c, spacing: tt::Spacing::Alone })) + } + + fn mk_doc_literal(comment: &ast::Comment) -> tt::TokenTree { + let lit = tt::Literal { text: doc_comment_text(comment) }; + + tt::TokenTree::from(tt::Leaf::from(lit)) + } +} + fn convert_tt( token_map: &mut TokenMap, global_offset: TextUnit, @@ -141,13 +204,17 @@ fn convert_tt( let mut child_iter = tt.children_with_tokens().skip(skip_first as usize).peekable(); while let Some(child) = child_iter.next() { - if (skip_first && (child == first_child || child == last_child)) || child.kind().is_trivia() - { + if skip_first && (child == first_child || child == last_child) { continue; } + match child { SyntaxElement::Token(token) => { - if token.kind().is_punct() { + if let Some(doc_tokens) = convert_doc_comment(&token) { + token_trees.extend(doc_tokens); + } else if token.kind().is_trivia() { + continue; + } else if token.kind().is_punct() { assert!(token.text().len() == 1, "Input ast::token punct must be single char."); let char = token.text().chars().next().unwrap(); diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index bd5a44240..897f76be1 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs @@ -867,6 +867,31 @@ fn test_meta() { ); } +#[test] +fn test_meta_doc_comments() { + let rules = create_rules( + r#" + macro_rules! foo { + ($(#[$ i:meta])+) => ( + $(#[$ i])+ + fn bar() {} + ) + } +"#, + ); + assert_expansion( + MacroKind::Items, + &rules, + r#"foo! { + /// Single Line Doc 1 + /** + MultiLines Doc + */ + }"#, + "# [doc = \" Single Line Doc 1\"] # [doc = \" \\n MultiLines Doc\\n \"] fn bar () {}", + ); +} + #[test] fn test_tt_block() { let rules = create_rules( -- cgit v1.2.3 From 048f12d9f0bcd7357d9a885639928a90915577ab Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 4 May 2019 17:03:22 +0800 Subject: Use explict `escape_default` instead of dbg print --- crates/ra_mbe/src/syntax_bridge.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index 8b4098c4a..63ca54cc2 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs @@ -135,7 +135,7 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr { // Quote the string // Note that `tt::Literal` expect an escaped string - let text = format!("{:?}", text); + let text = format!("\"{}\"", text.escape_default()); text.into() } -- cgit v1.2.3 From 036141663bd934da451461dbc47dc836c432e057 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 4 May 2019 19:34:02 +0800 Subject: Quote the quoted comment --- crates/ra_mbe/src/syntax_bridge.rs | 2 +- crates/ra_mbe/src/tests.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index 63ca54cc2..73a0780da 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs @@ -135,7 +135,7 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr { // Quote the string // Note that `tt::Literal` expect an escaped string - let text = format!("\"{}\"", text.escape_default()); + let text = format!("{:?}", text.escape_default().to_string()); text.into() } diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index 897f76be1..c487bbbd4 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs @@ -888,7 +888,7 @@ fn test_meta_doc_comments() { MultiLines Doc */ }"#, - "# [doc = \" Single Line Doc 1\"] # [doc = \" \\n MultiLines Doc\\n \"] fn bar () {}", + "# [doc = \" Single Line Doc 1\"] # [doc = \" \\\\n MultiLines Doc\\\\n \"] fn bar () {}", ); } -- cgit v1.2.3