Allow using a custom python function in tab_title_template

Makes it easier to do complex processing
This commit is contained in:
Kovid Goyal
2025-08-30 08:48:47 +05:30
parent 7ff25fd77c
commit 1544830307
3 changed files with 43 additions and 4 deletions

View File

@@ -140,6 +140,9 @@ Detailed list of changes
controlled by :opt:`cursor_blink_interval` and
:opt:`cursor_stop_blinking_after`. (:pull:`8551`)
- Allow using a custom python function to draw tab titles in the tab bar, see
:opt:`tab_title_template`
- Wayland: Fix incorrect window size calculation when transitioning from
full screen to non-full screen with client side decorations (:iss:`8826`)

View File

@@ -1511,6 +1511,14 @@ use :code:`{sup.index}`. All data available is:
:code:`tab.progress_percent`
If a command running in a window reports the progress for a task, show this progress as a percentage
from all windows in the tab, averaged. Empty string is no progress is reported.
:code:`custom`
This will call a function named :code:`draw_title(data)` from the file :file:`tab_bar.py` placed in
the kitty config directory. The function will be passed a dictionary of data, the same data that
can be used in this template. It can then perform arbitrarily complex processing and return a string.
For example: :code:`tab_title_template "{custom}"` will use the output of the function as the tab title.
Any print statements in the :code:`draw_title()` will print to the STDOUT of the kitty process, useful
for debugging.
Note that formatting is done by Python's string formatting machinery, so you can
use, for instance, :code:`{layout_name[:2].upper()}` to show only the first two

View File

@@ -303,6 +303,7 @@ def draw_title(draw_data: DrawData, screen: Screen, tab: TabBarData, index: int,
prefix += '{activity_symbol}'
if prefix:
template = '{fmt.fg.red}' + prefix + '{fmt.fg.tab}' + template
eval_locals['custom'] = load_custom_draw_title(eval_locals)
try:
title = eval(compile_template(template), {'__builtins__': safe_builtins}, eval_locals)
except Exception as e:
@@ -486,15 +487,24 @@ def draw_tab_with_powerline(
@run_once
def load_custom_draw_tab() -> DrawTabFunc:
def load_custom_draw_tab_module() -> dict[str, Any]:
import runpy
import traceback
try:
m = runpy.run_path(os.path.join(config_dir, 'tab_bar.py'))
func: DrawTabFunc = m['draw_tab']
return runpy.run_path(os.path.join(config_dir, 'tab_bar.py'))
except FileNotFoundError:
return {}
except Exception as e:
traceback.print_exc()
log_error(f'Failed to load custom draw_tab function with error: {e}')
log_error(f'Failed to load custom tab_bar.py module with error: {e}')
return {}
@run_once
def load_custom_draw_tab() -> DrawTabFunc:
m = load_custom_draw_tab_module()
func: DrawTabFunc | None = m.get('draw_tab')
if func is None:
return draw_tab_with_fade
@wraps(func)
@@ -512,6 +522,24 @@ def load_custom_draw_tab() -> DrawTabFunc:
return draw_tab
class CustomDrawTitleFunc:
def __init__(self, data: dict[str, Any], implementation: Callable[[dict[str, Any]], str] | None = None):
self._implementation = implementation
self._data = data.copy()
def __str__(self) -> str:
if self._implementation is None:
return ''
return str(self._implementation(self._data))
__repr__ = __str__
def load_custom_draw_title(data: dict[str, Any]) -> CustomDrawTitleFunc:
m = load_custom_draw_tab_module()
return CustomDrawTitleFunc(data, m.get('draw_title'))
class CellRange(NamedTuple):
start: int
end: int