Fix less mouse pager fallback handling
This commit is contained in:
@@ -1,53 +1,81 @@
|
||||
"""Custom pager with mouse scroll support."""
|
||||
"""Less-based pager integration with mouse support and fallback."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import signal
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
from rich.pager import Pager, SystemPager
|
||||
|
||||
|
||||
def _is_mouse_capable_less() -> bool:
|
||||
"""Check whether the system has a usable ``less`` executable.
|
||||
def _mouse_capable_less() -> str | None:
|
||||
"""Return a ``less`` path that likely supports ``--mouse``.
|
||||
|
||||
Mouse support was added in ``less`` 543. This probe is best-effort: it
|
||||
checks that ``less`` exists, asks for its version string, and extracts the
|
||||
version number from the leading ``less <version>`` banner. Any unexpected
|
||||
output is treated as unsupported so the caller can fall back safely.
|
||||
|
||||
Returns:
|
||||
True if ``less`` is installed and responds to ``--version``.
|
||||
The resolved ``less`` executable path when it appears mouse-capable,
|
||||
otherwise ``None``.
|
||||
"""
|
||||
less = shutil.which("less")
|
||||
if less is None:
|
||||
return False
|
||||
return None
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[less, "--version"], capture_output=True, text=True, timeout=5
|
||||
)
|
||||
version_str = result.stdout or result.stderr or ""
|
||||
return "GNU" in version_str or "less" in version_str.lower()
|
||||
except (subprocess.TimeoutExpired, OSError):
|
||||
return False
|
||||
return None
|
||||
|
||||
version_str = result.stdout or result.stderr or ""
|
||||
match = re.search(r"\bless\s+(\d+)\b", version_str, re.IGNORECASE)
|
||||
if match is None:
|
||||
return None
|
||||
|
||||
if int(match.group(1)) < 543:
|
||||
return None
|
||||
|
||||
return less
|
||||
|
||||
|
||||
class MousePager(Pager):
|
||||
"""Pager that prefers ``less --mouse`` and falls back to the system pager."""
|
||||
"""Pager that prefers ``less -R --mouse`` for mouse scrolling.
|
||||
|
||||
``less`` is the prerequisite for mouse-enabled paging in this project. If
|
||||
``less`` is missing, too old, cannot be launched, or exits with a non-zero
|
||||
status, we fall back to Rich's ``SystemPager``. A user interrupt does not
|
||||
trigger a second pager session.
|
||||
"""
|
||||
|
||||
def show(self, content: str) -> None:
|
||||
"""Display content with mouse-scroll support when available.
|
||||
|
||||
Uses ``less --mouse`` when available; otherwise falls back to the system
|
||||
pager.
|
||||
"""Display rendered content in ``less -R --mouse`` when possible.
|
||||
|
||||
Args:
|
||||
content: Content to display in pager.
|
||||
content: Fully rendered text from Rich.
|
||||
"""
|
||||
if _is_mouse_capable_less():
|
||||
try:
|
||||
subprocess.run(
|
||||
["less", "-R", "--mouse"],
|
||||
input=content,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
return
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
pass
|
||||
SystemPager().show(content)
|
||||
less = _mouse_capable_less()
|
||||
if less is None:
|
||||
SystemPager().show(content)
|
||||
return
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[less, "-R", "--mouse"],
|
||||
input=content,
|
||||
text=True,
|
||||
)
|
||||
except OSError:
|
||||
SystemPager().show(content)
|
||||
return
|
||||
|
||||
if result.returncode in (-signal.SIGINT, 128 + signal.SIGINT):
|
||||
return
|
||||
|
||||
if result.returncode != 0:
|
||||
SystemPager().show(content)
|
||||
|
||||
Reference in New Issue
Block a user