diff options
author | Vlad Ureche <vlad.ureche@gmail.com> | 2012-07-18 20:28:06 +0200 |
---|---|---|
committer | Vlad Ureche <vlad.ureche@gmail.com> | 2012-07-19 15:05:04 +0200 |
commit | 0d367d4794e45ef021d9dfc7eeca186ba9fb632a (patch) | |
tree | 3ad6ff57c5a8291e86c5938b171785c6c8be3e46 /src | |
parent | 0018f9b3649f14d16debc966e7da4e54a9a0a4c3 (diff) | |
download | scala-0d367d4794e45ef021d9dfc7eeca186ba9fb632a.tar.gz scala-0d367d4794e45ef021d9dfc7eeca186ba9fb632a.tar.bz2 scala-0d367d4794e45ef021d9dfc7eeca186ba9fb632a.zip |
Scaladoc: Groups
Group class members based on their semantic relationship. To do this:
- @group on members, only need to do it for the non-overridden members
- -groups flag passes to scaladoc, groups="on" in ant
- @groupdesc Group Group Description to add descriptions
- @groupname Group New name for group
- @groupprio Group <int> (lower is better)
See test/scaladoc/run/groups.scala for a top-to-bottom example
Diffstat (limited to 'src')
13 files changed, 294 insertions, 52 deletions
diff --git a/src/compiler/scala/tools/ant/Scaladoc.scala b/src/compiler/scala/tools/ant/Scaladoc.scala index 03cb770474..6201501a71 100644 --- a/src/compiler/scala/tools/ant/Scaladoc.scala +++ b/src/compiler/scala/tools/ant/Scaladoc.scala @@ -156,6 +156,9 @@ class Scaladoc extends ScalaMatchingTask { /** Instruct the scaladoc not to generate prefixes */ private var docNoPrefixes: Boolean = false + /** Instruct the scaladoc tool to group similar functions together */ + private var docGroups: Boolean = false + /*============================================================================*\ ** Properties setters ** \*============================================================================*/ @@ -435,6 +438,10 @@ class Scaladoc extends ScalaMatchingTask { def setNoPrefixes(input: String) = docNoPrefixes = Flag.getBooleanValue(input, "noPrefixes") + /** Instruct the scaladoc tool to group similar functions together */ + def setGroups(input: String) = + docGroups = Flag.getBooleanValue(input, "groups") + /*============================================================================*\ ** Properties getters ** \*============================================================================*/ @@ -634,6 +641,7 @@ class Scaladoc extends ScalaMatchingTask { docSettings.docDiagramsDebug.value = docDiagramsDebug docSettings.docRawOutput.value = docRawOutput docSettings.docNoPrefixes.value = docNoPrefixes + docSettings.docGroups.value = docGroups if(!docDiagramsDotPath.isEmpty) docSettings.docDiagramsDotPath.value = docDiagramsDotPath.get if (!docgenerator.isEmpty) docSettings.docgenerator.value = docgenerator.get diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala index 19af01bda8..c2530bd5c7 100755 --- a/src/compiler/scala/tools/nsc/ast/DocComments.scala +++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala @@ -171,15 +171,15 @@ trait DocComments { self: Global => * 3. If there is no @return section in `dst` but there is one in `src`, copy it. */ def merge(src: String, dst: String, sym: Symbol, copyFirstPara: Boolean = false): String = { - val srcSections = tagIndex(src) - val dstSections = tagIndex(dst) - val srcParams = paramDocs(src, "@param", srcSections) - val dstParams = paramDocs(dst, "@param", dstSections) - val srcTParams = paramDocs(src, "@tparam", srcSections) - val dstTParams = paramDocs(dst, "@tparam", dstSections) - val out = new StringBuilder - var copied = 0 - var tocopy = startTag(dst, dstSections dropWhile (!isMovable(dst, _))) + val srcSections = tagIndex(src) + val dstSections = tagIndex(dst) + val srcParams = paramDocs(src, "@param", srcSections) + val dstParams = paramDocs(dst, "@param", dstSections) + val srcTParams = paramDocs(src, "@tparam", srcSections) + val dstTParams = paramDocs(dst, "@tparam", dstSections) + val out = new StringBuilder + var copied = 0 + var tocopy = startTag(dst, dstSections dropWhile (!isMovable(dst, _))) if (copyFirstPara) { val eop = // end of comment body (first para), which is delimited by blank line, or tag, or end of comment @@ -209,6 +209,11 @@ trait DocComments { self: Global => for (tparam <- sym.typeParams) mergeSection(srcTParams get tparam.name.toString, dstTParams get tparam.name.toString) mergeSection(returnDoc(src, srcSections), returnDoc(dst, dstSections)) + if (sym.name.toString == "isEmpty") { + println(groupDoc(src, srcSections)) + println(groupDoc(dst, dstSections)) + } + mergeSection(groupDoc(src, srcSections), groupDoc(dst, dstSections)) if (out.length == 0) dst else { diff --git a/src/compiler/scala/tools/nsc/doc/Settings.scala b/src/compiler/scala/tools/nsc/doc/Settings.scala index 7662381186..ad178b8158 100644 --- a/src/compiler/scala/tools/nsc/doc/Settings.scala +++ b/src/compiler/scala/tools/nsc/doc/Settings.scala @@ -188,6 +188,11 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) "Expand all type aliases and abstract types into full template pages. (locally this can be done with the @template annotation)" ) + val docGroups = BooleanSetting ( + "-groups", + "Group similar functions together (based on the @group annotation)" + ) + // Somewhere slightly before r18708 scaladoc stopped building unless the // self-type check was suppressed. I hijacked the slotted-for-removal-anyway // suppress-vt-warnings option and renamed it for this purpose. @@ -201,7 +206,7 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) docImplicits, docImplicitsDebug, docImplicitsShowAll, docDiagramsMaxNormalClasses, docDiagramsMaxImplicitClasses, docNoPrefixes, docNoLinkWarnings, docRawOutput, docSkipPackages, - docExpandAllTypes + docExpandAllTypes, docGroups ) val isScaladocSpecific: String => Boolean = scaladocSpecific map (_.name) diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala index 487ef447e9..422ea5ef1c 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -110,10 +110,26 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp <div id="mbrsel"> <div id='textfilter'><span class='pre'/><span class='input'><input id='mbrsel-input' type='text' accesskey='/'/></span><span class='post'/></div> - { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) NodeSeq.Empty else + { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty && (!universe.settings.docGroups.value || (tpl.members.map(_.group).distinct.length == 1))) + NodeSeq.Empty + else <div id="order"> <span class="filtertype">Ordering</span> - <ol><li class="alpha in"><span>Alphabetic</span></li><li class="inherit out"><span>By inheritance</span></li></ol> + <ol> + { + if (!universe.settings.docGroups.value || (tpl.members.map(_.group).distinct.length == 1)) + NodeSeq.Empty + else + <li class="group out"><span>Grouped</span></li> + } + <li class="alpha in"><span>Alphabetic</span></li> + { + if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) + NodeSeq.Empty + else + <li class="inherit out"><span>By inheritance</span></li> + } + </ol> </div> } { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) NodeSeq.Empty else @@ -223,6 +239,25 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp } </div> + <div id="groupedMembers"> + { + val allGroups = tpl.members.map(_.group).distinct + val orderedGroups = allGroups.map(group => (tpl.groupPriority(group), group)).sorted.map(_._2) + // linearization + NodeSeq fromSeq (for (group <- orderedGroups) yield + <div class="group" name={ group }> + <h3>{ tpl.groupName(group) }</h3> + { + tpl.groupDescription(group) match { + case Some(body) => <div class="comment cmt">{ bodyToHtml(body) }</div> + case _ => NodeSeq.Empty + } + } + </div> + ) + } + </div> + </div> <div id="tooltip" ></div> @@ -242,7 +277,8 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp val memberComment = memberToCommentHtml(mbr, inTpl, false) <li name={ mbr.definitionName } visbl={ if (mbr.visibility.isProtected) "prt" else "pub" } data-isabs={ mbr.isAbstract.toString } - fullComment={ if(memberComment.filter(_.label=="div").isEmpty) "no" else "yes" }> + fullComment={ if(memberComment.filter(_.label=="div").isEmpty) "no" else "yes" } + group={ mbr.group }> <a id={ mbr.signature }/> { signature(mbr, false) } { memberComment } diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css index 1bee55313b..6fb7953724 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css @@ -253,6 +253,17 @@ dl.attributes > dd { color: white; } +#groupedMembers > div.group > h3 { + background: #dadada url("typebg.gif") repeat-x bottom left; /* green */ + height: 17px; + font-size: 12pt; +} + +#groupedMembers > div.group > h3 * { + color: white; +} + + /* Member cells */ div.members > ol { @@ -516,6 +527,20 @@ div.members > ol > li:last-child { /* Comments structured layout */ +.group > div.comment { + padding-top: 5px; + padding-bottom: 5px; + padding-right: 5px; + padding-left: 5px; + border: 1px solid #ddd; + background-color: #eeeee; + margin-top:5px; + margin-bottom:5px; + margin-right:5px; + margin-left:5px; + display: block; +} + p.comment { display: block; margin-left: 14.7em; diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js index c418c3280b..afd0293fe1 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js @@ -43,20 +43,20 @@ $(document).ready(function(){ case 33: //page up input.val(""); - filter(false); + filter(false); break; case 34: //page down input.val(""); - filter(false); - break; + filter(false); + break; - default: + default: window.scrollTo(0, $("#mbrsel").offset().top); - filter(true); + filter(true); break; - } + } }); input.focus(function(event) { input.select(); @@ -70,7 +70,7 @@ $(document).ready(function(){ if (event.keyCode == 9) { // tab $("#index-input", window.parent.document).focus(); input.attr("value", ""); - return false; + return false; } }); @@ -135,18 +135,21 @@ $(document).ready(function(){ }); $("#order > ol > li.alpha").click(function() { if ($(this).hasClass("out")) { - $(this).removeClass("out").addClass("in"); - $("#order > ol > li.inherit").removeClass("in").addClass("out"); orderAlpha(); }; }) $("#order > ol > li.inherit").click(function() { if ($(this).hasClass("out")) { - $(this).removeClass("out").addClass("in"); - $("#order > ol > li.alpha").removeClass("in").addClass("out"); orderInherit(); }; }); + $("#order > ol > li.group").click(function() { + if ($(this).hasClass("out")) { + orderGroup(); + }; + }); + $("#groupedMembers").hide(); + initInherit(); // Create tooltips @@ -202,9 +205,14 @@ $(document).ready(function(){ // Set parent window title windowTitle(); + + if ($("#order > ol > li.group").length == 1) { orderGroup(); }; }); function orderAlpha() { + $("#order > ol > li.alpha").removeClass("out").addClass("in"); + $("#order > ol > li.inherit").removeClass("in").addClass("out"); + $("#order > ol > li.group").removeClass("in").addClass("out"); $("#template > div.parent").hide(); $("#template > div.conversion").hide(); $("#mbrsel > div[id=ancestors]").show(); @@ -212,12 +220,25 @@ function orderAlpha() { }; function orderInherit() { + $("#order > ol > li.inherit").removeClass("out").addClass("in"); + $("#order > ol > li.alpha").removeClass("in").addClass("out"); + $("#order > ol > li.group").removeClass("in").addClass("out"); $("#template > div.parent").show(); $("#template > div.conversion").show(); $("#mbrsel > div[id=ancestors]").hide(); filter(); }; +function orderGroup() { + $("#order > ol > li.group").removeClass("out").addClass("in"); + $("#order > ol > li.alpha").removeClass("in").addClass("out"); + $("#order > ol > li.inherit").removeClass("in").addClass("out"); + $("#template > div.parent").hide(); + $("#template > div.conversion").hide(); + $("#mbrsel > div[id=ancestors]").show(); + filter(); +}; + /** Prepares the DOM for inheritance-based display. To do so it will: * - hide all statically-generated parents headings; * - copy all members from the value and type members lists (flat members) to corresponding lists nested below the @@ -225,44 +246,74 @@ function orderInherit() { * - initialises a control variable used by the filter method to control whether filtering happens on flat members * or on inheritance-grouped members. */ function initInherit() { - // parents is a map from fully-qualified names to the DOM node of parent headings. - var parents = new Object(); + // inheritParents is a map from fully-qualified names to the DOM node of parent headings. + var inheritParents = new Object(); + var groupParents = new Object(); $("#inheritedMembers > div.parent").each(function(){ - parents[$(this).attr("name")] = $(this); + inheritParents[$(this).attr("name")] = $(this); }); $("#inheritedMembers > div.conversion").each(function(){ - parents[$(this).attr("name")] = $(this); + inheritParents[$(this).attr("name")] = $(this); + }); + $("#groupedMembers > div.group").each(function(){ + groupParents[$(this).attr("name")] = $(this); }); + $("#types > ol > li").each(function(){ var mbr = $(this); this.mbrText = mbr.find("> .fullcomment .cmt").text(); var qualName = mbr.attr("name"); var owner = qualName.slice(0, qualName.indexOf("#")); var name = qualName.slice(qualName.indexOf("#") + 1); - var parent = parents[owner]; - if (parent != undefined) { - var types = $("> .types > ol", parent); + var inheritParent = inheritParents[owner]; + if (inheritParent != undefined) { + var types = $("> .types > ol", inheritParent); + if (types.length == 0) { + inheritParent.append("<div class='types members'><h3>Type Members</h3><ol></ol></div>"); + types = $("> .types > ol", inheritParent); + } + var clone = mbr.clone(); + clone[0].mbrText = this.mbrText; + types.append(clone); + } + var group = mbr.attr("group") + var groupParent = groupParents[group]; + if (groupParent != undefined) { + var types = $("> .types > ol", groupParent); if (types.length == 0) { - parent.append("<div class='types members'><h3>Type Members</h3><ol></ol></div>"); - types = $("> .types > ol", parent); + groupParent.append("<div class='types members'><ol></ol></div>"); + types = $("> .types > ol", groupParent); } var clone = mbr.clone(); clone[0].mbrText = this.mbrText; types.append(clone); } }); + $("#values > ol > li").each(function(){ var mbr = $(this); this.mbrText = mbr.find("> .fullcomment .cmt").text(); var qualName = mbr.attr("name"); var owner = qualName.slice(0, qualName.indexOf("#")); var name = qualName.slice(qualName.indexOf("#") + 1); - var parent = parents[owner]; - if (parent != undefined) { - var values = $("> .values > ol", parent); + var inheritParent = inheritParents[owner]; + if (inheritParent != undefined) { + var values = $("> .values > ol", inheritParent); if (values.length == 0) { - parent.append("<div class='values members'><h3>Value Members</h3><ol></ol></div>"); - values = $("> .values > ol", parent); + inheritParent.append("<div class='values members'><h3>Value Members</h3><ol></ol></div>"); + values = $("> .values > ol", inheritParent); + } + var clone = mbr.clone(); + clone[0].mbrText = this.mbrText; + values.append(clone); + } + var group = mbr.attr("group") + var groupParent = groupParents[group]; + if (groupParent != undefined) { + var values = $("> .values > ol", groupParent); + if (values.length == 0) { + groupParent.append("<div class='values members'><ol></ol></div>"); + values = $("> .values > ol", groupParent); } var clone = mbr.clone(); clone[0].mbrText = this.mbrText; @@ -275,6 +326,9 @@ function initInherit() { $("#inheritedMembers > div.conversion").each(function() { if ($("> div.members", this).length == 0) { $(this).remove(); }; }); + $("#groupedMembers > div.group").each(function() { + if ($("> div.members", this).length == 0) { $(this).remove(); }; + }); }; /* filter used to take boolean scrollToMember */ @@ -284,26 +338,43 @@ function filter() { var queryRegExp = new RegExp(query, "i"); var privateMembersHidden = $("#visbl > ol > li.public").hasClass("in"); var orderingAlphabetic = $("#order > ol > li.alpha").hasClass("in"); - var hiddenSuperclassElementsLinearization = orderingAlphabetic ? $("#linearization > li.out") : $("#linearization > li:gt(0)"); + var orderingInheritance = $("#order > ol > li.inherit").hasClass("in"); + var orderingGroups = $("#order > ol > li.group").hasClass("in"); + var hiddenSuperclassElementsLinearization = orderingInheritance ? $("#linearization > li:gt(0)") : $("#linearization > li.out"); var hiddenSuperclassesLinearization = hiddenSuperclassElementsLinearization.map(function() { return $(this).attr("name"); }).get(); - var hiddenSuperclassElementsImplicits = orderingAlphabetic ? $("#implicits > li.out") : $("#implicits > li"); + var hiddenSuperclassElementsImplicits = orderingInheritance ? $("#implicits > li") : $("#implicits > li.out"); var hiddenSuperclassesImplicits = hiddenSuperclassElementsImplicits.map(function() { return $(this).attr("name"); }).get(); var hideInheritedMembers; - if(orderingAlphabetic) { + if (orderingAlphabetic) { + $("#allMembers").show(); $("#inheritedMembers").hide(); + $("#groupedMembers").hide(); hideInheritedMembers = true; $("#allMembers > .members").each(filterFunc); - } - else { - $("#inheritedMembers").show(); + } else if (orderingGroups) { + $("#groupedMembers").show(); + $("#inheritedMembers").hide(); + $("#allMembers").hide(); hideInheritedMembers = true; - $("#allMembers > .members").each(filterFunc); + $("#groupedMembers > .group > .members").each(filterFunc); + $("#groupedMembers > div.group").each(function() { + $(this).show(); + if ($("> div.members", this).not(":hidden").length == 0) { + $(this).hide(); + } else { + $(this).show(); + } + }); + } else if (orderingInheritance) { + $("#inheritedMembers").show(); + $("#groupedMembers").hide(); + $("#allMembers").hide(); hideInheritedMembers = false; $("#inheritedMembers > .parent > .members").each(filterFunc); $("#inheritedMembers > .conversion > .members").each(filterFunc); diff --git a/src/compiler/scala/tools/nsc/doc/model/Entity.scala b/src/compiler/scala/tools/nsc/doc/model/Entity.scala index 0836d7e4da..16ade0787e 100644 --- a/src/compiler/scala/tools/nsc/doc/model/Entity.scala +++ b/src/compiler/scala/tools/nsc/doc/model/Entity.scala @@ -116,6 +116,9 @@ trait MemberEntity extends Entity { /** The comment attached to this member, if any. */ def comment: Option[Comment] + /** The group this member is from */ + def group: String + /** The template of which this entity is a member. */ def inTemplate: DocTemplateEntity @@ -218,7 +221,6 @@ trait HigherKinded { /** The type parameters of this entity. */ def typeParams: List[TypeParam] - } @@ -328,6 +330,15 @@ trait DocTemplateEntity extends MemberTemplateEntity { /** If this template contains other templates, such as classes and traits, they will be shown in this diagram */ def contentDiagram: Option[Diagram] + + /** Returns the group description taken either from this template or its linearizationTypes */ + def groupDescription(group: String): Option[Body] + + /** Returns the group description taken either from this template or its linearizationTypes */ + def groupPriority(group: String): Int + + /** Returns the group description taken either from this template or its linearizationTypes */ + def groupName(group: String): String } /** A trait template. */ diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index 0ba32fceaa..eba3e080ef 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -121,6 +121,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } if (inTpl != null) thisFactory.comment(sym, thisTpl, inTpl) else None } + def group = if (comment.isDefined) comment.get.group.getOrElse("No Group") else "No Group" override def inTemplate = inTpl override def toRoot: List[MemberImpl] = this :: inTpl.toRoot def inDefinitionTemplates = this match { @@ -479,9 +480,29 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { case None => List() } else List.empty + // These are generated on-demand, make sure you don't call them more than once def inheritanceDiagram = makeInheritanceDiagram(this) def contentDiagram = makeContentDiagram(this) + + def groupSearch[T](extractor: Comment => T, default: T): T = { + // query this template + if (comment.isDefined) { + val entity = extractor(comment.get) + if (entity != default) return entity + } + // query linearization + if (!sym.isAliasType && !sym.isAbstractType) + for (tpl <- linearizationTemplates.collect{ case dtpl: DocTemplateImpl if dtpl!=this => dtpl}) { + val entity = tpl.groupSearch(extractor, default) + if (entity != default) return entity + } + default + } + + def groupDescription(group: String): Option[Body] = groupSearch(_.groupDesc.get(group), None) + def groupPriority(group: String): Int = groupSearch(_.groupPrio.get(group) match { case Some(prio) => prio; case _ => 0 }, 0) + def groupName(group: String): String = groupSearch(_.groupNames.get(group) match { case Some(name) => name; case _ => group }, group) } abstract class PackageImpl(sym: Symbol, inTpl: PackageImpl) extends DocTemplateImpl(sym, inTpl) with Package { diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala b/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala index 3ab8fc7805..3f0024cb68 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala @@ -71,7 +71,7 @@ final case class Monospace(text: Inline) extends Inline final case class Text(text: String) extends Inline abstract class EntityLink(val title: Inline) extends Inline { def link: LinkTo } object EntityLink { - def apply(title: Inline, linkTo: LinkTo) = new EntityLink(title) { def link: LinkTo = linkTo} + def apply(title: Inline, linkTo: LinkTo) = new EntityLink(title) { def link: LinkTo = linkTo } def unapply(el: EntityLink): Option[(Inline, LinkTo)] = Some((el.title, el.link)) } final case class HtmlTag(data: String) extends Inline { diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala b/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala index 7b70683db5..c8f4c2f285 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala @@ -114,6 +114,18 @@ abstract class Comment { /** A set of diagram directives for the content diagram */ def contentDiagram: List[String] + /** The group this member is part of */ + def group: Option[String] + + /** Member group descriptions */ + def groupDesc: Map[String,Body] + + /** Member group names (overriding the short tag) */ + def groupNames: Map[String,String] + + /** Member group priorities */ + def groupPrio: Map[String,Int] + override def toString = body.toString + "\n" + (authors map ("@author " + _.toString)).mkString("\n") + diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala index a57ccd36c2..ac737b6ee3 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala @@ -114,7 +114,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member constructor0: Option[Body] = None, source0: Option[String] = None, inheritDiagram0: List[String] = List.empty, - contentDiagram0: List[String] = List.empty + contentDiagram0: List[String] = List.empty, + group0: Option[Body] = None, + groupDesc0: Map[String,Body] = Map.empty, + groupNames0: Map[String,Body] = Map.empty, + groupPrio0: Map[String,Body] = Map.empty ) : Comment = new Comment{ val body = if(body0 isDefined) body0.get else Body(Seq.empty) val authors = authors0 @@ -133,6 +137,35 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member val source = source0 val inheritDiagram = inheritDiagram0 val contentDiagram = contentDiagram0 + val groupDesc = groupDesc0 + val group = + group0 match { + case Some(Body(List(Paragraph(Chain(List(Summary(Text(groupId)))))))) => Some(groupId.toString.trim) + case _ => None + } + val groupPrio = groupPrio0 flatMap { + case (group, body) => + try { + body match { + case Body(List(Paragraph(Chain(List(Summary(Text(prio))))))) => List(group -> prio.toInt) + case _ => List() + } + } catch { + case _: java.lang.NumberFormatException => List() + } + } + val groupNames = groupNames0 flatMap { + case (group, body) => + try { + body match { + case Body(List(Paragraph(Chain(List(Summary(Text(name))))))) if (!name.trim.contains("\n")) => List(group -> (name.trim)) + case _ => List() + } + } catch { + case _: java.lang.NumberFormatException => List() + } + } + } protected val endOfText = '\u0003' @@ -202,7 +235,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member /** A Scaladoc tag linked to a symbol. Returns the name of the tag, the name * of the symbol, and the rest of the line. */ protected val SymbolTag = - new Regex("""\s*@(param|tparam|throws)\s+(\S*)\s*(.*)""") + new Regex("""\s*@(param|tparam|throws|groupdesc|groupname|groupprio)\s+(\S*)\s*(.*)""") /** The start of a scaladoc code block */ protected val CodeBlockStart = @@ -403,7 +436,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member constructor0 = oneTag(SimpleTagKey("constructor")), source0 = Some(clean(src).mkString("\n")), inheritDiagram0 = inheritDiagramText, - contentDiagram0 = contentDiagramText + contentDiagram0 = contentDiagramText, + group0 = oneTag(SimpleTagKey("group")), + groupDesc0 = allSymsOneTag(SimpleTagKey("groupdesc")), + groupNames0 = allSymsOneTag(SimpleTagKey("groupname")), + groupPrio0 = allSymsOneTag(SimpleTagKey("groupprio")) ) for ((key, _) <- bodyTags) diff --git a/src/compiler/scala/tools/nsc/util/DocStrings.scala b/src/compiler/scala/tools/nsc/util/DocStrings.scala index f4ce6d6ef1..c88414c423 100755 --- a/src/compiler/scala/tools/nsc/util/DocStrings.scala +++ b/src/compiler/scala/tools/nsc/util/DocStrings.scala @@ -144,6 +144,12 @@ object DocStrings { } /** Optionally start and end index of return section in `str`, or `None` + * if `str` does not have a @group. */ + def groupDoc(str: String, sections: List[(Int, Int)]): Option[(Int, Int)] = + sections find (startsWithTag(str, _, "@group")) + + + /** Optionally start and end index of return section in `str`, or `None` * if `str` does not have a @return. */ def returnDoc(str: String, sections: List[(Int, Int)]): Option[(Int, Int)] = diff --git a/src/partest/scala/tools/partest/ScaladocModelTest.scala b/src/partest/scala/tools/partest/ScaladocModelTest.scala index adf7abe11c..ffc5e74cc0 100644 --- a/src/partest/scala/tools/partest/ScaladocModelTest.scala +++ b/src/partest/scala/tools/partest/ScaladocModelTest.scala @@ -169,14 +169,19 @@ abstract class ScaladocModelTest extends DirectTest { }).mkString(", ") + "]") } - def extractCommentText(c: Comment) = { + def extractCommentText(c: Any) = { def extractText(body: Any): String = body match { case s: String => s case s: Seq[_] => s.toList.map(extractText(_)).mkString case p: Product => p.productIterator.toList.map(extractText(_)).mkString case _ => "" } - extractText(c.body) + c match { + case c: Comment => + extractText(c.body) + case b: Body => + extractText(b) + } } def countLinks(c: Comment, p: EntityLink => Boolean) = { |