diff --git a/src/.editorconfig b/.editorconfig
similarity index 100%
rename from src/.editorconfig
rename to .editorconfig
diff --git a/src/NuGet.config b/NuGet.config
similarity index 100%
rename from src/NuGet.config
rename to NuGet.config
diff --git a/src/dotnet-repl.sln b/dotnet-repl.sln
similarity index 94%
rename from src/dotnet-repl.sln
rename to dotnet-repl.sln
index 5a90616..669a376 100644
--- a/src/dotnet-repl.sln
+++ b/dotnet-repl.sln
@@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31112.23
MinimumVisualStudioVersion = 15.0.26124.0
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-repl", "dotnet-repl\dotnet-repl.csproj", "{D84A3673-0973-4C16-B0A1-1E00BD9146A3}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-repl", "src\dotnet-repl\dotnet-repl.csproj", "{D84A3673-0973-4C16-B0A1-1E00BD9146A3}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-repl.Tests", "dotnet-repl.Tests\dotnet-repl.Tests.csproj", "{77D12413-604B-47ED-85B0-3CC253AFA9A4}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-repl.Tests", "src\dotnet-repl.Tests\dotnet-repl.Tests.csproj", "{77D12413-604B-47ED-85B0-3CC253AFA9A4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7D05BFEB-F647-47E1-B50A-BACE5005B627}"
EndProject
diff --git a/src/dotnet-repl.sln.DotSettings b/dotnet-repl.sln.DotSettings
similarity index 100%
rename from src/dotnet-repl.sln.DotSettings
rename to dotnet-repl.sln.DotSettings
diff --git a/src/dotnet-repl.v3.ncrunchsolution b/dotnet-repl.v3.ncrunchsolution
similarity index 100%
rename from src/dotnet-repl.v3.ncrunchsolution
rename to dotnet-repl.v3.ncrunchsolution
diff --git a/src/dotnet-repl.Tests/Automation/NotebookRunnerTests.cs b/src/dotnet-repl.Tests/Automation/NotebookRunnerTests.cs
index c0294e1..ebad568 100644
--- a/src/dotnet-repl.Tests/Automation/NotebookRunnerTests.cs
+++ b/src/dotnet-repl.Tests/Automation/NotebookRunnerTests.cs
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
-using System.CommandLine.Invocation;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
@@ -49,22 +48,23 @@ public NotebookRunnerTests()
[Fact]
public async Task When_an_ipynb_is_run_and_no_error_is_produced_then_the_exit_code_is_0()
{
- var parseResult =_rootCommand.Parse($"--run \"{_directory}/succeed.ipynb\" --exit-after-run");
- parseResult.Configuration.Error = new StringWriter();
- var result = await ((AsynchronousCommandLineAction)_rootCommand.Action).InvokeAsync(parseResult);
+ var error = new StringWriter();
+ var result = await _rootCommand
+ .Parse($"--run \"{_directory}/succeed.ipynb\" --exit-after-run")
+ .InvokeAsync(new() { Output = new StringWriter(), Error = error });
- parseResult.Configuration.Error.ToString().Should().BeEmpty();
+ error.ToString().Should().BeEmpty();
result.Should().Be(0);
}
[Fact]
public async Task When_an_ipynb_is_run_and_an_error_is_produced_from_a_cell_then_the_exit_code_is_2()
{
- var parseResult = _rootCommand.Parse($"--run \"{_directory}/fail.ipynb\" --exit-after-run");
- parseResult.Configuration.Error = new StringWriter();
- var result = await ((AsynchronousCommandLineAction)_rootCommand.Action).InvokeAsync(parseResult);
+ var error = new StringWriter();
+ var result = await _rootCommand
+ .Parse($"--run \"{_directory}/fail.ipynb\" --exit-after-run").InvokeAsync(new() { Output = new StringWriter(), Error = error });
- parseResult.Configuration.Error.ToString().Should().BeEmpty();
+ error.ToString().Should().BeEmpty();
result.Should().Be(2);
}
diff --git a/src/dotnet-repl.Tests/CommandLineParserTests.cs b/src/dotnet-repl.Tests/CommandLineParserTests.cs
index 65efbe4..71b612a 100644
--- a/src/dotnet-repl.Tests/CommandLineParserTests.cs
+++ b/src/dotnet-repl.Tests/CommandLineParserTests.cs
@@ -1,15 +1,14 @@
+using dotnet_repl.Tests.Utility;
+using FluentAssertions;
using System;
using System.CommandLine;
-using System.CommandLine.Invocation;
using System.IO;
using System.Linq;
-using System.Threading.Tasks;
-using FluentAssertions;
using Xunit;
namespace dotnet_repl.Tests;
-public class CommandLineParserTests
+public partial class CommandLineParserTests
{
private readonly RootCommand _rootCommand;
@@ -22,10 +21,9 @@ public CommandLineParserTests()
public void Help_is_snazzy()
{
var parseResult = _rootCommand.Parse("-h");
- parseResult.Configuration.Output = new StringWriter();
- ((SynchronousCommandLineAction)parseResult.Action).Invoke(parseResult);
+ parseResult.Invoke(new() { Output = new StringWriter() });
- var outputLines = parseResult.Configuration
+ var outputLines = parseResult.InvocationConfiguration
.Output
.ToString()
.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries)
@@ -34,19 +32,19 @@ public void Help_is_snazzy()
string.Join('\n', outputLines)
.Should().Contain(
"""
- [38;5;215m _ _ _____ _____ ____ _____ ____ _ [0m
- [38;5;215m | \ | | | ____| |_ _| | _ \ | ____| | _ \ | | [0m
- [38;5;215m | \| | | _| | | | |_) | | _| | |_) | | | [0m
- [38;5;215m _ | |\ | | |___ | | | _ < | |___ | __/ | |___ [0m
- [38;5;215m (_) |_| \_| |_____| |_| |_| \_\ |_____| |_| |_____|[0m
- [38;5;215m [0m
- """.Replace("\r", ""));
+ [38;5;215m _ _ _____ _____ ____ _____ ____ _ [0m
+ [38;5;215m | \ | | | ____| |_ _| | _ \ | ____| | _ \ | | [0m
+ [38;5;215m | \| | | _| | | | |_) | | _| | |_) | | | [0m
+ [38;5;215m _ | |\ | | |___ | | | _ < | |___ | __/ | |___ [0m
+ [38;5;215m (_) |_| \_| |_____| |_| |_| \_\ |_____| |_| |_____|[0m
+ [38;5;215m [0m
+ """.Replace("\r", ""));
}
[Fact]
public void Parser_configuration_is_valid()
{
- _rootCommand.Parse("").Configuration.ThrowIfInvalid();
+ _rootCommand.ThrowIfInvalid();
}
[Fact]
diff --git a/src/dotnet-repl.Tests/Utility/CommandExtensions.cs b/src/dotnet-repl.Tests/Utility/CommandExtensions.cs
new file mode 100644
index 0000000..44e28cf
--- /dev/null
+++ b/src/dotnet-repl.Tests/Utility/CommandExtensions.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.CommandLine;
+using System.Linq;
+
+namespace dotnet_repl.Tests.Utility;
+
+public static class CommandExtensions
+{
+ ///
+ /// Throws an exception if the parser configuration is ambiguous or otherwise not valid.
+ ///
+ /// Due to the performance cost of this method, it is recommended to be used in unit testing or in scenarios where the parser is configured dynamically at runtime.
+ /// Thrown if the configuration is found to be invalid.
+ public static void ThrowIfInvalid(this Command command)
+ {
+ if (command.Parents.FlattenBreadthFirst(c => c.Parents).Any(ancestor => ancestor == command))
+ {
+ throw new InvalidOperationException($"Cycle detected in command tree. Command '{command.Name}' is its own ancestor.");
+ }
+
+ int count = command.Subcommands.Count + command.Options.Count;
+ for (var i = 0; i < count; i++)
+ {
+ Symbol symbol1 = GetChild(i, command, out HashSet aliases1);
+
+ for (var j = i + 1; j < count; j++)
+ {
+ Symbol symbol2 = GetChild(j, command, out HashSet aliases2);
+
+ if (symbol1.Name.Equals(symbol2.Name, StringComparison.Ordinal)
+ || aliases1 is not null && aliases1.Contains(symbol2.Name))
+ {
+ throw new InvalidOperationException($"Duplicate alias '{symbol2.Name}' found on command '{command.Name}'.");
+ }
+ else if (aliases2 is not null && aliases2.Contains(symbol1.Name))
+ {
+ throw new InvalidOperationException($"Duplicate alias '{symbol1.Name}' found on command '{command.Name}'.");
+ }
+
+ if (aliases1 is not null && aliases2 is not null)
+ {
+ // take advantage of the fact that we are dealing with two hash sets
+ if (aliases1.Overlaps(aliases2))
+ {
+ foreach (string symbol2Alias in aliases2)
+ {
+ if (aliases1.Contains(symbol2Alias))
+ {
+ throw new InvalidOperationException($"Duplicate alias '{symbol2Alias}' found on command '{command.Name}'.");
+ }
+ }
+ }
+ }
+ }
+
+ if (symbol1 is Command childCommand)
+ {
+ childCommand.ThrowIfInvalid();
+ }
+ }
+
+ static Symbol GetChild(int index, Command command, out HashSet aliases)
+ {
+ if (index < command.Subcommands.Count)
+ {
+ aliases = command.Subcommands[index].Aliases.ToHashSet();
+ return command.Subcommands[index];
+ }
+
+ aliases = command.Options[index - command.Subcommands.Count].Aliases.ToHashSet();
+ return command.Options[index - command.Subcommands.Count];
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/dotnet-repl.Tests/Utility/EnumerableExtensions.cs b/src/dotnet-repl.Tests/Utility/EnumerableExtensions.cs
new file mode 100644
index 0000000..6661827
--- /dev/null
+++ b/src/dotnet-repl.Tests/Utility/EnumerableExtensions.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+
+namespace dotnet_repl.Tests.Utility;
+
+internal static class EnumerableExtensions
+{
+ internal static IEnumerable FlattenBreadthFirst(
+ this IEnumerable source,
+ Func> children)
+ {
+ var queue = new Queue();
+
+ foreach (var item in source)
+ {
+ queue.Enqueue(item);
+ }
+
+ while (queue.Count > 0)
+ {
+ var current = queue.Dequeue();
+
+ foreach (var option in children(current))
+ {
+ queue.Enqueue(option);
+ }
+
+ yield return current;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/dotnet-repl.Tests/Utility/ParserConfigurationValidationTests.cs b/src/dotnet-repl.Tests/Utility/ParserConfigurationValidationTests.cs
new file mode 100644
index 0000000..ad9ad90
--- /dev/null
+++ b/src/dotnet-repl.Tests/Utility/ParserConfigurationValidationTests.cs
@@ -0,0 +1,243 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.CommandLine;
+using FluentAssertions;
+using Xunit;
+
+namespace dotnet_repl.Tests.Utility;
+
+public class CommandLineConfigurationValidationTests
+{
+ [Fact]
+ public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_option_aliases_on_the_root_command()
+ {
+ var option1 = new Option("--dupe");
+ var option2 = new Option("-y");
+ option2.Aliases.Add("--dupe");
+
+ var command = new RootCommand
+ {
+ option1,
+ option2
+ };
+
+ var validate = () => command.ThrowIfInvalid();
+
+ validate.Should()
+ .Throw()
+ .Which
+ .Message
+ .Should()
+ .Be($"Duplicate alias '--dupe' found on command '{command.Name}'.");
+ }
+
+ [Fact]
+ public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_option_aliases_on_a_subcommand()
+ {
+ var option1 = new Option("--dupe");
+ var option2 = new Option("--ok");
+ option2.Aliases.Add("--dupe");
+
+ var command = new RootCommand
+ {
+ new Command("subcommand")
+ {
+ option1,
+ option2
+ }
+ };
+
+ var validate = () => command.ThrowIfInvalid();
+
+ validate.Should()
+ .Throw()
+ .Which
+ .Message
+ .Should()
+ .Be("Duplicate alias '--dupe' found on command 'subcommand'.");
+ }
+
+ [Fact]
+ public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_subcommand_aliases_on_the_root_command()
+ {
+ var command1 = new Command("dupe");
+ var command2 = new Command("not-a-dupe");
+ command2.Aliases.Add("dupe");
+
+ var rootCommand = new RootCommand
+ {
+ command1,
+ command2
+ };
+
+ var validate = () => rootCommand.ThrowIfInvalid();
+
+ validate.Should()
+ .Throw()
+ .Which
+ .Message
+ .Should()
+ .Be($"Duplicate alias 'dupe' found on command '{rootCommand.Name}'.");
+ }
+
+ [Fact]
+ public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_subcommand_aliases_on_a_subcommand()
+ {
+ var command = new RootCommand
+ {
+ new Command("subcommand")
+ {
+ new Command("dupe"),
+ new Command("not-a-dupe") { Aliases = { "dupe" } }
+ }
+ };
+
+ var validate = () => command.ThrowIfInvalid();
+
+ validate.Should()
+ .Throw()
+ .Which
+ .Message
+ .Should()
+ .Be("Duplicate alias 'dupe' found on command 'subcommand'.");
+ }
+
+ [Fact]
+ public void ThrowIfInvalid_throws_if_sibling_command_and_option_aliases_collide_on_the_root_command()
+ {
+ var option = new Option("dupe");
+ var command = new Command("not-a-dupe");
+ command.Aliases.Add("dupe");
+
+ var rootCommand = new RootCommand
+ {
+ option,
+ command
+ };
+
+ var validate = () => rootCommand.ThrowIfInvalid();
+
+ validate.Should()
+ .Throw()
+ .Which
+ .Message
+ .Should()
+ .Be($"Duplicate alias 'dupe' found on command '{rootCommand.Name}'.");
+ }
+
+ [Fact]
+ public void ThrowIfInvalid_throws_if_sibling_command_and_option_aliases_collide_on_a_subcommand()
+ {
+ var option = new Option("dupe");
+ var command = new Command("not-a-dupe");
+ command.Aliases.Add("dupe");
+
+ var rootCommand = new RootCommand
+ {
+ new Command("subcommand")
+ {
+ option,
+ command
+ }
+ };
+
+ var validate = () => rootCommand.ThrowIfInvalid();
+
+ validate.Should()
+ .Throw()
+ .Which
+ .Message
+ .Should()
+ .Be("Duplicate alias 'dupe' found on command 'subcommand'.");
+ }
+
+ [Fact]
+ public void ThrowIfInvalid_throws_if_there_are_duplicate_sibling_global_option_aliases_on_the_root_command()
+ {
+ var option1 = new Option("--dupe") { Recursive = true };
+ var option2 = new Option("-y") { Recursive = true };
+ option2.Aliases.Add("--dupe");
+
+ var command = new RootCommand();
+ command.Options.Add(option1);
+ command.Options.Add(option2);
+
+ var validate = () => command.ThrowIfInvalid();
+
+ validate.Should()
+ .Throw()
+ .Which
+ .Message
+ .Should()
+ .Be($"Duplicate alias '--dupe' found on command '{command.Name}'.");
+ }
+
+ [Fact]
+ public void ThrowIfInvalid_does_not_throw_if_global_option_alias_is_the_same_as_local_option_alias()
+ {
+ var rootCommand = new RootCommand
+ {
+ new Command("subcommand")
+ {
+ new Option("--dupe")
+ }
+ };
+ rootCommand.Options.Add(new Option("--dupe") { Recursive = true });
+
+ var validate = () => rootCommand.ThrowIfInvalid();
+
+ validate.Should().NotThrow();
+ }
+
+ [Fact]
+ public void ThrowIfInvalid_does_not_throw_if_global_option_alias_is_the_same_as_subcommand_alias()
+ {
+ var rootCommand = new RootCommand
+ {
+ new Command("subcommand")
+ {
+ new Command("--dupe")
+ }
+ };
+ rootCommand.Options.Add(new Option("--dupe") { Recursive = true });
+
+ var validate = () => rootCommand.ThrowIfInvalid();
+
+ validate.Should().NotThrow();
+ }
+
+ [Fact]
+ public void ThrowIfInvalid_throws_if_a_command_is_its_own_parent()
+ {
+ var command = new RootCommand();
+ command.Add(command);
+
+ var validate = () => command.ThrowIfInvalid();
+
+ validate.Should()
+ .Throw()
+ .Which
+ .Message
+ .Should()
+ .Be($"Cycle detected in command tree. Command '{command.Name}' is its own ancestor.");
+ }
+
+ [Fact]
+ public void ThrowIfInvalid_throws_if_a_parentage_cycle_is_detected()
+ {
+ var command = new Command("command");
+ var rootCommand = new RootCommand { command };
+ command.Add(rootCommand);
+
+ var validate = () => command.ThrowIfInvalid();
+
+ validate.Should()
+ .Throw()
+ .Which
+ .Message
+ .Should()
+ .Be($"Cycle detected in command tree. Command '{command.Name}' is its own ancestor.");
+ }
+}
\ No newline at end of file
diff --git a/src/dotnet-repl.Tests/dotnet-repl.Tests.csproj b/src/dotnet-repl.Tests/dotnet-repl.Tests.csproj
index 8f1c6ae..28e1102 100644
--- a/src/dotnet-repl.Tests/dotnet-repl.Tests.csproj
+++ b/src/dotnet-repl.Tests/dotnet-repl.Tests.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/src/dotnet-repl/CommandLineParser.cs b/src/dotnet-repl/CommandLineParser.cs
index de0ee3c..437b4ec 100644
--- a/src/dotnet-repl/CommandLineParser.cs
+++ b/src/dotnet-repl/CommandLineParser.cs
@@ -1,16 +1,18 @@
-using System;
+
+using System;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Help;
+using System.CommandLine.Invocation;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Automation;
+using Spectre.Console;
+using Pocket;
using Microsoft.DotNet.Interactive.Documents;
using Microsoft.DotNet.Interactive.Documents.Jupyter;
-using Pocket;
-using Spectre.Console;
+using Automation;
namespace dotnet_repl;
@@ -109,8 +111,10 @@ public static RootCommand Create(
DescribeCommand(),
};
- var helpOption = rootCommand.Options.OfType().Single();
- ((HelpAction)helpOption.Action).Builder = new SpectreHelpBuilder();
+ rootCommand.Options
+ .OfType()
+ .Single()
+ .Action = new SpectreHelpAction();
startRepl ??= StartAsync;
@@ -271,6 +275,6 @@ await repl.RunAsync(
: 0;
}
- return 0;
+
}
-}
\ No newline at end of file
+}
diff --git a/src/dotnet-repl/Program.cs b/src/dotnet-repl/Program.cs
index 8b3631a..a2ae506 100644
--- a/src/dotnet-repl/Program.cs
+++ b/src/dotnet-repl/Program.cs
@@ -1,5 +1,4 @@
using System;
-using System.CommandLine.Parsing;
using System.IO;
using System.Reflection;
using System.Text;
diff --git a/src/dotnet-repl/SpectreHelpAction.cs b/src/dotnet-repl/SpectreHelpAction.cs
new file mode 100644
index 0000000..7acdc8f
--- /dev/null
+++ b/src/dotnet-repl/SpectreHelpAction.cs
@@ -0,0 +1,15 @@
+using System.CommandLine;
+using System.CommandLine.Invocation;
+using dotnet_repl;
+
+public class SpectreHelpAction : SynchronousCommandLineAction
+{
+ public override int Invoke(ParseResult parseResult)
+ {
+ var output = parseResult.InvocationConfiguration.Output;
+
+ new SpectreHelpBuilder().ShowHelp(parseResult.CommandResult.Command, output);
+
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/src/dotnet-repl/SpectreHelpBuilder.cs b/src/dotnet-repl/SpectreHelpBuilder.cs
index 03f04ae..e2ddb64 100644
--- a/src/dotnet-repl/SpectreHelpBuilder.cs
+++ b/src/dotnet-repl/SpectreHelpBuilder.cs
@@ -1,149 +1,126 @@
-using Spectre.Console;
-using System;
-using System.Collections.Generic;
-using System.CommandLine;
+using System.CommandLine;
using System.CommandLine.Completions;
-using System.CommandLine.Help;
+using System.IO;
using System.Linq;
-using HelpSectionDelegate = System.Func;
+using Spectre.Console;
namespace dotnet_repl;
-internal class SpectreHelpBuilder : HelpBuilder
+internal class SpectreHelpBuilder
{
- public SpectreHelpBuilder(int maxWidth = 2147483647) : base(maxWidth)
+ private readonly int _maxWidth;
+
+ public SpectreHelpBuilder(int maxWidth = int.MaxValue)
{
- Func> getLayout = GetLayout;
- CustomizeLayout(getLayout);
+ _maxWidth = maxWidth;
}
- private IEnumerable GetLayout(HelpContext context)
+ public void ShowHelp(Command command, TextWriter output)
{
- if (context.ParseResult.Errors.Any())
- {
- // don't show help on error
- yield break;
- }
-
- var console = AnsiConsole.Create(new AnsiConsoleSettings
+ var spectreConsole = AnsiConsole.Create(new AnsiConsoleSettings
{
Ansi = AnsiSupport.Yes,
- Out = new AnsiConsoleOutput(context.Output)
+ Out = new AnsiConsoleOutput(output)
});
- yield return TitleSection(console);
+ TitleSection(spectreConsole);
+ CommandUsageSection(spectreConsole, command);
+ OptionsSection(spectreConsole, command);
+ ReplHelpSection(spectreConsole, command);
+ }
+
+ private void TitleSection(IAnsiConsole console)
+ {
+ var panel = new Grid();
+ panel.AddColumn(new GridColumn());
+ var figletText = new FigletText(".NET REPL").Color(Color.SandyBrown);
+ figletText.Justification = Justify.Center;
+ panel.AddRow(figletText);
+ console.Write(panel);
+ }
- yield return CommandUsageSection(console);
+ private void CommandUsageSection(IAnsiConsole console, Command command)
+ {
+ if (command is RootCommand)
+ {
+ console.Write(new Markup("🔵[sandybrown italic] Start the REPL like this:[/]\n\n"));
+ }
- yield return OptionsSection(console);
+ var panel = new Panel($"{command.Name} [[[Magenta1]options[/]]]")
+ .NoBorder()
+ .Expand();
- yield return ReplHelpSection(console);
+ console.Write(panel);
}
- private HelpSectionDelegate TitleSection(IAnsiConsole console) =>
- _ =>
- {
- var panel = new Grid();
- panel.AddColumn(new GridColumn());
- var figletText = new FigletText(".NET REPL").Color(Color.SandyBrown);
- figletText.Justification = Justify.Center;
- panel.AddRow(figletText);
- console.Write(panel);
- return true;
- };
-
- private HelpSectionDelegate CommandUsageSection(IAnsiConsole console) =>
- ctx =>
+ private void OptionsSection(IAnsiConsole console, Command command)
+ {
+ if (!command.Options.Any())
{
- if (ctx.Command is RootCommand)
- {
- console.Write(new Markup("🔵[sandybrown italic] Start the REPL like this:[/]\n\n"));
- }
+ return;
+ }
- var panel = new Panel($"{ctx.Command.Name} [[[Magenta1]options[/]]]")
- .NoBorder()
- .Expand();
+ var table = new Table()
+ .AddColumn("[magenta1 italic]Option[/]")
+ .AddColumn("[magenta1 italic]Description[/]")
+ .BorderColor(Color.Magenta1);
- console.Write(panel);
+ foreach (var option in command.Options)
+ {
+ var aliases = string.Join(", ", option.Aliases
+ .Concat(new[] { option.Name })
+ .Where(a => !a.StartsWith("/"))
+ .OrderBy(a => a.Length));
+
+ table.AddRow(
+ $"{aliases} {OptionArgumentHelpName(option)}",
+ option.Description ?? "");
+ }
- return true;
- };
+ console.Write(table);
- private HelpSectionDelegate OptionsSection(IAnsiConsole console) =>
- ctx =>
+ string OptionArgumentHelpName(Option option)
{
- if (!ctx.Command.Options.Any())
+ if (option.HelpName is not null)
{
- return false;
+ return InAngleBrackets($"{option.HelpName}");
}
- var table = new Table()
- .AddColumn("[magenta1 italic]Option[/]")
- .AddColumn("[magenta1 italic]Description[/]")
- .BorderColor(Color.Magenta1);
-
- foreach (var option in ctx.Command.Options)
+ if (option.ValueType == typeof(bool))
{
- var aliases = string.Join(", ", option.Aliases
- .Concat([option.Name])
- .Where(a => !a.StartsWith("/"))
- .OrderBy(a => a.Length));
-
- table.AddRow(
- $"{aliases} {OptionArgumentHelpName(option)}",
- option.Description ?? "");
+ return "";
}
- console.Write(table);
-
- return true;
-
- string OptionArgumentHelpName(Option option)
+ var completions = option.GetCompletions(CompletionContext.Empty).ToArray();
+ if (completions.Length > 0)
{
- if (option.HelpName is not null)
- {
- return InAngleBrackets($"{option.HelpName}");
- }
-
- if (option.ValueType == typeof(bool))
- {
- return "";
- }
-
- var completions = option.GetCompletions(CompletionContext.Empty).ToArray();
- if (completions.Length > 0)
- {
- return InAngleBrackets($"{string.Join("[gray]|[/]", completions.Select(c => c.Label)).Replace("csharp", "[bold aqua]csharp[/]")}");
- }
-
- return InAngleBrackets(option.Name.ToUpper());
+ return InAngleBrackets($"{string.Join("[gray]|[/]", completions.Select(c => c.Label)).Replace("csharp", "[bold aqua]csharp[/]")}");
}
- string InAngleBrackets(string value)
- {
- return $"[gray]<[/]{value}[gray]>[/]";
- }
- };
+ return InAngleBrackets(option.Name.ToUpper());
+ }
- private static HelpSectionDelegate ReplHelpSection(IAnsiConsole console)
- {
- return ctx =>
+ string InAngleBrackets(string value)
{
- if (ctx.Command is not RootCommand)
- {
- return false;
- }
+ return $"[gray]<[/]{value}[gray]>[/]";
+ }
+ }
- console.Write(new Markup("🟢[sandybrown italic] Once it's running, here are some things you can do:[/]\n\n"));
+ private void ReplHelpSection(IAnsiConsole console, Command command)
+ {
+ if (command is not RootCommand)
+ {
+ return;
+ }
- var grid = new Grid();
- grid.AddColumn();
+ console.Write(new Markup("🟢[sandybrown italic] Once it's running, here are some things you can do:[/]\n\n"));
- grid.ShowShortcutKeys();
+ var grid = new Grid();
+ grid.AddColumn();
- console.Announce(grid);
+ // Add your REPL shortcut keys or help rows here
+ // grid.AddRow(...);
- return true;
- };
+ console.Write(grid);
}
}
\ No newline at end of file
diff --git a/src/dotnet-repl/dotnet-repl.csproj b/src/dotnet-repl/dotnet-repl.csproj
index a5fe1ae..3dd8d35 100644
--- a/src/dotnet-repl/dotnet-repl.csproj
+++ b/src/dotnet-repl/dotnet-repl.csproj
@@ -38,7 +38,7 @@
-
+
all