8000 [Console] Add a Tree Helper + multiple Styles by smnandre · Pull Request #59588 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Console] Add a Tree Helper + multiple Styles #59588

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our 8000 terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 26, 2025

Conversation

smnandre
Copy link
Member
@smnandre smnandre commented Jan 23, 2025
Q A
Branch? 7.3
Bug fix? no
New feature? yes
Deprecations? no
Issues Fix #...
License MIT

Important

Code examples below are not up to date, documentation incoming.


Feature

The goal of the Tree helper is to ease rendering hierarchical data in the console:

  • file system
  • configuration
  • workflows
  • ...

Usage

Two methods available, manual or via SymfonyStyle

SymfonyStyle (easiest)

$io = new SymfonyStyle($input, $ouput); 

$io->tree([
    'A' => [
        'A1',
        'A2',
    ],
    'B' => [
        'B1' => [
            'B11',
        ],
        'B2',
    ],
]); 

Will render

root
├── A
│   ├── A1
│   └── A2
├── B
│   ├── B1
│   │   ├── B11
│   │   └── B12
│   └── B2
└── C

You also can pass a custom TreeStyle here, or one of the one availbes (see below)

Manually

You can build the Tree the way you want thanks to the TreeNode class.

$root = (new TreeNode('root'))
    ->addChild((new TreeNode('A'))
        ->addChild(new TreeNode('A1'))
   // ...

$tree = TreeHelper::create($output, $root, style: $style);
$tree->render();

Styles

Various styles are available (based on the styles of Table .. and some new ones), or you can also build your own Style from scratch.

Default

root
├── A
│   ├── A1
│   └── A2
├── B
│   ├── B1
│   │   ├── B11
│   │   └── B12
│   └── B2
└── C

Box

root
┃╸ A
┃  ┃╸ A1
┃  ┗╸ A2
┃╸ B
┃  ┃╸ B1
┃  ┃  ┃╸ B11
┃  ┃  ┗╸ B12
┃  ┗╸ B2
┗╸ C

Box Double

root
╠═ A
║  ╠═ A1
║  ╚═ A2
╠═ B
║  ╠═ B1
║  ║  ╠═ B11
║  ║  ╚═ B12
║  ╚═ B2
╚═ C

Rounded

root
├─ A
│  ├─ A1
│  ╰─ A2
├─ B
│  ├─ B1
│  │  ├─ B11
│  │  ╰─ B12
│  ╰─ B2
╰─ C

Compact

root
├ A
│ ├ A1
│ └ A2
├ B
│ ├ B1
│ │ ├ B11
│ │ └ B12
│ └ B2
└ C

Light

root
|-- A
|   |-- A1
|   `-- A2
|-- B
|   |-- B1
|   |   |-- B11
|   |   `-- B12
|   `-- B2
`-- C

Minimal

root
 A
.  A1
. . A2
 B
.  B1
. .  B11
. . . B12
. . B2
. C

@carsonbot

This comment has been minimized.

@carsonbot carsonbot added this to the 7.3 milestone Jan 23, 2025
Copy link
Member
@chalasr chalasr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it!
Memory consumption should be considered as this deals with potentially large directories, ideally nodes should be yielded as they come.
Regarding the public API, we should remove or internalize any getter/setter for which we don't have a strong use case.

@smnandre
Copy link
Member Author

I like it! Memory consumption should be considered as this deals with potentially large directories, ideally nodes should be yielded as they come.

Yep! I'll make the changes depending on the following questions.

Regarding the public API, we should remove or internalize any getter/setter for which we don't have a strong use case.

Do you have some examples in mind ?

Do we need both createTree and tree in SymfonyStyle (i used Table as base/inspiration)...
Not sure there is a real need for the ->tree() method..

@chalasr
Copy link
Member
chalasr commented Jan 25, 2025

Do you have some examples in mind ?

I was refering to Tree::getStyle()/setStyle() and TreeStyle::getPrefix*()

@smnandre
Copy link
Member Author
smnandre commented Jan 26, 2025

I've removed the getters and added a "appy" method on TreeStyle (we can consider RecursiveTreeIterator is part of the contract?)

public function applyPrefixes(RecursiveTreeIterator $iterator): void
{
    $iterator->setPrefixPart(RecursiveTreeIterator::PREFIX_LEFT, $this->prefixLeft);
    $iterator->setPrefixPart(RecursiveTreeIterator::PREFIX_MID_HAS_NEXT, $this->prefixMidHasNext);
    $iterator->setPrefixPart(RecursiveTreeIterator::PREFIX_MID_LAST, $this->prefixMidLast);
    $iterator->setPrefixPart(RecursiveTreeIterator::PREFIX_END_HAS_NEXT, $this->prefixEndHasNext);
    $iterator->setPrefixPart(RecursiveTreeIterator::PREFIX_END_LAST, $this->prefixEndLast);
    $iterator->setPrefixPart(RecursiveTreeIterator::PREFIX_RIGHT, $this->prefixRight);
}

Also removed getStyle / setStyle

Will refactor as lazy iterator but i'm not sure how to handle the cycles yet

@chadyred
Copy link
Contributor
chadyred commented Jan 27, 2025

@smnandre It will be really useful 🙏 great job

@smnandre smnandre force-pushed the sa/tree-node-console branch from e4e66de to 5ffb3fd Compare February 3, 2025 18:14
Copy link
Contributor
@chadyred chadyred left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

@smnandre
Copy link
Member Author
smnandre commented Feb 9, 2025

I've fusioned Tree and TreeBuilder into TreeHelper, keeping in line with existing ones.

Removed the Finder integration for now (i'd rather have separated PR as it will require some discussion)

@smnandre smnandre changed the title [Console] Add a Tree Helper + multiple Styles (wip/feedback) [Console] Add a Tree Helper + multiple Styles Feb 9, 2025
@smnandre
Copy link
Member Author
smnandre commented Feb 9, 2025

Open to review :)

@@ -369,6 +373,24 @@ private function getProgressBar(): ProgressBar
?? throw new RuntimeException('The ProgressBar is not started.');
}

/**
* @param iterable<string, iterable|string|TreeNode> $nodes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some words or example about what can be passed would be nice either here or in TreeHelper or both

Copy link
Contributor
@chadyred chadyred Feb 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HA ! It is to avoid back and forth (with a future link to the doc) :), a sample like :

$treeHelper = new TreeHelper($output, TreeNode::fromValues([
	'a' => [
	    'a1',
	    'a2'
	],
	'b',
	'c' => [
	    'c1',
	    'c2' => [
	        'c21' => [
	            'c211',
	            'c212',
	        ],
	    ],
	],
],
    new TreeNode($rootNode),
), TreeStyle::rounded());

$treeHelper->render();

Copy link
Member
@alexandre-daubois alexandre-daubois left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments but looks good otherwise 🙂

$parent->addChild($this);
}

foreach ($children as $child) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iterating in the constructor breaks lazy evaluation of the iterable, is that justified?


public static function box(): self
{
return new self(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on one line things would be better visually - not consuming my screen's space for nothing ;)

@chadyred
Copy link
Contributor

Last comments are none blocking suggestion

@fabpot fabpot force-pushed the sa/tree-node-console branch from 177f738 to 6a641c9 Compare February 26, 2025 07:12
@fabpot
Copy link
Member
fabpot commented Feb 26, 2025

Thank you @smnandre.

@fabpot fabpot merged commit 5c29eec into symfony:7.3 Feb 26, 2025
8 of 11 checks passed
javiereguiluz added a commit to symfony/symfony-docs that referenced this pull request Mar 24, 2025
This PR was squashed before being merged into the 7.3 branch.

Discussion
----------

[Console] Document the `TreeHelper`

| Q | A |
| - | - |
| Feature PR | [symfony/symfony#59588](symfony/symfony#59588) |
| PR author(s) | [`@smnandre`](https://github.com/smnandre) |
| Merged in | 7.3 |
| Doc Issue | Fix #20692 |

fix #20692

Commits
-------

d9a48be [Console] Document the `TreeHelper`
@fabpot fabpot mentioned this pull request May 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants
0