Repository: ManimCommunity/manim
File: manim/animation/creation.py (Python)

Code before:
            mobj.set_opacity(1)
        for mobj in self.all_submobs[index:]:
            mobj.set_opacity(0)


class AddTextLetterByLetter(ShowIncreasingSubsets):
    """Show a :class:`~.Text` letter by letter on the scene.

    Parameters
    ----------
    time_per_char
        Frequency of appearance of the letters.

    .. tip::

        This is currently only possible for class:`~.Text` and not for class:`~.MathTex`

    """

    # Added an edge case exception catch for blank / empty input characters.
    # As when the input is blank, the AddTextLetterByLetter will have an error in which it will try to render a video file with a run_time of 0.
    # As a result this will cause an error in the video rendering process (i.e., "Output file does not contain any stream"). To add more error feedback
    # For this class, I have opted to include an exception catch for this edge case.
    # If either an input with the data type Text or MarkupText has blank input,
    # examples would be Text() and MarkupText('<span bgcolor="#777777"></span>') respectively, then an error will be raised.

    def __init__(
        self,
        text: Text,
        suspend_mobject_updating: bool = False,
        int_func: Callable[[np.ndarray], np.ndarray] = np.ceil,
        rate_func: Callable[[float], float] = linear,
        time_per_char: float = 0.1,
        run_time: float | None = None,
        reverse_rate_function=False,
        introducer=True,
        **kwargs,
    ) -> None:
        self.time_per_char = time_per_char

        if self.is_text_empty(text) or self.is_markup_text_empty(
            text
        ):  # If input is empty, i.e., (Text("") or MarkupText("")), provide error feedback
            raise ValueError(
                "The input text is empty i.e., input has no characters to read thus cannot render a video file."
            )
            run_time = 1  # Set the run_time to 1.
            # This will render a blank video with a duration of 1 second. Just in case the user wants to render a blank animation instead of throwing an error.
            # To do so, just comment out the raise ValueError line and keep the run_time code.
        elif run_time is None:
            run_time = np.max((1 / config.frame_rate, self.time_per_char)) * len(text)

Review comment (suggestion):
These comments are more suitable for the PR description than for the source code. They should be removed.

```suggestion
```

Code after:


class AddTextLetterByLetter(ShowIncreasingSubsets):
    """Show a :class:`~.Text` letter by letter on the scene.

    Parameters
    ----------
    time_per_char
        Frequency of appearance of the letters.

    .. tip::

        This is currently only possible for class:`~.Text` and not for class:`~.MathTex`

    """

    def __init__(
        self,
        text: Text,
        suspend_mobject_updating: bool = False,
        int_func: Callable[[np.ndarray], np.ndarray] = np.ceil,
        rate_func: Callable[[float], float] = linear,
        time_per_char: float = 0.1,
        run_time: float | None = None,
        reverse_rate_function=False,
        introducer=True,
        **kwargs,
    ) -> None:
        self.time_per_char = time_per_char
        # Check for empty text using family_members_with_points()
        if not text.family_members_with_points():
            raise ValueError(
                f"The text mobject {text} does not seem to contain any characters."
            )
        if run_time is None:
            # minimum time per character is 1/frame_rate, otherwise
            # the animation does not finish.
            run_time = np.max((1 / config.frame_rate, self.time_per_char)) * len(text)
        super().__init__(
            text,
            suspend_mobject_updating=suspend_mobject_updating,
            int_func=int_func,
            rate_func=rate_func,
            run_time=run_time,
            reverse_rate_function=reverse_rate_function,
            introducer=introducer,
            **kwargs,
        )


class RemoveTextLetterByLetter(AddTextLetterByLetter):