Skip to content

Commit

Permalink
feat: Allow MdHeading.Anchor to be set
Browse files Browse the repository at this point in the history
Make MdHeading.Anchor property settable. By default, property is initialized with auto-generated value (thus keeping previous behavior). In combination with the option to include anchor tags in the output, this allows customizing the names of anchors for linking within a page.

If MdHeading.Anchor is set to a null or whitespace value, no anchor tag is emittted, even if MdHeadingAnchorStyle is set to 'Tag'

Closes #17 
Pull-Request: #19
  • Loading branch information
ap0llo committed Feb 11, 2020
2 parents 8c3c80a + 6632228 commit c794d1e
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ If a heading is serialized as ATX heading (lines prefixed with '\#') or as setex

| Name | Description |
| ------------------------------ | ------------------------------------------------------------- |
| [Anchor](properties/Anchor.md) | Gets the HTML anchor for this heading. |
| [Anchor](properties/Anchor.md) | Gets or sets the HTML anchor for this heading. |
| [Level](properties/Level.md) | Gets the level of the heading, 1 being the top\-most heading. |
| [Text](properties/Text.md) | The text of the heading |

Expand All @@ -41,6 +41,7 @@ If a heading is serialized as ATX heading (lines prefixed with '\#') or as setex
## See Also

- [MdHeadingStyle](../MdHeadingStyle/index.md)
- [MdHeadingAnchorStyle](../MdHeadingAnchorStyle/index.md)

___

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

**Declaring Type:** [MdHeading](../index.md)

Gets the HTML anchor for this heading.
Gets or sets the HTML anchor for this heading.

```csharp
public string Anchor { get; }
public string Anchor { get; set; }
```

## Property Value
Expand All @@ -14,12 +14,14 @@ string

## Remarks

The HTML anchor can be used for linking to a heading within a page. It is automatically derived from the heading text by removing all punctuation, replacing spaces with dashes and converting the text to lower case.
The HTML anchor can be used for linking to a heading within a page.

Note: Text anchors are not part of the CommonMark spec so linking to this anchor might not work in every markdown implementation.
Property is initialized with an auto\-generated value derived from the heading text by removing all punctuation, replacing spaces with dashes and converting the text to lower case.

The anchor does not include the leading '\#' required for links.

Note: Text anchors are not part of the CommonMark spec so linking to this anchor might not work in every markdown implementation. To explicitly include an anchor tag in the output, set [HeadingAnchorStyle](../../MdSerializationOptions/properties/HeadingAnchorStyle.md) to Tag.

___

*Documentation generated by [MdDocs](https://github.com/ap0llo/mddocs)*
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ namespace Grynwald.MarkdownGenerator
public MdHeading(Grynwald.MarkdownGenerator.MdSpan text, int level) { }
public MdHeading(int level, Grynwald.MarkdownGenerator.MdSpan text) { }
public MdHeading(int level, params Grynwald.MarkdownGenerator.MdSpan[] text) { }
public string Anchor { get; }
public string Anchor { get; set; }
public int Level { get; }
public Grynwald.MarkdownGenerator.MdSingleLineSpan Text { get; }
public override bool DeepEquals(Grynwald.MarkdownGenerator.MdBlock other) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,29 @@ public void Serializer_respects_HeadingAnchor_serialization_option_03()
}


[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
[InlineData("\t")]
public void Serializer_does_not_include_anchor_in_output_is_anchor_is_null_or_whitespace(string anchor)
{
var options = new MdSerializationOptions()
{
HeadingStyle = MdHeadingStyle.Atx,
HeadingAnchorStyle = MdHeadingAnchorStyle.Tag
};

var heading = new MdHeading(2, "Heading");
heading.Anchor = anchor;

AssertToStringEquals(
"## Heading\r\n",
new MdDocument(heading),
options
);
}

[Theory]
[InlineData(MdCodeBlockStyle.Tilde, '~')]
[InlineData(MdCodeBlockStyle.Backtick, '`')]
Expand Down
22 changes: 16 additions & 6 deletions src/MarkdownGenerator.Test/_Model/_Blocks/MdHeadingTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,13 @@ public void MdHeading_can_be_initialized_with_span_content()
[InlineData("Heading", "heading")]
[InlineData("My Heading", "my-heading")]
[InlineData("My Heading with a [link]()", "my-heading-with-a-link")]
public void Anchor_returns_expected_value(string headingText, string expectedAnchor)
public void Anchor_is_initlaized_with_auto_generated_value(string headingText, string expectedAnchor)
{
// ARRANGE
var heading = new MdHeading(1, new MdRawMarkdownSpan(headingText));

// ACT
var actualAnchor = heading.Anchor;

// ASSERT
Assert.Equal(expectedAnchor, actualAnchor);
// ACT / ASSERT
Assert.Equal(expectedAnchor, heading.Anchor);
}


Expand All @@ -90,5 +87,18 @@ public void DeepEquals_returns_expected_value(
Assert.False(instance1.DeepEquals(instance3));
Assert.False(instance1.DeepEquals(new MdParagraph()));
}

[Fact]
public void Anchor_can_be_set()
{
// ARRANGE
var heading = new MdHeading(1, "My Heading");

// ACT
heading.Anchor = "custom-anchor";

// ASSERT
Assert.Equal("custom-anchor", heading.Anchor);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public void Visit(MdHeading block)
m_Writer.RequestBlankLine();

string anchor = "";
if (m_Options.HeadingAnchorStyle == MdHeadingAnchorStyle.Tag)
if (m_Options.HeadingAnchorStyle == MdHeadingAnchorStyle.Tag && !String.IsNullOrWhiteSpace(block.Anchor))
{
anchor = $"<a id=\"{block.Anchor}\"></a>";
}
Expand Down
34 changes: 14 additions & 20 deletions src/MarkdownGenerator/_Model/_Blocks/MdHeading.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ namespace Grynwald.MarkdownGenerator
/// See <see cref="MdSerializationOptions"/> for details.
/// </remarks>
/// <seealso cref="MdHeadingStyle"/>
/// <seealso cref="MdHeadingAnchorStyle"/>
public sealed class MdHeading : MdLeafBlock
{
private string? m_Anchor;

/// <summary>
/// The text of the heading
/// </summary>
Expand All @@ -34,33 +33,26 @@ public sealed class MdHeading : MdLeafBlock


/// <summary>
/// Gets the HTML anchor for this heading.
/// Gets or sets the HTML anchor for this heading.
/// </summary>
/// <remarks>
/// The HTML anchor can be used for linking to a heading within a page.
/// It is automatically derived from the heading text by removing all
/// punctuation, replacing spaces with dashes and converting the text to lower case.
/// <para>
/// Note: Text anchors are not part of the CommonMark spec so linking to this anchor
/// might not work in every markdown implementation.
/// Property is initialized with an auto-generated value derived from the heading text by removing all
/// punctuation, replacing spaces with dashes and converting the text to lower case.
/// </para>
/// <para>
/// The anchor does not include the leading '#' required for links.
/// </para>
/// <para>
/// Note: Text anchors are not part of the CommonMark spec so linking to this anchor
/// might not work in every markdown implementation.
/// To explicitly include an anchor tag in the output, set <see cref="MdSerializationOptions.HeadingAnchorStyle"/> to <see cref="MdHeadingAnchorStyle.Tag"/>.
/// </para>
/// </remarks>
public string Anchor
{
get
{
if (m_Anchor == null)
{
m_Anchor = GetAnchor();
}
return m_Anchor;
}
}

public string? Anchor { get; set; }


/// <summary>
/// Initializes a new instance of <see cref="MdHeading"/>
/// </summary>
Expand All @@ -84,6 +76,8 @@ public MdHeading(MdSpan text, int level)
}

Level = level;

Anchor = GetAutoGeneratedAnchor();
}

/// <summary>
Expand Down Expand Up @@ -123,7 +117,7 @@ private bool DeepEquals(MdHeading? other)
Text.DeepEquals(other.Text);
}

private string GetAnchor()
private string GetAutoGeneratedAnchor()
{
// There is no official spec for how anchors for headings work
// This implementation follows the guidance here
Expand Down

0 comments on commit c794d1e

Please sign in to comment.