Command/ProcessHelpCommand.php
protected function resolveBranchOutput(&$branches, $taskCode, ProcessConfiguration $process, OutputInterface $output)
    {
        $task = $process->getTaskConfiguration($taskCode);
        $branchesToMerge = [];
        $gapBranches = [];
        $origin = null;
        $final = null;

        // Get unique previous branches
        $previousTasks = [];
        foreach ($task->getPreviousTasksConfigurations() as $previousTasksConfiguration) {
            $previousTasks[$previousTasksConfiguration->getCode()] = $previousTasksConfiguration;
        }

        // Check previous branches
        if (empty($previousTasks)) {
            $branches[] = $task->getCode();
        } elseif (1 === \count($previousTasks)) {
            $prevTask = current($previousTasks)->getCode();
            foreach (array_reverse($branches, true) as $i => $branchTask) {
                if ($branchTask === $prevTask) {
                    $branches[$i] = $taskCode;
                    break;
                }
            }
        } else {
            foreach ($previousTasks as $prevTask) {
                $foundBranch = false;
                foreach (array_reverse($branches, true) as $i => $branchTask) {
                    if ($branchTask === $prevTask->getCode()) {
                        $branchesToMerge[] = $i;
                        $foundBranch = true;
                        break;
                    }
                }

                if (!$foundBranch) {
                    $output->writeln(
                        "<error>Could not find previous branch : {$taskCode} depends on {$prevTask->getCode()}</error>"
                    );
                }
            }

            // Don't touch the 1st branch to merge
            sort($branchesToMerge);

            $gapFrom = null;
            $gapTo = null;
            foreach ($branchesToMerge as $i) {
                $gapTo = $i;
                if (null !== $gapFrom) {
                    for ($j = $gapFrom + 1; $j < $gapTo; ++$j) {
                        $gapBranches[] = $j;
                    }
                }
                $gapFrom = $i;
            }

            $origin = array_shift($branchesToMerge);
            $final = $gapFrom;
            $branches[$origin] = $taskCode;
        }

        // Merge branches
        if (!empty($branchesToMerge)) {
            $this->writeBranches($output, $branches);

            $this->writeBranches(
                $output,
                $branches,
                '',
                function ($taskCode, $i) use ($branchesToMerge, $gapBranches, $origin) {
                    return \in_array($i, $branchesToMerge, true)
                        || \in_array($i, $gapBranches, true)
                        || $i === $origin;
                },
                function ($taskCode, $i) use ($gapBranches, $origin, $final, $branches) {
                    if ($i === $origin) {
                        return self::CHAR_RECEIVE;
                    }
                    if (\in_array($i, $gapBranches, true)) {
                        if (null !== $branches[$i]) {
                            return self::CHAR_JUMP;
                        }

                        return self::CHAR_HORIZ;
                    }

                    if ($i === $final) {
                        return self::CHAR_MERGE;
                    }

                    return self::CHAR_MULTIMERGE;
                }
            );

            foreach ($branches as $i => $branchTask) {
                if (\in_array($i, $branchesToMerge, true)) {
                    $branches[$i] = null;
                }
            }
            $this->writeBranches($output, $branches);
        }

        // Cleanup empty trailing branches
        foreach (array_reverse($branches, true) as $i => $branchTask) {
            if (null !== $branchTask) {
                $branches = \array_slice($branches, 0, $i + 1);
                break;
            }
        }

        // Write main line
        $nodeStr = self::CHAR_NODE;
        if ($task->isInErrorBranch()) {
            $nodeStr = "<fire>{$nodeStr}</fire>";
        }

        $this->writeBranches(
            $output,
            $branches,
            $this->getTaskDescription($task),
            function ($branchTask, $i) use ($taskCode) {
                return $branchTask === $taskCode;
            },
            $nodeStr
        );

        // Write task help message
        if ($output->isVerbose() && $task->getHelp()) {
            $helpLines = array_filter(explode("\n", $task->getHelp()));
            foreach ($helpLines as $helpLine) {
                $helpMessage = str_repeat(' ', self::INDENT_SIZE) . "<info>{$helpLine}</info>";
                $this->writeBranches($output, $branches, $helpMessage);
            }
        }

        // Check next tasks
        $nextTasks = array_unique(array_map(
            function (TaskConfiguration $task) {
                return $task->getCode();
            },
            array_merge($task->getNextTasksConfigurations(), $task->getErrorTasksConfigurations())
        ));
        if (\count($nextTasks) > 1) {
            $this->writeBranches($output, $branches);
            array_shift($nextTasks);
            $origin = array_search($taskCode, $branches, true);
            $expandBranches = [];
            foreach ($nextTasks as $nextTask) {
                $index = array_search(null, $branches, true);
                if (false !== $index && $index >= $origin) {
                    /** @var $index int */
                    $branches[$index] = $taskCode;
                    $expandBranches[] = $index;
                } else {
                    $expandBranches[] = \count($branches);
                    $branches[] = $taskCode;
                }
            }
            $gapBranches = [];
            sort($expandBranches);
            $gapFrom = $origin;
            $gapTo = null;

            foreach ($expandBranches as $i) {
                $gapTo = $i;
                for ($j = $gapFrom + 1; $j < $gapTo; ++$j) {
                    $gapBranches[] = $j;
                }
                $gapFrom = $i;
            }
            $final = $gapFrom;

            $this->writeBranches(
                $output,
                $branches,
                '',
                function ($branchTask, $i) use ($origin, $final) {
                    return $i >= $origin && $i <= $final;
                },
                function ($branchTask, $i) use ($origin, $branches, $gapBranches, $final) {
                    if ($i === $origin) {
                        return self::CHAR_RECEIVE;
                    }
                    if (\in_array($i, $gapBranches, true)) {
                        if (null !== $branches[$i]) {
                            return self::CHAR_JUMP;
                        }

                        return self::CHAR_HORIZ;
                    }
                    if ($final === $i) {
                        return self::CHAR_EXPAND;
                    }

                    return self::CHAR_MULTIEXPAND;
                }
            );
        }

        if (empty($nextTasks)) {
            foreach ($branches as $i => $branchTask) {
                if ($branchTask === $taskCode) {
                    $branches[$i] = null;
                }
            }
        }

        // Cleanup empty trailing branches
        foreach (array_reverse($branches, true) as $i => $branchTask) {
            if (null !== $branchTask) {
                $branches = \array_slice($branches, 0, $i + 1);
                break;
            }
        }

        $this->writeBranches($output, $branches);
    }
protected function resolveBranchOutput(&$branches, $taskCode, ProcessConfiguration $process, OutputInterface $output)
    {
        $task = $process->getTaskConfiguration($taskCode);
        $branchesToMerge = [];
        $gapBranches = [];
        $origin = null;
        $final = null;

        // Get unique previous branches
        $previousTasks = [];
        foreach ($task->getPreviousTasksConfigurations() as $previousTasksConfiguration) {
            $previousTasks[$previousTasksConfiguration->getCode()] = $previousTasksConfiguration;
        }

        // Check previous branches
        if (empty($previousTasks)) {
            $branches[] = $task->getCode();
        } elseif (1 === \count($previousTasks)) {
            $prevTask = current($previousTasks)->getCode();
            foreach (array_reverse($branches, true) as $i => $branchTask) {
                if ($branchTask === $prevTask) {
                    $branches[$i] = $taskCode;
                    break;
                }
            }
        } else {
            foreach ($previousTasks as $prevTask) {
                $foundBranch = false;
                foreach (array_reverse($branches, true) as $i => $branchTask) {
                    if ($branchTask === $prevTask->getCode()) {
                        $branchesToMerge[] = $i;
                        $foundBranch = true;
                        break;
                    }
                }

                if (!$foundBranch) {
                    $output->writeln(
                        "<error>Could not find previous branch : {$taskCode} depends on {$prevTask->getCode()}</error>"
                    );
                }
            }

            // Don't touch the 1st branch to merge
            sort($branchesToMerge);

            $gapFrom = null;
            $gapTo = null;
            foreach ($branchesToMerge as $i) {
                $gapTo = $i;
                if (null !== $gapFrom) {
                    for ($j = $gapFrom + 1; $j < $gapTo; ++$j) {
                        $gapBranches[] = $j;
                    }
                }
                $gapFrom = $i;
            }

            $origin = array_shift($branchesToMerge);
            $final = $gapFrom;
            $branches[$origin] = $taskCode;
        }

        // Merge branches
        if (!empty($branchesToMerge)) {
            $this->writeBranches($output, $branches);

            $this->writeBranches(
                $output,
                $branches,
                '',
                function ($taskCode, $i) use ($branchesToMerge, $gapBranches, $origin) {
                    return \in_array($i, $branchesToMerge, true)
                        || \in_array($i, $gapBranches, true)
                        || $i === $origin;
                },
                function ($taskCode, $i) use ($gapBranches, $origin, $final, $branches) {
                    if ($i === $origin) {
                        return self::CHAR_RECEIVE;
                    }
                    if (\in_array($i, $gapBranches, true)) {
                        if (null !== $branches[$i]) {
                            return self::CHAR_JUMP;
                        }

                        return self::CHAR_HORIZ;
                    }

                    if ($i === $final) {
                        return self::CHAR_MERGE;
                    }

                    return self::CHAR_MULTIMERGE;
                }
            );

            foreach ($branches as $i => $branchTask) {
                if (\in_array($i, $branchesToMerge, true)) {
                    $branches[$i] = null;
                }
            }
            $this->writeBranches($output, $branches);
        }

        // Cleanup empty trailing branches
        foreach (array_reverse($branches, true) as $i => $branchTask) {
            if (null !== $branchTask) {
                $branches = \array_slice($branches, 0, $i + 1);
                break;
            }
        }

        // Write main line
        $nodeStr = self::CHAR_NODE;
        if ($task->isInErrorBranch()) {
            $nodeStr = "<fire>{$nodeStr}</fire>";
        }

        $this->writeBranches(
            $output,
            $branches,
            $this->getTaskDescription($task),
            function ($branchTask, $i) use ($taskCode) {
                return $branchTask === $taskCode;
            },
            $nodeStr
        );

        // Write task help message
        if ($output->isVerbose() && $task->getHelp()) {
            $helpLines = array_filter(explode("\n", $task->getHelp()));
            foreach ($helpLines as $helpLine) {
                $helpMessage = str_repeat(' ', self::INDENT_SIZE) . "<info>{$helpLine}</info>";
                $this->writeBranches($output, $branches, $helpMessage);
            }
        }

        // Check next tasks
        $nextTasks = array_unique(array_map(
            function (TaskConfiguration $task) {
                return $task->getCode();
            },
            array_merge($task->getNextTasksConfigurations(), $task->getErrorTasksConfigurations())
        ));
        if (\count($nextTasks) > 1) {
            $this->writeBranches($output, $branches);
            array_shift($nextTasks);
            $origin = array_search($taskCode, $branches, true);
            $expandBranches = [];
            foreach ($nextTasks as $nextTask) {
                $index = array_search(null, $branches, true);
                if (false !== $index && $index >= $origin) {
                    /** @var $index int */
                    $branches[$index] = $taskCode;
                    $expandBranches[] = $index;
                } else {
                    $expandBranches[] = \count($branches);
                    $branches[] = $taskCode;
                }
            }
            $gapBranches = [];
            sort($expandBranches);
            $gapFrom = $origin;
            $gapTo = null;

            foreach ($expandBranches as $i) {
                $gapTo = $i;
                for ($j = $gapFrom + 1; $j < $gapTo; ++$j) {
                    $gapBranches[] = $j;
                }
                $gapFrom = $i;
            }
            $final = $gapFrom;

            $this->writeBranches(
                $output,
                $branches,
                '',
                function ($branchTask, $i) use ($origin, $final) {
                    return $i >= $origin && $i <= $final;
                },
                function ($branchTask, $i) use ($origin, $branches, $gapBranches, $final) {
                    if ($i === $origin) {
                        return self::CHAR_RECEIVE;
                    }
                    if (\in_array($i, $gapBranches, true)) {
                        if (null !== $branches[$i]) {
                            return self::CHAR_JUMP;
                        }

                        return self::CHAR_HORIZ;
                    }
                    if ($final === $i) {
                        return self::CHAR_EXPAND;
                    }

                    return self::CHAR_MULTIEXPAND;
                }
            );
        }

        if (empty($nextTasks)) {
            foreach ($branches as $i => $branchTask) {
                if ($branchTask === $taskCode) {
                    $branches[$i] = null;
                }
            }
        }

        // Cleanup empty trailing branches
        foreach (array_reverse($branches, true) as $i => $branchTask) {
            if (null !== $branchTask) {
                $branches = \array_slice($branches, 0, $i + 1);
                break;
            }
        }

        $this->writeBranches($output, $branches);
    }
Merge needed branches, display a task node, split following needed branches

@param                      $branches
@param                      $taskCode
@param ProcessConfiguration $process
@param OutputInterface      $output