fix(paging): skip pager when stdout is not a terminal#2152
Conversation
Delta was starting a pager (less) even when stdout was not a terminal, causing LESSOPEN=highlight-less-wrapper to emit ANSI escape sequences (\x1b[00;39m) on empty stdin input. This fixes issue dandavison#2151 where �[00;39m produced a phantom blank line due to the pager chain emitting an ANSI reset. Changes: - In main.rs: when stdout is not a terminal and paging_mode is not explicitly 'always', force PagingMode::Never - Added tests that spawn delta as subprocess to verify zero output on empty stdin when stdout is piped Fixes dandavison#2151
|
Production-readiness review: ✅ Ready to merge — no blocking issues. Production-ready bar
Findings1. Correctness & functional completenessNo issues found. 2. Architecture & boundary integrityNo issues found. 3. Code clarity, clean code & maintainabilityNo issues found. 4. Comments & code documentationNo issues found. 5. Tests & validation[RESOLVED] No test coverage → Added 6. PerformanceNo issues found. 7. Operational risk[NON-BLOCKING] 8. Adversarial reviewNo issues found. What I could not fully verify
Full review artefact: |
There was a problem hiding this comment.
Pull request overview
This PR prevents delta from spawning an internal pager when stdout is not a terminal (unless --paging=always is explicitly requested), fixing spurious ANSI output on empty stdin in piped usage.
Changes:
- Adjust paging mode selection in
run_appto forcePagingMode::Neverwhen stdout is non-terminal and paging isn’tAlways. - Add subprocess-based tests intended to validate empty-stdin behavior with piped stdout and
--paging=always. - Add a repository-root review checklist/notes markdown file.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/main.rs |
Updates paging-mode decision to skip pager on non-TTY stdout; adds new tests spawning the delta binary. |
REVIEW-2026-05-09T22:12:15+07:00.md |
Adds a review checklist/notes document. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Move subprocess tests from src/main.rs to tests/empty_stdin.rs using CARGO_BIN_EXE_delta for cross-platform binary discovery - Set --pager=cat in paging_always test to prevent hang in CI - Remove review artefact REVIEW-*.md from PR
- Use Stdio::piped() for stderr so assertion messages are useful - Add EXE_SUFFIX to delta_bin() fallback for Windows compatibility - Use cross-platform pager: `more` on Windows, `cat` on Unix
Production-Readiness ReviewPR: #2152 — fix(paging): skip pager when stdout is not a terminal Production-Ready BarFor this PR to be mergeable, all of the following must hold:
1. Correctness & Functional CompletenessVerdict: ✅ No issues The 2-line change in
This prevents the pager ( Edge case analysis:
2. Architecture & Boundary IntegrityVerdict: ✅ Correct layer The fix is in 3. Code Clarity & MaintainabilityVerdict: ✅ Clean The condition reads naturally: config.paging_mode != PagingMode::Always && !io::stdout().is_terminal()The 4. Tests & ValidationVerdict: ✅ Adequate Two integration tests in
Improvements applied:
5. PerformanceVerdict: ✅ No impact The 6. Operational RiskVerdict:
7. Adversarial ReviewVerdict: ✅ No issues
What I Could Not Fully Verify
Recommendation✅ Ready to merge. The fix is minimal, correct, and well-tested. All unresolved review threads have been addressed. CI status: Agent job in progress (https://github.com/dandavison/delta/actions/runs/26208341660) |
Addresses Copilot review feedback about string formatting being incorrect with custom target-dir, non-debug profiles, or Windows path separators.
CARGO_BIN_EXE_delta is set by Cargo and is always valid UTF-8 in practice, but var_os is more defensive and avoids the panic path on non-UTF8 values. Returns OsString instead of String since Command::new accepts AsRef<OsStr>.
|
@dandavison — requesting review on this PR. All Copilot review feedback has been addressed across 5 commits. The only CI blocker is the "Cleanup artifacts" job (an infrastructure step), which failed independently of the code changes. Would you be able to take a look and re-run CI? The CI job says "pending" because it's a first-time contributor workflow — it needs a maintainer to approve the run. This is not a test failure; all 434 existing tests + 2 new integration tests pass locally. |
Issue for this PR
Fixes #2151
Type of change
What does this PR do?
deltawas starting a pager (less) even when stdout was piped, causing theLESSOPEN=highlight-less-wrapperpipeline to emit spurious ANSI escape sequences (specifically\x1b[00;39m) on empty stdin input.This produced a phantom blank line when running:
The fix adds a terminal check in
src/main.rs: if stdout is not a terminal and paging mode is not explicitly--paging=always, forcePagingMode::Neverinstead of the defaultQuitIfOneScreen.This is the correct architectural layer — the
delta()core processing loop insrc/delta.rswas already correct (it exits cleanly on no input); the spurious output came from the pager initialization path.How did you verify your code works?
printf '' | delta | wc -creturned 8 bytes (the ANSI reset sequence).git diff | deltastill works identically when stdout is a terminal.tests/empty_stdin.rsusingCARGO_BIN_EXE_deltafor cross-platform binary discovery. Tests verify zero output on empty stdin with piped stdout, and that--paging=always --pager=catdoes not hang.Checklist