In UTF-16BE a newline is 00 0A, so the initial read_until(b'\n') usually
already ends exactly at the end of line 1. The BE branch then always called
read_utf16_line, which read on to the next newline, merging lines 1 and 2.
Only keep reading when the first read did not end on a complete 00 0A.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VVn4SeAXPsUajXT2rtHQfo
The main output path has exited with status 0 on EPIPE since #232, but
auxiliary outputs still used println!/print!/io::stdout().flush().unwrap()
and panicked (exit code 101) when the reader of a piped stdout went away
early, e.g. 'bat --completion bash | head -n0'.
Route them through the existing convention instead: write!/writeln! to
io::stdout() and '?' so that Error::Io(BrokenPipe) reaches
default_error_handler, which exits with status 0. This covers
--completion, --config-file, --diagnostic (bugreport's print() becomes
format() + writeln!), --generate-config-file, the 'cache' subcommand
progress messages, the OSC title sequence of --set-terminal-title, and
the 'built without feature' notices.
Closes#4034
The `Attributes` ANSI style tracker in `src/vscreen.rs` declares, initializes,
clears (in `sgr_reset`), and re-emits (in `Display`) a `strike` field
documented as `ON: ^[9m / OFF: ^[29m`, but `update_with_sgr()` had no match
arm for SGR 9 (set strikethrough) or 29 (clear strikethrough). As a result
`strike` was only ever cleared, never set.
Because bat re-applies the tracked input ANSI style before each highlighted
text run (nu_ansi_term appends a full reset after every token), attributes
that are tracked — bold, dim, italic, underline — survive across tokens,
while strikethrough was silently dropped after the first token. This path is
active by default (`--strip-ansi`/`--sanitize` default to `never`).
Add the missing `9` and `29` arms, mirroring the sibling underline (4/24) and
italic (3/23) arms, so strikethrough is tracked and re-emitted like every
other supported attribute.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
bat prepends the arguments from its config file to the command line, so a
user who has `--show-all` in their config and also types `-A` passes the
flag twice, and clap rejects that with "the argument '--show-all' cannot be
used multiple times" and exit code 2. The file is never displayed.
`--plain`, `--number`, `--number-nonblank`, `--force-colorization` and
`--no-paging` already avoid this by overriding themselves. The same is now
done for the eight remaining flags that affect how a file is displayed:
--show-all, --diff, --chop-long-lines, --squeeze-blank, --unbuffered,
--quiet-empty, --no-custom-assets and --set-terminal-title.
Repeating a flag produces byte-identical output to giving it once, and
--show-all still conflicts with --language as before.
Adds a `-b` / `--number-nonblank` flag that numbers only non-blank
lines. Blank lines (those containing only the line terminator) are
printed without a number and don't advance the counter, matching
`cat -b` behaviour.
- `-b` implies `--style=numbers` (same as `-n`), but only counts
non-blank lines
- `-b` takes precedence over `-n`: both `-bn` and `-nb` behave like `-b`
- `-bp` disables numbering (plain mode wins)
- Works with files, stdin, and piped output (like `-n`)
Closes#3856.
When an ignored suffix is also a registered extension (e.g. `.txt` maps to
"Plain Text"), `get_syntax_for_file_extension` matched the raw extension
before attempting the suffix strip. As a result `--ignored-suffix .txt` had
no effect on such files and first-line/shebang detection was never reached,
so a shell script saved as `*.txt` stayed unhighlighted.
Strip ignored suffixes first and detect on the remainder; only fall back to
the raw extension when no ignored suffix applies. When a suffix is stripped
but the remainder has no syntax, return `None` so the caller can use
first-line detection. Default behavior (no `--ignored-suffix`) is unchanged.
See #2745.
`InteractivePrinter::print_snip` computes the snip separator width as
`term_width - panel_count - snip_left_count - title_count`. At `--terminal-width 1`
the panel is disabled and `title_count == 2`, so the subtraction underflows
`usize` (release has no overflow-checks, so it wraps to ~usize::MAX); `str::repeat`
then aborts with "capacity overflow" whenever a snip separator is emitted (two or
more disjoint line ranges, or a diff gap) — which the default style does.
Use `saturating_sub` for the repeat counts so they clamp to 0 instead of
underflowing. Added an integration test.
When piped input has no filename hint, bat falls back to plain text. Many
common formats can be reliably identified by shebang or magic-string in the
first line (#!/bin/bash, <?xml, {"json: ...}, etc.). Adds a first-line
pattern matcher to assets.rs.
Fixes#3697
The Tcl.sublime-syntax.patch adds first_line_match so bat recognizes
tclsh/wish/expect shebang files as Tcl. With the patch applied, the
leading '#!/...' and '# comment' lines get tokenized as Tcl comments
and rendered in Monokai-Extended's comment color instead of default
foreground.
The goldens were generated against unpatched bat in the original
commit, so CI's 'Run tests with updated syntaxes and themes' job (which
runs assets/create.sh before the regression test) disagreed. Regenerate
against the patched build so the regression test passes.
CI reported FileNotFoundError for Tcl/{tclsh,expect,wish}_shebang in
tests/syntax-tests/source/Tcl/. The highlighted files existed but the
source files had been omitted, so create_highlighted_versions.py had
nothing to read from.
Source files match the shebang regression test inputs at
tests/examples/regression_tests/issue_3647_*.
Add extensionless regression test files for tclsh, wish, and expect
shebangs so syntect bumps don't silently break first-line detection.
Move changelog entry from Features to Syntaxes per reviewer suggestion.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Control characters displayed in caret notation (e.g. ^@ for NUL)
occupy 2 terminal columns, but the width calculation treated them
as 0-width. Add a char_width() helper that returns 2 for control
characters, fixing incorrect line wrapping with --binary=as-text.
Fixes#3631
* feat(cli): add fallback syntax option
Expose a new fallback syntax CLI option so users can opt into syntax highlighting only when auto-detection fails.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
* feat(syntax): apply fallback only after detection fails
Use the fallback syntax only when path and first-line detection fail, preserving existing behavior for detected files and explicit language selection.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
* test(cli): cover fallback syntax behavior
Add integration coverage for fallback syntax usage, precedence with --language, and no-op behavior when syntax is already detected; update help snapshots for the new option.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
* docs(changelog): document fallback syntax option
Record the new fallback syntax feature in the unreleased changelog section.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
---------
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
* feat: add word wrapping mode for --wrap flag
* Run `cargo fmt` and add CHANGELOG entry
* Add word wrap tests, update manpage and shell completions
- Add integration tests for word wrapping: basic word boundary breaking,
fallback to character wrapping for long words, line numbers, and
short lines that fit without wrapping
- Update manpage to document the new 'word' wrapping mode
- Update bash, fish, zsh, and PowerShell completions with 'word' option
- Avoid unnecessary clone of `line_buf` when word wrap is disabled
* make clippy and cargo fmt happy
---------
Co-authored-by: Keith Hall <keith-hall@users.noreply.github.com>
When output is piped to a pager, the wrapping_mode logic was not checking the explicit --wrap=never flag and would always default to NoWrapping(false). This meant the -S flag was not passed to less, causing lines to wrap despite the user's explicit request.
The fix prioritizes explicit CLI flags (--wrap=never, --chop-long-lines) over the interactive_output-based logic, ensuring they are always respected.
Fixes#3587
when a configured pager (via BAT_PAGER, PAGER, or --pager) is not found,
bat now shows a warning message before falling back to stdout. this helps
users understand why their pager isn't running and makes it obvious when
there's a typo or PATH issue.
fixes issue #2904
Repurpose the existing --unbuffered/-u flag (previously a POSIX no-op)
to enable unbuffered input reading using fill_buf()/consume() instead
of read_until(b'\n'). This allows partial lines to display immediately
when piping streaming input like `tail -f` into bat.
- Add unbuffered field to Config and InputReader
- Add read_line_unbuffered() using BufRead::fill_buf()/consume()
- Add flush() to OutputHandle, called after each line in unbuffered mode
- Auto-disable line numbers in unbuffered mode to avoid partial line confusion
- Update help text, man page, and shell completions
- Add unit tests and integration tests