Skip to content

Commit 81d53ea

Browse files
committed
Add support for Slack-flavored markdown
1 parent 3c2af15 commit 81d53ea

File tree

14 files changed

+96
-9
lines changed

14 files changed

+96
-9
lines changed

src/ReverseMarkdown/Cleaner.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ namespace ReverseMarkdown
66
{
77
public static class Cleaner
88
{
9+
private static readonly Regex SlackBoldCleaner = new Regex(@"\*(\s\*)+");
10+
private static readonly Regex SlackItalicCleaner = new Regex(@"_(\s_)+");
11+
912
private static string CleanTagBorders(string content)
1013
{
1114
// content from some htl editors such as CKEditor emits newline and tab between tags, clean that up
@@ -28,5 +31,15 @@ public static string PreTidy(string content, bool removeComments)
2831

2932
return content;
3033
}
34+
35+
public static string SlackTidy(string content)
36+
{
37+
// Slack's escaping rules depend on whether the key characters appear in
38+
// next to word characters or not.
39+
content = SlackBoldCleaner.Replace(content, "*");
40+
content = SlackItalicCleaner.Replace(content, "_");
41+
42+
return content;
43+
}
3144
}
3245
}

src/ReverseMarkdown/Config.cs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ public class Config
88
public UnknownTagsOption UnknownTags { get; set; } = UnknownTagsOption.PassThrough;
99

1010
public bool GithubFlavored { get; set; } = false;
11-
11+
12+
public bool SlackFlavored { get; set; } = false;
13+
1214
public bool SuppressDivNewlines { get; set; } = false;
1315

1416
public bool RemoveComments { get; set; } = false;
@@ -31,10 +33,19 @@ public class Config
3133
public TableWithoutHeaderRowHandlingOption TableWithoutHeaderRowHandling { get; set; } =
3234
TableWithoutHeaderRowHandlingOption.Default;
3335

36+
private char _listBulletChar = '-';
37+
3438
/// <summary>
3539
/// Option to set a different bullet character for un-ordered lists
3640
/// </summary>
37-
public char ListBulletChar { get; set; } = '-';
41+
/// <remarks>
42+
/// This option is ignored when <see cref="SlackFlavored"/> is enabled.
43+
/// </remarks>
44+
public char ListBulletChar
45+
{
46+
get => SlackFlavored ? '•' : _listBulletChar;
47+
set => _listBulletChar = value;
48+
}
3849

3950
/// <summary>
4051
/// Option to set a default GFM code block language if class based language markers are not available
@@ -52,14 +63,17 @@ public enum UnknownTagsOption
5263
/// Include the unknown tag completely into the result. That is, the tag along with the text will be left in output.
5364
/// </summary>
5465
PassThrough,
66+
5567
/// <summary>
5668
/// Drop the unknown tag and its content
5769
/// </summary>
5870
Drop,
71+
5972
/// <summary>
6073
/// Ignore the unknown tag but try to convert its content
6174
/// </summary>
6275
Bypass,
76+
6377
/// <summary>
6478
/// Raise an error to let you know
6579
/// </summary>
@@ -72,6 +86,7 @@ public enum TableWithoutHeaderRowHandlingOption
7286
/// By default, first row will be used as header row
7387
/// </summary>
7488
Default,
89+
7590
/// <summary>
7691
/// An empty row will be added as the header row
7792
/// </summary>
@@ -90,10 +105,12 @@ public enum TableWithoutHeaderRowHandlingOption
90105
/// Determines whether url is allowed: WhitelistUriSchemes contains no elements or contains passed url.
91106
/// </summary>
92107
/// <param name="scheme">Scheme name without trailing colon</param>
93-
internal bool IsSchemeWhitelisted(string scheme) {
108+
internal bool IsSchemeWhitelisted(string scheme)
109+
{
94110
if (scheme == null) throw new ArgumentNullException(nameof(scheme));
95-
var isSchemeAllowed = WhitelistUriSchemes == null || WhitelistUriSchemes.Length == 0 || WhitelistUriSchemes.Contains(scheme, StringComparer.OrdinalIgnoreCase);
111+
var isSchemeAllowed = WhitelistUriSchemes == null || WhitelistUriSchemes.Length == 0 ||
112+
WhitelistUriSchemes.Contains(scheme, StringComparer.OrdinalIgnoreCase);
96113
return isSchemeAllowed;
97114
}
98115
}
99-
}
116+
}

src/ReverseMarkdown/Converter.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ public virtual string Convert(string html)
9494
// cleanup multiple new lines
9595
result = Regex.Replace( result, @"(^\p{Zs}*(\r\n|\n)){2,}", Environment.NewLine, RegexOptions.Multiline);
9696

97+
if (Config.SlackFlavored)
98+
{
99+
result = Cleaner.SlackTidy(result);
100+
}
101+
97102
return Config.CleanupUnnecessarySpaces ? result.Trim().FixMultipleNewlines() : result;
98103
}
99104

src/ReverseMarkdown/Converters/Em.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ public override string Convert(HtmlNode node)
3030
? " "
3131
: "";
3232

33-
return content.EmphasizeContentWhitespaceGuard("*", spaceSuffix);
33+
var emphasis = Converter.Config.SlackFlavored ? "_" : "*";
34+
return content.EmphasizeContentWhitespaceGuard(emphasis, spaceSuffix);
3435
}
3536

3637
private static bool AlreadyItalic(HtmlNode node)

src/ReverseMarkdown/Converters/Hr.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ public Hr(Converter converter) : base(converter)
1212

1313
public override string Convert(HtmlNode node)
1414
{
15+
if (Converter.Config.SlackFlavored)
16+
{
17+
throw new SlackUnsupportedTagException(node.Name);
18+
}
19+
1520
return $"{Environment.NewLine}* * *{Environment.NewLine}";
1621
}
1722
}

src/ReverseMarkdown/Converters/Img.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ public Img(Converter converter) : base(converter)
1111

1212
public override string Convert(HtmlNode node)
1313
{
14+
if (Converter.Config.SlackFlavored)
15+
{
16+
throw new SlackUnsupportedTagException(node.Name);
17+
}
18+
1419
var alt = node.GetAttributeValue("alt", string.Empty);
1520
var src = node.GetAttributeValue("src", string.Empty);
1621

src/ReverseMarkdown/Converters/S.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ public override string Convert(HtmlNode node)
2020
return content;
2121
}
2222

23-
return content.EmphasizeContentWhitespaceGuard("~~");
23+
var emphasis = Converter.Config.SlackFlavored ? "~" : "~~";
24+
return content.EmphasizeContentWhitespaceGuard(emphasis);
2425
}
2526

2627
private static bool AlreadyStrikethrough(HtmlNode node)

src/ReverseMarkdown/Converters/Strong.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ public override string Convert(HtmlNode node)
2727
? " "
2828
: "";
2929

30-
return content.EmphasizeContentWhitespaceGuard("**", spaceSuffix);
30+
var emphasis = Converter.Config.SlackFlavored ? "*" : "**";
31+
return content.EmphasizeContentWhitespaceGuard(emphasis, spaceSuffix);
3132
}
3233

3334
private static bool AlreadyBold(HtmlNode node)

src/ReverseMarkdown/Converters/Sup.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ public Sup(Converter converter) : base(converter)
1212

1313
public override string Convert(HtmlNode node)
1414
{
15+
if (Converter.Config.SlackFlavored)
16+
{
17+
throw new SlackUnsupportedTagException(node.Name);
18+
}
19+
1520
var content = TreatChildren(node);
1621
if (string.IsNullOrEmpty(content) || AlreadySup(node))
1722
{

src/ReverseMarkdown/Converters/Table.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ public Table(Converter converter) : base(converter)
1414

1515
public override string Convert(HtmlNode node)
1616
{
17+
if (Converter.Config.SlackFlavored)
18+
{
19+
throw new SlackUnsupportedTagException(node.Name);
20+
}
21+
1722
// if table does not have a header row , add empty header row if set in config
1823
var useEmptyRowForHeader = this.Converter.Config.TableWithoutHeaderRowHandling ==
1924
Config.TableWithoutHeaderRowHandlingOption.EmptyRow;

0 commit comments

Comments
 (0)