gh-138122: Integrate live profiler TUI with _colorize theming system

The Tachyon profiler's curses-based TUI now uses the centralized theming
infrastructure in _colorize.py, enabling users to customize colors via
the standard Python theming API. This adds a LiveProfiler theme section
with two pre-configured themes: the default dark theme optimized for
dark terminal backgrounds, and LiveProfilerLight for white/light
backgrounds. Users can switch themes by calling _colorize.set_theme() in
their PYTHONSTARTUP or sitecustomize.py.

The table header rendering was also improved to draw a continuous
background, eliminating visual gaps between columns when using reverse
video styling.
This commit is contained in:
Pablo Galindo Salgado 2025-12-06 19:49:11 +00:00
parent eba449a198
commit b024e50773
4 changed files with 197 additions and 76 deletions

View file

@ -639,8 +639,6 @@ def render(self, line, width, **kwargs):
def draw_column_headers(self, line, width):
"""Draw column headers with sort indicators."""
col = 0
# Determine which columns to show based on width
show_sample_pct = width >= WIDTH_THRESHOLD_SAMPLE_PCT
show_tottime = width >= WIDTH_THRESHOLD_TOTTIME
@ -659,38 +657,38 @@ def draw_column_headers(self, line, width):
"cumtime": 4,
}.get(self.collector.sort_by, -1)
# Build the full header line first, then draw it
# This avoids gaps between columns when using reverse video
header_parts = []
col = 0
# Column 0: nsamples
attr = sorted_header if sort_col == 0 else normal_header
text = f"{'▼nsamples' if sort_col == 0 else 'nsamples':>13}"
self.add_str(line, col, text, attr)
text = f"{'▼nsamples' if sort_col == 0 else 'nsamples':>13} "
header_parts.append((col, text, sorted_header if sort_col == 0 else normal_header))
col += 15
# Column 1: sample %
if show_sample_pct:
attr = sorted_header if sort_col == 1 else normal_header
text = f"{'%' if sort_col == 1 else '%':>5}"
self.add_str(line, col, text, attr)
text = f"{'%' if sort_col == 1 else '%':>5} "
header_parts.append((col, text, sorted_header if sort_col == 1 else normal_header))
col += 7
# Column 2: tottime
if show_tottime:
attr = sorted_header if sort_col == 2 else normal_header
text = f"{'▼tottime' if sort_col == 2 else 'tottime':>10}"
self.add_str(line, col, text, attr)
text = f"{'▼tottime' if sort_col == 2 else 'tottime':>10} "
header_parts.append((col, text, sorted_header if sort_col == 2 else normal_header))
col += 12
# Column 3: cumul %
if show_cumul_pct:
attr = sorted_header if sort_col == 3 else normal_header
text = f"{'%' if sort_col == 3 else '%':>5}"
self.add_str(line, col, text, attr)
text = f"{'%' if sort_col == 3 else '%':>5} "
header_parts.append((col, text, sorted_header if sort_col == 3 else normal_header))
col += 7
# Column 4: cumtime
if show_cumtime:
attr = sorted_header if sort_col == 4 else normal_header
text = f"{'▼cumtime' if sort_col == 4 else 'cumtime':>10}"
self.add_str(line, col, text, attr)
text = f"{'▼cumtime' if sort_col == 4 else 'cumtime':>10} "
header_parts.append((col, text, sorted_header if sort_col == 4 else normal_header))
col += 12
# Remaining headers
@ -700,13 +698,22 @@ def draw_column_headers(self, line, width):
MAX_FUNC_NAME_WIDTH,
max(MIN_FUNC_NAME_WIDTH, remaining_space // 2),
)
self.add_str(
line, col, f"{'function':<{func_width}}", normal_header
)
text = f"{'function':<{func_width}} "
header_parts.append((col, text, normal_header))
col += func_width + 2
if col < width - 10:
self.add_str(line, col, "file:line", normal_header)
file_text = "file:line"
padding = width - col - len(file_text)
text = file_text + " " * max(0, padding)
header_parts.append((col, text, normal_header))
# Draw full-width background first
self.add_str(line, 0, " " * (width - 1), normal_header)
# Draw each header part on top
for col_pos, text, attr in header_parts:
self.add_str(line, col_pos, text.rstrip(), attr)
return (
line + 1,