Enable nullable reference types

Closes #36
This commit is contained in:
Patrik Svensson 2020-08-11 17:15:58 +02:00 committed by Patrik Svensson
parent a273f74758
commit 5d132220ba
25 changed files with 98 additions and 72 deletions

View File

@ -78,3 +78,6 @@ dotnet_diagnostic.CA1826.severity = none
# RCS1079: Throwing of new NotImplementedException. # RCS1079: Throwing of new NotImplementedException.
dotnet_diagnostic.RCS1079.severity = warning dotnet_diagnostic.RCS1079.severity = warning
# RCS1057: Add empty line between declarations.
dotnet_diagnostic.RCS1057.severity = none

View File

@ -34,7 +34,7 @@
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="MinVer" PrivateAssets="All" Version="2.3.0" /> <PackageReference Include="MinVer" PrivateAssets="All" Version="2.3.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" Version="1.0.0" /> <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" Version="1.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8"> <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.3.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -7,11 +7,13 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
<PackageReference Include="Shouldly" Version="4.0.0-beta0002" /> <PackageReference Include="Shouldly" Version="4.0.0-beta0002" />
<PackageReference Include="xunit" Version="2.4.0" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PackageReference Include="coverlet.collector" Version="1.2.0" /> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -21,6 +21,6 @@ namespace Spectre.Console
/// <summary> /// <summary>
/// Gets or sets the out buffer. /// Gets or sets the out buffer.
/// </summary> /// </summary>
public TextWriter Out { get; set; } public TextWriter? Out { get; set; }
} }
} }

View File

@ -50,7 +50,7 @@ namespace Spectre.Console
ColorSystem.Standard => "4 bits", ColorSystem.Standard => "4 bits",
ColorSystem.EightBit => "8 bits", ColorSystem.EightBit => "8 bits",
ColorSystem.TrueColor => "24 bits", ColorSystem.TrueColor => "24 bits",
_ => "?" _ => "?",
}; };
return $"ANSI={supportsAnsi}, Colors={ColorSystem}, Kind={legacyConsole} ({bits})"; return $"ANSI={supportsAnsi}, Colors={ColorSystem}, Kind={legacyConsole} ({bits})";

View File

@ -74,7 +74,7 @@ namespace Spectre.Console
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is Color color && Equals(color); return obj is Color color && Equals(color);
} }

View File

@ -57,15 +57,20 @@ namespace Spectre.Console.Composition
private Dictionary<BorderPart, string> Initialize() private Dictionary<BorderPart, string> Initialize()
{ {
var lookup = new Dictionary<BorderPart, string>(); var lookup = new Dictionary<BorderPart, string>();
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) if (text.Length > 1)
{ {
throw new InvalidOperationException("A box part cannot contain more than one character."); 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; return lookup;

View File

@ -9,22 +9,22 @@ namespace Spectre.Console
/// Gets or sets the width of the column. /// Gets or sets the width of the column.
/// If <c>null</c>, the column will adapt to it's contents. /// If <c>null</c>, the column will adapt to it's contents.
/// </summary> /// </summary>
public int? Width { get; set; } = null; public int? Width { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether wrapping of /// Gets or sets a value indicating whether wrapping of
/// text within the column should be prevented. /// text within the column should be prevented.
/// </summary> /// </summary>
public bool NoWrap { get; set; } = false; public bool NoWrap { get; set; }
/// <summary> /// <summary>
/// Gets or sets the padding of the column. /// Gets or sets the padding of the column.
/// </summary> /// </summary>
public Padding? Padding { get; set; } = null; public Padding? Padding { get; set; }
/// <summary> /// <summary>
/// Gets or sets the alignment of the column. /// Gets or sets the alignment of the column.
/// </summary> /// </summary>
public Justify? Alignment { get; set; } = null; public Justify? Alignment { get; set; }
} }
} }

View File

@ -16,7 +16,7 @@ namespace Spectre.Console
Right = 1, Right = 1,
/// <summary> /// <summary>
/// Centered /// Centered.
/// </summary> /// </summary>
Center = 2, Center = 2,
} }

View File

@ -29,7 +29,7 @@ namespace Spectre.Console.Composition
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is Measurement measurement && Equals(measurement); return obj is Measurement measurement && Equals(measurement);
} }

View File

@ -29,7 +29,7 @@ namespace Spectre.Console
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return obj is Padding padding && Equals(padding); return obj is Padding padding && Equals(padding);
} }

View File

@ -9,6 +9,8 @@ namespace Spectre.Console
/// </summary> /// </summary>
public sealed class Panel : IRenderable public sealed class Panel : IRenderable
{ {
private const int EdgeWidth = 2;
private readonly IRenderable _child; private readonly IRenderable _child;
/// <summary> /// <summary>
@ -26,14 +28,14 @@ namespace Spectre.Console
/// <summary> /// <summary>
/// Gets or sets the alignment of the panel contents. /// Gets or sets the alignment of the panel contents.
/// </summary> /// </summary>
public Justify? Alignment { get; set; } = null; public Justify? Alignment { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether or not the panel should /// Gets or sets a value indicating whether or not the panel should
/// fit the available space. If <c>false</c>, the panel width will be /// fit the available space. If <c>false</c>, the panel width will be
/// auto calculated. Defaults to <c>false</c>. /// auto calculated. Defaults to <c>false</c>.
/// </summary> /// </summary>
public bool Expand { get; set; } = false; public bool Expand { get; set; }
/// <summary> /// <summary>
/// Gets or sets the padding. /// Gets or sets the padding.
@ -61,13 +63,12 @@ namespace Spectre.Console
{ {
var border = Composition.Border.GetBorder(Border, (context.LegacyConsole || !context.Unicode) && SafeBorder); var border = Composition.Border.GetBorder(Border, (context.LegacyConsole || !context.Unicode) && SafeBorder);
var edgeWidth = 2;
var paddingWidth = Padding.GetHorizontalPadding(); var paddingWidth = Padding.GetHorizontalPadding();
var childWidth = width - edgeWidth - paddingWidth; var childWidth = width - EdgeWidth - paddingWidth;
if (!Expand) if (!Expand)
{ {
var measurement = _child.Measure(context, width - edgeWidth - paddingWidth); var measurement = _child.Measure(context, width - EdgeWidth - paddingWidth);
childWidth = measurement.Max; childWidth = measurement.Max;
} }

View File

@ -50,7 +50,12 @@ namespace Spectre.Console.Composition
private Segment(string text, Style style, bool lineBreak) 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; Style = style;
IsLineBreak = lineBreak; IsLineBreak = lineBreak;
} }
@ -89,7 +94,7 @@ namespace Spectre.Console.Composition
/// </summary> /// </summary>
/// <param name="offset">The offset where to split the segment.</param> /// <param name="offset">The offset where to split the segment.</param>
/// <returns>One or two new segments representing the split.</returns> /// <returns>One or two new segments representing the split.</returns>
public (Segment First, Segment Second) Split(int offset) public (Segment First, Segment? Second) Split(int offset)
{ {
if (offset < 0) if (offset < 0)
{ {

View File

@ -11,6 +11,8 @@ namespace Spectre.Console
/// </summary> /// </summary>
public sealed partial class Table public sealed partial class Table
{ {
private const int EdgeCount = 2;
// Calculate the widths of each column, including padding, not including borders. // Calculate the widths of each column, including padding, not including borders.
// Ported from Rich by Will McGugan, licensed under MIT. // Ported from Rich by Will McGugan, licensed under MIT.
// https://github.com/willmcgugan/rich/blob/527475837ebbfc427530b3ee0d4d0741d2d0fc6d/rich/table.py#L394 // https://github.com/willmcgugan/rich/blob/527475837ebbfc427530b3ee0d4d0741d2d0fc6d/rich/table.py#L394
@ -115,10 +117,9 @@ namespace Spectre.Console
private int GetExtraWidth(bool includePadding) private int GetExtraWidth(bool includePadding)
{ {
var edges = 2;
var separators = _columns.Count - 1; var separators = _columns.Count - 1;
var padding = includePadding ? _columns.Select(x => x.Padding.GetHorizontalPadding()).Sum() : 0; var padding = includePadding ? _columns.Select(x => x.Padding.GetHorizontalPadding()).Sum() : 0;
return separators + edges + padding; return separators + EdgeCount + padding;
} }
} }
} }

View File

@ -39,12 +39,12 @@ namespace Spectre.Console
/// fit the available space. If <c>false</c>, the table width will be /// fit the available space. If <c>false</c>, the table width will be
/// auto calculated. Defaults to <c>false</c>. /// auto calculated. Defaults to <c>false</c>.
/// </summary> /// </summary>
public bool Expand { get; set; } = false; public bool Expand { get; set; }
/// <summary> /// <summary>
/// Gets or sets the width of the table. /// Gets or sets the width of the table.
/// </summary> /// </summary>
public int? Width { get; set; } = null; public int? Width { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether or not to use /// Gets or sets a value indicating whether or not to use
@ -54,7 +54,7 @@ namespace Spectre.Console
public bool SafeBorder { get; set; } = true; public bool SafeBorder { get; set; } = true;
// Whether this is a grid or not. // 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. // Whether or not the most right cell should be padded.
// This is almost always the case, unless we're rendering // This is almost always the case, unless we're rendering

View File

@ -201,7 +201,7 @@ namespace Spectre.Console
return result; return result;
} }
private IEnumerable<Segment> SplitLineBreaks(IEnumerable<Segment> segments) private static IEnumerable<Segment> SplitLineBreaks(IEnumerable<Segment> segments)
{ {
// Creates individual segments of line breaks. // Creates individual segments of line breaks.
var result = new List<Segment>(); var result = new List<Segment>();
@ -228,9 +228,13 @@ namespace Spectre.Console
} }
result.Add(Segment.LineBreak()); result.Add(Segment.LineBreak());
if (second != null)
{
queue.Push(new Segment(second.Text.Substring(1), second.Style)); queue.Push(new Segment(second.Text.Substring(1), second.Style));
} }
} }
}
return result; return result;
} }

View File

@ -45,7 +45,7 @@ namespace Spectre.Console.Internal
return ColorPalette.EightBit[number]; return ColorPalette.EightBit[number];
} }
public static string GetName(int number) public static string? GetName(int number)
{ {
_nameLookup.TryGetValue(number, out var name); _nameLookup.TryGetValue(number, out var name);
return name; return name;

View File

@ -17,14 +17,9 @@ namespace Spectre.Console.Internal
public static string NormalizeLineEndings(this string text, bool native = false) public static string NormalizeLineEndings(this string text, bool native = false)
{ {
if (text == null) text ??= string.Empty;
{
return null;
}
var normalized = text?.Replace("\r\n", "\n")
?.Replace("\r", string.Empty);
var normalized = text?.Replace("\r\n", "\n")?.Replace("\r", string.Empty) ?? string.Empty;
if (native && !_alreadyNormalized) if (native && !_alreadyNormalized)
{ {
normalized = normalized.Replace("\n", Environment.NewLine); normalized = normalized.Replace("\n", Environment.NewLine);
@ -35,7 +30,8 @@ namespace Spectre.Console.Internal
public static string[] SplitLines(this string text) 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<string>();
} }
} }
} }

View File

@ -6,7 +6,7 @@ namespace Spectre.Console.Internal
{ {
internal static class MarkupParser 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; style ??= Style.Plain;
@ -18,6 +18,10 @@ namespace Spectre.Console.Internal
while (tokenizer.MoveNext()) while (tokenizer.MoveNext())
{ {
var token = tokenizer.Current; var token = tokenizer.Current;
if (token == null)
{
break;
}
if (token.Kind == MarkupTokenKind.Open) if (token.Kind == MarkupTokenKind.Open)
{ {

View File

@ -7,7 +7,7 @@ namespace Spectre.Console.Internal
{ {
private readonly StringBuffer _reader; private readonly StringBuffer _reader;
public MarkupToken Current { get; private set; } public MarkupToken? Current { get; private set; }
public MarkupTokenizer(string text) public MarkupTokenizer(string text)
{ {

View File

@ -1,10 +1,12 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
namespace Spectre.Console.Internal namespace Spectre.Console.Internal
{ {
internal sealed class StringBuffer : IDisposable internal sealed class StringBuffer : IDisposable
{ {
[SuppressMessage("Usage", "CA2213:Disposable fields should be disposed", Justification = "False positive")]
private readonly StringReader _reader; private readonly StringReader _reader;
private readonly int _length; private readonly int _length;

View File

@ -14,16 +14,23 @@ namespace Spectre.Console.Internal
throw new InvalidOperationException(error); 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; 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); style = Parse(text, out var error);
return error == null; 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 effectiveDecoration = (Decoration?)null;
var effectiveForeground = (Color?)null; var effectiveForeground = (Color?)null;
@ -113,11 +120,11 @@ namespace Spectre.Console.Internal
} }
[SuppressMessage("Design", "CA1031:Do not catch general exception types")] [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; error = null;
hex = hex ?? string.Empty; hex ??= string.Empty;
hex = hex.Replace("#", string.Empty).Trim(); hex = hex.Replace("#", string.Empty).Trim();
try try
@ -151,11 +158,12 @@ namespace Spectre.Console.Internal
} }
[SuppressMessage("Design", "CA1031:Do not catch general exception types")] [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 try
{ {
error = null; error = null;
var normalized = rgb ?? string.Empty; var normalized = rgb ?? string.Empty;
if (normalized.Length >= 3) if (normalized.Length >= 3)
{ {

View File

@ -40,7 +40,7 @@ namespace Spectre.Console.Internal
return result; return result;
} }
public static List<int> Distribute(int total, List<int> ratios, List<int> minimums = null) public static List<int> Distribute(int total, List<int> ratios, List<int>? minimums = null)
{ {
if (minimums != null) if (minimums != null)
{ {

View File

@ -2,6 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -9,27 +10,21 @@
<None Include="../../gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" /> <None Include="../../gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Update="**/ColorPalette.*.cs">
<DependentUpon>**/ColorPalette.cs</DependentUpon>
</Compile>
<Compile Update="Color.*.cs">
<DependentUpon>Color.cs</DependentUpon>
</Compile>
<Compile Update="AnsiConsole.*.cs">
<DependentUpon>AnsiConsole.cs</DependentUpon>
</Compile>
<Compile Update="ConsoleExtensions.*.cs">
<DependentUpon>ConsoleExtensions.cs</DependentUpon>
</Compile>
<Compile Update="**/Table.*.cs">
<DependentUpon>**/Table.cs</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.4" /> <PackageReference Include="System.Memory" Version="4.5.4" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" Version="1.0.0-alpha.160" PrivateAssets="all" />
<PackageDownload Include="Microsoft.NETCore.App.Ref" Version="[$(AnnotatedReferenceAssemblyVersion)]" />
<PackageReference Include="Nullable" Version="1.2.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup>
<AnnotatedReferenceAssemblyVersion>3.0.0</AnnotatedReferenceAssemblyVersion>
<GenerateNullableAttributes>False</GenerateNullableAttributes>
</PropertyGroup>
</Project> </Project>

View File

@ -67,7 +67,7 @@ namespace Spectre.Console
/// if the conversion succeeded, or <c>null</c> if the conversion failed. /// if the conversion succeeded, or <c>null</c> if the conversion failed.
/// </param> /// </param>
/// <returns><c>true</c> if s was converted successfully; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if s was converted successfully; otherwise, <c>false</c>.</returns>
public static bool TryParse(string text, out Style result) public static bool TryParse(string text, out Style? result)
{ {
return StyleParser.TryParse(text, out result); return StyleParser.TryParse(text, out result);
} }
@ -113,13 +113,13 @@ namespace Spectre.Console
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(object obj) public override bool Equals(object? obj)
{ {
return Equals(obj as Style); return Equals(obj as Style);
} }
/// <inheritdoc/> /// <inheritdoc/>
public bool Equals(Style other) public bool Equals(Style? other)
{ {
if (other == null) if (other == null)
{ {