Skip to content

Add format_text() method for unified color, background, and style application #7

@razbuild

Description

@razbuild

Problem

Currently, combining multiple styles (e.g., bold + underline + red) requires nested function calls:

bold(underline(red("Important message")))

This quickly becomes deeply nested, hard to read, and less obvious for newcomers – especially when 3+ styles are used.

Proposed solution

Introduce a new method (on the tint instance and optionally as a module‑level helper) that accepts keyword arguments for foreground color, background color, and text styles.

Example usage:

# Module-level function
from raztint import format_text
print(format_text("Important message", color="red", bg="blue", styles=["bold", "underline"]))

# Or on the tint instance
from raztint import tint
print(tint.format_text("Important message", color="red", bg="blue", styles=["bold", "underline"]))

The method internally builds the appropriate ANSI sequence by combining the chosen foreground, optional background, and any number of style codes (bold, dim, italic, underline, strikethrough). A reset parameter (default True) can append a reset code at the end.

Detailed design & improvements over a naive implementation

Method naming

While the issue originally used style(), consider format_text() or paint() to avoid confusion with the existing text style functions (bold, underline). We'll keep style() as an alias or choose the clearest name during implementation.

Flexible styles argument

The styles parameter should accept:

  • A single string: styles="bold"
  • Multiple string arguments: styles="bold", "underline" (using *args)
  • A list: styles=["bold", "underline"]

This makes short calls concise and is forward‑compatible.

Reset behaviour – must be explicit

Setting reset=True (default) appends \033[0m after the text, resetting all styles and colors. This is safe for a single call but may break chaining (e.g., two format_text calls on the same line).
Better: Make reset=False the default, or clearly document that reset=True is a “hard reset”. Add a reset_only_applied=False option for advanced use (requires tracking which codes were added).

Accept ANSI numeric codes directly

To make the API future‑proof and friendly to power users, color and bg should accept:

  • Color names (e.g., "red", "bright_green")
  • Integer ANSI codes (e.g., 31 for red, 44 for blue background)
  • (Future) RGB tuples – for now raise NotImplementedError or ignore.

If an unknown name or out‑of‑range code is provided, raise ValueError.

Reuse internal low‑level methods

The implementation must not duplicate ANSI logic. Instead, it should call existing methods:

  • tint.color(text, fg_code) or a new _code_for_color(name)
  • tint.background(text, bg_code)
  • internal helpers for style codes (_code_for_style("bold"))

This ensures a single source of truth and consistent behavior with NO_COLOR / FORCE_COLOR.

Handling invalid styles and colors

  • Define VALID_STYLES = {"bold","dim","italic","underline","strikethrough"}.
  • If any style string is not in VALID_STYLES, raise ValueError.
  • If color or bg name is not recognised, raise ValueError.

Interaction with environment / color toggles

  • The method must respect tint.use_color (or the global detection state).
  • When colour is disabled (NO_COLOR, not a TTY, etc.), return plain text without any ANSI codes (including no reset).
  • When RAZTINT_FORCE_COLOR is set, always output ANSI codes even without a TTY.

Documentation examples in README

The README should show:

  • Basic usage: format_text("Hello", color="red", styles="bold")
  • With background: format_text("Alert", color="white", bg="red", styles=["bold", "underline"])
  • With reset=False for concatenation:
    part1 = tint.format_text("WARNING:", color="yellow", reset=False)
    part2 = tint.format_text(" Disk full", color="red")
    print(part1 + part2)
  • Comparison with old nested style: show that both work.

9. Acceptance criteria (updated)

  • The implementation uses only the Python standard library (no new dependencies).
  • A new method format_text() (or style()) is available on the RazTint class and as a module‑level helper.
  • Parameters: text: str, color: str | int | None = None, bg: str | int | None = None, styles: str | list[str] | None = None, reset: bool = True.
  • color and bg accept named ANSI colors (e.g., "red", "bright_green") and integer codes (31, 44).
  • styles accepts a single style string, a list, or multiple string arguments.
  • The method works identically when colour is globally disabled (returns plain text).
  • All existing colour/style functions (red(), bold(), bg_blue(), …) remain unchanged and work alongside the new method.
  • Invalid style names or colour names raise ValueError.
  • Code is formatted with black, passes ruff check, and passes ty type checking.
  • Documented in README with clear examples (including reset behaviour and chaining).
  • Edge cases tested: empty string "", None (treated as empty string), mutually exclusive styles (bold+dim – documented as unpredictable), nested calls.

Additional considerations

  • The method should be a convenience wrapper, not a replacement for the fine‑grained functions.
  • For performance, detection of colour support and icon mode remains cached.
  • If reset=False is used, the caller is responsible for resetting styles later to avoid “bleeding” into subsequent output.

Metadata

Metadata

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions