Skip to content

Commit

Permalink
feat(apireference): Support external links in <see /> element
Browse files Browse the repository at this point in the history
Adds support for linking to e.g. websites from using the <see /> inline
text element by specifying the target using a "href" attribute instead
of "cref".
The value of the "href" attribute must be a valid uri, otherwise the
element is ignored.
When a <see /> element has both "cref" and "href" attributes the
"cref" attribute takes precendence and the "href" attribute is ignored.
  • Loading branch information
ap0llo committed Sep 29, 2019
1 parent f386601 commit e4f9b9d
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 33 deletions.
6 changes: 3 additions & 3 deletions docs/demoprojects/api/DemoProject/DemoClass/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ All of a types constructors will be listed in a table in the "Constructors" sect

Similar tables are generated for a type's public fields, events, properties, indexers, methods and operator overloads

Links to other members are supported (using the xml tag `see`), for example a link to [IDemoInterface](../IDemoInterface/index.md). References to types outside the assembly are written to the output but cannot be linked to, e.g. a reference to string
Links to other members are supported (using the xml tag `see`), for example a link to [IDemoInterface](../IDemoInterface/index.md). References to types outside the assembly are written to the output but cannot be linked to, e.g. a reference to stringTo specify the link text, insert content inside the `see` element, for example `<see cref="IDemoInterface">Custom text</see>` which is rendered like this: [Custom text](../IDemoInterface/index.md)

For the `see` tag alternative text is supported as well. To specify the link text, insert content inside the `see` element, for example `<see cref="IDemoInterface">Custom text</see>` which is rendered like this: [Custom text](../IDemoInterface/index.md)
Additionally, links to websites can be added using the `href` attribute, for example,`<see href="http://example.com" />` is rendered as [http:\/\/example.com\/](http://example.com/) and`<see href="http://example.com">Link text</see>` is rendered as [Link text](http://example.com/). Note: `href` is supported by mddocs but is not part of the official specification and thus not supported by Visual Studio. When both `cref` and `href` attributes are specified, the `href` attribute is ignored.
The last section is the "See Also" section with links provided in the xml documentation using the `seealso` tag. Using the `cref` attribute, links to other members can be added. Additionally, links to websites can be added using the `href` attribute. Note: `href` is supported by mddocs but not part of the official specification and thus not supported by Visual Studio. When both `cref` and `href` attributes are specified, the `href` attribute is ignored.
The last section is the "See Also" section with links provided in the xml documentation using the `seealso` tag. Using the `cref` attribute, links to other members can be added. Additionally, links to websites can be added using the `href` attribute. Note: `href` is supported by mddocs is but not part of the official specification and thus not supported by Visual Studio. When both `cref` and `href` attributes are specified, the `href` attribute is ignored.

Similar pages are also generated for interfaces (see [IDemoInterface](../IDemoInterface/index.md)), structs (see [DemoStruct](../DemoStruct/index.md)) and enums (see [DemoEnum](../DemoEnum/index.md))Documentation can also contains list and tables. See [ListExample](../ListExample/index.md) for a showcase of the different supported list formats.

Expand Down
14 changes: 10 additions & 4 deletions src/MdDocs.ApiReference.DemoProject/DemoClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,22 @@ namespace DemoProject
/// Links to other members are supported (using the xml tag <c>see</c>), for example a link to
/// <see cref="IDemoInterface" />. References to types outside the assembly are written to the output
/// but cannot be linked to, e.g. a reference to <see cref="String"/>
/// To specify the link text, insert content inside the <c>see</c> element,
/// for example <c><![CDATA[<see cref="IDemoInterface">Custom text</see>]]></c> which is rendered
/// like this: <see cref="IDemoInterface">Custom text</see>
/// </para>
/// <para>
/// For the <c>see</c> tag alternative text is supported as well. To specify the link text, insert content inside
/// the <c>see</c> element, for example <c><![CDATA[<see cref="IDemoInterface">Custom text</see>]]></c> which is rendered
/// like this: <see cref="IDemoInterface">Custom text</see>
/// Additionally, links to websites can be added using the <c>href</c> attribute, for example,
/// <c><![CDATA[<see href="http://example.com" />]]></c> is rendered as <see href="http://example.com" /> and
/// <c><![CDATA[<see href="http://example.com">Link text</see>]]></c> is rendered as <see href="http://example.com">Link text</see>.
/// Note: <c>href</c> is supported by mddocs but is not part of the official specification
/// and thus not supported by Visual Studio. When both <c>cref</c> and <c>href</c> attributes are specified,
/// the <c>href</c> attribute is ignored.
/// </para>
/// <para>
/// The last section is the "See Also" section with links provided in the xml documentation using the <c>seealso</c> tag.
/// Using the <c>cref</c> attribute, links to other members can be added. Additionally, links to websites can be added
/// using the <c>href</c> attribute. Note: <c>href</c> is supported by mddocs but not part of the official specification
/// using the <c>href</c> attribute. Note: <c>href</c> is supported by mddocs is but not part of the official specification
/// and thus not supported by Visual Studio. When both <c>cref</c> and <c>href</c> attributes are specified,
/// the <c>href</c> attribute is ignored.
/// </para>
Expand Down
14 changes: 10 additions & 4 deletions src/MdDocs.ApiReference.DemoProject/DemoProject.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

79 changes: 78 additions & 1 deletion src/MdDocs.ApiReference.Model.Test/XmlDocs/XmlDocsReaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,6 @@ public void ReadTextBlock_returns_the_expected_elements_06()
);
}


private void ReadTextBlock_returns_the_expected_elements(string xml, params Element[] expectedElements)
{
// ARRANGE
Expand All @@ -245,6 +244,84 @@ private void ReadTextBlock_returns_the_expected_elements(string xml, params Elem
}


[Fact]
public void ReadTextBlock_correctly_reads_see_elements_01()
{
var xml = @"<see cref=""T:SomeNamespace.SomeClass"" />";
var expected = new SeeElement(MemberId.Parse("T:SomeNamespace.SomeClass"));

ReadTextBlock_correctly_reads_see_elements(xml, expected);
}

[Fact]
public void ReadTextBlock_correctly_reads_see_elements_02()
{
var xml = @"<see cref=""T:SomeNamespace.SomeClass"">Lorem ipsum dolor sit amet.</see>";
var expected = new SeeElement(
MemberId.Parse("T:SomeNamespace.SomeClass"),
new TextBlock(new[] { new TextElement("Lorem ipsum dolor sit amet.") } ));

ReadTextBlock_correctly_reads_see_elements(xml, expected);
}

[Fact]
public void ReadTextBlock_correctly_reads_see_elements_03()
{
var xml = @"<see href=""http://example.com"" />";
var expected = new SeeElement(new Uri("http://example.com"));

ReadTextBlock_correctly_reads_see_elements(xml, expected);
}

[Fact]
public void ReadTextBlock_correctly_reads_see_elements_04()
{
var xml = @"<see href=""http://example.com"">Lorem ipsum dolor sit amet.</see>";
var expected = new SeeElement(
new Uri("http://example.com"),
new TextBlock(new[] { new TextElement("Lorem ipsum dolor sit amet.") }));

ReadTextBlock_correctly_reads_see_elements(xml, expected);
}

[Fact]
public void ReadTextBlock_correctly_reads_see_elements_05()
{
var xml = @"<see href=""http://example.com"" cref=""T:SomeNamespace.SomeClass"" />";
var expected = new SeeElement(MemberId.Parse("T:SomeNamespace.SomeClass"));

ReadTextBlock_correctly_reads_see_elements(xml, expected);
}

[Fact]
public void ReadTextBlock_correctly_reads_see_elements_06()
{
var xml = @"<see href=""http://example.com"" cref=""T:SomeNamespace.SomeClass"">Lorem ipsum dolor sit amet.</see>";
var expected = new SeeElement(
MemberId.Parse("T:SomeNamespace.SomeClass"),
new TextBlock(new[] { new TextElement("Lorem ipsum dolor sit amet.") }));

ReadTextBlock_correctly_reads_see_elements(xml, expected);
}


private void ReadTextBlock_correctly_reads_see_elements(string xml, SeeElement expected)
{
// ARRANGE
xml = $@"<para>{xml}</para>";
var sut = new XmlDocsReader(NullLogger.Instance);

// ACT
var textBlock = sut.ReadTextBlock(XElement.Parse(xml));

// ASSERT

var element = Assert.Single(textBlock.Elements);
var seeElement = Assert.IsType<SeeElement>(element);
Assert.Equal(expected, seeElement);
}


[Fact]
public void ReadMemberContent_correctly_parses_seealso_elements_01()
{
Expand Down
12 changes: 6 additions & 6 deletions src/MdDocs.ApiReference.Model/XmlDocs/SeeAlsoElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public sealed class SeeAlsoElement : IEquatable<SeeAlsoElement>


/// <summary>
/// Initializes a new instance of <see cref="SeeAlsoElement"/>referencing a code element (using the <c>cref</c> attribute).
/// Initializes a new instance of <see cref="SeeAlsoElement"/> referencing a code element (using the <c>cref</c> attribute).
/// </summary>
/// <param name="memberId">The id of the member being referenced.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="memberId"/> is <c>null</c>.</exception>
Expand All @@ -68,19 +68,19 @@ public SeeAlsoElement(MemberId memberId, TextBlock text)


/// <summary>
/// Initializes a new instance of <see cref="SeeAlsoElement"/> referencing a external resource (using the <c>href</c> attribute).
/// Initializes a new instance of <see cref="SeeAlsoElement"/> referencing an external resource (using the <c>href</c> attribute).
/// </summary>
/// <param name="target">The uri of the external resource.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="memberId"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="target"/> is <c>null</c>.</exception>
public SeeAlsoElement(Uri target) : this(target, TextBlock.Empty)
{ }

/// <summary>
/// Initializes a new instance of <see cref="SeeAlsoElement"/> referencing a external resource (using the <c>href</c> attribute).
/// Initializes a new instance of <see cref="SeeAlsoElement"/> referencing an external resource (using the <c>href</c> attribute).
/// </summary>
/// <param name="target">The url of the external resource.</param>
/// <param name="target">The uri of the external resource.</param>
/// <param name="text">The content of the <c>seealso</c> element.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="memberId"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="target"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="text"/> is <c>null</c>.</exception>
public SeeAlsoElement(Uri target, TextBlock text)
{
Expand Down
26 changes: 22 additions & 4 deletions src/MdDocs.ApiReference.Model/XmlDocs/XmlDocsReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ internal void ReadMemberContent(XElement xml, MemberElement member)
// <seealso /> allows adding links to the documentation
//
// - using <seealso cref="..." /> a link to other assembly members
// can be inserted (supported by Visual Studio=
// can be inserted (supported by Visual Studio)
// - using <seealso href="..." /> a link to an external resource,
// typically a website can be specified (unofficial extension, not supported by VS)
//
Expand Down Expand Up @@ -276,10 +276,28 @@ internal TextBlock ReadTextBlock(XElement xml)
break;

case "see":
if (elementNode.TryGetAttributeValue("cref", out var cref) &&
TryParseMemberId(cref, out var memberId))
// <see /> allows adding links to the documentation
//
// - using <see cref="..." /> a link to other assembly members
// can be inserted (supported by Visual Studio)
// - using <see href="..." /> a link to an external resource,
// typically a website can be specified (unofficial extension, not supported by VS)
//
// If both cref and href attributes are present, href is ignored
//
if (elementNode.TryGetAttributeValue("cref", out var cref))
{
element = new SeeElement(memberId, ReadTextBlock(elementNode));
if(TryParseMemberId(cref, out var memberId))
{
element = new SeeElement(memberId, ReadTextBlock(elementNode));
}
}
else if(elementNode.TryGetAttributeValue("href", out var href))
{
if(Uri.TryCreate(href, UriKind.Absolute, out var target))
{
element = new SeeElement(target, ReadTextBlock(elementNode));
}
}
break;

Expand Down
Loading

0 comments on commit e4f9b9d

Please sign in to comment.