[PATCH] monodocs2html support for cross-type hyperlinks

Jonathan Pryor <[email protected]> Wed, 20 Sep 2006 17:34:43 -0400
Newsgroups gmane.comp.gnome.mono.documentation
Message-ID <[email protected]>
Attached is a patch to support to monodocs2html for cross-type
hyperlinks.

In short, it allows you to use <see cref="M:Foo.Bar" /> from within the
Baz type comments and have it turn into a hyperlink to Foo.html#Foo.Bar.

It requires removing the generate-id() XSLT calls to use full type
+member names instead.

For an example of this in action, compare the new file:

http://balthasar.jprl.com/~jon/Projects/mono-fuse/docs/Mono.Fuse/FileSystem.html

to the original output:

http://www.jprl.com/Projects/mono-fuse/docs/Mono.Fuse/FileSystem.html

It also uses <h2>...<h4> elements instead of <div> elements everywhere,
as this provides additional content structure.  (I also have an XSLT
that uses h1..h3 to generate a table-of-contents, and I'd like to use
this XSLT in the future.)

Permission to commit?

Thanks,
 - Jon

_______________________________________________
Mono-docs-list maillist  -  [email protected]
http://lists.ximian.com/mailman/listinfo/mono-docs-list
monodocs2html.patch (text/x-patch, 15.1 KB)
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 65740)
+++ ChangeLog	(working copy)
@@ -1,3 +1,14 @@
+2006-09-20  Joshua Tauberer  <[email protected]>
+
+	* defaulttemplate.xsl: Add additional CSS styles so that the tr/@bgcolor
+	  attribute isn't needed (makes XHTML validators happier).
+	* overview.xsl: Use <h2/> instead of <div/>.
+	* stylesheet.xsl: Use <h2/>..<h4/> instead of <div/> -- provides better
+	  structure.  Implement more support for <see cref="" /> handling, so that
+	  you can now reference members in other types within the same assembly, and
+	  have them become proper hyperlinks.  Don't use generate-id(), and create
+	  link names manually so that cross-page hyperlinks work.
+
 2006-04-01  Joshua Tauberer  <[email protected]>
 
 	* monodocs2html.cs: Skip files that are missing.
Index: defaulttemplate.xsl
===================================================================
--- defaulttemplate.xsl	(revision 65740)
+++ defaulttemplate.xsl	(working copy)
@@ -41,6 +41,8 @@
 					.MembersListing td { margin: 0px; border: 1px solid black; padding: .25em }
 					
 					.TypesListing td { margin: 0px;  padding: .25em }
+					.InnerSignatureTable tr { background-color: #f2f2f2; }
+					.TypePermissionsTable tr { background-color: #f2f2f2; }
 					
 				</style>
 				
Index: overview.xsl
===================================================================
--- overview.xsl	(revision 65740)
+++ overview.xsl	(working copy)
@@ -79,7 +79,7 @@
 			<Remarks>			
 				<xsl:for-each select="Types/Namespace[@Name=$namespace or $namespace='all' or count(parent::*/Namespace)=1][count(Type)>0]">
 					<xsl:if test="$namespace='all' or count(parent::*/Namespace)=1">
-						<div class="Section"><xsl:value-of select="@Name"/> Namespace</div>
+						<h2 class="Section"><xsl:value-of select="@Name"/> Namespace</h2>
 					</xsl:if>
 					
 					<p><xsl:apply-templates select="document(concat(@Name,'.xml'), .)/Namespace/Docs/remarks" mode="notoppara"/></p>
Index: stylesheet.xsl
===================================================================
--- stylesheet.xsl	(revision 65740)
+++ stylesheet.xsl	(working copy)
@@ -19,6 +19,8 @@
 
 	<!-- The namespace that the current type belongs to. -->
 	<xsl:variable name="TypeNamespace" select="substring(/Type/@FullName, 1, string-length(/Type/@FullName) - string-length(/Type/@Name) - 1)"/>		
+	<xsl:variable name="TypeFullName" select="/Type/@FullName"/>		
+	<xsl:variable name="mono-docs">http://www.go-mono.com/docs/monodoc.ashx?link=</xsl:variable>
 
 	<!-- THE MAIN RENDERING TEMPLATE -->
 
@@ -66,7 +68,7 @@
 			<table class="SignatureTable" cellspacing="0" width="100%">
 			<tr><td>
 				<table class="InnerSignatureTable" cellpadding="10" cellspacing="0" width="100%">
-				<tr bgcolor="#f2f2f2"><td>
+				<tr><td>
 					<xsl:choose>
 					<xsl:when test="$language='C#'">
 
@@ -190,7 +192,7 @@
 
 			<!-- MEMBER LISTING -->
 			<xsl:if test="not(Base/BaseTypeName='System.Delegate' or Base/BaseTypeName='System.MulticastDelegate' or Base/BaseTypeName='System.Enum')">
-				<div class="Section">Members</div>
+				<h2 class="Section">Members</h2>
 	
 				<div class="SectionBox">
 				
@@ -268,15 +270,21 @@
 			<xsl:if test="not(Base/BaseTypeName='System.Delegate' or Base/BaseTypeName='System.MulticastDelegate' or Base/BaseTypeName='System.Enum')">
 			<xsl:variable name="Type" select="."/>
 			
-			<div class="Section">Member Details</div>
+			<h2 class="Section">Member Details</h2>
 			
 			<div class="SectionBox">
 
 			<xsl:for-each select="Members/Member">
 			
-				<a name="member_{generate-id(.)}"></a>
+				<xsl:variable name="linkname"><xsl:call-template name="GetLinkName"
+					><xsl:with-param name="type" select="../.."
+					/><xsl:with-param name="member" select="." /></xsl:call-template></xsl:variable>
+				<xsl:variable name="linkid"><xsl:call-template name="GetLinkId"
+					><xsl:with-param name="type" select="../.."
+					/><xsl:with-param name="member" select="." /></xsl:call-template></xsl:variable>
+				<a name="{$linkname}"></a>
 
-				<div class="MemberName">
+				<h3 id="{$linkid}" class="MemberName">
 					<xsl:choose>
 					<xsl:when test="MemberType='Constructor'">
 						<xsl:value-of select="$Type/@Name"/> Constructor
@@ -288,7 +296,7 @@
 						<xsl:value-of select="@MemberName"/>
 					</xsl:otherwise>
 					</xsl:choose>
-				</div>
+				</h3>
 					
 				<div class="MemberSignature">
 					<!-- recreate the signature -->
@@ -485,7 +493,7 @@
 		<!-- alt member: not sure what these are for, actually -->
 
 		<xsl:if test="count(Docs/altmember)">
-			<div class="Subsection">See Also</div>
+			<h4 class="Subsection">See Also</h4>
 			<div class="SubsectionBox">
 			<xsl:for-each select="Docs/altmember">
 				<div><xsl:apply-templates select="@cref" mode="cref"/></div>
@@ -496,7 +504,7 @@
 		<!-- parameters & return & value -->
 
 		<xsl:if test="count(Docs/param)">
-			<div class="Subsection">Parameters</div>
+			<h4 class="Subsection">Parameters</h4>
 			<div class="SubsectionBox">
 			<dl>
 			<xsl:for-each select="Docs/param">
@@ -509,13 +517,13 @@
 			</div>
 		</xsl:if>
 		<xsl:if test="count(Docs/returns)">
-			<div class="Subsection">Returns</div>
+			<h4 class="Subsection">Returns</h4>
 			<div class="SubsectionBox">
 				<xsl:apply-templates select="Docs/returns" mode="notoppara"/>
 			</div>
 		</xsl:if>
 		<xsl:if test="count(Docs/value)">
-			<div class="Subsection">Value</div>
+			<h4 class="Subsection">Value</h4>
 			<div class="SubsectionBox">
 				<xsl:apply-templates select="Docs/value" mode="notoppara"/>
 			</div>
@@ -524,7 +532,7 @@
 		<!-- thread safety -->
 
 		<xsl:if test="count(ThreadingSafetyStatement)">
-			<div class="Subsection">Thread Safety</div>
+			<h4 class="Subsection">Thread Safety</h4>
 			<div class="SubsectionBox">
 			<xsl:apply-templates select="ThreadingSafetyStatement" mode="notoppara"/>
 			</div>
@@ -534,10 +542,10 @@
 		<!-- permissions -->
 
 		<xsl:if test="count(Docs/permission)">
-			<div>Permissions</div>
+			<h4>Permissions</h4>
 			<div class="SubsectionBox">
 			<table class="TypePermissionsTable" border="1" cellpadding="6">
-			<tr bgcolor="#f2f2f2"><th>Type</th><th>Reason</th></tr>
+			<tr><th>Type</th><th>Reason</th></tr>
 			<xsl:for-each select="Docs/permission">
 				<tr valign="top">
 				<td><xsl:apply-templates select="@cref" mode="typelink"><xsl:with-param name="wrt" select="$TypeNamespace"/></xsl:apply-templates></td>
@@ -553,7 +561,7 @@
 		<!-- method/property/constructor exceptions -->
 
 		<xsl:if test="count(Docs/exception)">
-			<div class="Subsection">Exceptions</div>
+			<h4 class="Subsection">Exceptions</h4>
 			<div class="SubsectionBox">
 			<table class="ExceptionsTable">
 			<tr><th>Type</th><th>Condition</th></tr>
@@ -572,7 +580,7 @@
 		<!-- remarks -->
 
 		<xsl:if test="count(Docs/remarks)">
-			<div class="Subsection">Remarks</div>
+			<h4 class="Subsection">Remarks</h4>
 			<div class="SubsectionBox">
 			<xsl:apply-templates select="Docs/remarks" mode="notoppara"/>
 			</div>
@@ -678,12 +686,10 @@
 					<xsl:when test="count($typeentry)">
 						<xsl:value-of select="concat($basepath,$typeentry/parent::Namespace/@Name, '/', $typeentry/@Name)"/>.<xsl:value-of select="$ext"/>
 					</xsl:when>
-					<xsl:when test="starts-with($T, 'System.')">
-						http://mono.ximian.com:8080/monodoc.ashx?link=T:<xsl:value-of select="$T"/>
-					</xsl:when>
-					<xsl:otherwise>
-						javascript:alert("Documentation not found.")
-					</xsl:otherwise>
+					<xsl:when test="starts-with($T, 'System.')"
+						><xsl:value-of select="$mono-docs" />T:<xsl:value-of select="$T"
+						/></xsl:when>
+					<xsl:otherwise>javascript:alert("Documentation not found.")</xsl:otherwise>
 				</xsl:choose>
 				</xsl:attribute>
 	
@@ -740,11 +746,81 @@
 		</xsl:choose>
 	</xsl:template>
 
+	<xsl:template name="makememberlink">
+		<xsl:param name="cref"/>
+
+		<xsl:variable name="anchor" select="substring($cref, 3)" />
+
+		<xsl:variable name="fullname"
+			><xsl:choose><xsl:when test="starts-with($cref, 'C:')"
+			><xsl:value-of select="substring($cref, 3)"
+			/></xsl:when><xsl:otherwise><xsl:call-template name="GetTypeName"
+				><xsl:with-param name="type" select="substring($cref, 3)"
+				/></xsl:call-template></xsl:otherwise></xsl:choose>
+		</xsl:variable>
+
+		<xsl:variable name="memberName"
+			><xsl:choose><xsl:when test="starts-with($cref, 'C:')"
+			></xsl:when><xsl:otherwise>.<xsl:call-template name="GetMemberName"
+				><xsl:with-param name="type" select="substring($cref, 3)"
+				/></xsl:call-template></xsl:otherwise></xsl:choose>
+		</xsl:variable>
+
+		<xsl:variable name="T" select="$fullname"/>
+		<a>
+			<!-- Search for type in the index.xml file. -->
+			<xsl:variable name="typeentry" select="document('index.xml')/Overview/Types/Namespace/Type[concat(parent::Namespace/@Name,'.',@Name) = $T]"/>
+			
+			<xsl:attribute name="href">
+			<xsl:choose>
+				<xsl:when test="count($typeentry)">
+					<xsl:value-of select="concat($basepath,$typeentry/parent::Namespace/@Name, '/', $typeentry/@Name)"/>.<xsl:value-of select="$ext"/>#<xsl:value-of select="$anchor" />
+				</xsl:when>
+				<xsl:when test="starts-with($T, 'System.')"
+				><xsl:value-of select="$mono-docs" />T:<xsl:value-of select="$T"
+				/></xsl:when>
+				<xsl:otherwise>javascript:alert("Documentation not found.")</xsl:otherwise>
+			</xsl:choose>
+			</xsl:attribute>
+			<xsl:value-of select="concat($fullname, $memberName)" />
+		</a>
+	</xsl:template>
+
+	<xsl:template name="GetTypeName">
+		<xsl:param name="type" />
+		<xsl:variable name="prefix" select="substring-before($type, '.')" />
+		<xsl:variable name="suffix" select="substring-after($type, '.')" />
+		<xsl:choose>
+			<xsl:when test="not(contains($suffix, '.'))"><xsl:value-of
+			select="$prefix" /></xsl:when>
+			<xsl:otherwise><xsl:value-of select="$prefix" 
+			/>.<xsl:call-template name="GetTypeName"><xsl:with-param name="type"
+			select="$suffix" /></xsl:call-template></xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+
+	<xsl:template name="GetMemberName">
+		<xsl:param name="type" />
+		<xsl:variable name="prefix" select="substring-before($type, '.')" />
+		<xsl:variable name="suffix" select="substring-after($type, '.')" />
+		<xsl:choose>
+			<xsl:when test="not(contains($suffix, '.'))"><xsl:value-of
+			select="$suffix" /></xsl:when>
+			<xsl:otherwise><xsl:call-template name="GetMemberName"><xsl:with-param name="type"
+			select="$suffix" /></xsl:call-template></xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+	
 	<!-- Transforms the contents of the selected node containing a cref into a hyperlink. -->
 	<xsl:template match="*|@*" mode="cref">
+		<xsl:call-template name="makememberlink">
+			<xsl:with-param name="cref" select="."/>
+		</xsl:call-template>
+		<!--
 		<a>
 			<xsl:attribute name="href"><xsl:value-of select="."/></xsl:attribute>
 			<xsl:value-of select="substring-after(., ':')"/></a>
+			-->
 	</xsl:template>
 
 	<xsl:template name="membertypeplural">
@@ -901,22 +977,22 @@
 	</xsl:template>
 
 	<xsl:template match="list[@type='bullet']">
-		<UL>
+		<ul>
 			<xsl:for-each select="item">
-				<LI>
+				<li>
 					<xsl:apply-templates select="term" mode="notoppara"/>
-				</LI>
+				</li>
 			</xsl:for-each>
-		</UL>
+		</ul>
 	</xsl:template>
 	<xsl:template match="list[@type='number']">
-		<OL>
+		<ol>
 			<xsl:for-each select="item">
-				<LI>
+				<li>
 					<xsl:apply-templates select="term" mode="notoppara"/>
-				</LI>
+				</li>
 			</xsl:for-each>
-		</OL>
+		</ol>
 	</xsl:template>
 
 	<xsl:template match="list">
@@ -935,7 +1011,9 @@
 			</xsl:call-template>
 		</xsl:when>
 		<xsl:otherwise>
-			<xsl:value-of select="substring-after(@cref, ':')"/>
+			<xsl:call-template name="makememberlink">
+				<xsl:with-param name="cref" select="@cref"/>
+			</xsl:call-template>
 		</xsl:otherwise>
 		</xsl:choose>
 	</xsl:template>
@@ -973,13 +1051,13 @@
 		<xsl:if test="count($MEMBERS)">
 
 		<!-- header -->
-		<div class="Subsection" style="margin-bottom: .75em">
+		<h3>
 			<xsl:if test="$showprotected">Protected </xsl:if>
 			<xsl:call-template name="membertypeplural"><xsl:with-param name="name" select="$listmembertype"/></xsl:call-template>
-			</div>
+			</h3>
 
 		<div class="SubsectionBox">
-		<table Class="MembersListing">
+		<table class="MembersListing">
 
 		<xsl:for-each select="$MEMBERS">
 			<!--<xsl:sort select="contains(MemberSignature[@Language='C#']/@Value,' static ')" data-type="text"/>-->
@@ -988,7 +1066,11 @@
 			<xsl:sort select="count(Parameters/Parameter)"/>
 			<xsl:sort select="Parameters/Parameter/@Type"/>
 			
-			<xsl:variable name="linkname" select="generate-id(.)"/>
+			<!-- <xsl:variable name="linkname" select="generate-id(.)"/> -->
+			
+			<xsl:variable name="linkid"><xsl:call-template name="GetLinkId"
+				><xsl:with-param name="type" select="../.."
+				/><xsl:with-param name="member" select="." /></xsl:call-template></xsl:variable>
 
 			<tr valign="top">
 
@@ -999,7 +1081,7 @@
 					<td>
 					<div>
 					<b>
-					<a href="#member_{$linkname}">
+					<a href="#{$linkid}">
 							<xsl:value-of select="$TypeName"/>
 					</a>
 					</b>
@@ -1030,7 +1112,7 @@
 
 					<!-- link to member page -->
 					<b>
-					<a href="#member_{$linkname}">
+					<a href="#{$linkid}">
 						<xsl:value-of select="@MemberName"/>
 					</a>
 					</b>
@@ -1101,7 +1183,7 @@
 
 					<!-- link to method page -->
 					<b>
-					<a href="#member_{$linkname}">
+					<a href="#{$linkid}">
 						<xsl:value-of select="@MemberName"/>
 					</a>
 					</b>
@@ -1142,7 +1224,7 @@
 					<xsl:choose>
 					<xsl:when test="@MemberName='op_Implicit' or @MemberName='op_Explicit'">
 						<b>
-						<a href="#member_{$linkname}">
+						<a href="#{$linkid}">
 							Conversion
 							<xsl:choose>
 							<xsl:when test="ReturnValue/ReturnType = //Type/@FullName">
@@ -1166,14 +1248,14 @@
 					</xsl:when>
 					<xsl:when test="count(Parameters/Parameter)=1">
 						<b>
-						<a href="#member_{$linkname}">
+						<a href="#{$linkid}">
 							<xsl:value-of select="substring-after(@MemberName, 'op_')"/>
 						</a>
 						</b>
 					</xsl:when>
 					<xsl:otherwise>
 						<b>
-						<a href="#member_{$linkname}">
+						<a href="#{$linkid}">
 							<xsl:value-of select="substring-after(@MemberName, 'op_')"/>
 						</a>
 						</b>
@@ -1202,7 +1284,7 @@
 				
 				<xsl:otherwise>
 					<!-- Other types: just provide a link -->
-					<a href="#member_{$linkname}">
+					<a href="#{$linkid}">
 						<xsl:value-of select="@MemberName"/>
 					</a>
 				</xsl:otherwise>
@@ -1219,7 +1301,24 @@
 		</xsl:if>
 	</xsl:template>
 
+	<xsl:template name="GetLinkName">
+		<xsl:param name="type"/>
+		<xsl:param name="member"
+		/><xsl:value-of select="concat($type/@FullName, '.', $member/@MemberName)" 
+	/></xsl:template>
 
+	<xsl:template name="GetLinkId">
+		<xsl:param name="type"/>
+		<xsl:param name="member"
+	/><xsl:value-of select="concat($type/@FullName, '.', $member/@MemberName)"
+	/><xsl:if test="count($member/Parameters/Parameter) &gt; 0"
+	><xsl:value-of select="'('"
+	/><xsl:for-each select="Parameters/Parameter"
+	><xsl:if test="not(position()=1)">, </xsl:if
+	><xsl:value-of select="@Type"
+	/></xsl:for-each><xsl:value-of select="')'" 
+	/></xsl:if></xsl:template>
+
 	<xsl:template name="getmodifiers">
 		<xsl:param name="sig"/>
 		<xsl:param name="protection" select="true()"/>