From 5951079d5cdfedde5f2652b3f735c0f15c76c67e Mon Sep 17 00:00:00 2001 From: ak2k <19240940+ak2k@users.noreply.github.com> Date: Mon, 26 Oct 2026 08:23:42 -0400 Subject: [PATCH 1/3] flight-cli: test that a wrapped carrier legend keeps codes with names Unit: work-z8ovl --- tests/test_carrier_legend_docs.py | 10 ++++++++++ tests/test_gf_carrier_legend.py | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/test_carrier_legend_docs.py b/tests/test_carrier_legend_docs.py index 27c4f0e0..348790da 100644 --- a/tests/test_carrier_legend_docs.py +++ b/tests/test_carrier_legend_docs.py @@ -14,3 +14,13 @@ def test_memo_says_a_legend_name_cannot_break_the_line() -> None: "collapses the whitespace runs of an entry, a newline and a tab included, to one " "space, so a name cannot break the legend over two lines" ) in memo + + +def test_memo_says_a_wrapped_legend_breaks_only_between_entries() -> None: + text = (_ROOT / "docs/memories/gf_routing_and_carriers.md").read_text() + memo = " ".join(text.split()) + assert ( + "When the legend outgrows the console it breaks only between entries, never between a " + "code and its name; Rich breaks at any space, a no-break space included, so " + "`_print_carrier_legend_line` picks the break itself." + ) in memo diff --git a/tests/test_gf_carrier_legend.py b/tests/test_gf_carrier_legend.py index c55b598e..9b93e75e 100644 --- a/tests/test_gf_carrier_legend.py +++ b/tests/test_gf_carrier_legend.py @@ -195,6 +195,18 @@ def test_a_narrow_console_wraps_the_legend_within_its_width( assert _LEGEND in " ".join(" ".join(lines).split()) +def test_a_wrapping_legend_keeps_each_code_with_its_name( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """At 40 columns Rich alone ends the first line on `F9`, apart from its name.""" + lines = _printed(monkeypatch, [_row(("MX", "100")), _row(("F9", "200"))], width=40) + start = next(i for i, ln in enumerate(lines) if ln.startswith("Carriers:")) + assert [ln.rstrip() for ln in lines[start:]] == [ + "Carriers: MX Breeze Airways ·", + "F9 Frontier Airlines", + ] + + def test_a_search_prints_the_legend_under_its_table(gf_session: Callable[..., Any]) -> None: body = _answering( _ds1("ds1_jfk_lhr_tfu.json"), origin="JFK", destination="LHR", date=_DEP.isoformat() From 5fd28a566cd10dae37672933616404caf06d4932 Mon Sep 17 00:00:00 2001 From: ak2k <19240940+ak2k@users.noreply.github.com> Date: Mon, 26 Oct 2026 08:26:20 -0400 Subject: [PATCH 2/3] flight-cli: a wrapped carrier legend breaks only between entries Rich wraps a long line at any space, a no-break space included, so a narrow console could end a legend line on a carrier code and print its name on the next. The legend now packs whole entries into lines that fit the console width, and an entry wider than the console gets a line of its own. A legend that fits one line prints as before. Unit: work-z8ovl --- docs/memories/gf_routing_and_carriers.md | 4 +++- src/flight_cli/cli.py | 23 ++++++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/docs/memories/gf_routing_and_carriers.md b/docs/memories/gf_routing_and_carriers.md index da5b78c1..35137885 100644 --- a/docs/memories/gf_routing_and_carriers.md +++ b/docs/memories/gf_routing_and_carriers.md @@ -324,4 +324,6 @@ The Matrix table prints the same line through `_print_matrix_carrier_legend`, naming each code by Matrix's `shortName`. Both go through `_print_carrier_legend_line`, which collapses the whitespace runs of an entry, a newline and a tab included, to one space, so a name cannot break the legend over -two lines. +two lines. When the legend outgrows the console it breaks only between entries, +never between a code and its name; Rich breaks at any space, a no-break space +included, so `_print_carrier_legend_line` picks the break itself. diff --git a/src/flight_cli/cli.py b/src/flight_cli/cli.py index 39e8d818..fa8274ec 100644 --- a/src/flight_cli/cli.py +++ b/src/flight_cli/cli.py @@ -5069,12 +5069,25 @@ def _render_search( def _print_carrier_legend_line(named: Mapping[str, str | None]) -> None: - """The one `Carriers:` line both tables print, each named code once. An entry's + """The `Carriers:` legend both tables print, each named code once. An entry's whitespace runs, a newline and a tab included, collapse to one space so a name - cannot break the legend over two lines; a table with no named code prints none.""" - if any(named.values()): - entries = (" ".join(f"{code} {name}".split()) for code, name in named.items() if name) - console.print("[dim]Carriers: " + _safe_text(" · ".join(entries)) + "[/]") + cannot break the legend over two lines; a table with no named code prints none. + When it outgrows the console it breaks only between entries, never between a + code and its name: Rich breaks at any space, a no-break space included, so the + break is chosen here, and an entry wider than the console is left for Rich to fold.""" + entries = [ + " ".join(f"{code} {name}".translate(_CTRL).split()) for code, name in named.items() if name + ] + entries = [entry for entry in entries if entry] + lines: list[str] = [] + for i, entry in enumerate(entries): + piece = entry + (" ·" if i < len(entries) - 1 else "") + if lines and cell_len(lines[-1] + " " + piece) <= console.width: + lines[-1] += " " + piece + else: + lines.append(("Carriers: " if not lines else "") + piece) + for line in lines: + console.print("[dim]" + _safe_text(line) + "[/]") def _print_matrix_carrier_legend(shown: Sequence[Itinerary]) -> None: From fb37ef3728cc480f9746dee336f1f06d11da5ee8 Mon Sep 17 00:00:00 2001 From: ak2k <19240940+ak2k@users.noreply.github.com> Date: Mon, 26 Oct 2026 09:35:00 -0400 Subject: [PATCH 3/3] flight-cli: the first carrier legend entry also wraps whole The Carriers: prefix now counts toward the first line, so on a console too narrow for the prefix and the first entry together, the prefix stands alone and the entry starts the next line instead of being split between its code and its name. Unit: work-z8ovl Fixes-unit: work-z8ovl --- src/flight_cli/cli.py | 9 ++++++--- tests/test_gf_carrier_legend.py | 13 +++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/flight_cli/cli.py b/src/flight_cli/cli.py index fa8274ec..ac97013f 100644 --- a/src/flight_cli/cli.py +++ b/src/flight_cli/cli.py @@ -5079,13 +5079,16 @@ def _print_carrier_legend_line(named: Mapping[str, str | None]) -> None: " ".join(f"{code} {name}".translate(_CTRL).split()) for code, name in named.items() if name ] entries = [entry for entry in entries if entry] - lines: list[str] = [] + if not entries: + return + # The prefix counts toward the first line, so the first entry is measured too. + lines = ["Carriers:"] for i, entry in enumerate(entries): piece = entry + (" ·" if i < len(entries) - 1 else "") - if lines and cell_len(lines[-1] + " " + piece) <= console.width: + if cell_len(lines[-1] + " " + piece) <= console.width: lines[-1] += " " + piece else: - lines.append(("Carriers: " if not lines else "") + piece) + lines.append(piece) for line in lines: console.print("[dim]" + _safe_text(line) + "[/]") diff --git a/tests/test_gf_carrier_legend.py b/tests/test_gf_carrier_legend.py index 9b93e75e..738a0af4 100644 --- a/tests/test_gf_carrier_legend.py +++ b/tests/test_gf_carrier_legend.py @@ -207,6 +207,19 @@ def test_a_wrapping_legend_keeps_each_code_with_its_name( ] +def test_a_legend_entry_that_fits_the_console_is_never_split_the_first_included( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """`LH Lufthansa ·` fits 14 columns; with `Carriers: ` before it, it needs 24.""" + split: list[int] = [] + for width in range(14, 61): + lines = _printed(monkeypatch, [_row(("LH", "1")), _row(("B6", "2"))], width=width) + legend = lines[next(i for i, ln in enumerate(lines) if ln.startswith("Carriers:")) :] + if not all(any(e in ln for ln in legend) for e in ("LH Lufthansa", "B6 JetBlue")): + split.append(width) + assert split == [] + + def test_a_search_prints_the_legend_under_its_table(gf_session: Callable[..., Any]) -> None: body = _answering( _ds1("ds1_jfk_lhr_tfu.json"), origin="JFK", destination="LHR", date=_DEP.isoformat()