Source code for framed_text.shorten_text

import textwrap
from typing import Literal

from framed_text.labeled_data import AnyLabeledData, LabeledData
from framed_text.status import AnyStatus
from framed_text.utils import (
    _remove_ansi,
    _truncate_text_middle,
    _get_terminal_width,
)


[docs] class ShortenText:
[docs] def __init__(self, text: str | AnyStatus | AnyLabeledData, limit: int = 0, allow_ansi: bool = False, mode: Literal["char", "word", "middle"] = "char"): """ Shorten text to a specified limit. Supports ``LabeledData``, ``Status`` and regular strings. **Modes** - ``char``: Cutoff char at end of text (The quick brown fox jumped ove…) - ``word``: Cutoff word at end of text (The quick brown fox jumped…) - ``middle``: Cutoff chars at middle of text (The quick brown…the lazy dog) :param text: Text to shorten :param limit: Limit. Text will be shortened to this length. If no limit is passed, will default to the width of the terminal. :param allow_ansi: If true, allow ANSI codes in the text. This parameter only applies to regular strings. :param mode: Mode to use to shorten text. See above for more info. """ self.text: str | AnyStatus | AnyLabeledData = text self._limit: int = limit if limit > 0 else _get_terminal_width() self._allow_ansi: bool = allow_ansi self._mode: Literal["char", "word", "middle"] = mode self.text_shorten: str = '' self._text_no_ansi: str = '' if self._limit < 0: raise ValueError("Limit must be a positive integer.") # Handle regular strings if isinstance(self.text, str): self._text_no_ansi = _remove_ansi(text=self.text.rstrip()) if len(self._text_no_ansi) > self._limit: text: str = self.text if self._allow_ansi else self._text_no_ansi match self._mode.lower(): case "char": # Shorten by char self.text_shorten = f"{text[:self._limit - 1]}…" case "word": # Shorten by word self.text_shorten = textwrap.shorten(text=text, width=self._limit, placeholder="…") case "middle": # Shorten by middle characters self.text_shorten = _truncate_text_middle(text=text, limit=self._limit) else: self.text_shorten = self.text else: # Handle special framed-text types self._text_no_ansi = _remove_ansi(text=self.text.__str__().rstrip()) if isinstance(self.text, AnyStatus): # Status object if len(self._text_no_ansi) > self._limit: self.text.cutoff_text(limit=self._limit, mode=self._mode) self.text_shorten = self.text.text_shorten else: self.text_shorten = self.text.text elif isinstance(self.text, AnyLabeledData): # LabeledData object if len(self._text_no_ansi) > self._limit: if (isinstance(self.text, LabeledData.String) or isinstance(self.text, LabeledData.Path)): # These types use mode self.text.cutoff_text(limit=self._limit, mode=self._mode) self.text_shorten = self.text.text_shorten elif isinstance(self.text, AnyLabeledData): # These types don't use mode self.text.cutoff_text(limit=self._limit) self.text_shorten = self.text.text_shorten else: # Text does not need to be shortened self.text_shorten = self.text.text else: # Not a valid type raise ValueError("Text must be a string, Status, or LabeledData.")
def __str__(self) -> str: return self.text_shorten