@@ -50,6 +50,10 @@ const (
5050 lastElemPrefix = `└─`
5151 indent = " "
5252 pipe = `│ `
53+
54+ // lastObjectAnnotation defines the last object in the ObjectTree.
55+ // This is necessary to built the prefix for multiline condition messages.
56+ lastObjectAnnotation = "tree.cluster.x-k8s.io.io/last-object"
5357)
5458
5559var (
@@ -304,9 +308,10 @@ func addObjectRowV1Beta2(prefix string, tbl *tablewriter.Table, objectTree *tree
304308 rowDescriptor .age ,
305309 msg0 })
306310
311+ multilinePrefix := getRootMultiLineObjectPrefix (obj , objectTree )
307312 for _ , m := range msg [1 :] {
308313 tbl .Append ([]string {
309- getMultilinePrefix ( gray .Sprint (prefix ) ),
314+ gray .Sprint (multilinePrefix ),
310315 "" ,
311316 "" ,
312317 "" ,
@@ -324,7 +329,14 @@ func addObjectRowV1Beta2(prefix string, tbl *tablewriter.Table, objectTree *tree
324329
325330 // Add a row for each object's children, taking care of updating the tree view prefix.
326331 childrenObj := objectTree .GetObjectsByParent (obj .GetUID ())
332+ childrenObj = orderChildrenObjects (childrenObj )
327333
334+ for i , child := range childrenObj {
335+ addObjectRowV1Beta2 (getChildPrefix (prefix , i , len (childrenObj )), tbl , objectTree , child )
336+ }
337+ }
338+
339+ func orderChildrenObjects (childrenObj []ctrlclient.Object ) []ctrlclient.Object {
328340 // printBefore returns true if children[i] should be printed before children[j]. Objects are sorted by z-order and
329341 // row name such that objects with higher z-order are printed first, and objects with the same z-order are
330342 // printed in alphabetical order.
@@ -336,10 +348,7 @@ func addObjectRowV1Beta2(prefix string, tbl *tablewriter.Table, objectTree *tree
336348 return tree .GetZOrder (childrenObj [i ]) > tree .GetZOrder (childrenObj [j ])
337349 }
338350 sort .Slice (childrenObj , printBefore )
339-
340- for i , child := range childrenObj {
341- addObjectRowV1Beta2 (getChildPrefix (prefix , i , len (childrenObj )), tbl , objectTree , child )
342- }
351+ return childrenObj
343352}
344353
345354// addObjectRow add a row for a given object, and recursively for all the object's children.
@@ -452,7 +461,7 @@ func addOtherConditionsV1Beta2(prefix string, tbl *tablewriter.Table, objectTree
452461
453462 for _ , m := range msg [1 :] {
454463 tbl .Append ([]string {
455- gray .Sprint (getMultilinePrefix (childPrefix )),
464+ gray .Sprint (getMultilineConditionPrefix (childPrefix )),
456465 "" ,
457466 "" ,
458467 "" ,
@@ -510,8 +519,8 @@ func getChildPrefix(currentPrefix string, childIndex, childCount int) string {
510519 return nextPrefix + lastElemPrefix
511520}
512521
513- // getMultilinePrefix return the tree view prefix for a multiline condition.
514- func getMultilinePrefix (currentPrefix string ) string {
522+ // getMultilineConditionPrefix return the tree view prefix for a multiline condition.
523+ func getMultilineConditionPrefix (currentPrefix string ) string {
515524 // All ├─ should be replaced by |, so all the existing hierarchic dependencies are carried on
516525 if strings .HasSuffix (currentPrefix , firstElemPrefix ) {
517526 return strings .TrimSuffix (currentPrefix , firstElemPrefix ) + pipe
@@ -524,6 +533,81 @@ func getMultilinePrefix(currentPrefix string) string {
524533 return "?"
525534}
526535
536+ // getRootMultiLineObjectPrefix generates the multiline prefix for an object.
537+ func getRootMultiLineObjectPrefix (obj ctrlclient.Object , objectTree * tree.ObjectTree ) string {
538+ // If the object is the last one in the tree, no prefix is needed.
539+ if ensureLastObjectInTree (objectTree ) == string (obj .GetUID ()) {
540+ return ""
541+ }
542+
543+ // Determine the prefix for the current object.
544+ // If it is a leaf we don't have to add any prefix.
545+ var prefix string
546+ if len (objectTree .GetObjectsByParent (obj .GetUID ())) > 0 {
547+ prefix = pipe
548+ }
549+
550+ // Traverse upward through the tree, calculating each parent's prefix.
551+ // The parent of the root object is nil, so we stop when we reach that point.
552+ previousUID := obj .GetUID ()
553+ parent := objectTree .GetParent (obj .GetUID ())
554+ for parent != nil {
555+ // We have to break the loop if the previous ID is the same as the current ID.
556+ // This should never happen as the root object doesn't have set the parentship.
557+ if previousUID == parent .GetUID () {
558+ break
559+ }
560+
561+ // Use pipe if the parent has children and the current node is not the last child.
562+ parentChildren := orderChildrenObjects (objectTree .GetObjectsByParent (parent .GetUID ()))
563+ isLastChild := len (parentChildren ) > 0 && parentChildren [len (parentChildren )- 1 ].GetUID () == previousUID
564+ if objectTree .IsObjectWithChild (parent .GetUID ()) && ! isLastChild {
565+ prefix = pipe + prefix
566+ } else {
567+ prefix = indent + prefix
568+ }
569+
570+ // Set prefix and move up the tree.
571+ previousUID = parent .GetUID ()
572+ parent = objectTree .GetParent (parent .GetUID ())
573+ }
574+ return prefix
575+ }
576+
577+ func ensureLastObjectInTree (objectTree * tree.ObjectTree ) string {
578+ // Compute last object in the tree and set it in the annotations.
579+ annotations := objectTree .GetRoot ().GetAnnotations ()
580+ if annotations == nil {
581+ annotations = map [string ]string {}
582+ }
583+
584+ // Return if last object is already set.
585+ if val , ok := annotations [lastObjectAnnotation ]; ok {
586+ return val
587+ }
588+
589+ lastObjectInTree := string (getLastObjectInTree (objectTree ).GetUID ())
590+ annotations [lastObjectAnnotation ] = lastObjectInTree
591+ objectTree .GetRoot ().SetAnnotations (annotations )
592+ return lastObjectInTree
593+ }
594+
595+ func getLastObjectInTree (objectTree * tree.ObjectTree ) ctrlclient.Object {
596+ var objs []ctrlclient.Object
597+
598+ var traverse func (obj ctrlclient.Object )
599+ traverse = func (obj ctrlclient.Object ) {
600+ objs = append (objs , obj )
601+ children := orderChildrenObjects (objectTree .GetObjectsByParent (obj .GetUID ()))
602+ for _ , child := range children {
603+ traverse (child )
604+ }
605+ }
606+
607+ traverse (objectTree .GetRoot ())
608+ return objs [len (objs )- 1 ]
609+ }
610+
527611// getRowName returns the object name in the tree view according to following rules:
528612// - group objects are represented as #of objects kind, e.g. 3 Machines...
529613// - other virtual objects are represented using the object name, e.g. Workers, or meta name if provided.
0 commit comments