diff --git a/src/Markdig/Extensions/Footnotes/FootnoteParser.cs b/src/Markdig/Extensions/Footnotes/FootnoteParser.cs index f66fcf397..d6c701ec1 100644 --- a/src/Markdig/Extensions/Footnotes/FootnoteParser.cs +++ b/src/Markdig/Extensions/Footnotes/FootnoteParser.cs @@ -2,6 +2,8 @@ // This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. +using System; + using Markdig.Helpers; using Markdig.Parsers; using Markdig.Syntax; @@ -41,7 +43,7 @@ private BlockState TryOpen(BlockProcessor processor, bool isContinue) var saved = processor.Column; int start = processor.Start; - if (!LinkHelper.TryParseLabel(ref processor.Line, false, out string? label, out SourceSpan labelSpan) || !label.StartsWith("^") || processor.CurrentChar != ':') + if (!LinkHelper.TryParseLabel(ref processor.Line, false, out string? label, out SourceSpan labelSpan) || !label.StartsWith("^", StringComparison.Ordinal) || processor.CurrentChar != ':') { processor.GoToColumn(saved); return BlockState.None; diff --git a/src/Markdig/Extensions/MediaLinks/HostProviderBuilder.cs b/src/Markdig/Extensions/MediaLinks/HostProviderBuilder.cs index 9b8516359..f0bf9f4ef 100644 --- a/src/Markdig/Extensions/MediaLinks/HostProviderBuilder.cs +++ b/src/Markdig/Extensions/MediaLinks/HostProviderBuilder.cs @@ -92,8 +92,8 @@ private static string[] SplitQuery(Uri uri) } var queryParams = SplitQuery(uri); return BuildYouTubeIframeUrl( - queryParams.FirstOrDefault(p => p.StartsWith("v="))?.Substring(2), - queryParams.FirstOrDefault(p => p.StartsWith("t="))?.Substring(2) + queryParams.FirstOrDefault(p => p.StartsWith("v=", StringComparison.Ordinal))?.Substring(2), + queryParams.FirstOrDefault(p => p.StartsWith("t=", StringComparison.Ordinal))?.Substring(2) ); } @@ -101,7 +101,7 @@ private static string[] SplitQuery(Uri uri) { return BuildYouTubeIframeUrl( uri.AbsolutePath.Substring(1), - SplitQuery(uri).FirstOrDefault(p => p.StartsWith("t="))?.Substring(2) + SplitQuery(uri).FirstOrDefault(p => p.StartsWith("t=", StringComparison.Ordinal))?.Substring(2) ); } @@ -135,7 +135,7 @@ private static string[] SplitQuery(Uri uri) string? trackKeyword = items.Skip(2).FirstOrDefault(); string? trackId = items.Skip(3).FirstOrDefault(); - if (albumKeyword != "album" || albumId is null || trackKeyword != "track" || trackId is null) + if (albumKeyword is not "album" || albumId is null || trackKeyword is not "track" || trackId is null) { return null; } diff --git a/src/Markdig/Extensions/MediaLinks/MediaLinkExtension.cs b/src/Markdig/Extensions/MediaLinks/MediaLinkExtension.cs index 4319e91aa..3fef8173e 100644 --- a/src/Markdig/Extensions/MediaLinks/MediaLinkExtension.cs +++ b/src/Markdig/Extensions/MediaLinks/MediaLinkExtension.cs @@ -57,7 +57,7 @@ private bool TryLinkInlineRenderer(HtmlRenderer renderer, LinkInline linkInline) { // see https://tools.ietf.org/html/rfc3986#section-4.2 // since relative uri doesn't support many properties, "http" is used as a placeholder here. - if (linkInline.Url.StartsWith("//") && Uri.TryCreate("http:" + linkInline.Url, UriKind.Absolute, out uri)) + if (linkInline.Url.StartsWith("//", StringComparison.Ordinal) && Uri.TryCreate("http:" + linkInline.Url, UriKind.Absolute, out uri)) { isSchemaRelative = true; } @@ -101,7 +101,7 @@ private bool TryGuessAudioVideoFile(Uri uri, bool isSchemaRelative, HtmlRenderer Options.ExtensionToMimeType.TryGetValue(path.Substring(lastDot), out string? mimeType)) { var htmlAttributes = GetHtmlAttributes(linkInline); - var isAudio = mimeType.StartsWith("audio"); + var isAudio = mimeType.StartsWith("audio", StringComparison.Ordinal); var tagType = isAudio ? "audio" : "video"; renderer.Write($"<{tagType}"); diff --git a/src/Markdig/Extensions/ReferralLinks/ReferralLinksExtension.cs b/src/Markdig/Extensions/ReferralLinks/ReferralLinksExtension.cs index b5bb6ae89..801b00da0 100644 --- a/src/Markdig/Extensions/ReferralLinks/ReferralLinksExtension.cs +++ b/src/Markdig/Extensions/ReferralLinks/ReferralLinksExtension.cs @@ -9,14 +9,14 @@ namespace Markdig.Extensions.ReferralLinks { - public class ReferralLinksExtension : IMarkdownExtension + public sealed class ReferralLinksExtension : IMarkdownExtension { public ReferralLinksExtension(string[] rels) { - Rels = rels?.ToList(); + Rels = rels.ToList(); } - public List? Rels { get; } + public List Rels { get; } public void Setup(MarkdownPipelineBuilder pipeline) { diff --git a/src/Markdig/Extensions/SelfPipeline/SelfPipelineExtension.cs b/src/Markdig/Extensions/SelfPipeline/SelfPipelineExtension.cs index 895e6bcbc..1596fc08a 100644 --- a/src/Markdig/Extensions/SelfPipeline/SelfPipelineExtension.cs +++ b/src/Markdig/Extensions/SelfPipeline/SelfPipelineExtension.cs @@ -86,7 +86,7 @@ public MarkdownPipeline CreatePipelineFromInput(string inputText) var endOfTag = inputText.IndexOf("-->", optionStart, StringComparison.OrdinalIgnoreCase); if (endOfTag >= 0) { - defaultConfig = inputText.Substring(optionStart, endOfTag - optionStart).Trim(); + defaultConfig = inputText.AsSpan(optionStart, endOfTag - optionStart).Trim().ToString(); } } diff --git a/src/Markdig/Helpers/CompactPrefixTree.cs b/src/Markdig/Helpers/CompactPrefixTree.cs index 03a933628..b9729ef4a 100644 --- a/src/Markdig/Helpers/CompactPrefixTree.cs +++ b/src/Markdig/Helpers/CompactPrefixTree.cs @@ -519,7 +519,7 @@ private bool TryInsert(in KeyValuePair pair, InsertionBehavior b Debug.Assert(key.Length != previousKey.Length); if (previousKey.Length < key.Length) // If the input was sorted, this should be hit { - Debug.Assert(key.StartsWith(previousKey)); + Debug.Assert(key.StartsWith(previousKey, StringComparison.Ordinal)); node.ChildChar = key[i]; node.MatchIndex = previousMatchIndex; EnsureTreeCapacity(TreeSize + 1); @@ -533,7 +533,7 @@ private bool TryInsert(in KeyValuePair pair, InsertionBehavior b else // if key.Length < previousKey.Length { Debug.Assert(key.Length < previousKey.Length); - Debug.Assert(previousKey.StartsWith(key)); + Debug.Assert(previousKey.StartsWith(key, StringComparison.Ordinal)); node.ChildChar = previousKey[i]; node.MatchIndex = Count; EnsureTreeCapacity(TreeSize + 1); @@ -583,7 +583,7 @@ private bool TryInsert(in KeyValuePair pair, InsertionBehavior b else { // This node has a child char, therefore we either don't have a match attached or that match is simply a prefix of the current key - Debug.Assert(node.MatchIndex == -1 || key.StartsWith(_matches[node.MatchIndex].Key)); + Debug.Assert(node.MatchIndex == -1 || key.StartsWith(_matches[node.MatchIndex].Key, StringComparison.Ordinal)); // Set this pair as the current node's first element in the Children list node.Children = _childrenIndex; @@ -641,7 +641,7 @@ private bool TryInsert(in KeyValuePair pair, InsertionBehavior b // It's not a duplicate but shares key.Length characters, therefore it's longer // This will never occur if the input was sorted Debug.Assert(previousMatch.Key.Length > key.Length); - Debug.Assert(previousMatch.Key.StartsWith(key)); + Debug.Assert(previousMatch.Key.StartsWith(key, StringComparison.Ordinal)); Debug.Assert(node.ChildChar == 0 && node.Children == -1); // It is a leaf node diff --git a/src/Markdig/Markdig.targets b/src/Markdig/Markdig.targets index dd5f3aacf..865a966f4 100644 --- a/src/Markdig/Markdig.targets +++ b/src/Markdig/Markdig.targets @@ -6,6 +6,9 @@ en-US 0.23.0 Alexandre Mutel + + + net452;netstandard2.0;netstandard2.1;netcoreapp2.1;netcoreapp3.1 Markdown CommonMark md html md2html https://github.com/lunet-io/markdig/blob/master/changelog.md diff --git a/src/Markdig/MarkdownExtensions.cs b/src/Markdig/MarkdownExtensions.cs index 03e2fb98e..bde524f16 100644 --- a/src/Markdig/MarkdownExtensions.cs +++ b/src/Markdig/MarkdownExtensions.cs @@ -452,6 +452,7 @@ public static MarkdownPipelineBuilder UseEmojiAndSmiley(this MarkdownPipelineBui } return pipeline; } + /// /// Add rel=nofollow to all links rendered to HTML. /// @@ -463,20 +464,23 @@ public static MarkdownPipelineBuilder UseNoFollowLinks(this MarkdownPipelineBuil return pipeline.UseReferralLinks("nofollow"); } - public static MarkdownPipelineBuilder UseReferralLinks(this MarkdownPipelineBuilder pipeline, params string[] rels) + public static MarkdownPipelineBuilder UseReferralLinks(this MarkdownPipelineBuilder pipeline, params string[]? rels) { - if (!pipeline.Extensions.Contains()) + rels ??= ArrayHelper.Empty(); + + var extension = pipeline.Extensions.Find(); + + if (extension is null) { pipeline.Extensions.Add(new ReferralLinksExtension(rels)); } else { - var referralLinksExtension = pipeline.Extensions.Find(); - foreach(string rel in rels) + foreach (string rel in rels) { - if (!referralLinksExtension.Rels.Contains(rel)) + if (!extension.Rels.Contains(rel)) { - referralLinksExtension.Rels.Add(rel); + extension.Rels.Add(rel); } } } diff --git a/src/Markdig/Parsers/FencedBlockParserBase.cs b/src/Markdig/Parsers/FencedBlockParserBase.cs index 8d72cface..f595d67e3 100644 --- a/src/Markdig/Parsers/FencedBlockParserBase.cs +++ b/src/Markdig/Parsers/FencedBlockParserBase.cs @@ -234,7 +234,7 @@ public static bool DefaultInfoParser(BlockProcessor state, ref StringSlice line, } } - argString = line.Text.Substring(firstSpace, line.End - firstSpace + 1).Trim(); + argString = line.Text.AsSpan(firstSpace, line.End - firstSpace + 1).Trim().ToString(); } else { diff --git a/src/Markdig/Parsers/ListBlockParser.cs b/src/Markdig/Parsers/ListBlockParser.cs index a4ec5d901..0906faae8 100644 --- a/src/Markdig/Parsers/ListBlockParser.cs +++ b/src/Markdig/Parsers/ListBlockParser.cs @@ -262,7 +262,7 @@ private BlockState TryParseListItem(BlockProcessor state, Block? block) if ((block ?? state.LastBlock) is ParagraphBlock previousParagraph) { if (state.IsBlankLine || - state.IsOpen(previousParagraph) && listInfo.BulletType == '1' && listInfo.OrderedStart != "1") + state.IsOpen(previousParagraph) && listInfo.BulletType == '1' && listInfo.OrderedStart is not "1") { state.GoToColumn(initColumn); state.TriviaStart = savedTriviaStart; // restore changed TriviaStart state diff --git a/src/Markdig/Renderers/Html/CodeBlockRenderer.cs b/src/Markdig/Renderers/Html/CodeBlockRenderer.cs index a4b84faa9..906ce4bec 100644 --- a/src/Markdig/Renderers/Html/CodeBlockRenderer.cs +++ b/src/Markdig/Renderers/Html/CodeBlockRenderer.cs @@ -47,7 +47,7 @@ protected override void Write(HtmlRenderer renderer, CodeBlock obj) { renderer.Write(" cls.StartsWith(infoPrefix) ? cls.Substring(infoPrefix.Length) : cls) + cls => cls.StartsWith(infoPrefix, StringComparison.Ordinal) ? cls.Substring(infoPrefix.Length) : cls) .Write(">"); } diff --git a/src/Markdig/Renderers/Html/ListRenderer.cs b/src/Markdig/Renderers/Html/ListRenderer.cs index d41c55c93..269d6050e 100644 --- a/src/Markdig/Renderers/Html/ListRenderer.cs +++ b/src/Markdig/Renderers/Html/ListRenderer.cs @@ -25,7 +25,7 @@ protected override void Write(HtmlRenderer renderer, ListBlock listBlock) renderer.Write(" type=\"").Write(listBlock.BulletType).Write("\""); } - if (listBlock.OrderedStart != null && (listBlock.OrderedStart != "1")) + if (listBlock.OrderedStart != null && (listBlock.OrderedStart is not "1")) { renderer.Write(" start=\"").Write(listBlock.OrderedStart).Write("\""); } diff --git a/src/Markdig/Renderers/Roundtrip/CodeBlockRenderer.cs b/src/Markdig/Renderers/Roundtrip/CodeBlockRenderer.cs index 69b52b734..2f70192b9 100644 --- a/src/Markdig/Renderers/Roundtrip/CodeBlockRenderer.cs +++ b/src/Markdig/Renderers/Roundtrip/CodeBlockRenderer.cs @@ -67,11 +67,13 @@ protected override void Write(RoundtripRenderer renderer, CodeBlock obj) } else { - var indents = new List(); - foreach (var cbl in obj.CodeBlockLines) + var indents = new string[obj.CodeBlockLines.Count]; + + for (int i = 0; i < obj.CodeBlockLines.Count; i++) { - indents.Add(cbl.TriviaBefore.ToString()); + indents[i] = obj.CodeBlockLines[i].TriviaBefore.ToString(); } + renderer.PushIndent(indents); WriteLeafRawLines(renderer, obj); renderer.PopIndent(); diff --git a/src/Markdig/Renderers/Roundtrip/ListRenderer.cs b/src/Markdig/Renderers/Roundtrip/ListRenderer.cs index 87412f862..5e3776bb9 100644 --- a/src/Markdig/Renderers/Roundtrip/ListRenderer.cs +++ b/src/Markdig/Renderers/Roundtrip/ListRenderer.cs @@ -28,7 +28,7 @@ protected override void Write(RoundtripRenderer renderer, ListBlock listBlock) var bws = listItem.TriviaBefore.ToString(); var bullet = listItem.SourceBullet.ToString(); var delimiter = listBlock.OrderedDelimiter; - renderer.PushIndent(new List { $@"{bws}{bullet}{delimiter}" }); + renderer.PushIndent(new string[] { $"{bws}{bullet}{delimiter}" }); renderer.WriteChildren(listItem); renderer.RenderLinesAfter(listItem); } @@ -45,7 +45,7 @@ protected override void Write(RoundtripRenderer renderer, ListBlock listBlock) char bullet = listBlock.BulletType; StringSlice aws = listItem.TriviaAfter; - renderer.PushIndent(new List { $@"{bws}{bullet}{aws}" }); + renderer.PushIndent(new string[] { $"{bws}{bullet}{aws}" }); if (listItem.Count == 0) { renderer.Write(""); // trigger writing of indent diff --git a/src/Markdig/Renderers/Roundtrip/QuoteBlockRenderer.cs b/src/Markdig/Renderers/Roundtrip/QuoteBlockRenderer.cs index dbc92b349..3c3076208 100644 --- a/src/Markdig/Renderers/Roundtrip/QuoteBlockRenderer.cs +++ b/src/Markdig/Renderers/Roundtrip/QuoteBlockRenderer.cs @@ -18,14 +18,15 @@ protected override void Write(RoundtripRenderer renderer, QuoteBlock quoteBlock) renderer.RenderLinesBefore(quoteBlock); renderer.Write(quoteBlock.TriviaBefore); - var indents = new List(); - foreach (var quoteLine in quoteBlock.QuoteLines) + var indents = new string[quoteBlock.QuoteLines.Count]; + for (int i = 0; i < quoteBlock.QuoteLines.Count; i++) { + var quoteLine = quoteBlock.QuoteLines[i]; var wsb = quoteLine.TriviaBefore.ToString(); var quoteChar = quoteLine.QuoteChar ? ">" : ""; var spaceAfterQuoteChar = quoteLine.HasSpaceAfterQuoteChar ? " " : ""; var wsa = quoteLine.TriviaAfter.ToString(); - indents.Add(wsb + quoteChar + spaceAfterQuoteChar + wsa); + indents[i] = (wsb + quoteChar + spaceAfterQuoteChar + wsa); } bool noChildren = false; if (quoteBlock.Count == 0) diff --git a/src/Markdig/Renderers/TextRendererBase.cs b/src/Markdig/Renderers/TextRendererBase.cs index 9de4ba278..ec76caa2c 100644 --- a/src/Markdig/Renderers/TextRendererBase.cs +++ b/src/Markdig/Renderers/TextRendererBase.cs @@ -71,19 +71,21 @@ public override object Render(MarkdownObject markdownObject) /// public abstract class TextRendererBase : TextRendererBase where T : TextRendererBase { - private class Indent + private sealed class Indent { private readonly string? _constant; - private readonly Queue? _lineSpecific; + private readonly string[]? _lines; + + private int position = 0; internal Indent(string constant) { _constant = constant; } - internal Indent(IEnumerable lineSpecific) + internal Indent(string[] lineSpecific) { - _lineSpecific = new Queue(lineSpecific); + _lines = lineSpecific; } internal string Next() @@ -93,10 +95,10 @@ internal string Next() return _constant; } - //if (_lineSpecific.Count == 0) throw new Exception("Indents empty"); - if (_lineSpecific!.Count == 0) return string.Empty; - var next = _lineSpecific.Dequeue(); - return next; + //if (_lines.Count == 0) throw new Exception("Indents empty"); + if (position == _lines!.Length) return string.Empty; + + return _lines![position++]; } } @@ -160,7 +162,7 @@ public void PushIndent(string indent) indents.Add(new Indent(indent)); } - public void PushIndent(IEnumerable lineSpecific) + public void PushIndent(string[] lineSpecific) { if (indents is null) ThrowHelper.ArgumentNullException(nameof(indents)); indents.Add(new Indent(lineSpecific));