Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ var converter = new ReverseMarkdown.Converter(config);
<sup><a href='/src/ReverseMarkdown.Test/Snippets.cs#L28-L44' title='Snippet source file'>snippet source</a> | <a href='#snippet-UsageWithConfig' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

If you need to preserve markdown-like text as literal content (for example `# Heading` or `- Item`), either enable `EscapeMarkdownLineStarts` or use `CommonMark`:

```cs
var config = new ReverseMarkdown.Config
{
EscapeMarkdownLineStarts = true
// or CommonMark = true
};

var converter = new ReverseMarkdown.Converter(config);
```

## Configuration options

* `DefaultCodeBlockLanguage` - Option to set the default code block language for Github style markdown if class based language markers are not available
Expand All @@ -94,6 +106,9 @@ var converter = new ReverseMarkdown.Converter(config);
* `CommonMarkUseHtmlInlineTags` - When CommonMark is enabled, emit HTML for inline tags (`em`, `strong`, `a`, `img`) to avoid delimiter edge cases. Default is true
* `CommonMarkIntrawordEmphasisSpacing` - When CommonMark is enabled, insert spaces to avoid intraword emphasis. Default is false
* Note: CommonMark is best used on its own. Combining `CommonMark` with `GithubFlavored` can produce mixed output; keep them separate unless you explicitly want that behavior.
* `EscapeMarkdownLineStarts` - Escape markdown line starts (headings, lists, block markers) in plain text output. Default is false
* Note: If you need to preserve markdown-like text as literal content, enable `EscapeMarkdownLineStarts` or use `CommonMark`.
* `OutputLineEnding` - Output line endings used in generated markdown. Default is `Environment.NewLine`
* `CleanupUnnecessarySpaces` - Cleanup unnecessary spaces in the output. Default is true
* `SuppressDivNewlines` - Removes prefixed newlines from `div` tags. Default is false
* `ListBulletChar` - Allows you to change the bullet character. Default value is `-`. Some systems expect the bullet character to be `*` rather than `-`, this config allows you to change it. Note: This option is ignored when `SlackFlavored` is enabled
Expand Down
16 changes: 16 additions & 0 deletions src/ReverseMarkdown.Test/ConverterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,22 @@ public void WhenEscapeMarkdownLineStartsEnabled_ThenEscapeHeadingAndListMarkers(
Assert.Equal(@"1\. Point 1", converter.Convert("<p>1. Point 1</p>"));
}

[Fact]
public void WhenOutputLineEndingConfigured_ThenNormalizeOutputLineEndings()
{
var html = "<p>one</p>\r\n<p>two</p>\r<p>three</p>\n<p>four</p>";
var config = new Config
{
OutputLineEnding = "\n"
};
var converter = new Converter(config);

var result = converter.Convert(html);

Assert.Equal(result, result.ReplaceLineEndings("\n"));
Assert.DoesNotContain("\r", result);
}




Expand Down
6 changes: 6 additions & 0 deletions src/ReverseMarkdown/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ public class Config
/// </summary>
public bool EscapeMarkdownLineStarts { get; set; } = false;

/// <summary>
/// Output line endings to use for the generated markdown.
/// Defaults to <see cref="Environment.NewLine" />.
/// </summary>
public string OutputLineEnding { get; set; } = Environment.NewLine;

public bool SuppressDivNewlines { get; set; } = false;

public bool RemoveComments { get; set; } = false;
Expand Down
22 changes: 16 additions & 6 deletions src/ReverseMarkdown/Converter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,24 +96,26 @@ public virtual string Convert(string html)
{
using var _ = EnsureContext();

html = html.ReplaceLineEndings("\n");

if (Config.CommonMark && LooksLikeCommonMarkHtmlBlock(html)) {
return html;
return ApplyOutputLineEndings(html);
}

if (Config.CommonMark) {
var trimmed = html.TrimStart('\uFEFF', ' ', '\t', '\r', '\n');
if (trimmed.StartsWith("</", StringComparison.Ordinal) ||
html.Contains("<!--", StringComparison.Ordinal) ||
html.Contains("<![CDATA[", StringComparison.Ordinal)) {
return html;
return ApplyOutputLineEndings(html);
}

var paragraphTrimmed = html.Trim();
if (paragraphTrimmed.StartsWith("<p>", StringComparison.OrdinalIgnoreCase) &&
paragraphTrimmed.EndsWith("</p>", StringComparison.OrdinalIgnoreCase)) {
var inner = paragraphTrimmed.Substring(3, paragraphTrimmed.Length - 7);
if (inner.TrimStart().StartsWith("</", StringComparison.Ordinal)) {
return inner;
return ApplyOutputLineEndings(inner);
}
}
}
Expand Down Expand Up @@ -146,16 +148,24 @@ public virtual string Convert(string html)
}

if (!Config.CleanupUnnecessarySpaces) {
return result;
return ApplyOutputLineEndings(result);
}

if (Config.CommonMark) {
result = result.TrimEnd();
result = result.TrimStart('\r', '\n');
return result;
return ApplyOutputLineEndings(result);
}

return result.Trim().FixMultipleNewlines();
return ApplyOutputLineEndings(result.Trim().FixMultipleNewlines());
}

private string ApplyOutputLineEndings(string content)
{
var lineEnding = string.IsNullOrEmpty(Config.OutputLineEnding)
? Environment.NewLine
: Config.OutputLineEnding;
return content.ReplaceLineEndings(lineEnding);
}

private static bool LooksLikeCommonMarkHtmlBlock(string html)
Expand Down