Source code for hydraclick.terminal_effects
import shutil
from typing import Optional, Callable
import os
import sys
import click
from click import Context, Option
[docs]
def get_no_terminal_effects() -> bool:
"""Check if terminal effects should be disabled by looking at environment variables.
This function checks the environment variables `OMEGACLICK_NO_TERMINAL_EFFECTS`
and `NO_TERMINAL_EFFECTS` to determine if terminal effects should be disabled.
If no environment variable is set, it returns `False`.
Returns:
bool: `True` if terminal effects should be disabled, `False` otherwise.
Example:
>>> os.environ["NO_TERMINAL_EFFECTS"] = "true"
>>> get_no_terminal_effects()
True
"""
val = os.environ.get("OMEGACLICK_NO_TERMINAL_EFFECTS", os.environ.get("NO_TERMINAL_EFFECTS"))
if val is None:
return False
return val.lower() in {"true", "1", "yes"}
[docs]
def config_effect(effect):
"""Configure terminal effects such as print speed and gradient colors.
This function adjusts the terminal effect's configuration, such as print speed,
return speed, and gradient colors for rendering.
Args:
effect: The terminal effect object to be configured.
Returns:
The modified terminal effect object.
Example:
>>> effect = SomeEffect()
>>> config_effect(effect)
<configured effect>
"""
from terminaltexteffects.utils.graphics import Color # noqa: PLC0415
effect.effect_config.print_speed = 15
effect.effect_config.print_head_return_speed = 5
effect.effect_config.final_gradient_stops = (Color("00ffae"), Color("00D1FF"), Color("FFFFFF"))
return effect
[docs]
def remove_lines(num_lines: int):
"""Remove the last `num_lines` lines printed in the terminal.
This function sends ANSI escape codes to move the terminal cursor up and clear
the last `num_lines` lines from the terminal.
Args:
num_lines (int): The number of lines to remove.
Example:
>>> remove_lines(3) # Removes the last 3 printed lines
"""
for _ in range(num_lines):
sys.stdout.write("\x1b[1A") # Move the cursor up one line
sys.stdout.write("\x1b[2K") # Clear the entire line
sys.stdout.flush()
[docs]
def count_wrapped_lines(text: str, terminal_width: int) -> int:
"""Calculate the number of lines the given text will take when wrapped in the terminal.
Args:
text (str): The text to be wrapped.
terminal_width (int): The width of the terminal in characters.
Returns:
int: The number of lines the text will occupy in the terminal.
Example:
>>> count_wrapped_lines("This is a long line of text.", 10)
3
"""
lines = text.splitlines()
total_lines = 0
for line in lines:
if terminal_width > 0:
num_terminal_lines = (len(line) + terminal_width - 1) // terminal_width
else:
num_terminal_lines = 1
total_lines += max(num_terminal_lines, 1)
return total_lines
[docs]
def display_terminal_effect(value: str, effect_cls=None):
"""Display a terminal effect animation for a given text.
This function displays a text-based terminal effect using the provided effect class.
The effect is rendered with custom configurations, and once the animation is complete,
the effect is cleaned up from the terminal.
Args:
value (str): The text to display with the terminal effect.
effect_cls (optional): The class of the terminal effect to use. Defaults to `Print`.
Example:
>>> display_terminal_effect("Hello World!")
"""
from terminaltexteffects.effects.effect_print import Print # noqa: PLC0415
effect_cls = effect_cls or Print
effect = effect_cls(value)
effect = config_effect(effect)
with effect.terminal_output() as terminal:
for frame in effect:
terminal.print(frame)
terminal_width = shutil.get_terminal_size().columns
n_lines_last_rendered_frame = count_wrapped_lines(frame, terminal_width)
remove_lines(n_lines_last_rendered_frame)
last_effect = effect_cls(value)
last_effect = config_effect(last_effect)
last_effect.terminal_config.ignore_terminal_dimensions = True
last_frame = list(last_effect)[-1]
sys.stdout.write(last_frame.lstrip())
sys.stdout.write("\n")
sys.stdout.flush()
[docs]
def patch_parse_args(terminal_effect: Callable):
"""Patch the Click `parse_args` function to display a terminal effect.
This function overrides the `parse_args` method of Click's `MultiCommand` to
display a custom terminal effect for the help message when no arguments are passed.
Args:
terminal_effect (Callable): A callable that renders the terminal effect.
Example:
>>> patch_parse_args(display_terminal_effect)
"""
def parse_args(self, ctx: Context, args: list[str]) -> list[str]:
"""Display the help message with terminal effects when no arguments are provided."""
if not args and self.no_args_is_help and not ctx.resilient_parsing:
terminal_effect(ctx.get_help())
ctx.exit()
rest = super(click.core.MultiCommand, self).parse_args(ctx, args)
if self.chain:
ctx.protected_args = rest
ctx.args = []
elif rest:
ctx.protected_args, ctx.args = rest[:1], rest[1:]
return ctx.args
click.core.MultiCommand.parse_args = parse_args
[docs]
def patch_get_help_option(terminal_effect: Callable):
"""Patch the Click `get_help_option` function to display a terminal effect for the help option.
This function overrides Click's `get_help_option` method to display a terminal
effect whenever the help message is requested.
Args:
terminal_effect (Callable): A callable that renders the terminal effect.
Example:
>>> patch_get_help_option(display_terminal_effect)
"""
def get_help_option(self, ctx: Context) -> Optional["Option"]:
"""Return the help option with a terminal effect callback."""
from gettext import gettext # noqa: PLC0415
help_options = self.get_help_option_names(ctx)
if not help_options or not self.add_help_option:
return None
def show_help(ctx: Context, param: "click.Parameter", value: str) -> None: # noqa: ARG001
if value and not ctx.resilient_parsing:
terminal_effect(ctx.get_help())
ctx.exit()
return Option(
help_options,
is_flag=True,
is_eager=True,
expose_value=False,
callback=show_help,
help=gettext("Show this message and exit."),
)
click.core.Command.get_help_option = get_help_option
[docs]
def set_terminal_effect(terminal_effect: Callable):
"""Set a terminal effect animation for displaying help in Click commands.
This function applies a patch to the Click `parse_args` and `get_help_option`
methods, so the help message is displayed with the specified terminal effect.
Args:
terminal_effect (Callable): A callable that renders the terminal effect.
Example:
>>> set_terminal_effect(display_terminal_effect)
"""
if not get_no_terminal_effects():
patch_parse_args(terminal_effect)
patch_get_help_option(terminal_effect)