diff --git a/src/LanguageServer/Impl/Documentation/DocstringConverter.cs b/src/LanguageServer/Impl/Documentation/DocstringConverter.cs index 1f5b697d4..466668890 100644 --- a/src/LanguageServer/Impl/Documentation/DocstringConverter.cs +++ b/src/LanguageServer/Impl/Documentation/DocstringConverter.cs @@ -23,8 +23,6 @@ namespace Microsoft.Python.LanguageServer.Documentation { internal class DocstringConverter { - private static readonly string[] PotentialHeaders = new[] { "=", "-", "~", "+" }; - /// /// Converts a docstring to a plaintext, human readable form. This will /// first strip any common leading indention (like inspect.cleandoc), @@ -55,7 +53,7 @@ public static string ToPlaintext(string docstring) { /// The converted docstring, with Environment.NewLine line endings. public static string ToMarkdown(string docstring) => new DocstringConverter(docstring).Convert(); - private readonly StringBuilder _builder = new StringBuilder(); + private readonly StringBuilder _builder; private bool _skipAppendEmptyLine = true; private bool _insideInlineCode = false; private bool _appendDirectiveBlock = false; @@ -79,6 +77,7 @@ private int NextBlockIndent private string CurrentLineWithinBlock => CurrentLine.Substring(_blockIndent); private DocstringConverter(string input) { + _builder = new StringBuilder(input.Length); _state = ParseText; _lines = SplitDocstring(input); } @@ -153,12 +152,28 @@ private void ParseText() { EatLine(); } + private static readonly Regex DirectivesExtraNewlineRegex = new Regex(@"^\s*:(param|arg|type|return|rtype|raise|except|var|ivar|cvar|copyright|license)", RegexOptions.Singleline | RegexOptions.Compiled); + + private static readonly (Regex, string)[] PotentialHeaders = new[] { + (new Regex(@"^\s*=+(\s+=+)+$", RegexOptions.Singleline | RegexOptions.Compiled), "="), + (new Regex(@"^\s*-+(\s+-+)+$", RegexOptions.Singleline | RegexOptions.Compiled), "-"), + (new Regex(@"^\s*~+(\s+~+)+$", RegexOptions.Singleline | RegexOptions.Compiled), "~"), + (new Regex(@"^\s*\++(\s+\++)+$", RegexOptions.Singleline | RegexOptions.Compiled), "+"), + }; + + private static readonly Regex WhitespaceRegex = new Regex(@"\s", RegexOptions.Singleline | RegexOptions.Compiled); + + private static readonly Regex TildaHeaderRegex = new Regex(@"^\s*~~~+$", RegexOptions.Singleline | RegexOptions.Compiled); + private static readonly Regex PlusHeaderRegex = new Regex(@"^\s*\+\+\++$", RegexOptions.Singleline | RegexOptions.Compiled); + private static readonly Regex LeadingAsteriskRegex = new Regex(@"^(\s+\* )(.*)$", RegexOptions.Singleline | RegexOptions.Compiled); + private static readonly Regex UnescapedMarkdownCharsRegex = new Regex(@"(?>> ", RegexOptions.Singleline | RegexOptions.Compiled); + private bool BeginDoctest() { - if (!Regex.IsMatch(CurrentLine, @" *>>> ")) { + if (!DoctestRegex.IsMatch(CurrentLine)) { return false; } @@ -387,8 +411,10 @@ private void ParseLiteralBlockSingleLine() { EatLine(); } + private static readonly Regex SpaceDotDotRegex = new Regex(@"^\s*\.\. ", RegexOptions.Singleline | RegexOptions.Compiled); + private bool BeginDirective() { - if (!Regex.IsMatch(CurrentLine, @"^\s*\.\. ")) { + if (!SpaceDotDotRegex.IsMatch(CurrentLine)) { return false; } @@ -398,10 +424,12 @@ private bool BeginDirective() { return true; } + private static readonly Regex DirectiveLikeRegex = new Regex(@"^\s*\.\.\s+(\w+)::\s*(.*)$", RegexOptions.Singleline | RegexOptions.Compiled); + private void ParseDirective() { // http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#directives - var match = Regex.Match(CurrentLine, @"^\s*\.\.\s+(\w+)::\s*(.*)$"); + var match = DirectiveLikeRegex.Match(CurrentLine); if (match.Success) { var directiveType = match.Groups[1].Value; var directive = match.Groups[2].Value;