From 5d132220ba18304e7e4b9f00b41d0e1df9850db8 Mon Sep 17 00:00:00 2001 From: Patrik Svensson Date: Tue, 11 Aug 2020 17:15:58 +0200 Subject: [PATCH] Enable nullable reference types Closes #36 --- src/.editorconfig | 5 ++- src/Directory.Build.props | 2 +- .../Spectre.Console.Tests.csproj | 10 +++--- src/Spectre.Console/AnsiConsoleSettings.cs | 2 +- src/Spectre.Console/Capabilities.cs | 2 +- src/Spectre.Console/Color.cs | 2 +- src/Spectre.Console/Composition/Border.cs | 11 +++++-- src/Spectre.Console/Composition/GridColumn.cs | 8 ++--- src/Spectre.Console/Composition/Justify.cs | 2 +- .../Composition/Measurement.cs | 2 +- src/Spectre.Console/Composition/Padding.cs | 2 +- src/Spectre.Console/Composition/Panel.cs | 11 ++++--- src/Spectre.Console/Composition/Segment.cs | 9 +++-- .../Composition/Table.Calculations.cs | 5 +-- src/Spectre.Console/Composition/Table.cs | 6 ++-- src/Spectre.Console/Composition/Text.cs | 8 +++-- .../Internal/Colors/ColorTable.cs | 2 +- .../Internal/Extensions/StringExtensions.cs | 12 +++---- .../Internal/Text/Markup/MarkupParser.cs | 6 +++- .../Internal/Text/Markup/MarkupTokenizer.cs | 2 +- .../Internal/Text/StringBuffer.cs | 2 ++ .../Internal/Text/StyleParser.cs | 18 +++++++--- .../Internal/Utilities/Ratio.cs | 2 +- src/Spectre.Console/Spectre.Console.csproj | 33 ++++++++----------- src/Spectre.Console/Style.cs | 6 ++-- 25 files changed, 98 insertions(+), 72 deletions(-) diff --git a/src/.editorconfig b/src/.editorconfig index a9a7a34..a156b68 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -77,4 +77,7 @@ dotnet_diagnostic.CA1032.severity = none dotnet_diagnostic.CA1826.severity = none # RCS1079: Throwing of new NotImplementedException. -dotnet_diagnostic.RCS1079.severity = warning \ No newline at end of file +dotnet_diagnostic.RCS1079.severity = warning + +# RCS1057: Add empty line between declarations. +dotnet_diagnostic.RCS1057.severity = none diff --git a/src/Directory.Build.props b/src/Directory.Build.props index e8ff92c..93475ab 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -34,7 +34,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Spectre.Console.Tests/Spectre.Console.Tests.csproj b/src/Spectre.Console.Tests/Spectre.Console.Tests.csproj index c59f346..e77c363 100644 --- a/src/Spectre.Console.Tests/Spectre.Console.Tests.csproj +++ b/src/Spectre.Console.Tests/Spectre.Console.Tests.csproj @@ -7,11 +7,13 @@ - + - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Spectre.Console/AnsiConsoleSettings.cs b/src/Spectre.Console/AnsiConsoleSettings.cs index fe951e9..6e26131 100644 --- a/src/Spectre.Console/AnsiConsoleSettings.cs +++ b/src/Spectre.Console/AnsiConsoleSettings.cs @@ -21,6 +21,6 @@ namespace Spectre.Console /// /// Gets or sets the out buffer. /// - public TextWriter Out { get; set; } + public TextWriter? Out { get; set; } } } diff --git a/src/Spectre.Console/Capabilities.cs b/src/Spectre.Console/Capabilities.cs index be0db18..9232813 100644 --- a/src/Spectre.Console/Capabilities.cs +++ b/src/Spectre.Console/Capabilities.cs @@ -50,7 +50,7 @@ namespace Spectre.Console ColorSystem.Standard => "4 bits", ColorSystem.EightBit => "8 bits", ColorSystem.TrueColor => "24 bits", - _ => "?" + _ => "?", }; return $"ANSI={supportsAnsi}, Colors={ColorSystem}, Kind={legacyConsole} ({bits})"; diff --git a/src/Spectre.Console/Color.cs b/src/Spectre.Console/Color.cs index d2f0668..77330de 100644 --- a/src/Spectre.Console/Color.cs +++ b/src/Spectre.Console/Color.cs @@ -74,7 +74,7 @@ namespace Spectre.Console } /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Color color && Equals(color); } diff --git a/src/Spectre.Console/Composition/Border.cs b/src/Spectre.Console/Composition/Border.cs index 3ccdfb8..1ae3198 100644 --- a/src/Spectre.Console/Composition/Border.cs +++ b/src/Spectre.Console/Composition/Border.cs @@ -57,15 +57,20 @@ namespace Spectre.Console.Composition private Dictionary Initialize() { var lookup = new Dictionary(); - foreach (BorderPart part in Enum.GetValues(typeof(BorderPart))) + foreach (BorderPart? part in Enum.GetValues(typeof(BorderPart))) { - var text = GetBoxPart(part); + if (part == null) + { + continue; + } + + var text = GetBoxPart(part.Value); if (text.Length > 1) { throw new InvalidOperationException("A box part cannot contain more than one character."); } - lookup.Add(part, GetBoxPart(part)); + lookup.Add(part.Value, GetBoxPart(part.Value)); } return lookup; diff --git a/src/Spectre.Console/Composition/GridColumn.cs b/src/Spectre.Console/Composition/GridColumn.cs index 1fc658a..706b26e 100644 --- a/src/Spectre.Console/Composition/GridColumn.cs +++ b/src/Spectre.Console/Composition/GridColumn.cs @@ -9,22 +9,22 @@ namespace Spectre.Console /// Gets or sets the width of the column. /// If null, the column will adapt to it's contents. /// - public int? Width { get; set; } = null; + public int? Width { get; set; } /// /// Gets or sets a value indicating whether wrapping of /// text within the column should be prevented. /// - public bool NoWrap { get; set; } = false; + public bool NoWrap { get; set; } /// /// Gets or sets the padding of the column. /// - public Padding? Padding { get; set; } = null; + public Padding? Padding { get; set; } /// /// Gets or sets the alignment of the column. /// - public Justify? Alignment { get; set; } = null; + public Justify? Alignment { get; set; } } } diff --git a/src/Spectre.Console/Composition/Justify.cs b/src/Spectre.Console/Composition/Justify.cs index a3f25e7..01c450e 100644 --- a/src/Spectre.Console/Composition/Justify.cs +++ b/src/Spectre.Console/Composition/Justify.cs @@ -16,7 +16,7 @@ namespace Spectre.Console Right = 1, /// - /// Centered + /// Centered. /// Center = 2, } diff --git a/src/Spectre.Console/Composition/Measurement.cs b/src/Spectre.Console/Composition/Measurement.cs index 0d67bb2..83ec738 100644 --- a/src/Spectre.Console/Composition/Measurement.cs +++ b/src/Spectre.Console/Composition/Measurement.cs @@ -29,7 +29,7 @@ namespace Spectre.Console.Composition } /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Measurement measurement && Equals(measurement); } diff --git a/src/Spectre.Console/Composition/Padding.cs b/src/Spectre.Console/Composition/Padding.cs index 21d710c..f268c1a 100644 --- a/src/Spectre.Console/Composition/Padding.cs +++ b/src/Spectre.Console/Composition/Padding.cs @@ -29,7 +29,7 @@ namespace Spectre.Console } /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Padding padding && Equals(padding); } diff --git a/src/Spectre.Console/Composition/Panel.cs b/src/Spectre.Console/Composition/Panel.cs index 94168f5..a53445f 100644 --- a/src/Spectre.Console/Composition/Panel.cs +++ b/src/Spectre.Console/Composition/Panel.cs @@ -9,6 +9,8 @@ namespace Spectre.Console /// public sealed class Panel : IRenderable { + private const int EdgeWidth = 2; + private readonly IRenderable _child; /// @@ -26,14 +28,14 @@ namespace Spectre.Console /// /// Gets or sets the alignment of the panel contents. /// - public Justify? Alignment { get; set; } = null; + public Justify? Alignment { get; set; } /// /// Gets or sets a value indicating whether or not the panel should /// fit the available space. If false, the panel width will be /// auto calculated. Defaults to false. /// - public bool Expand { get; set; } = false; + public bool Expand { get; set; } /// /// Gets or sets the padding. @@ -61,13 +63,12 @@ namespace Spectre.Console { var border = Composition.Border.GetBorder(Border, (context.LegacyConsole || !context.Unicode) && SafeBorder); - var edgeWidth = 2; var paddingWidth = Padding.GetHorizontalPadding(); - var childWidth = width - edgeWidth - paddingWidth; + var childWidth = width - EdgeWidth - paddingWidth; if (!Expand) { - var measurement = _child.Measure(context, width - edgeWidth - paddingWidth); + var measurement = _child.Measure(context, width - EdgeWidth - paddingWidth); childWidth = measurement.Max; } diff --git a/src/Spectre.Console/Composition/Segment.cs b/src/Spectre.Console/Composition/Segment.cs index 803f510..0f4fb43 100644 --- a/src/Spectre.Console/Composition/Segment.cs +++ b/src/Spectre.Console/Composition/Segment.cs @@ -50,7 +50,12 @@ namespace Spectre.Console.Composition private Segment(string text, Style style, bool lineBreak) { - Text = text?.NormalizeLineEndings() ?? throw new ArgumentNullException(nameof(text)); + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + Text = text.NormalizeLineEndings(); Style = style; IsLineBreak = lineBreak; } @@ -89,7 +94,7 @@ namespace Spectre.Console.Composition /// /// The offset where to split the segment. /// One or two new segments representing the split. - public (Segment First, Segment Second) Split(int offset) + public (Segment First, Segment? Second) Split(int offset) { if (offset < 0) { diff --git a/src/Spectre.Console/Composition/Table.Calculations.cs b/src/Spectre.Console/Composition/Table.Calculations.cs index 98e36d0..ee1bd65 100644 --- a/src/Spectre.Console/Composition/Table.Calculations.cs +++ b/src/Spectre.Console/Composition/Table.Calculations.cs @@ -11,6 +11,8 @@ namespace Spectre.Console /// public sealed partial class Table { + private const int EdgeCount = 2; + // Calculate the widths of each column, including padding, not including borders. // Ported from Rich by Will McGugan, licensed under MIT. // https://github.com/willmcgugan/rich/blob/527475837ebbfc427530b3ee0d4d0741d2d0fc6d/rich/table.py#L394 @@ -115,10 +117,9 @@ namespace Spectre.Console private int GetExtraWidth(bool includePadding) { - var edges = 2; var separators = _columns.Count - 1; var padding = includePadding ? _columns.Select(x => x.Padding.GetHorizontalPadding()).Sum() : 0; - return separators + edges + padding; + return separators + EdgeCount + padding; } } } diff --git a/src/Spectre.Console/Composition/Table.cs b/src/Spectre.Console/Composition/Table.cs index 3e6f286..45f6b26 100644 --- a/src/Spectre.Console/Composition/Table.cs +++ b/src/Spectre.Console/Composition/Table.cs @@ -39,12 +39,12 @@ namespace Spectre.Console /// fit the available space. If false, the table width will be /// auto calculated. Defaults to false. /// - public bool Expand { get; set; } = false; + public bool Expand { get; set; } /// /// Gets or sets the width of the table. /// - public int? Width { get; set; } = null; + public int? Width { get; set; } /// /// Gets or sets a value indicating whether or not to use @@ -54,7 +54,7 @@ namespace Spectre.Console public bool SafeBorder { get; set; } = true; // Whether this is a grid or not. - internal bool IsGrid { get; set; } = false; + internal bool IsGrid { get; set; } // Whether or not the most right cell should be padded. // This is almost always the case, unless we're rendering diff --git a/src/Spectre.Console/Composition/Text.cs b/src/Spectre.Console/Composition/Text.cs index d55f9a6..86afd66 100644 --- a/src/Spectre.Console/Composition/Text.cs +++ b/src/Spectre.Console/Composition/Text.cs @@ -201,7 +201,7 @@ namespace Spectre.Console return result; } - private IEnumerable SplitLineBreaks(IEnumerable segments) + private static IEnumerable SplitLineBreaks(IEnumerable segments) { // Creates individual segments of line breaks. var result = new List(); @@ -228,7 +228,11 @@ namespace Spectre.Console } result.Add(Segment.LineBreak()); - queue.Push(new Segment(second.Text.Substring(1), second.Style)); + + if (second != null) + { + queue.Push(new Segment(second.Text.Substring(1), second.Style)); + } } } diff --git a/src/Spectre.Console/Internal/Colors/ColorTable.cs b/src/Spectre.Console/Internal/Colors/ColorTable.cs index 58c6190..0e12185 100644 --- a/src/Spectre.Console/Internal/Colors/ColorTable.cs +++ b/src/Spectre.Console/Internal/Colors/ColorTable.cs @@ -45,7 +45,7 @@ namespace Spectre.Console.Internal return ColorPalette.EightBit[number]; } - public static string GetName(int number) + public static string? GetName(int number) { _nameLookup.TryGetValue(number, out var name); return name; diff --git a/src/Spectre.Console/Internal/Extensions/StringExtensions.cs b/src/Spectre.Console/Internal/Extensions/StringExtensions.cs index f02feb3..8431843 100644 --- a/src/Spectre.Console/Internal/Extensions/StringExtensions.cs +++ b/src/Spectre.Console/Internal/Extensions/StringExtensions.cs @@ -17,14 +17,9 @@ namespace Spectre.Console.Internal public static string NormalizeLineEndings(this string text, bool native = false) { - if (text == null) - { - return null; - } - - var normalized = text?.Replace("\r\n", "\n") - ?.Replace("\r", string.Empty); + text ??= string.Empty; + var normalized = text?.Replace("\r\n", "\n")?.Replace("\r", string.Empty) ?? string.Empty; if (native && !_alreadyNormalized) { normalized = normalized.Replace("\n", Environment.NewLine); @@ -35,7 +30,8 @@ namespace Spectre.Console.Internal public static string[] SplitLines(this string text) { - return text.NormalizeLineEndings().Split(new[] { '\n' }, StringSplitOptions.None); + var result = text?.NormalizeLineEndings()?.Split(new[] { '\n' }, StringSplitOptions.None); + return result ?? Array.Empty(); } } } diff --git a/src/Spectre.Console/Internal/Text/Markup/MarkupParser.cs b/src/Spectre.Console/Internal/Text/Markup/MarkupParser.cs index fbf1eaf..3eef019 100644 --- a/src/Spectre.Console/Internal/Text/Markup/MarkupParser.cs +++ b/src/Spectre.Console/Internal/Text/Markup/MarkupParser.cs @@ -6,7 +6,7 @@ namespace Spectre.Console.Internal { internal static class MarkupParser { - public static Text Parse(string text, Style style = null) + public static Text Parse(string text, Style? style = null) { style ??= Style.Plain; @@ -18,6 +18,10 @@ namespace Spectre.Console.Internal while (tokenizer.MoveNext()) { var token = tokenizer.Current; + if (token == null) + { + break; + } if (token.Kind == MarkupTokenKind.Open) { diff --git a/src/Spectre.Console/Internal/Text/Markup/MarkupTokenizer.cs b/src/Spectre.Console/Internal/Text/Markup/MarkupTokenizer.cs index bce67c8..5737a8a 100644 --- a/src/Spectre.Console/Internal/Text/Markup/MarkupTokenizer.cs +++ b/src/Spectre.Console/Internal/Text/Markup/MarkupTokenizer.cs @@ -7,7 +7,7 @@ namespace Spectre.Console.Internal { private readonly StringBuffer _reader; - public MarkupToken Current { get; private set; } + public MarkupToken? Current { get; private set; } public MarkupTokenizer(string text) { diff --git a/src/Spectre.Console/Internal/Text/StringBuffer.cs b/src/Spectre.Console/Internal/Text/StringBuffer.cs index d941465..bba60c7 100644 --- a/src/Spectre.Console/Internal/Text/StringBuffer.cs +++ b/src/Spectre.Console/Internal/Text/StringBuffer.cs @@ -1,10 +1,12 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.IO; namespace Spectre.Console.Internal { internal sealed class StringBuffer : IDisposable { + [SuppressMessage("Usage", "CA2213:Disposable fields should be disposed", Justification = "False positive")] private readonly StringReader _reader; private readonly int _length; diff --git a/src/Spectre.Console/Internal/Text/StyleParser.cs b/src/Spectre.Console/Internal/Text/StyleParser.cs index 1365c48..184880b 100644 --- a/src/Spectre.Console/Internal/Text/StyleParser.cs +++ b/src/Spectre.Console/Internal/Text/StyleParser.cs @@ -14,16 +14,23 @@ namespace Spectre.Console.Internal throw new InvalidOperationException(error); } + if (style == null) + { + // This should not happen, but we need to please the compiler + // which cannot know that style isn't null here. + throw new InvalidOperationException("Could not parse style."); + } + return style; } - public static bool TryParse(string text, out Style style) + public static bool TryParse(string text, out Style? style) { style = Parse(text, out var error); return error == null; } - private static Style Parse(string text, out string error) + private static Style? Parse(string text, out string? error) { var effectiveDecoration = (Decoration?)null; var effectiveForeground = (Color?)null; @@ -113,11 +120,11 @@ namespace Spectre.Console.Internal } [SuppressMessage("Design", "CA1031:Do not catch general exception types")] - private static Color? ParseHexColor(string hex, out string error) + private static Color? ParseHexColor(string hex, out string? error) { error = null; - hex = hex ?? string.Empty; + hex ??= string.Empty; hex = hex.Replace("#", string.Empty).Trim(); try @@ -151,11 +158,12 @@ namespace Spectre.Console.Internal } [SuppressMessage("Design", "CA1031:Do not catch general exception types")] - private static Color? ParseRgbColor(string rgb, out string error) + private static Color? ParseRgbColor(string rgb, out string? error) { try { error = null; + var normalized = rgb ?? string.Empty; if (normalized.Length >= 3) { diff --git a/src/Spectre.Console/Internal/Utilities/Ratio.cs b/src/Spectre.Console/Internal/Utilities/Ratio.cs index dbbf4eb..79fa787 100644 --- a/src/Spectre.Console/Internal/Utilities/Ratio.cs +++ b/src/Spectre.Console/Internal/Utilities/Ratio.cs @@ -40,7 +40,7 @@ namespace Spectre.Console.Internal return result; } - public static List Distribute(int total, List ratios, List minimums = null) + public static List Distribute(int total, List ratios, List? minimums = null) { if (minimums != null) { diff --git a/src/Spectre.Console/Spectre.Console.csproj b/src/Spectre.Console/Spectre.Console.csproj index de6b91f..8cf2895 100644 --- a/src/Spectre.Console/Spectre.Console.csproj +++ b/src/Spectre.Console/Spectre.Console.csproj @@ -2,6 +2,7 @@ netstandard2.0 + enable @@ -9,27 +10,21 @@ - - - **/ColorPalette.cs - - - Color.cs - - - AnsiConsole.cs - - - ConsoleExtensions.cs - - - **/Table.cs - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + 3.0.0 + False + + diff --git a/src/Spectre.Console/Style.cs b/src/Spectre.Console/Style.cs index 9a7d1f8..df70865 100644 --- a/src/Spectre.Console/Style.cs +++ b/src/Spectre.Console/Style.cs @@ -67,7 +67,7 @@ namespace Spectre.Console /// if the conversion succeeded, or null if the conversion failed. /// /// true if s was converted successfully; otherwise, false. - public static bool TryParse(string text, out Style result) + public static bool TryParse(string text, out Style? result) { return StyleParser.TryParse(text, out result); } @@ -113,13 +113,13 @@ namespace Spectre.Console } /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { return Equals(obj as Style); } /// - public bool Equals(Style other) + public bool Equals(Style? other) { if (other == null) {