Skip to content

Commit fa2dec9

Browse files
authored
dotnet nuget why: write target package in graph (#6975) (#6990)
1 parent 1281d70 commit fa2dec9

15 files changed

Lines changed: 253 additions & 209 deletions

File tree

Directory.Packages.props

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@
8484
<PackageVersion Include="NuGet.Core" Version="2.14.0-rtm-832" />
8585
<PackageVersion Include="NuGetValidator" version="2.1.1" />
8686
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
87+
<PackageVersion Include="Spectre.Console" Version="0.54.0" />
88+
<PackageVersion Include="Spectre.Console.Testing" Version="0.54.0" />
8789
<PackageVersion Include="System.Collections.Immutable" Version="8.0.0" />
8890
<PackageVersion Include="System.CommandLine" Version="$(SystemCommandLineVersion)" />
8991
<PackageVersion Include="System.ComponentModel.Composition" Version="$(SystemComponentModelCompositionPackageVersion)" />
@@ -179,6 +181,7 @@
179181
<_allowBuildFromSourcePackage Include="Microsoft.Extensions.FileSystemGlobbing" />
180182
<_allowBuildFromSourcePackage Include="Microsoft.Web.Xdt" />
181183
<_allowBuildFromSourcePackage Include="Newtonsoft.Json" />
184+
<_allowBuildFromSourcePackage Include="Spectre.Console" />
182185
<_allowBuildFromSourcePackage Include="System.Collections.Immutable" />
183186
<_allowBuildFromSourcePackage Include="System.CommandLine" />
184187
<_allowBuildFromSourcePackage Include="System.ComponentModel.Composition" />

NuGet.Config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
<package pattern="nuget.*" />
4242
<package pattern="runtime.*" />
4343
<package pattern="sharpziplib" />
44+
<package pattern="Spectre.*" />
4445
<package pattern="streamjsonrpc" />
4546
<package pattern="system.*" />
4647
<package pattern="Validation" />

NuGet.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
1919
build\config.props = build\config.props
2020
Directory.Packages.props = Directory.Packages.props
2121
build\DotNetSdkVersions.txt = build\DotNetSdkVersions.txt
22+
NuGet.Config = NuGet.Config
2223
build\sign.targets = build\sign.targets
2324
spelling.dic = spelling.dic
2425
build\test.targets = build\test.targets

src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Why/DependencyGraphPrinter.cs

Lines changed: 40 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,25 @@
77
using System.Collections.Generic;
88
using System.Linq;
99
using NuGet.Shared;
10+
using Spectre.Console;
11+
using Spectre.Console.Rendering;
1012

1113
namespace NuGet.CommandLine.XPlat.Commands.Why
1214
{
1315
internal static class DependencyGraphPrinter
1416
{
15-
private const ConsoleColor TargetPackageColor = ConsoleColor.Cyan;
16-
17-
// Dependency graph console output symbols
18-
private const string ChildNodeSymbol = "├─ ";
19-
private const string LastChildNodeSymbol = "└─ ";
20-
21-
private const string ChildPrefixSymbol = "│ ";
22-
private const string LastChildPrefixSymbol = " ";
17+
private static readonly Color TargetPackageColor = Color.Cyan;
2318

2419
/// <summary>
2520
/// Prints the dependency graphs for all target frameworks.
2621
/// </summary>
2722
/// <param name="dependencyGraphPerFramework">A dictionary mapping target frameworks to their dependency graphs.</param>
2823
/// <param name="targetPackage">The package we want the dependency paths for.</param>
2924
/// <param name="logger"></param>
30-
public static void PrintAllDependencyGraphs(Dictionary<string, List<DependencyNode>?> dependencyGraphPerFramework, string targetPackage, ILoggerWithColor logger)
25+
public static void PrintAllDependencyGraphs(Dictionary<string, List<DependencyNode>?> dependencyGraphPerFramework, string targetPackage, IAnsiConsole logger)
3126
{
3227
// print empty line
33-
logger.LogMinimal("");
28+
logger.WriteLine();
3429

3530
// deduplicate the dependency graphs
3631
List<List<string>> deduplicatedFrameworks = GetDeduplicatedFrameworks(dependencyGraphPerFramework);
@@ -51,71 +46,67 @@ public static void PrintAllDependencyGraphs(Dictionary<string, List<DependencyNo
5146
/// <param name="topLevelNodes">The top-level package nodes of the dependency graph.</param>
5247
/// <param name="targetPackage">The package we want the dependency paths for.</param>
5348
/// <param name="logger"></param>
54-
private static void PrintDependencyGraphPerFramework(List<string> frameworks, List<DependencyNode>? topLevelNodes, string targetPackage, ILoggerWithColor logger)
49+
private static void PrintDependencyGraphPerFramework(List<string> frameworks, List<DependencyNode>? topLevelNodes, string targetPackage, IAnsiConsole logger)
5550
{
56-
// print framework header
57-
foreach (var framework in frameworks)
58-
{
59-
logger.LogMinimal($" [{framework}]");
60-
}
61-
62-
logger.LogMinimal($" {ChildPrefixSymbol}");
51+
var tree = new Tree(string.Join("\n", frameworks.Select(f => $"[[{f}]]")));
6352

6453
if (topLevelNodes == null || topLevelNodes.Count == 0)
6554
{
66-
logger.LogMinimal($" {LastChildNodeSymbol}{Strings.WhyCommand_Message_NoDependencyGraphsFoundForFramework}\n\n");
55+
tree.AddNode(Strings.WhyCommand_Message_NoDependencyGraphsFoundForFramework);
56+
logger.Write(PadTree(tree));
6757
return;
6858
}
6959

7060
var stack = new Stack<StackOutputData>();
7161

7262
// initialize the stack with all top-level nodes
73-
int counter = 0;
7463
foreach (var node in topLevelNodes.OrderByDescending(c => c.Id, StringComparer.OrdinalIgnoreCase))
7564
{
76-
stack.Push(new StackOutputData(node, prefix: " ", isLastChild: counter++ == 0));
65+
stack.Push(new StackOutputData
66+
{
67+
Node = node,
68+
ParentNode = tree
69+
});
7770
}
7871

7972
// print the dependency graph
8073
while (stack.Count > 0)
8174
{
8275
var current = stack.Pop();
8376

84-
string currentPrefix, childPrefix;
85-
if (current.IsLastChild)
86-
{
87-
currentPrefix = current.Prefix + LastChildNodeSymbol;
88-
childPrefix = current.Prefix + LastChildPrefixSymbol;
89-
}
90-
else
91-
{
92-
currentPrefix = current.Prefix + ChildNodeSymbol;
93-
childPrefix = current.Prefix + ChildPrefixSymbol;
94-
}
95-
96-
// print current node
97-
if (current.Node.Id.Equals(targetPackage, StringComparison.OrdinalIgnoreCase))
98-
{
99-
logger.LogMinimal($"{currentPrefix}", Console.ForegroundColor);
100-
logger.LogMinimal($"{current.Node.Id} (v{current.Node.Version})\n", TargetPackageColor);
101-
}
102-
else
103-
{
104-
logger.LogMinimal($"{currentPrefix}{current.Node.Id} (v{current.Node.Version})");
105-
}
77+
var treeNodeText = GetNodeText(current.Node, targetPackage);
78+
var treeNode = current.ParentNode.AddNode(treeNodeText);
10679

10780
if (current.Node.Children?.Count > 0)
10881
{
109-
// push all the node's children onto the stack
110-
counter = 0;
11182
foreach (var child in current.Node.Children.OrderByDescending(c => c.Id, StringComparer.OrdinalIgnoreCase))
11283
{
113-
stack.Push(new StackOutputData(child, childPrefix, isLastChild: counter++ == 0));
84+
stack.Push(new StackOutputData
85+
{
86+
Node = child,
87+
ParentNode = treeNode
88+
});
11489
}
11590
}
11691
}
11792

118-
logger.LogMinimal("");
93+
logger.Write(PadTree(tree));
94+
logger.WriteLine();
95+
}
96+
97+
private static IRenderable GetNodeText(DependencyNode node, string targetPackage)
98+
{
99+
string text = $"{node.Id} (v{node.Version})";
100+
Style? style = node.Id.Equals(targetPackage, StringComparison.OrdinalIgnoreCase)
101+
? new Style(foreground: TargetPackageColor)
102+
: null;
103+
104+
return new Text(text, style);
105+
}
106+
107+
private static IRenderable PadTree(Tree tree)
108+
{
109+
return new Padder(tree, new Padding(left: 2, 0, 0, 0));
119110
}
120111

121112
/// <summary>
@@ -173,16 +164,9 @@ private static int GetDependencyGraphHashCode(List<DependencyNode>? graph)
173164

174165
private class StackOutputData
175166
{
176-
public DependencyNode Node { get; set; }
177-
public string Prefix { get; set; }
178-
public bool IsLastChild { get; set; }
167+
public required DependencyNode Node { get; init; }
179168

180-
public StackOutputData(DependencyNode node, string prefix, bool isLastChild)
181-
{
182-
Node = node;
183-
Prefix = prefix;
184-
IsLastChild = isLastChild;
185-
}
169+
public required IHasTreeNodes ParentNode { get; init; }
186170
}
187171
}
188172
}

src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Why/WhyCommand.cs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.IO;
1010
using System.Threading.Tasks;
1111
using Microsoft.Extensions.CommandLineUtils;
12+
using Spectre.Console;
1213

1314
namespace NuGet.CommandLine.XPlat.Commands.Why
1415
{
@@ -22,9 +23,9 @@ internal static void Register(CommandLineApplication app)
2223
});
2324
}
2425

25-
internal static void Register(Command rootCommand, Func<ILoggerWithColor> getLogger)
26+
internal static void Register(Command rootCommand, IAnsiConsole console)
2627
{
27-
Register(rootCommand, getLogger, WhyCommandRunner.ExecuteCommand);
28+
Register(rootCommand, console, WhyCommandRunner.ExecuteCommand);
2829
}
2930

3031
/// <summary>
@@ -34,10 +35,12 @@ internal static void Register(Command rootCommand, Func<ILoggerWithColor> getLog
3435
/// <param name="rootCommand">The <c>dotnet nuget</c> command handler, to add <c>why</c> to.</param>
3536
public static void GetWhyCommand(Command rootCommand)
3637
{
37-
Register(rootCommand, CommandOutputLogger.Create, WhyCommandRunner.ExecuteCommand);
38+
Register(rootCommand,
39+
Spectre.Console.AnsiConsole.Console,
40+
WhyCommandRunner.ExecuteCommand);
3841
}
3942

40-
internal static void Register(Command rootCommand, Func<ILoggerWithColor> getLogger, Func<WhyCommandArgs, Task<int>> action)
43+
internal static void Register(Command rootCommand, IAnsiConsole console, Func<WhyCommandArgs, Task<int>> action)
4144
{
4245
var whyCommand = new DocumentedCommand("why", Strings.WhyCommand_Description, "https://aka.ms/dotnet/nuget/why");
4346

@@ -100,23 +103,21 @@ bool HasPathArgument(ArgumentResult ar)
100103

101104
whyCommand.SetAction(async (parseResult, cancellationToken) =>
102105
{
103-
ILoggerWithColor logger = getLogger();
104-
105106
try
106107
{
107108
var whyCommandArgs = new WhyCommandArgs(
108-
parseResult.GetValue(path),
109-
parseResult.GetValue(package),
110-
parseResult.GetValue(frameworks),
111-
logger,
109+
parseResult.GetValue(path)!,
110+
parseResult.GetValue(package)!,
111+
parseResult.GetValue(frameworks)!,
112+
console,
112113
cancellationToken);
113114

114115
int exitCode = await action(whyCommandArgs);
115116
return exitCode;
116117
}
117118
catch (ArgumentException ex)
118119
{
119-
logger.LogError(ex.Message);
120+
console.Markup($"[red]{ex.Message}[/]");
120121
return ExitCodes.InvalidArguments;
121122
}
122123
});

src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Why/WhyCommandArgs.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System;
77
using System.Collections.Generic;
88
using System.Threading;
9+
using Spectre.Console;
910

1011
namespace NuGet.CommandLine.XPlat.Commands.Why
1112
{
@@ -14,7 +15,7 @@ internal class WhyCommandArgs
1415
public string Path { get; }
1516
public string Package { get; }
1617
public List<string> Frameworks { get; }
17-
public ILoggerWithColor Logger { get; }
18+
public IAnsiConsole Logger { get; }
1819
public CancellationToken CancellationToken { get; }
1920

2021
/// <summary>
@@ -29,7 +30,7 @@ public WhyCommandArgs(
2930
string path,
3031
string package,
3132
List<string> frameworks,
32-
ILoggerWithColor logger,
33+
IAnsiConsole logger,
3334
CancellationToken cancellationToken)
3435
{
3536
Path = path ?? throw new ArgumentNullException(nameof(path));

0 commit comments

Comments
 (0)