diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 6d81d9b..cf85d9d 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -2,7 +2,6 @@ name: CICD env: CICD_INTERMEDIATES_DIR: "_cicd-intermediates" - MSRV_FEATURES: "--all-features" on: workflow_dispatch: @@ -58,9 +57,13 @@ jobs: toolchain: ${{ needs.crate_metadata.outputs.msrv }} components: clippy - name: Run clippy (on minimum supported rust version to prevent warnings we can't fix) - run: cargo clippy --all-targets ${{ env.MSRV_FEATURES }} + run: | + cargo clippy --all-targets --features=gnu_legacy + cargo clippy --all-targets --features=crossterm,ansi_term,nu-ansi-term - name: Run tests - run: cargo test ${{ env.MSRV_FEATURES }} + run: | + cargo test --features=gnu_legacy + cargo test --features=crossterm,ansi_term,nu-ansi-term documentation: name: Documentation @@ -75,10 +78,12 @@ jobs: - name: Check documentation env: RUSTDOCFLAGS: -D warnings - run: cargo doc --no-deps --document-private-items --all-features + run: | + cargo doc --no-deps --document-private-items --features=gnu_legacy + cargo doc --no-deps --document-private-items --features=crossterm,ansi_term,nu-ansi-term build: - name: ${{ matrix.job.target }} (${{ matrix.job.os }}) + name: ${{ matrix.job.target }} (${{ matrix.job.os }} with ${{ matrix.terminal }}) runs-on: ${{ matrix.job.os }} needs: crate_metadata strategy: @@ -96,6 +101,11 @@ jobs: - { target: x86_64-pc-windows-msvc , os: windows-2019 } - { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } + terminal: + - ansi_term + - crossterm + - nu-ansi-term + - gnu_legacy env: BUILD_CMD: cargo steps: @@ -138,7 +148,7 @@ jobs: - name: Build shell: bash - run: $BUILD_CMD build --release --target=${{ matrix.job.target }} --features=nu-ansi-term + run: $BUILD_CMD build --release --target=${{ matrix.job.target }} --features=${{ matrix.terminal }} - name: Set binary name & path id: bin @@ -158,36 +168,20 @@ jobs: echo "BIN_PATH=${BIN_PATH}" >> $GITHUB_OUTPUT echo "BIN_NAME=${BIN_NAME}" >> $GITHUB_OUTPUT - - name: Set testing options - id: test-options + - name: Run tests all targets shell: bash - run: | - # test only library unit tests and binary for arm-type targets - unset CARGO_TEST_OPTIONS - unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--all-features --lib --bin ${{ needs.crate_metadata.outputs.name }}" ;; esac; - echo "CARGO_TEST_OPTIONS=${CARGO_TEST_OPTIONS}" >> $GITHUB_OUTPUT - - - name: Run tests - shell: bash - run: $BUILD_CMD test --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}} + run: $BUILD_CMD test --target=${{ matrix.job.target }} --features=${{ matrix.terminal }} - name: Run lscolors shell: bash - run: $BUILD_CMD run --target=${{ matrix.job.target }} --features nu-ansi-term -- Cargo.toml Cargo.lock LICENSE-APACHE LICENSE-MIT README.md src/lib.rs - - - name: "Feature check: ansi_term" - shell: bash - run: $BUILD_CMD check --target=${{ matrix.job.target }} --verbose --lib --features ansi_term - - - name: "Feature check: nu-ansi-term" - shell: bash - run: $BUILD_CMD check --target=${{ matrix.job.target }} --verbose --lib --features nu-ansi-term + run: $BUILD_CMD run --target=${{ matrix.job.target }} --features ${{ matrix.terminal }} -- Cargo.toml Cargo.lock LICENSE-APACHE LICENSE-MIT README.md src/lib.rs - - name: "Feature check: crossterm" + - name: "Feature check: ${{ matrix.terminal }}" shell: bash - run: $BUILD_CMD check --target=${{ matrix.job.target }} --verbose --lib --features crossterm + run: $BUILD_CMD check --target=${{ matrix.job.target }} --verbose --lib --features ${{ matrix.terminal }} - name: "Feature check: ansi_term,nu-ansi-term,crossterm" + if: matrix.terminal == 'nu-ansi-term' shell: bash run: $BUILD_CMD check --target=${{ matrix.job.target }} --verbose --lib --features ansi_term,nu-ansi-term,crossterm diff --git a/Cargo.toml b/Cargo.toml index df1d78f..daeb2da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,8 @@ default = [] [dependencies] ansi_term = { version = "0.12", optional = true } -nu-ansi-term = { version = "0.47", optional = true } +nu-ansi-term= { package = "nu-ansi-term", git = "https://github.com/nushell/nu-ansi-term.git", optional = true } +gnu_legacy = { package = "nu-ansi-term", git = "https://github.com/nushell/nu-ansi-term.git", features = ["gnu_legacy"], optional = true } crossterm = { version = "0.26", optional = true } [dev-dependencies] @@ -32,9 +33,13 @@ tempfile = "^3" [[bin]] name = "lscolors" path = "src/bin.rs" -required-features = ["nu-ansi-term"] [profile.release] lto = true strip = true codegen-units = 1 + +[[test]] +name = "integration_tests" +path = "tests/integration_tests.rs" +required-features = ["nu-ansi-term"] \ No newline at end of file diff --git a/README.md b/README.md index ee4c563..737d933 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ let ansi_style = style.map(Style::to_ansi_term_style) .unwrap_or_default(); println!("{}", ansi_style.paint(path)); -// If you want to use `nu-ansi-term` (fork of ansi_term): +// If you want to use `nu-ansi-term or gnu_legacy` (fork of ansi_term): let ansi_style = style.map(Style::to_nu_ansi_term_style) .unwrap_or_default(); println!("{}", ansi_style.paint(path)); @@ -33,6 +33,7 @@ println!("{}", ansi_style.paint(path)); This crate also comes with a small command-line program `lscolors` that can be used to colorize the output of other commands: + ```bash > find . -maxdepth 2 | lscolors @@ -42,16 +43,33 @@ can be used to colorize the output of other commands: You can install it by running `cargo install lscolors` or by downloading one of the prebuilt binaries from the [release page](https://github.com/sharkdp/lscolors/releases). If you want to build the application from source, you can run + ```rs cargo build --release --features=nu-ansi-term --locked ``` +## Features + +```rust +// Cargo.toml + +[dependencies] +// use ansi-term coloring +lscolors = { version = "v0.14.0", features = ["ansi_term"] } +// use across-term coloring +lscolors = { version = "v0.14.0", features = ["crossterm"] } +// use nu-ansi-term coloring +lscolors = { version = "v0.14.0", features = ["nu-ansi-term"] } +// use nu-ansi-term coloring in gnu legacy mode, double digit styles and reset codes before coloring +lscolors = { version = "v0.14.0", features = ["gnu_legacy"] } +``` + ## License Licensed under either of - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) +* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. diff --git a/src/bin.rs b/src/bin.rs index 20f1933..0bd08b9 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -5,10 +5,33 @@ use std::path::Path; use lscolors::{LsColors, Style}; +#[cfg(all( + not(feature = "nu-ansi-term"), + not(feature = "gnu_legacy"), + not(feature = "ansi_term"), + not(feature = "crossterm") +))] +compile_error!("one feature must be enabled: ansi_term, nu-ansi-term, crossterm, gnu_legacy"); + fn print_path(handle: &mut dyn Write, ls_colors: &LsColors, path: &str) -> io::Result<()> { for (component, style) in ls_colors.style_for_path_components(Path::new(path)) { - let ansi_style = style.map(Style::to_nu_ansi_term_style).unwrap_or_default(); - write!(handle, "{}", ansi_style.paint(component.to_string_lossy()))?; + #[cfg(any(feature = "nu-ansi-term", feature = "gnu_legacy"))] + { + let ansi_style = style.map(Style::to_nu_ansi_term_style).unwrap_or_default(); + write!(handle, "{}", ansi_style.paint(component.to_string_lossy()))?; + } + + #[cfg(feature = "ansi_term")] + { + let ansi_style = style.map(Style::to_ansi_term_style).unwrap_or_default(); + write!(handle, "{}", ansi_style.paint(component.to_string_lossy()))?; + } + + #[cfg(feature = "crossterm")] + { + let ansi_style = style.map(Style::to_crossterm_style).unwrap_or_default(); + write!(handle, "{}", ansi_style.apply(component.to_string_lossy()))?; + } } writeln!(handle)?; diff --git a/src/style.rs b/src/style.rs index aaa8417..8e1b6b7 100644 --- a/src/style.rs +++ b/src/style.rs @@ -2,6 +2,12 @@ //! //! For more information, see //! [ANSI escape code (Wikipedia)](https://en.wikipedia.org/wiki/ANSI_escape_code). +#[cfg(all(feature = "nu-ansi-term", feature = "gnu_legacy"))] +compile_error!("`nu-ansi-term` and `gnu_legacy` may not be used at the same time"); +#[cfg(feature = "gnu_legacy")] +use gnu_legacy as nu_ansi_term; +#[cfg(feature = "nu-ansi-term")] +use nu_ansi_term; use std::collections::VecDeque; /// A `Color` can be one of the pre-defined ANSI colors (`Red`, `Green`, ..), @@ -57,8 +63,8 @@ impl Color { } } - /// Convert to a `nu_ansi_term::Color` (if the `nu_ansi_term` feature is enabled). - #[cfg(feature = "nu-ansi-term")] + /// Convert to a `nu_ansi_term::Color` (if the `nu_ansi_term` or `gnu_legacy` feature is enabled). + #[cfg(any(feature = "nu-ansi-term", feature = "gnu_legacy"))] pub fn to_nu_ansi_term_color(&self) -> nu_ansi_term::Color { match self { Color::RGB(r, g, b) => nu_ansi_term::Color::Rgb(*r, *g, *b), @@ -398,8 +404,51 @@ impl Style { } } - /// Convert to a `nu_ansi_term::Style` (if the `nu_ansi_term` feature is enabled). - #[cfg(feature = "nu-ansi-term")] + /// Convert to a `nu_ansi_term::Style` (if the `nu-ansi-term` or `gnu_legacy` feature is enabled). + /// + /// ## Example for nu-ansi-term feature + /// ``` + /// # #[cfg(feature = "nu-ansi-term")] + /// # { + /// + /// use lscolors::{Color, FontStyle, Style}; + /// + /// let style = Style { + /// font_style: FontStyle { + /// bold: true, + /// ..Default::default() + /// }, + /// foreground: Some(Color::Blue), + /// ..Default::default() + /// }; + /// let nu_ansi = style.to_nu_ansi_term_style(); + /// assert_eq!("\x1b[1;34mwow\x1b[0m", nu_ansi.paint("wow").to_string()); + /// # } + /// ``` + /// ## Example for gnu_legacy feature + /// ``` + /// # #[cfg(feature = "gnu_legacy")] + /// # { + /// + /// use lscolors::{Color, FontStyle, Style}; + /// + /// let style = Style { + /// font_style: FontStyle { + /// bold: true, + /// ..Default::default() + /// }, + /// foreground: Some(Color::Blue), + /// ..Default::default() + /// }; + /// let nu_ansi = style.to_nu_ansi_term_style(); + /// assert_eq!( + /// "\x1b[0m\x1b[01;34mwow\x1b[0m", + /// nu_ansi.paint("wow").to_string() + /// ); + /// # } + /// ``` + /// + #[cfg(any(feature = "nu-ansi-term", feature = "gnu_legacy"))] pub fn to_nu_ansi_term_style(&self) -> nu_ansi_term::Style { nu_ansi_term::Style { foreground: self.foreground.as_ref().map(Color::to_nu_ansi_term_color), @@ -412,6 +461,10 @@ impl Style { is_reverse: self.font_style.reverse, is_hidden: self.font_style.hidden, is_strikethrough: self.font_style.strikethrough, + #[cfg(feature = "gnu_legacy")] + with_reset: true, + #[cfg(not(feature = "gnu_legacy"))] + with_reset: false, } } @@ -608,4 +661,80 @@ mod tests { FontStyle::bold(), ); } + + #[cfg(feature = "nu-ansi-term")] + #[test] + fn coloring_nu_ansi_term() { + let style = Style { + font_style: FontStyle { + bold: true, + ..Default::default() + }, + foreground: Some(Color::Blue), + ..Default::default() + }; + let nu_ansi = style.to_nu_ansi_term_style(); + assert_eq!("\x1b[1;34mwow\x1b[0m", nu_ansi.paint("wow").to_string()); + } + + #[cfg(feature = "gnu_legacy")] + #[test] + fn coloring_gnu_legacy() { + let style = Style { + font_style: FontStyle { + bold: true, + ..Default::default() + }, + foreground: Some(Color::Blue), + ..Default::default() + }; + let nu_ansi = style.to_nu_ansi_term_style(); + assert_eq!( + "\x1b[0m\x1b[01;34mwow\x1b[0m", + nu_ansi.paint("wow").to_string() + ); + } + + #[cfg(feature = "gnu_legacy")] + #[test] + fn coloring_gnu_legacy_base() { + let style = Style { + ..Default::default() + }; + let nu_ansi = style.to_nu_ansi_term_style(); + assert_eq!("\x1b[0m\x1b[mwow\x1b[0m", nu_ansi.paint("wow").to_string()); + } + + #[cfg(feature = "ansi_term")] + #[test] + fn coloring_ansi_term() { + let style = Style { + font_style: FontStyle { + bold: true, + ..Default::default() + }, + foreground: Some(Color::Blue), + ..Default::default() + }; + let ansi = style.to_ansi_term_style(); + assert_eq!("\x1b[1;34mwow\x1b[0m", ansi.paint("wow").to_string()); + } + + #[cfg(feature = "crossterm")] + #[test] + fn coloring_crossterm() { + let style = Style { + font_style: FontStyle { + bold: true, + ..Default::default() + }, + foreground: Some(Color::Blue), + ..Default::default() + }; + let cross = style.to_crossterm_style(); + assert_eq!( + "\x1b[38;5;4m\x1b[1mwow\x1b[0m", + cross.apply("wow").to_string() + ); + } } diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs new file mode 100644 index 0000000..579b648 --- /dev/null +++ b/tests/integration_tests.rs @@ -0,0 +1,87 @@ +use lscolors::LsColors; +use lscolors::Style; + +use std::process::Command; + +fn get_ls_style(ls_colors_env: Option<&str>, path: &str) -> Option