Skip to content

Commit 8e2579a

Browse files
authored
dotnet nuget why: write target package in graph (#6975)
1 parent c1c501c commit 8e2579a

19 files changed

Lines changed: 264 additions & 217 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/DocumentedCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
#nullable disable
4+
#nullable enable
55

66
using System.CommandLine;
77

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: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
#nullable disable
4+
#nullable enable
55

66
using System;
77
using System.Collections.Generic;
@@ -11,6 +11,7 @@
1111
using System.IO;
1212
using System.Threading.Tasks;
1313
using Microsoft.Extensions.CommandLineUtils;
14+
using Spectre.Console;
1415

1516
namespace NuGet.CommandLine.XPlat.Commands.Why
1617
{
@@ -24,9 +25,9 @@ internal static void Register(CommandLineApplication app)
2425
});
2526
}
2627

27-
internal static void Register(Command rootCommand, Func<ILoggerWithColor> getLogger)
28+
internal static void Register(Command rootCommand, IAnsiConsole console)
2829
{
29-
Register(rootCommand, getLogger, WhyCommandRunner.ExecuteCommand);
30+
Register(rootCommand, console, WhyCommandRunner.ExecuteCommand);
3031
}
3132

3233
/// <summary>
@@ -36,10 +37,12 @@ internal static void Register(Command rootCommand, Func<ILoggerWithColor> getLog
3637
/// <param name="rootCommand">The <c>dotnet nuget</c> command handler, to add <c>why</c> to.</param>
3738
public static void GetWhyCommand(Command rootCommand)
3839
{
39-
Register(rootCommand, CommandOutputLogger.Create, WhyCommandRunner.ExecuteCommand);
40+
Register(rootCommand,
41+
Spectre.Console.AnsiConsole.Console,
42+
WhyCommandRunner.ExecuteCommand);
4043
}
4144

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

@@ -102,23 +105,21 @@ bool HasPathArgument(ArgumentResult ar)
102105

103106
whyCommand.SetAction(async (parseResult, cancellationToken) =>
104107
{
105-
ILoggerWithColor logger = getLogger();
106-
107108
try
108109
{
109110
var whyCommandArgs = new WhyCommandArgs(
110-
parseResult.GetValue(path),
111-
parseResult.GetValue(package),
112-
parseResult.GetValue(frameworks),
113-
logger,
111+
parseResult.GetValue(path)!,
112+
parseResult.GetValue(package)!,
113+
parseResult.GetValue(frameworks)!,
114+
console,
114115
cancellationToken);
115116

116117
int exitCode = await action(whyCommandArgs);
117118
return exitCode;
118119
}
119120
catch (ArgumentException ex)
120121
{
121-
logger.LogError(ex.Message);
122+
console.Markup($"[red]{ex.Message}[/]");
122123
return ExitCodes.InvalidArguments;
123124
}
124125
});

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)