diff --git a/src/Cake.Cli/Hosts/TreeScriptHost.cs b/src/Cake.Cli/Hosts/TreeScriptHost.cs
index f07a819623..1f232f5920 100644
--- a/src/Cake.Cli/Hosts/TreeScriptHost.cs
+++ b/src/Cake.Cli/Hosts/TreeScriptHost.cs
@@ -9,14 +9,13 @@
using Cake.Core;
using Cake.Core.Graph;
using Cake.Core.Scripting;
+using ThreadingTask = System.Threading.Tasks.Task;
-namespace Cake.Cli
-{
+namespace Cake.Cli {
///
/// The script host used for showing task descriptions.
///
- public sealed class TreeScriptHost : ScriptHost
- {
+ public sealed class TreeScriptHost : ScriptHost {
private const int _maxDepth = 0;
private const string _cross = "├─";
private const string _corner = "└─";
@@ -31,97 +30,105 @@ public sealed class TreeScriptHost : ScriptHost
/// The console.
public TreeScriptHost(ICakeEngine engine, ICakeContext context, IConsole console)
: base(engine, context)
- {
+ {
_console = console ?? throw new ArgumentNullException(nameof(console));
}
///
- public override Task RunTargetAsync(string target)
- {
+ public override Task RunTargetAsync(string target) {
PrintTaskTree();
-
- return System.Threading.Tasks.Task.FromResult(null);
+ return ThreadingTask.FromResult(null);
}
///
- public override Task RunTargetsAsync(IEnumerable targets)
- {
+ public override Task RunTargetsAsync(IEnumerable targets) {
PrintTaskTree();
-
- return System.Threading.Tasks.Task.FromResult(null);
+ return ThreadingTask.FromResult(null);
}
private void PrintTaskTree()
{
- var topLevelTasks = GetTopLevelTasks();
+ // Build the full graph once (includes Dependencies + Dependees)
+ var graph = CakeGraphBuilder.Build(Tasks);
+ var topLevelTasks = GetTopLevelTasks(graph);
+
_console.WriteLine();
- foreach (ICakeTaskInfo task in topLevelTasks)
+ foreach (var task in topLevelTasks)
{
- PrintTask(task, string.Empty, false, 0);
+ // root tasks start at depth 0, no branch characters yet
+ PrintTask(task, graph, string.Empty, isLast: false, depth: 0);
_console.WriteLine();
}
}
- private List GetTopLevelTasks()
+ private List GetTopLevelTasks(CakeGraph graph)
{
// Display "Default" first, then alphabetical
- var graph = CakeGraphBuilder.Build(Tasks);
- return Tasks.Where(task => !graph.Edges.Any(
- edge => edge.Start.Equals(task.Name, StringComparison.OrdinalIgnoreCase)))
+ // Top-level = tasks that never appear as Start (no outgoing edges)
+ return Tasks
+ .Where(task => !graph.Edges.Any(
+ edge => edge.Start.Equals(task.Name, StringComparison.OrdinalIgnoreCase)))
.OrderByDescending(task => task.Name.Equals("Default", StringComparison.OrdinalIgnoreCase))
.ThenBy(task => task.Name, StringComparer.OrdinalIgnoreCase)
.ToList();
}
- private void PrintTask(ICakeTaskInfo task, string indent, bool isLast, int depth)
- {
+ private void PrintTask(
+ ICakeTaskInfo task,
+ CakeGraph graph,
+ string indent,
+ bool isLast,
+ int depth)
+ {
// Builds ASCII graph
_console.Write(indent);
- if (isLast)
- {
+ if (isLast) {
_console.Write(_corner);
indent += " ";
- }
- else if (depth > 0)
- {
+ } else if (depth > 0) {
_console.Write(_cross);
indent += _vertical;
}
PrintName(task, depth);
- if ((_maxDepth > 0) && (depth >= _maxDepth))
- {
+ if ((_maxDepth > 0) && (depth >= _maxDepth)) {
return;
}
- for (var i = 0; i < task.Dependencies.Count; i++)
- {
- // First() is safe as CakeGraphBuilder has already validated graph is valid
- var childTask = Tasks
- .Where(x => x.Name.Equals(task.Dependencies[i].Name, StringComparison.OrdinalIgnoreCase))
- .First();
+ // Children = all tasks that have an edge Start -> End = current task.
+ // This respects both IsDependentOn and IsDependeeOf,
+ // because CakeGraphBuilder already encoded both into the graph.
+ var childTasks = graph.Edges
+ .Where(edge => edge.End.Equals(task.Name, StringComparison.OrdinalIgnoreCase))
+ .Select(edge => Tasks.First(t =>
+ t.Name.Equals(edge.Start, StringComparison.OrdinalIgnoreCase)))
+ .Distinct()
+ .OrderBy(t => t.Name, StringComparer.OrdinalIgnoreCase)
+ .ToList();
+
+ for (var i = 0; i < childTasks.Count; i++)
+ {
+ var childTask = childTasks[i];
+ var childIsLast = i == childTasks.Count - 1;
- PrintTask(childTask, indent, i == (task.Dependencies.Count - 1), depth + 1);
+ PrintTask(childTask, graph, indent, childIsLast, depth + 1);
}
}
- private void PrintName(ICakeTaskInfo task, int depth)
- {
+ private void PrintName(ICakeTaskInfo task, int depth) {
var originalColor = _console.ForegroundColor;
if (depth == 0)
- {
+ {
_console.ForegroundColor = ConsoleColor.Cyan;
}
else if (task is CakeTask cakeTask &&
- (cakeTask.Actions.Any() || cakeTask.DelayedActions.Any()))
- {
+ (cakeTask.Actions.Any() || cakeTask.DelayedActions.Any()))
+ {
_console.ForegroundColor = ConsoleColor.Green;
- }
- else
- {
+ } else {
_console.ForegroundColor = ConsoleColor.Gray;
}
@@ -129,4 +136,4 @@ private void PrintName(ICakeTaskInfo task, int depth)
_console.ForegroundColor = originalColor;
}
}
-}
\ No newline at end of file
+}