diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml
index 9a44b831..5d2d27ae 100644
--- a/.github/workflows/CICD.yml
+++ b/.github/workflows/CICD.yml
@@ -173,7 +173,6 @@ jobs:
- { target: i686-unknown-linux-musl , os: ubuntu-20.04, dpkg_arch: musl-linux-i686, use-cross: true }
- { target: x86_64-apple-darwin , os: macos-13, }
- { target: aarch64-apple-darwin , os: macos-14, }
- - { target: x86_64-pc-windows-gnu , os: windows-2019, }
- { target: x86_64-pc-windows-msvc , os: windows-2019, }
- { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04, dpkg_arch: amd64, use-cross: true }
- { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, dpkg_arch: musl-linux-amd64, use-cross: true }
diff --git a/.github/workflows/require-changelog-for-PRs.yml b/.github/workflows/require-changelog-for-PRs.yml
index 9b9d7cde..346ed385 100644
--- a/.github/workflows/require-changelog-for-PRs.yml
+++ b/.github/workflows/require-changelog-for-PRs.yml
@@ -30,4 +30,4 @@ jobs:
echo "Added lines in CHANGELOG.md:"
echo "$ADDED"
echo "Grepping for PR info (see CONTRIBUTING.md):"
- grep "#${PR_NUMBER}\\b.*@${PR_SUBMITTER}\\b" <<< "$ADDED"
+ grep "#${PR_NUMBER}\\b.*${PR_SUBMITTER}\\b" <<< "$ADDED"
diff --git a/.gitmodules b/.gitmodules
index f51cea4f..2c096761 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -269,3 +269,6 @@
[submodule "assets/syntaxes/02_Extra/GDScript-sublime"]
path = assets/syntaxes/02_Extra/GDScript-sublime
url = https://github.com/beefsack/GDScript-sublime
+[submodule "assets/syntaxes/02_Extra/sublime-odin"]
+ path = assets/syntaxes/02_Extra/sublime-odin
+ url = https://github.com/odin-lang/sublime-odin
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 08503047..b2f9dd63 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,15 +2,20 @@
## Features
+- Add paging to `--list-themes`, see PR #3239 (@einfachIrgendwer0815)
+
## Bugfixes
- Fix `BAT_THEME_DARK` and `BAT_THEME_LIGHT` being ignored, see issue #3171 and PR #3168 (@bash)
- Prevent `--list-themes` from outputting default theme info to stdout when it is piped, see #3189 (@einfachIrgendwer0815)
- Rename some submodules to fix Dependabot submodule updates, see issue #3198 and PR #3201 (@victor-gp)
+- Make highlight tests fail when new syntaxes don't have fixtures PR #3255 (@dan-hipschman)
+- Fix crash for multibyte characters in file path, see issue #3230 and PR #3245 (@HSM95)
## Other
- Work around build failures when building `bat` from vendored sources #3179 (@dtolnay)
+- CICD: Stop building for x86_64-pc-windows-gnu which fails #3261 (Enselic)
## Syntaxes
@@ -23,6 +28,7 @@
- Map files with `csproj`, `vbproj`, `props` and `targets` extensions to XML syntax, see #3213 (@keith-hall)
- Add debsources syntax to highlight `/etc/apt/sources.list` files, see #3215 (@keith-hall)
- Add syntax test file for GDScript highlighting, see #3236 (@chetanjangir0)
+- Add syntax test file for Odin highlighting, see #3241 (@chetanjangir0)
## Themes
diff --git a/Cargo.lock b/Cargo.lock
index a87351cd..5bb62706 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -77,9 +77,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.95"
+version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
+checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
[[package]]
name = "assert_cmd"
@@ -153,6 +153,7 @@ dependencies = [
"terminal-colorsaurus",
"thiserror 2.0.11",
"toml",
+ "unicode-segmentation",
"unicode-width 0.1.14",
"wait-timeout",
"walkdir",
@@ -483,13 +484,13 @@ dependencies = [
[[package]]
name = "etcetera"
-version = "0.8.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
+checksum = "26c7b13d0780cb82722fd59f6f57f925e143427e4a75313a6c77243bf5326ae6"
dependencies = [
"cfg-if",
"home",
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -607,7 +608,7 @@ dependencies = [
"cfg-if",
"libc",
"wasi 0.13.3+wasi-0.2.2",
- "windows-targets 0.52.6",
+ "windows-targets",
]
[[package]]
@@ -844,9 +845,9 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.7.0"
+version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
+checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
dependencies = [
"equivalent",
"hashbrown 0.15.2",
@@ -1078,7 +1079,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
- "windows-targets 0.52.6",
+ "windows-targets",
]
[[package]]
@@ -1678,6 +1679,12 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+[[package]]
+name = "unicode-segmentation"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+
[[package]]
name = "unicode-width"
version = "0.1.14"
@@ -1812,7 +1819,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132"
dependencies = [
"windows-core 0.56.0",
- "windows-targets 0.52.6",
+ "windows-targets",
]
[[package]]
@@ -1822,7 +1829,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
dependencies = [
"windows-core 0.57.0",
- "windows-targets 0.52.6",
+ "windows-targets",
]
[[package]]
@@ -1834,7 +1841,7 @@ dependencies = [
"windows-implement 0.56.0",
"windows-interface 0.56.0",
"windows-result",
- "windows-targets 0.52.6",
+ "windows-targets",
]
[[package]]
@@ -1846,7 +1853,7 @@ dependencies = [
"windows-implement 0.57.0",
"windows-interface 0.57.0",
"windows-result",
- "windows-targets 0.52.6",
+ "windows-targets",
]
[[package]]
@@ -1899,16 +1906,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
-dependencies = [
- "windows-targets 0.48.5",
+ "windows-targets",
]
[[package]]
@@ -1917,7 +1915,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
- "windows-targets 0.52.6",
+ "windows-targets",
]
[[package]]
@@ -1926,22 +1924,7 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
-dependencies = [
- "windows_aarch64_gnullvm 0.48.5",
- "windows_aarch64_msvc 0.48.5",
- "windows_i686_gnu 0.48.5",
- "windows_i686_msvc 0.48.5",
- "windows_x86_64_gnu 0.48.5",
- "windows_x86_64_gnullvm 0.48.5",
- "windows_x86_64_msvc 0.48.5",
+ "windows-targets",
]
[[package]]
@@ -1950,46 +1933,28 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
- "windows_aarch64_gnullvm 0.52.6",
- "windows_aarch64_msvc 0.52.6",
- "windows_i686_gnu 0.52.6",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
"windows_i686_gnullvm",
- "windows_i686_msvc 0.52.6",
- "windows_x86_64_gnu 0.52.6",
- "windows_x86_64_gnullvm 0.52.6",
- "windows_x86_64_msvc 0.52.6",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
]
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
-
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
-
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
-[[package]]
-name = "windows_i686_gnu"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
-
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@@ -2002,48 +1967,24 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
-[[package]]
-name = "windows_i686_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
-
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
-
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
-
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
-
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
diff --git a/Cargo.toml b/Cargo.toml
index 68172e72..2ee1b872 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,6 +10,7 @@ version = "0.25.0"
exclude = ["assets/syntaxes/*", "assets/themes/*"]
build = "build/main.rs"
edition = '2021'
+# You are free to bump MSRV as soon as a reason for bumping emerges.
rust-version = "1.74"
[features]
@@ -60,7 +61,7 @@ semver = "1.0"
path_abs = { version = "0.5", default-features = false }
clircle = { version = "0.6.1", default-features = false }
bugreport = { version = "0.5.0", optional = true }
-etcetera = { version = "0.8.0", optional = true }
+etcetera = { version = "0.10.0", optional = true }
grep-cli = { version = "0.1.11", optional = true }
regex = { version = "1.10.6", optional = true }
walkdir = { version = "2.5", optional = true }
@@ -68,6 +69,7 @@ bytesize = { version = "1.3.0" }
encoding_rs = "0.8.35"
execute = { version = "0.2.13", optional = true }
terminal-colorsaurus = "0.4"
+unicode-segmentation = "1.12.0"
[dependencies.git2]
version = "0.20"
@@ -101,8 +103,8 @@ serde = { version = "1.0", features = ["derive"] }
nix = { version = "0.29", default-features = false, features = ["term"] }
[build-dependencies]
-anyhow = "1.0.86"
-indexmap = { version = "2.3.0", features = ["serde"] }
+anyhow = "1.0.97"
+indexmap = { version = "2.8.0", features = ["serde"] }
itertools = "0.14.0"
once_cell = "1.20"
regex = "1.10.6"
diff --git a/assets/syntaxes/02_Extra/Org_mode b/assets/syntaxes/02_Extra/Org_mode
index 4976d8f8..bb6e5d84 160000
--- a/assets/syntaxes/02_Extra/Org_mode
+++ b/assets/syntaxes/02_Extra/Org_mode
@@ -1 +1 @@
-Subproject commit 4976d8f84eeecd94df7da872bf404c125df04c73
+Subproject commit bb6e5d848151135ab8f87bdcb997437b2308718a
diff --git a/assets/syntaxes/02_Extra/Zig b/assets/syntaxes/02_Extra/Zig
index 1a4a3844..8a4a3fe4 160000
--- a/assets/syntaxes/02_Extra/Zig
+++ b/assets/syntaxes/02_Extra/Zig
@@ -1 +1 @@
-Subproject commit 1a4a38445fec495817625bafbeb01e79c44abcba
+Subproject commit 8a4a3fe4a051f85c4752b82f586d395cab843c06
diff --git a/assets/syntaxes/02_Extra/sublime-odin b/assets/syntaxes/02_Extra/sublime-odin
new file mode 160000
index 00000000..5d6a0ed4
--- /dev/null
+++ b/assets/syntaxes/02_Extra/sublime-odin
@@ -0,0 +1 @@
+Subproject commit 5d6a0ed41e41ec3709ec74f40686dc3761d6596e
diff --git a/examples/buffer.rs b/examples/buffer.rs
index 839689d4..eefdb249 100644
--- a/examples/buffer.rs
+++ b/examples/buffer.rs
@@ -1,4 +1,6 @@
-use bat::{assets::HighlightingAssets, config::Config, controller::Controller, Input};
+use bat::{
+ assets::HighlightingAssets, config::Config, controller::Controller, output::OutputHandle, Input,
+};
fn main() {
let mut buffer = String::new();
@@ -10,7 +12,10 @@ fn main() {
let controller = Controller::new(&config, &assets);
let input = Input::from_file(file!());
controller
- .run(vec![input.into()], Some(&mut buffer))
+ .run(
+ vec![input.into()],
+ Some(OutputHandle::FmtWrite(&mut buffer)),
+ )
.unwrap();
println!("{buffer}");
diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs
index d339ba94..946ce5b1 100644
--- a/src/bin/bat/app.rs
+++ b/src/bin/bat/app.rs
@@ -124,7 +124,10 @@ impl App {
// If we have -pp as an option when in auto mode, the pager should be disabled.
if extra_plain || self.matches.get_flag("no-paging") {
PagingMode::Never
- } else if inputs.iter().any(Input::is_stdin) {
+ } else if inputs.iter().any(Input::is_stdin)
+ // ignore stdin when --list-themes is used because in that case no input will be read anyways
+ && !self.matches.get_flag("list-themes")
+ {
// If we are reading from stdin, only enable paging if we write to an
// interactive terminal and if we do not *read* from an interactive
// terminal.
diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs
index 70443b2f..4496032b 100644
--- a/src/bin/bat/main.rs
+++ b/src/bin/bat/main.rs
@@ -16,6 +16,7 @@ use std::io::{BufReader, Write};
use std::path::Path;
use std::process;
+use bat::output::{OutputHandle, OutputType};
use bat::theme::DetectColorScheme;
use nu_ansi_term::Color::Green;
use nu_ansi_term::Style;
@@ -205,8 +206,9 @@ pub fn list_themes(
config.language = Some("Rust");
config.style_components = StyleComponents(style);
- let stdout = io::stdout();
- let mut stdout = stdout.lock();
+ let mut output_type =
+ OutputType::from_mode(config.paging_mode, config.wrapping_mode, config.pager)?;
+ let mut writer = output_type.handle()?;
let default_theme_name = default_theme(color_scheme(detect_color_scheme).unwrap_or_default());
for theme in assets.themes() {
@@ -221,26 +223,29 @@ pub fn list_themes(
};
if config.colored_output {
writeln!(
- stdout,
+ writer,
"Theme: {}{}\n",
Style::new().bold().paint(theme.to_string()),
default_theme_info
)?;
config.theme = theme.to_string();
Controller::new(&config, &assets)
- .run(vec![theme_preview_file()], None)
+ .run(
+ vec![theme_preview_file()],
+ Some(OutputHandle::IoWrite(&mut writer)),
+ )
.ok();
- writeln!(stdout)?;
+ writeln!(writer)?;
} else if config.loop_through {
- writeln!(stdout, "{theme}")?;
+ writeln!(writer, "{theme}")?;
} else {
- writeln!(stdout, "{theme}{default_theme_info}")?;
+ writeln!(writer, "{theme}{default_theme_info}")?;
}
}
if config.colored_output {
writeln!(
- stdout,
+ writer,
"Further themes can be installed to '{}', \
and are added to the cache with `bat cache --build`. \
For more information, see:\n\n \
diff --git a/src/controller.rs b/src/controller.rs
index 422b381e..c8961b91 100644
--- a/src/controller.rs
+++ b/src/controller.rs
@@ -11,10 +11,10 @@ use crate::lessopen::LessOpenPreprocessor;
#[cfg(feature = "git")]
use crate::line_range::LineRange;
use crate::line_range::{LineRanges, RangeCheckResult};
-use crate::output::OutputType;
+use crate::output::{OutputHandle, OutputType};
#[cfg(feature = "paging")]
use crate::paging::PagingMode;
-use crate::printer::{InteractivePrinter, OutputHandle, Printer, SimplePrinter};
+use crate::printer::{InteractivePrinter, Printer, SimplePrinter};
use clircle::{Clircle, Identifier};
@@ -35,18 +35,14 @@ impl Controller<'_> {
}
}
- pub fn run(
- &self,
- inputs: Vec,
- output_buffer: Option<&mut dyn std::fmt::Write>,
- ) -> Result {
- self.run_with_error_handler(inputs, output_buffer, default_error_handler)
+ pub fn run(&self, inputs: Vec, output_handle: Option>) -> Result {
+ self.run_with_error_handler(inputs, output_handle, default_error_handler)
}
pub fn run_with_error_handler(
&self,
inputs: Vec,
- output_buffer: Option<&mut dyn std::fmt::Write>,
+ output_handle: Option>,
mut handle_error: impl FnMut(&Error, &mut dyn Write),
) -> Result {
let mut output_type;
@@ -88,8 +84,9 @@ impl Controller<'_> {
clircle::Identifier::stdout()
};
- let mut writer = match output_buffer {
- Some(buf) => OutputHandle::FmtWrite(buf),
+ let mut writer = match output_handle {
+ Some(OutputHandle::FmtWrite(w)) => OutputHandle::FmtWrite(w),
+ Some(OutputHandle::IoWrite(w)) => OutputHandle::IoWrite(w),
None => OutputHandle::IoWrite(output_type.handle()?),
};
let mut no_errors: bool = true;
diff --git a/src/lib.rs b/src/lib.rs
index 502427a7..4c60f10e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -38,7 +38,7 @@ mod less;
mod lessopen;
pub mod line_range;
pub(crate) mod nonprintable_notation;
-mod output;
+pub mod output;
#[cfg(feature = "paging")]
mod pager;
#[cfg(feature = "paging")]
diff --git a/src/output.rs b/src/output.rs
index dc75d6e7..bb9a45d5 100644
--- a/src/output.rs
+++ b/src/output.rs
@@ -1,3 +1,4 @@
+use std::fmt;
use std::io::{self, Write};
#[cfg(feature = "paging")]
use std::process::Child;
@@ -162,3 +163,17 @@ impl Drop for OutputType {
}
}
}
+
+pub enum OutputHandle<'a> {
+ IoWrite(&'a mut dyn io::Write),
+ FmtWrite(&'a mut dyn fmt::Write),
+}
+
+impl OutputHandle<'_> {
+ pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> {
+ match self {
+ Self::IoWrite(handle) => handle.write_fmt(args).map_err(Into::into),
+ Self::FmtWrite(handle) => handle.write_fmt(args).map_err(Into::into),
+ }
+ }
+}
diff --git a/src/pretty_printer.rs b/src/pretty_printer.rs
index a70ac021..4979bab5 100644
--- a/src/pretty_printer.rs
+++ b/src/pretty_printer.rs
@@ -10,6 +10,7 @@ use crate::{
error::Result,
input,
line_range::{HighlightedLineRanges, LineRange, LineRanges},
+ output::OutputHandle,
style::StyleComponent,
StripAnsiMode, SyntaxMapping, WrappingMode,
};
@@ -325,7 +326,10 @@ impl<'a> PrettyPrinter<'a> {
// If writer is provided, pass it to the controller, otherwise pass None
if let Some(mut w) = writer {
- controller.run(inputs.into_iter().map(|i| i.into()).collect(), Some(&mut w))
+ controller.run(
+ inputs.into_iter().map(|i| i.into()).collect(),
+ Some(OutputHandle::FmtWrite(&mut w)),
+ )
} else {
controller.run(inputs.into_iter().map(|i| i.into()).collect(), None)
}
diff --git a/src/printer.rs b/src/printer.rs
index 2c364bd7..dd2323cf 100644
--- a/src/printer.rs
+++ b/src/printer.rs
@@ -1,5 +1,3 @@
-use std::fmt;
-use std::io;
use std::vec::Vec;
use nu_ansi_term::Color::{Fixed, Green, Red, Yellow};
@@ -17,6 +15,7 @@ use content_inspector::ContentType;
use encoding_rs::{UTF_16BE, UTF_16LE};
+use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthChar;
use crate::assets::{HighlightingAssets, SyntaxReferenceInSet};
@@ -29,6 +28,7 @@ use crate::diff::LineChanges;
use crate::error::*;
use crate::input::OpenedInput;
use crate::line_range::RangeCheckResult;
+use crate::output::OutputHandle;
use crate::preprocessor::strip_ansi;
use crate::preprocessor::{expand_tabs, replace_nonprintable};
use crate::style::StyleComponent;
@@ -68,20 +68,6 @@ const EMPTY_SYNTECT_STYLE: syntect::highlighting::Style = syntect::highlighting:
font_style: FontStyle::empty(),
};
-pub enum OutputHandle<'a> {
- IoWrite(&'a mut dyn io::Write),
- FmtWrite(&'a mut dyn fmt::Write),
-}
-
-impl OutputHandle<'_> {
- fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> {
- match self {
- Self::IoWrite(handle) => handle.write_fmt(args).map_err(Into::into),
- Self::FmtWrite(handle) => handle.write_fmt(args).map_err(Into::into),
- }
- }
-}
-
pub(crate) trait Printer {
fn print_header(
&mut self,
@@ -403,14 +389,18 @@ impl<'a> InteractivePrinter<'a> {
handle: &mut OutputHandle,
content: &str,
) -> Result<()> {
- let mut content = content;
let content_width = self.config.term_width - self.get_header_component_indent_length();
- while content.len() > content_width {
- let (content_line, remaining) = content.split_at(content_width);
- self.print_header_component_with_indent(handle, content_line)?;
- content = remaining;
+ if content.chars().count() <= content_width {
+ return self.print_header_component_with_indent(handle, content);
}
- self.print_header_component_with_indent(handle, content)
+
+ let mut content_graphemes: Vec<&str> = content.graphemes(true).collect();
+ while content_graphemes.len() > content_width {
+ let (content_line, remaining) = content_graphemes.split_at(content_width);
+ self.print_header_component_with_indent(handle, content_line.join("").as_str())?;
+ content_graphemes = remaining.iter().cloned().collect();
+ }
+ self.print_header_component_with_indent(handle, content_graphemes.join("").as_str())
}
fn highlight_regions_for_line<'b>(
diff --git a/tests/examples/test.A—B가 b/tests/examples/test.A—B가
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs
index 3aafb17c..97dbd550 100644
--- a/tests/integration_tests.rs
+++ b/tests/integration_tests.rs
@@ -1600,6 +1600,17 @@ oken
.stderr("");
}
+#[test]
+fn header_narrow_terminal_with_multibyte_chars() {
+ bat()
+ .arg("--terminal-width=30")
+ .arg("--decorations=always")
+ .arg("test.A—B가")
+ .assert()
+ .success()
+ .stderr("");
+}
+
#[test]
#[cfg(feature = "git")] // Expected output assumes git is enabled
fn header_default() {
diff --git a/tests/syntax-tests/compare_highlighted_versions.py b/tests/syntax-tests/compare_highlighted_versions.py
index 9cf67e46..ad142e9f 100755
--- a/tests/syntax-tests/compare_highlighted_versions.py
+++ b/tests/syntax-tests/compare_highlighted_versions.py
@@ -12,13 +12,15 @@ def compare_highlighted_versions(root_old, root_new):
print(" -", root_old)
print(" -", root_new)
has_changes = False
+ # Used to check for newly added files that don't have a test
+ unknown_files = {strip_root(p) for p in glob.glob(path.join(root_new, "*", "*"))}
+
for path_old in glob.glob(path.join(root_old, "*", "*")):
- filename = path.basename(path_old)
- dirname = path.basename(path.dirname(path_old))
+ rel_path = strip_root(path_old)
+ unknown_files.discard(rel_path)
+ path_new = path.join(root_new, rel_path)
- path_new = path.join(root_new, dirname, filename)
-
- print("\n========== {}/{}".format(dirname, filename))
+ print("\n========== {}".format(rel_path))
with open(path_old) as file_old:
lines_old = file_old.readlines()
@@ -39,11 +41,21 @@ def compare_highlighted_versions(root_old, root_new):
has_changes = True
else:
print("No changes")
- print()
+ for f in unknown_files:
+ print("\n========== {}: No fixture for this language, run update.sh".format(f))
+ has_changes = True
+
+ print()
return has_changes
+def strip_root(p: str) -> str:
+ filename = path.basename(p)
+ dirname = path.basename(path.dirname(p))
+ return path.join(dirname, filename)
+
+
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="This script compares two directories that were created "
diff --git a/tests/syntax-tests/highlighted/Odin/test.odin b/tests/syntax-tests/highlighted/Odin/test.odin
new file mode 100644
index 00000000..ab13ed8c
--- /dev/null
+++ b/tests/syntax-tests/highlighted/Odin/test.odin
@@ -0,0 +1,27 @@
+[38;2;249;38;114mpackage[0m[38;2;248;248;242m main[0m
+
+[38;2;249;38;114mimport[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mcore:fmt[0m[38;2;230;219;116m"[0m
+[38;2;249;38;114mimport[0m[38;2;248;248;242m [0m[38;2;230;219;116m"[0m[38;2;230;219;116mcore:math[0m[38;2;230;219;116m"[0m
+
+[38;2;166;226;46mVector[0m[38;2;248;248;242m :: [0m[3;38;2;102;217;239mstruct[0m[38;2;248;248;242m {[0m
+[38;2;248;248;242m components: [0m[38;2;248;248;242m[[0m[38;2;248;248;242m][0m[3;38;2;102;217;239mf64[0m[38;2;248;248;242m,[0m
+[38;2;248;248;242m}[0m
+
+[38;2;166;226;46meuclidean_distance[0m[38;2;248;248;242m :: [0m[3;38;2;102;217;239mproc[0m[38;2;248;248;242m(v1: Vector, v2: Vector) -> [0m[3;38;2;102;217;239mf64[0m[38;2;248;248;242m {[0m
+[38;2;248;248;242m [0m[38;2;249;38;114mif[0m[38;2;248;248;242m [0m[38;2;102;217;239mlen[0m[38;2;248;248;242m(v1.components) != [0m[38;2;102;217;239mlen[0m[38;2;248;248;242m(v2.components) {[0m
+[38;2;248;248;242m [0m[38;2;102;217;239mpanic[0m[38;2;248;248;242m([0m[38;2;230;219;116m"[0m[38;2;230;219;116mVectors must be same dimension[0m[38;2;230;219;116m"[0m[38;2;248;248;242m)[0m
+[38;2;248;248;242m }[0m
+[38;2;248;248;242m sum: [0m[3;38;2;102;217;239mf64[0m[38;2;248;248;242m = [0m[38;2;190;132;255m0.0[0m[38;2;248;248;242m;[0m
+[38;2;248;248;242m [0m[38;2;249;38;114mfor[0m[38;2;248;248;242m i, comp [0m[38;2;249;38;114min[0m[38;2;248;248;242m v1.components {[0m
+[38;2;248;248;242m diff := comp - v2.components[i];[0m
+[38;2;248;248;242m sum += diff * diff;[0m
+[38;2;248;248;242m }[0m
+[38;2;248;248;242m [0m[38;2;249;38;114mreturn[0m[38;2;248;248;242m math.[0m[38;2;102;217;239msqrt[0m[38;2;248;248;242m(sum);[0m
+[38;2;248;248;242m}[0m
+
+[38;2;166;226;46mmain[0m[38;2;248;248;242m :: [0m[3;38;2;102;217;239mproc[0m[38;2;248;248;242m() {[0m
+[38;2;248;248;242m v1: Vector = Vector{components = [0m[38;2;248;248;242m[[0m[38;2;248;248;242m][0m[3;38;2;102;217;239mf64[0m[38;2;248;248;242m{[0m[38;2;190;132;255m1.0[0m[38;2;248;248;242m, [0m[38;2;190;132;255m2.0[0m[38;2;248;248;242m, [0m[38;2;190;132;255m3.0[0m[38;2;248;248;242m}};[0m
+[38;2;248;248;242m v2: Vector = Vector{components = [0m[38;2;248;248;242m[[0m[38;2;248;248;242m][0m[3;38;2;102;217;239mf64[0m[38;2;248;248;242m{[0m[38;2;190;132;255m4.0[0m[38;2;248;248;242m, [0m[38;2;190;132;255m6.0[0m[38;2;248;248;242m, [0m[38;2;190;132;255m8.0[0m[38;2;248;248;242m}};[0m
+[38;2;248;248;242m dist: [0m[3;38;2;102;217;239mf64[0m[38;2;248;248;242m = [0m[38;2;102;217;239meuclidean_distance[0m[38;2;248;248;242m(v1, v2);[0m
+[38;2;248;248;242m fmt.[0m[38;2;102;217;239mprintln[0m[38;2;248;248;242m([0m[38;2;230;219;116m"[0m[38;2;230;219;116mDistance:[0m[38;2;230;219;116m"[0m[38;2;248;248;242m, dist);[0m
+[38;2;248;248;242m}[0m
diff --git a/tests/syntax-tests/source/Odin/test.odin b/tests/syntax-tests/source/Odin/test.odin
new file mode 100644
index 00000000..dafcfcfd
--- /dev/null
+++ b/tests/syntax-tests/source/Odin/test.odin
@@ -0,0 +1,27 @@
+package main
+
+import "core:fmt"
+import "core:math"
+
+Vector :: struct {
+ components: []f64,
+}
+
+euclidean_distance :: proc(v1: Vector, v2: Vector) -> f64 {
+ if len(v1.components) != len(v2.components) {
+ panic("Vectors must be same dimension")
+ }
+ sum: f64 = 0.0;
+ for i, comp in v1.components {
+ diff := comp - v2.components[i];
+ sum += diff * diff;
+ }
+ return math.sqrt(sum);
+}
+
+main :: proc() {
+ v1: Vector = Vector{components = []f64{1.0, 2.0, 3.0}};
+ v2: Vector = Vector{components = []f64{4.0, 6.0, 8.0}};
+ dist: f64 = euclidean_distance(v1, v2);
+ fmt.println("Distance:", dist);
+}