From c80c4f63b63c99880cfbe5ea2eb4fb15519209fd Mon Sep 17 00:00:00 2001 From: rajvira10 Date: Fri, 16 Oct 2026 22:10:56 +0600 Subject: [PATCH] Use display widths for paging and vertical output --- CHANGELOG.md | 1 + litecli/main.py | 5 +++-- tests/test_main.py | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d91317..be7e71f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Bug Fixes +- Use visible terminal widths for automatic paging and vertical output. - Expand `~` in configured log file paths before opening the log. ### Internal diff --git a/litecli/main.py b/litecli/main.py index fa732c3..b79c259 100644 --- a/litecli/main.py +++ b/litecli/main.py @@ -30,6 +30,7 @@ ) from prompt_toolkit.lexers import PygmentsLexer from prompt_toolkit.shortcuts import CompleteStyle, PromptSession +from prompt_toolkit.utils import get_cwidth from .__init__ import __version__ from .clibuffer import cli_is_multiline @@ -727,7 +728,7 @@ def output(self, output: Iterable[str], status: str | None = None) -> None: if fits or output_via_pager: # buffering buf.append(line) - if len(line) > size.columns or i > (size.rows - margin): + if get_cwidth(click.unstyle(line)) > size.columns or i > (size.rows - margin): fits = False if not self.explicit_pager and special.is_pager_enabled(): # doesn't fit, use pager @@ -876,7 +877,7 @@ def format_output(self, title: Any, cur: Any, headers: Any, expanded: bool = Fal first_line = next(formatted) formatted = itertools.chain([first_line], formatted) - if not expanded and max_width and headers and cur and len(first_line) > max_width: + if not expanded and max_width and headers and cur and get_cwidth(click.unstyle(first_line)) > max_width: formatted = self.formatter.format_output( cur, headers, diff --git a/tests/test_main.py b/tests/test_main.py index 21ad5b6..47c7902 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -9,6 +9,7 @@ import click import pytest +from cli_helpers.tabular_output import TabularOutputFormatter from click.testing import CliRunner from prompt_toolkit import PromptSession @@ -233,6 +234,40 @@ def test_conditional_pager(monkeypatch): SPECIAL_COMMANDS["pager"].handler("") +@pytest.mark.parametrize( + "line,width,expect_pager", + [ + ("text", 4, False), + ("\x1b[31mtext\x1b[0m", 4, False), + ("資料", 3, True), + ("cafe\u0301", 4, False), + ("\x1b[31m資料\x1b[0m", 4, False), + ], +) +def test_conditional_pager_uses_display_width(monkeypatch, line, width, expect_pager): + output( + monkeypatch, + terminal_size=(width, 20), + testdata=[line], + explicit_pager=False, + expect_pager=expect_pager, + ) + + +@pytest.mark.parametrize( + "header,width,expect_vertical", + [("text", 4, False), ("資料", 3, True), ("cafe\u0301", 4, False)], +) +def test_auto_vertical_output_uses_display_width(header, width, expect_vertical): + m = cast(Any, object.__new__(LiteCli)) + m.formatter = TabularOutputFormatter(format_name="plain") + m.output_style = None + + rendered = "\n".join(m.format_output(None, [("v",)], [header], max_width=width)) + + assert ("[ 1. row ]" in rendered) == expect_vertical + + def test_reserved_space_is_integer(monkeypatch): """Make sure that reserved space is returned as an integer."""