From 8fec3a6e8a4ff1ebc2c6a1eba815a6591d431fbf Mon Sep 17 00:00:00 2001
From: Tatsuro Shibamura <me@shibayan.jp>
Date: Sat, 16 Nov 2024 15:10:20 +0900
Subject: [PATCH] Upgrade to .NET 8 and latest C# (#293)

* Upgrade to .NET 8 and latest C#

* Revert Optional<T> changes
---
 Sharprompt.Example/Program.cs               | 10 +++++-----
 Sharprompt.Tests/PropertyMetadataTests.cs   |  4 ++--
 Sharprompt/BindIgnoreAttribute.cs           |  4 +---
 Sharprompt/Forms/FormRenderer.cs            |  9 ++-------
 Sharprompt/Forms/ListForm.cs                |  2 +-
 Sharprompt/Forms/MultiSelectForm.cs         |  2 +-
 Sharprompt/Internal/EastAsianWidth.cs       |  4 ++--
 Sharprompt/Internal/EnumHelper.cs           |  4 ----
 Sharprompt/Internal/NullItemsProvider.cs    |  3 +--
 Sharprompt/Internal/OffscreenBuffer.cs      |  6 +++---
 Sharprompt/Internal/Optional.cs             |  4 ++--
 Sharprompt/Internal/Paginator.cs            |  2 +-
 Sharprompt/Internal/ValidatorsExtensions.cs |  3 +--
 Sharprompt/ListOptions.cs                   |  3 +--
 Sharprompt/MultiSelectOptions.cs            |  3 +--
 Sharprompt/Prompt.Bind.cs                   |  7 +++----
 Sharprompt/Sharprompt.csproj                |  2 +-
 Sharprompt/Symbol.cs                        | 13 ++-----------
 18 files changed, 30 insertions(+), 55 deletions(-)

diff --git a/Sharprompt.Example/Program.cs b/Sharprompt.Example/Program.cs
index b1bc8390..f2b30e62 100644
--- a/Sharprompt.Example/Program.cs
+++ b/Sharprompt.Example/Program.cs
@@ -54,7 +54,7 @@ static void Main(string[] args)
 
     private static void RunInputSample()
     {
-        var name = Prompt.Input<string>("What's your name?", defaultValue: "John Smith", placeholder: "At least 3 characters", validators: new[] { Validators.Required(), Validators.MinLength(3) });
+        var name = Prompt.Input<string>("What's your name?", defaultValue: "John Smith", placeholder: "At least 3 characters", validators: [Validators.Required(), Validators.MinLength(3)]);
         Console.WriteLine($"Hello, {name}!");
     }
 
@@ -66,19 +66,19 @@ private static void RunConfirmSample()
 
     private static void RunPasswordSample()
     {
-        var secret = Prompt.Password("Type new password", placeholder: "At least 8 characters", validators: new[] { Validators.Required(), Validators.MinLength(8) });
+        var secret = Prompt.Password("Type new password", placeholder: "At least 8 characters", validators: [Validators.Required(), Validators.MinLength(8)]);
         Console.WriteLine($"Password OK, {secret}");
     }
 
     private static void RunSelectSample()
     {
-        var city = Prompt.Select("Select your city", new[] { "Seattle", "London", "Tokyo", "New York", "Singapore", "Shanghai" }, pageSize: 3);
+        var city = Prompt.Select("Select your city", ["Seattle", "London", "Tokyo", "New York", "Singapore", "Shanghai"], pageSize: 3);
         Console.WriteLine($"Hello, {city}!");
     }
 
     private static void RunMultiSelectSample()
     {
-        var options = Prompt.MultiSelect("Which cities would you like to visit?", new[] { "Seattle", "London", "Tokyo", "New York", "Singapore", "Shanghai" }, pageSize: 3, defaultValues: new[] { "Tokyo" });
+        var options = Prompt.MultiSelect("Which cities would you like to visit?", ["Seattle", "London", "Tokyo", "New York", "Singapore", "Shanghai"], pageSize: 3, defaultValues: ["Tokyo"]);
         Console.WriteLine($"You picked {string.Join(", ", options)}");
     }
 
@@ -90,7 +90,7 @@ private static void RunSelectEnumSample()
 
     private static void RunMultiSelectEnumSample()
     {
-        var value = Prompt.MultiSelect<MyEnum>("Select enum value", defaultValues: new[] { MyEnum.Bar });
+        var value = Prompt.MultiSelect<MyEnum>("Select enum value", defaultValues: [MyEnum.Bar]);
         Console.WriteLine($"You picked {string.Join(", ", value)}");
     }
 
diff --git a/Sharprompt.Tests/PropertyMetadataTests.cs b/Sharprompt.Tests/PropertyMetadataTests.cs
index da6799a0..f04bea2c 100644
--- a/Sharprompt.Tests/PropertyMetadataTests.cs
+++ b/Sharprompt.Tests/PropertyMetadataTests.cs
@@ -297,9 +297,9 @@ public class MemberItemsModel
 
         public static IEnumerable<int> GetSelectItems()
         {
-            return new[] { 1, 2, 3, 4, 5 };
+            return [1, 2, 3, 4, 5];
         }
 
-        public static IEnumerable<int> SelectItems => new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+        public static IEnumerable<int> SelectItems => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
     }
 }
diff --git a/Sharprompt/BindIgnoreAttribute.cs b/Sharprompt/BindIgnoreAttribute.cs
index 7ae45f6a..f7eea913 100644
--- a/Sharprompt/BindIgnoreAttribute.cs
+++ b/Sharprompt/BindIgnoreAttribute.cs
@@ -3,6 +3,4 @@
 namespace Sharprompt;
 
 [AttributeUsage(AttributeTargets.Property)]
-public sealed class BindIgnoreAttribute : Attribute
-{
-}
+public sealed class BindIgnoreAttribute : Attribute;
diff --git a/Sharprompt/Forms/FormRenderer.cs b/Sharprompt/Forms/FormRenderer.cs
index afacafd2..afe2d8e1 100644
--- a/Sharprompt/Forms/FormRenderer.cs
+++ b/Sharprompt/Forms/FormRenderer.cs
@@ -5,14 +5,9 @@
 
 namespace Sharprompt.Forms;
 
-internal class FormRenderer : IDisposable
+internal class FormRenderer(IConsoleDriver consoleDriver) : IDisposable
 {
-    public FormRenderer(IConsoleDriver consoleDriver)
-    {
-        _offscreenBuffer = new OffscreenBuffer(consoleDriver);
-    }
-
-    private readonly OffscreenBuffer _offscreenBuffer;
+    private readonly OffscreenBuffer _offscreenBuffer = new(consoleDriver);
 
     public string? ErrorMessage { get; set; }
 
diff --git a/Sharprompt/Forms/ListForm.cs b/Sharprompt/Forms/ListForm.cs
index ae2d00b9..11aafadd 100644
--- a/Sharprompt/Forms/ListForm.cs
+++ b/Sharprompt/Forms/ListForm.cs
@@ -21,7 +21,7 @@ public ListForm(ListOptions<T> options)
 
     private readonly ListOptions<T> _options;
 
-    private readonly List<T> _inputItems = new();
+    private readonly List<T> _inputItems = [];
 
     protected override void InputTemplate(OffscreenBuffer offscreenBuffer)
     {
diff --git a/Sharprompt/Forms/MultiSelectForm.cs b/Sharprompt/Forms/MultiSelectForm.cs
index 0b477a14..3e2adbd4 100644
--- a/Sharprompt/Forms/MultiSelectForm.cs
+++ b/Sharprompt/Forms/MultiSelectForm.cs
@@ -41,7 +41,7 @@ public MultiSelectForm(MultiSelectOptions<T> options)
     private readonly MultiSelectOptions<T> _options;
     private readonly Paginator<T> _paginator;
 
-    private readonly HashSet<T> _selectedItems = new();
+    private readonly HashSet<T> _selectedItems = [];
 
     protected override void InputTemplate(OffscreenBuffer offscreenBuffer)
     {
diff --git a/Sharprompt/Internal/EastAsianWidth.cs b/Sharprompt/Internal/EastAsianWidth.cs
index 81595709..3339ba1d 100644
--- a/Sharprompt/Internal/EastAsianWidth.cs
+++ b/Sharprompt/Internal/EastAsianWidth.cs
@@ -61,7 +61,7 @@ private static bool IsFullWidth(uint codePoint)
     }
 
     private static readonly EastAsianWidthRange[] s_eastAsianWidthRanges =
-    {
+    [
         new(161, 0, true),
         new(164, 0, true),
         new(167, 1, true),
@@ -362,7 +362,7 @@ private static bool IsFullWidth(uint codePoint)
         new(917760, 239, true),
         new(983040, 65533, true),
         new(1048576, 65533, true)
-    };
+    ];
 
     public readonly record struct EastAsianWidthRange(uint Start, ushort Count, bool Ambiguous);
 }
diff --git a/Sharprompt/Internal/EnumHelper.cs b/Sharprompt/Internal/EnumHelper.cs
index 6519abe0..ad2833dc 100644
--- a/Sharprompt/Internal/EnumHelper.cs
+++ b/Sharprompt/Internal/EnumHelper.cs
@@ -10,11 +10,7 @@ internal static class EnumHelper<TEnum> where TEnum : notnull
 {
     static EnumHelper()
     {
-#if NET7_0_OR_GREATER
         var values = (TEnum[])Enum.GetValuesAsUnderlyingType(typeof(TEnum));
-#else
-        var values = (TEnum[])Enum.GetValues(typeof(TEnum));
-#endif
 
         foreach (var value in values)
         {
diff --git a/Sharprompt/Internal/NullItemsProvider.cs b/Sharprompt/Internal/NullItemsProvider.cs
index 78926746..72961600 100644
--- a/Sharprompt/Internal/NullItemsProvider.cs
+++ b/Sharprompt/Internal/NullItemsProvider.cs
@@ -1,12 +1,11 @@
 using System.Collections.Generic;
-using System.Linq;
 using System.Reflection;
 
 namespace Sharprompt.Internal;
 
 internal class NullItemsProvider : IItemsProvider
 {
-    public IEnumerable<T> GetItems<T>(PropertyInfo targetPropertyInfo) where T : notnull => Enumerable.Empty<T>();
+    public IEnumerable<T> GetItems<T>(PropertyInfo targetPropertyInfo) where T : notnull => [];
 
     public static IItemsProvider Instance { get; } = new NullItemsProvider();
 }
diff --git a/Sharprompt/Internal/OffscreenBuffer.cs b/Sharprompt/Internal/OffscreenBuffer.cs
index 1d76d334..da07b172 100644
--- a/Sharprompt/Internal/OffscreenBuffer.cs
+++ b/Sharprompt/Internal/OffscreenBuffer.cs
@@ -16,7 +16,7 @@ public OffscreenBuffer(IConsoleDriver consoleDriver)
     }
 
     private readonly IConsoleDriver _consoleDriver;
-    private readonly List<List<TextInfo>> _outputBuffer = new() { new List<TextInfo>() };
+    private readonly List<List<TextInfo>> _outputBuffer = [new()];
 
     private int _cursorBottom;
     private Cursor? _pushedCursor;
@@ -37,7 +37,7 @@ public void Write(string text, ConsoleColor color)
         _outputBuffer.Last().Add(new TextInfo(text, color));
     }
 
-    public void WriteLine() => _outputBuffer.Add(new List<TextInfo>());
+    public void WriteLine() => _outputBuffer.Add([]);
 
     public void PushCursor()
     {
@@ -105,7 +105,7 @@ public void ClearConsole(int cursorBottom, int writtenLineCount)
     public void ClearBuffer()
     {
         _outputBuffer.Clear();
-        _outputBuffer.Add(new List<TextInfo>());
+        _outputBuffer.Add([]);
 
         _pushedCursor = null;
     }
diff --git a/Sharprompt/Internal/Optional.cs b/Sharprompt/Internal/Optional.cs
index c3aa1fe7..15367c8d 100644
--- a/Sharprompt/Internal/Optional.cs
+++ b/Sharprompt/Internal/Optional.cs
@@ -8,9 +8,9 @@ public Optional(T value)
         Value = value;
     }
 
-    public bool HasValue { get; }
+    public bool HasValue { get; } = false;
 
-    public T Value { get; }
+    public T Value { get; } = default!;
 
     public static readonly Optional<T> Empty = new();
 
diff --git a/Sharprompt/Internal/Paginator.cs b/Sharprompt/Internal/Paginator.cs
index 34b80268..70ae8ebf 100644
--- a/Sharprompt/Internal/Paginator.cs
+++ b/Sharprompt/Internal/Paginator.cs
@@ -21,7 +21,7 @@ public Paginator(IEnumerable<T> items, int pageSize, Optional<T> defaultValue, F
     private readonly Func<T, string> _textSelector;
 
     private int _pageSize;
-    private T[] _filteredItems = Array.Empty<T>();
+    private T[] _filteredItems = [];
     private int _selectedIndex = -1;
 
     public ReadOnlySpan<T> CurrentItems => new(_filteredItems, _pageSize * CurrentPage, Count);
diff --git a/Sharprompt/Internal/ValidatorsExtensions.cs b/Sharprompt/Internal/ValidatorsExtensions.cs
index 776e1236..5de9b581 100644
--- a/Sharprompt/Internal/ValidatorsExtensions.cs
+++ b/Sharprompt/Internal/ValidatorsExtensions.cs
@@ -1,7 +1,6 @@
 using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
-using System.Linq;
 
 namespace Sharprompt.Internal;
 
@@ -9,7 +8,7 @@ internal static class ValidatorsExtensions
 {
     public static void Merge(this IList<Func<object?, ValidationResult?>> source, IEnumerable<Func<object?, ValidationResult?>>? validators)
     {
-        foreach (var validator in validators ?? Enumerable.Empty<Func<object?, ValidationResult?>>())
+        foreach (var validator in validators ?? [])
         {
             source.Add(validator);
         }
diff --git a/Sharprompt/ListOptions.cs b/Sharprompt/ListOptions.cs
index bed2229e..9a00c549 100644
--- a/Sharprompt/ListOptions.cs
+++ b/Sharprompt/ListOptions.cs
@@ -1,7 +1,6 @@
 using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
-using System.Linq;
 
 using Sharprompt.Strings;
 
@@ -11,7 +10,7 @@ public class ListOptions<T> where T : notnull
 {
     public string Message { get; set; } = null!;
 
-    public IEnumerable<T> DefaultValues { get; set; } = Enumerable.Empty<T>();
+    public IEnumerable<T> DefaultValues { get; set; } = [];
 
     public int Minimum { get; set; } = 1;
 
diff --git a/Sharprompt/MultiSelectOptions.cs b/Sharprompt/MultiSelectOptions.cs
index 02e636a0..c7dbdad1 100644
--- a/Sharprompt/MultiSelectOptions.cs
+++ b/Sharprompt/MultiSelectOptions.cs
@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Linq;
 
 using Sharprompt.Internal;
 using Sharprompt.Strings;
@@ -22,7 +21,7 @@ public MultiSelectOptions()
 
     public IEnumerable<T> Items { get; set; } = null!;
 
-    public IEnumerable<T> DefaultValues { get; set; } = Enumerable.Empty<T>();
+    public IEnumerable<T> DefaultValues { get; set; } = [];
 
     public int PageSize { get; set; } = int.MaxValue;
 
diff --git a/Sharprompt/Prompt.Bind.cs b/Sharprompt/Prompt.Bind.cs
index cc44976d..f23c6bcf 100644
--- a/Sharprompt/Prompt.Bind.cs
+++ b/Sharprompt/Prompt.Bind.cs
@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Linq;
 using System.Reflection;
 
 using Sharprompt.Forms;
@@ -77,7 +76,7 @@ private static IEnumerable<T> MakeListCore<T>(PropertyMetadata propertyMetadata)
         return List<T>(options =>
         {
             options.Message = propertyMetadata.Message;
-            options.DefaultValues = (IEnumerable<T>?)propertyMetadata.DefaultValue ?? Enumerable.Empty<T>();
+            options.DefaultValues = (IEnumerable<T>?)propertyMetadata.DefaultValue ?? [];
 
             options.Validators.Merge(propertyMetadata.Validators);
         });
@@ -91,7 +90,7 @@ private static IEnumerable<T> MakeMultiSelectCore<T>(PropertyMetadata propertyMe
         {
             options.Message = propertyMetadata.Message;
             options.Items = propertyMetadata.ItemsProvider.GetItems<T>(propertyMetadata.PropertyInfo);
-            options.DefaultValues = (IEnumerable<T>?)propertyMetadata.DefaultValue ?? Enumerable.Empty<T>();
+            options.DefaultValues = (IEnumerable<T>?)propertyMetadata.DefaultValue ?? [];
         });
     }
 
@@ -123,6 +122,6 @@ private static object InvokeMethod(string name, PropertyMetadata propertyMetadat
         var method = typeof(Prompt).GetMethod(name, BindingFlags.NonPublic | BindingFlags.Static)!
                                    .MakeGenericMethod(genericType ?? propertyMetadata.Type);
 
-        return method.Invoke(null, new object[] { propertyMetadata })!;
+        return method.Invoke(null, [propertyMetadata])!;
     }
 }
diff --git a/Sharprompt/Sharprompt.csproj b/Sharprompt/Sharprompt.csproj
index aa855368..cdbc4639 100644
--- a/Sharprompt/Sharprompt.csproj
+++ b/Sharprompt/Sharprompt.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
+    <TargetFramework>net8.0</TargetFramework>
     <Nullable>enable</Nullable>
   </PropertyGroup>
 
diff --git a/Sharprompt/Symbol.cs b/Sharprompt/Symbol.cs
index 01ea13b2..ff1591b2 100644
--- a/Sharprompt/Symbol.cs
+++ b/Sharprompt/Symbol.cs
@@ -3,18 +3,9 @@
 
 namespace Sharprompt;
 
-public class Symbol
+public class Symbol(string value, string fallbackValue)
 {
-    public Symbol(string value, string fallbackValue)
-    {
-        _value = value;
-        _fallbackValue = fallbackValue;
-    }
-
-    private readonly string _value;
-    private readonly string _fallbackValue;
-
-    public override string ToString() => IsUnicodeSupported ? _value : _fallbackValue;
+    public override string ToString() => IsUnicodeSupported ? value : fallbackValue;
 
     public static implicit operator string(Symbol symbol) => symbol.ToString();