summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.xml37
-rw-r--r--src/compiler/scala/tools/ant/Scaladoc.scala8
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/DocComments.scala23
-rw-r--r--src/compiler/scala/tools/nsc/doc/Settings.scala7
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/page/Template.scala42
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css25
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js135
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/Entity.scala13
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala21
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/comment/Body.scala2
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala12
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala43
-rwxr-xr-xsrc/compiler/scala/tools/nsc/util/DocStrings.scala6
-rw-r--r--src/partest/scala/tools/partest/ScaladocModelTest.scala9
-rw-r--r--test/scaladoc/run/groups.check1
-rw-r--r--test/scaladoc/run/groups.scala119
16 files changed, 436 insertions, 67 deletions
diff --git a/build.xml b/build.xml
index b5db4ab4f4..304df1caf1 100644
--- a/build.xml
+++ b/build.xml
@@ -2044,7 +2044,7 @@ BOOTSTRAPPING BUILD (STRAP)
LIBRARIES (Forkjoin, FJBG, ASM)
============================================================================ -->
-
+
<target name="libs.clean" depends="pack.clean, asm.clean">
<delete dir="${build-libs.dir}" includeemptydirs="yes" quiet="yes" failonerror="no"/>
</target>
@@ -2112,9 +2112,10 @@ DOCUMENTATION
classpathref="pack.classpath"
addparams="${scalac.args.all}"
docRootContent="${src.dir}/library/rootdoc.txt"
- implicits="on"
- diagrams="on"
- rawOutput="${scaladoc.raw.output}"
+ implicits="on"
+ diagrams="on"
+ groups="on"
+ rawOutput="${scaladoc.raw.output}"
noPrefixes="${scaladoc.no.prefixes}">
<src>
<files includes="${src.dir}/actors-migration"/>
@@ -2199,8 +2200,9 @@ DOCUMENTATION
srcdir="${src.dir}/compiler"
docRootContent="${src.dir}/compiler/rootdoc.txt"
addparams="${scalac.args.all}"
- implicits="on"
- diagrams="on"
+ implicits="on"
+ diagrams="on"
+ groups="on"
rawOutput="${scaladoc.raw.output}"
noPrefixes="${scaladoc.no.prefixes}">
<include name="**/*.scala"/>
@@ -2224,8 +2226,9 @@ DOCUMENTATION
classpathref="pack.classpath"
srcdir="${src.dir}/jline/src/main/java"
addparams="${scalac.args.all}"
- implicits="on"
- diagrams="on"
+ implicits="on"
+ diagrams="on"
+ groups="on"
rawOutput="${scaladoc.raw.output}"
noPrefixes="${scaladoc.no.prefixes}">
<include name="**/*.scala"/>
@@ -2252,7 +2255,8 @@ DOCUMENTATION
srcdir="${src.dir}/scalap"
addparams="${scalac.args.all}"
implicits="on"
- diagrams="on"
+ diagrams="on"
+ groups="on"
rawOutput="${scaladoc.raw.output}"
noPrefixes="${scaladoc.no.prefixes}">
<include name="**/*.scala"/>
@@ -2276,8 +2280,9 @@ DOCUMENTATION
classpathref="pack.classpath"
srcdir="${src.dir}/partest"
addparams="${scalac.args.all}"
- implicits="on"
- diagrams="on"
+ implicits="on"
+ diagrams="on"
+ groups="on"
rawOutput="${scaladoc.raw.output}"
noPrefixes="${scaladoc.no.prefixes}">
<include name="**/*.scala"/>
@@ -2301,8 +2306,9 @@ DOCUMENTATION
classpathref="pack.classpath"
srcdir="${src.dir}/continuations/plugin"
addparams="${scalac.args.all}"
- implicits="on"
- diagrams="on"
+ implicits="on"
+ diagrams="on"
+ groups="on"
rawOutput="${scaladoc.raw.output}"
noPrefixes="${scaladoc.no.prefixes}">
<include name="**/*.scala"/>
@@ -2326,8 +2332,9 @@ DOCUMENTATION
classpathref="pack.classpath"
srcdir="${src.dir}/actors-migration"
addparams="${scalac.args.all}"
- implicits="on"
- diagrams="on"
+ implicits="on"
+ diagrams="on"
+ groups="on"
rawOutput="${scaladoc.raw.output}"
noPrefixes="${scaladoc.no.prefixes}">
<include name="**/*.scala"/>
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) = {
diff --git a/test/scaladoc/run/groups.check b/test/scaladoc/run/groups.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/groups.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/groups.scala b/test/scaladoc/run/groups.scala
new file mode 100644
index 0000000000..05324c2ec9
--- /dev/null
+++ b/test/scaladoc/run/groups.scala
@@ -0,0 +1,119 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ override def code = """
+ package test.scaladoc.groups {
+
+ /**
+ * The trait A
+ * @groupdesc A Group A is the group that contains functions starting with f
+ * For example:
+ * {{{
+ * this is an example
+ * }}}
+ * @groupdesc B Group B is the group that contains functions starting with b
+ * @groupname B Group B has a nice new name and a high priority
+ * @groupprio B -10
+ * @group Traits
+ * @note This is a note
+ */
+ trait A {
+ /** foo description
+ * @group A */
+ def foo = 1
+
+ /** bar description
+ * @group B */
+ def bar = 2
+ }
+
+ /** The trait B
+ * @group Traits
+ * @groupdesc C Group C is introduced by B
+ */
+ trait B {
+ /** baz descriptopn
+ * @group C */
+ def baz = 3
+ }
+
+ /** The class C which inherits from both A and B
+ * @group Classes
+ * @groupdesc B Look ma, I'm overriding group descriptions!!!
+ * @groupname B And names
+ */
+ class C extends A with B {
+ /** Oh noes, I lost my group -- or did I?!? */
+ override def baz = 4
+ }
+ }
+ """
+
+ // no need for special settings
+ def scaladocSettings = "-feature"
+
+ def testModel(rootPackage: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ // just need to check the member exists, access methods will throw an error if there's a problem
+ val base = rootPackage._package("test")._package("scaladoc")._package("groups")
+
+ def checkGroup(mbr: MemberEntity, grp: String) =
+ assert(mbr.group == grp, "Incorrect group for " + mbr.qualifiedName + ": " + mbr.group + " instead of " + grp)
+
+ def checkGroupDesc(dtpl: DocTemplateEntity, grp: String, grpDesc: String) = {
+ assert(dtpl.groupDescription(grp).isDefined,
+ "Group description for " + grp + " not defined in " + dtpl.qualifiedName)
+ assert(extractCommentText(dtpl.groupDescription(grp).get).contains(grpDesc),
+ "Group description for " + grp + " in " + dtpl.qualifiedName + " does not contain \"" + grpDesc + "\": \"" +
+ extractCommentText(dtpl.groupDescription(grp).get) + "\"")
+ }
+
+ def checkGroupName(dtpl: DocTemplateEntity, grp: String, grpName: String) =
+ // TODO: See why we need trim here, we already do trimming in the CommentFactory
+ assert(dtpl.groupName(grp) == grpName,
+ "Group name for " + grp + " in " + dtpl.qualifiedName + " does not equal \"" + grpName + "\": \"" + dtpl.groupName(grp) + "\"")
+
+ def checkGroupPrio(dtpl: DocTemplateEntity, grp: String, grpPrio: Int) =
+ assert(dtpl.groupPriority(grp) == grpPrio,
+ "Group priority for " + grp + " in " + dtpl.qualifiedName + " does not equal " + grpPrio + ": " + dtpl.groupPriority(grp))
+
+
+ val A = base._trait("A")
+ val B = base._trait("B")
+ val C = base._class("C")
+ checkGroup(A, "Traits")
+ checkGroup(B, "Traits")
+ checkGroup(C, "Classes")
+ checkGroup(A._method("foo"), "A")
+ checkGroup(A._method("bar"), "B")
+ checkGroup(B._method("baz"), "C")
+ checkGroup(C._method("foo"), "A")
+ checkGroup(C._method("bar"), "B")
+ checkGroup(C._method("baz"), "C")
+
+ checkGroupDesc(A, "A", "Group A is the group that contains functions starting with f")
+ checkGroupName(A, "A", "A")
+ checkGroupPrio(A, "A", 0)
+ checkGroupDesc(A, "B", "Group B is the group that contains functions starting with b")
+ checkGroupName(A, "B", "Group B has a nice new name and a high priority")
+ checkGroupPrio(A, "B", -10)
+
+ checkGroupDesc(B, "C", "Group C is introduced by B")
+ checkGroupName(B, "C", "C")
+ checkGroupPrio(B, "C", 0)
+
+ checkGroupDesc(C, "A", "Group A is the group that contains functions starting with f")
+ checkGroupName(C, "A", "A")
+ checkGroupPrio(C, "A", 0)
+ checkGroupDesc(C, "B", "Look ma, I'm overriding group descriptions!!!")
+ checkGroupName(C, "B", "And names")
+ checkGroupPrio(C, "B", -10)
+ checkGroupDesc(C, "C", "Group C is introduced by B")
+ checkGroupName(C, "C", "C")
+ checkGroupPrio(C, "C", 0)
+ }
+} \ No newline at end of file