From c0e06a663f7e2258e1a445e2831e477100ae73bb Mon Sep 17 00:00:00 2001 From: Benjamin Herr Date: Fri, 24 May 2013 17:42:36 +0200 Subject: [PATCH] rustdoc: insert blank lines between consecutive doc comment blocks This is to prevent, for example, /** * foo */ /// bar mod m { //! baz } from being rendered into a single paragraph "foo bar baz" in the html. --- src/librustdoc/attr_parser.rs | 40 +++++++++++++++++++++++++++------ src/libsyntax/attr.rs | 23 ++++++++++++++----- src/libsyntax/parse/comments.rs | 22 ++++++++++++++---- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/src/librustdoc/attr_parser.rs b/src/librustdoc/attr_parser.rs index 99bdf2284595f..e8df76c123563 100644 --- a/src/librustdoc/attr_parser.rs +++ b/src/librustdoc/attr_parser.rs @@ -26,11 +26,12 @@ pub struct CrateAttrs { fn doc_metas( attrs: ~[ast::attribute] -) -> ~[@ast::meta_item] { +) -> ~[(@ast::meta_item, attr::doc_style)] { let doc_attrs = attr::find_attrs_by_name(attrs, "doc"); let doc_metas = do doc_attrs.map |attr| { - attr::attr_meta(attr::desugar_doc_attr(attr)) + let (attr, style) = attr::desugar_doc_attr(attr); + (attr::attr_meta(attr), style) }; return doc_metas; @@ -46,9 +47,27 @@ pub fn parse_crate(attrs: ~[ast::attribute]) -> CrateAttrs { } pub fn parse_desc(attrs: ~[ast::attribute]) -> Option<~str> { - let doc_strs = do doc_metas(attrs).filter_mapped |meta| { - attr::get_meta_item_value_str(*meta).map(|s| copy **s) - }; + let mut doc_strs = ~[]; + let mut last_doc_style: Option = None; + + for doc_metas(attrs).each |&(meta, style)| { + for attr::get_meta_item_value_str(meta).each |s| { + // consecutive line comments of the same (inner/outer) style must + // be emitted without spacing inbetween so that they can form + // multi-line markdown constructs (like long paragraphs), but + // for everything else we need a blank line to prevent multiple + // comments from merging into a single paragraph. + match (style, last_doc_style) { + (attr::doc_line_inner, Some(attr::doc_line_inner)) + | (attr::doc_line_outer, Some(attr::doc_line_outer)) + | (_, None) => {} + _ => { doc_strs.push(~"") } + } + last_doc_style = Some(style); + + doc_strs.push(copy **s); + } + } if doc_strs.is_empty() { None } else { @@ -57,8 +76,8 @@ pub fn parse_desc(attrs: ~[ast::attribute]) -> Option<~str> { } pub fn parse_hidden(attrs: ~[ast::attribute]) -> bool { - do doc_metas(attrs).find |meta| { - match attr::get_meta_item_list(*meta) { + do doc_metas(attrs).find |&(meta, _style)| { + match attr::get_meta_item_list(meta) { Some(metas) => { let hiddens = attr::find_meta_items_by_name(metas, "hidden"); !hiddens.is_empty() @@ -155,4 +174,11 @@ mod test { let desc = parse_desc(parse_attributes(source)); assert!(desc == Some(~"foo\nbar")); } + #[test] + fn should_space_out_doc_comments() { + let source = ~"/** c1*//// c2\n/** c3*/\n/// c4\n/// c5\n/** c6*/"; + let desc = parse_desc(parse_attributes(source)); + println(fmt!("%?", desc)); + assert!(desc == Some(~"c1\n\nc2\n\nc3\n\nc4\nc5\n\nc6")); + } } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 6ac743d3844d2..fb5defcc85dad 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -22,6 +22,15 @@ use parse::comments::{doc_comment_style, strip_doc_comment_decoration}; use core::hashmap::HashSet; use extra; +pub enum doc_style { + doc_block_outer, + doc_block_inner, + doc_line_outer, + doc_line_inner, + doc_attr_outer, + doc_attr_inner, +} + /* Constructors */ pub fn mk_name_value_item_str(name: @~str, value: @~str) @@ -73,14 +82,18 @@ pub fn attr_metas(attrs: &[ast::attribute]) -> ~[@ast::meta_item] { do attrs.map |a| { attr_meta(*a) } } -pub fn desugar_doc_attr(attr: &ast::attribute) -> ast::attribute { +pub fn desugar_doc_attr(attr: &ast::attribute) -> (ast::attribute, doc_style) { if attr.node.is_sugared_doc { let comment = get_meta_item_value_str(attr.node.value).get(); - let meta = mk_name_value_item_str(@~"doc", - @strip_doc_comment_decoration(*comment)); - mk_attr(meta) + let (content, style) = strip_doc_comment_decoration(*comment); + let meta = mk_name_value_item_str(@~"doc", @content); + (mk_attr(meta), style) } else { - *attr + let style = match attr.node.style { + ast::attr_inner => doc_attr_inner, + ast::attr_outer => doc_attr_outer, + }; + (*attr, style) } } diff --git a/src/libsyntax/parse/comments.rs b/src/libsyntax/parse/comments.rs index 66d2d46cc584d..9d38cbaa341ee 100644 --- a/src/libsyntax/parse/comments.rs +++ b/src/libsyntax/parse/comments.rs @@ -11,6 +11,7 @@ use core::prelude::*; use ast; +use attr; use codemap::{BytePos, CharPos, CodeMap, Pos}; use diagnostic; use parse::lexer::{is_whitespace, get_str_from, reader}; @@ -50,7 +51,7 @@ pub fn doc_comment_style(comment: &str) -> ast::attr_style { } } -pub fn strip_doc_comment_decoration(comment: &str) -> ~str { +pub fn strip_doc_comment_decoration(comment: &str) -> (~str, attr::doc_style) { /// remove whitespace-only lines from the start/end of lines fn vertical_trim(lines: ~[~str]) -> ~[~str] { @@ -94,10 +95,18 @@ pub fn strip_doc_comment_decoration(comment: &str) -> ~str { }; } + assert!(comment.len() >= 3); + if comment.starts_with("//") { + let style = if comment.char_at(2) == '!' { + attr::doc_line_inner + } else { + attr::doc_line_outer + }; // FIXME #5475: - // return comment.slice(3u, comment.len()).trim().to_owned(); - let r = comment.slice(3u, comment.len()); return r.trim().to_owned(); + // return (comment.slice(3u, comment.len()).trim().to_owned(), style); + let r = comment.slice(3u, comment.len()); + return (r.trim().to_owned(), style); } @@ -110,7 +119,12 @@ pub fn strip_doc_comment_decoration(comment: &str) -> ~str { let lines = block_trim(lines, ~"\t ", None); let lines = block_trim(lines, ~"*", Some(1u)); let lines = block_trim(lines, ~"\t ", None); - return str::connect(lines, "\n"); + let style = if comment.char_at(2) == '!' { + attr::doc_block_inner + } else { + attr::doc_block_outer + }; + return (str::connect(lines, "\n"), style); } fail!("not a doc-comment: %s", comment);