diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml index 1384e31..5aba894 100644 --- a/.github/workflows/actionlint.yml +++ b/.github/workflows/actionlint.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -26,7 +26,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "actionlint" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c58e9c6..2e4fc2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -24,7 +24,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "install editorconfig-checker" @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -50,7 +50,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "lychee" diff --git a/.github/workflows/nix-develop.yml b/.github/workflows/nix-develop.yml index ec787df..da91ed8 100644 --- a/.github/workflows/nix-develop.yml +++ b/.github/workflows/nix-develop.yml @@ -28,7 +28,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 # Nix Flakes doesn't work on shallow clones - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -37,7 +37,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: 'nix develop --command "echo hello"' diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 88319a4..42fd494 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 # Nix Flakes doesn't work on shallow clones - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -28,7 +28,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "nix flake check" @@ -39,7 +39,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 # Nix Flakes doesn't work on shallow clones - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -48,7 +48,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "cargo clippy" @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -67,7 +67,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "cargo deny" @@ -77,7 +77,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -86,7 +86,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "cargo udeps" @@ -96,7 +96,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -105,7 +105,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "treefmt" @@ -115,7 +115,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -124,7 +124,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "build documentation" @@ -136,7 +136,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -145,7 +145,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "build dependencies" @@ -155,7 +155,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -164,7 +164,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "build dependencies msrv" @@ -175,7 +175,7 @@ jobs: needs: dependencies steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -184,7 +184,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "build examples" @@ -200,7 +200,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -209,7 +209,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "cargo test" @@ -221,7 +221,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -230,7 +230,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "cargo doc" @@ -240,7 +240,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} @@ -249,7 +249,7 @@ jobs: name: kenji # If you chose API tokens for write access OR if you have a private cache authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - - uses: DeterminateSystems/magic-nix-cache-action@v3 + - uses: DeterminateSystems/magic-nix-cache-action@v4 with: diagnostic-endpoint: "" - name: "typos" diff --git a/.github/workflows/update-flake-lock.yml b/.github/workflows/update-flake-lock.yml index 0291632..d355bc3 100644 --- a/.github/workflows/update-flake-lock.yml +++ b/.github/workflows/update-flake-lock.yml @@ -12,12 +12,12 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - name: Install Nix - uses: cachix/install-nix-action@v25 + uses: cachix/install-nix-action@v26 with: extra_nix_config: | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} - name: Update flake.lock - uses: DeterminateSystems/update-flake-lock@v20 + uses: DeterminateSystems/update-flake-lock@v21 with: token: ${{ secrets.GH_TOKEN_FOR_UPDATES }} branch: development diff --git a/Cargo.lock b/Cargo.lock index 4743e1b..4b60a8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,9 +109,9 @@ checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cassowary" @@ -411,15 +411,14 @@ checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" [[package]] name = "insta" -version = "1.34.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d64600be34b2fcfc267740a243fa7744441bb4947a619ac4e5bb6507f35fbfc" +checksum = "3eab73f58e59ca6526037208f0e98851159ec1633cf17b6cd2e1f2c3fd5d53cc" dependencies = [ "console", "lazy_static", "linked-hash-map", "similar", - "yaml-rust", ] [[package]] @@ -550,9 +549,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -1547,15 +1546,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "zerocopy" version = "0.7.32" diff --git a/Cargo.toml b/Cargo.toml index 0f29b46..9a7ba3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,25 +33,28 @@ categories = ["command-line-interface", "command-line-utilities"] # ] [features] +default = ["vt100"] unstable = ["dep:portable-pty"] [dependencies] -ratatui = "0.26.1" -vt100 = "0.15.2" +ratatui = { version = "0.26.1", default-features = false } +vt100 = { version = "0.15.2", optional = true } portable-pty = { version = "0.8.1", optional = true } [dev-dependencies] -bytes = "1.5.0" +bytes = "1.6.0" criterion = { version = "0.5.1", features = ["html_reports"] } # divan now needs unstable rust, activate manually for now # divan = "0.1.7" #TODO: go back to release version, once it is fixed # iai = "0.1.1" iai = { git = "https://github.com/sigaloid/iai", rev = "6c83e942" } -insta = "1.34.0" +insta = "1.38.0" once_cell = "1.19.0" # for examples +# enable the features used in tests +ratatui = { version = "0.26.1", default-features = true } crossterm = "0.27" portable-pty = "0.8.1" tokio = { version = "1", features = ["full"] } @@ -98,6 +101,10 @@ doc-scrape-examples = true name = "nested_shell_async" doc-scrape-examples = true +[[example]] +name = "simple_ls_controller" +required-features = ["unstable"] + [[example]] name = "smux" doc-scrape-examples = true diff --git a/README.md b/README.md index c77bb7b..71f5c84 100644 --- a/README.md +++ b/README.md @@ -15,21 +15,21 @@ A pseudoterminal widget for the [ratatui](https://github.com/tui-rs-revival/rat ## Installation -To use `tui-term`, simply add it as a dependency in your Cargo.toml file: +To use `tui-term`, simply add it as a dependency in your `Cargo.toml` file: -``` +```sh [dependencies] -tui-term = "0.1.6" +tui-term = "0.1.7" ``` or use `cargo add`: -``` +```sh cargo add tui-term ``` ## Examples Check out the examples directory, for more information, or run an example: -``` +```sh cargo run --example simple_ls_rw ``` @@ -40,24 +40,23 @@ The controller is an `experimental` feature helping with managing the lifecycle Currently the support is limited to oneshot commands. To activate the feature: -``` +```sh cargo add tui-term -F unstable ``` ## Chat Room -Join our matrix chat room, for possibly synchronous communication. +Join our [matrix chat room](https://matrix.to/#/#tui-term-main:matrix.org), for possibly synchronous communication. ## Architecture -For a top-level understanding of the architecture of `tui-term` and the design choices made, please refer to the [Architecture](docs/ARCHITECTURE.md) document. +For an overview of `tui-term`'s architecture and design principles, please refer to the [Architecture](docs/ARCHITECTURE.md) documentation. ## Contributing -We welcome contributions from the community! If you're interested in contributing to tui-term, please refer to the contribution guidelines for instructions on how to get started. - -[How to contribute.](./docs/CONTRIBUTING.md) +We welcome contributions from the community! +Check out the [Contributing Guidelines](./docs/CONTRIBUTING.md) on how to get started. -## Changes -[Changelog](./CHANGELOG.md) +## Release Notes +Stay updated with the latest changes by viewing the [Changelog](./CHANGELOG.md). ## License -MIT +`tui-term` is available under the MIT license. See [LICENCE](LICENSE) for more information. diff --git a/examples/README.md b/examples/README.md index fa66b8e..34b98fa 100644 --- a/examples/README.md +++ b/examples/README.md @@ -4,7 +4,7 @@ In this folder you find examples for using `tui-term`. To run the `simple_ls_rw` example: -``` +```sh cargo run --example simple_ls_rw ``` @@ -32,11 +32,10 @@ The RWLock ensures that multiple threads can read from the pseudoterminal simult Uses the tui-term's controller to handle the command lifecycle. This feature is gated behind the `unstable` flag. Run it with: -``` +```sh cargo run --example simple_ls_controller --features unstable ``` - ## `nested_shell` - Description: Demonstrates nested shell functionality. diff --git a/flake.lock b/flake.lock index 6c3d72c..f223015 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1707685877, - "narHash": "sha256-XoXRS+5whotelr1rHiZle5t5hDg9kpguS5yk8c8qzOc=", + "lastModified": 1711586303, + "narHash": "sha256-iZDHWTqQj6z6ccqTSEOPOxQ8KMFAemInUObN2R9vHSs=", "owner": "ipetkov", "repo": "crane", - "rev": "2c653e4478476a52c6aa3ac0495e4dea7449ea0e", + "rev": "a329cd00398379c62e76fc3b8d4ec2934260d636", "type": "github" }, "original": { @@ -25,11 +25,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1705309234, - "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -39,11 +39,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1707877513, - "narHash": "sha256-sp0w2apswd3wv0sAEF7StOGHkns3XUQaO5erhWFZWXk=", + "lastModified": 1711593151, + "narHash": "sha256-/9NCoPI7fqJIN8viONsY9X0fAeq8jc3GslFCO0ky6TQ=", "owner": "nixos", "repo": "nixpkgs", - "rev": "89653a03e0915e4a872788d10680e7eec92f8600", + "rev": "bb2b73df7bcfbd2dd55ff39b944d70547d53c267", "type": "github" }, "original": { @@ -71,11 +71,11 @@ ] }, "locked": { - "lastModified": 1707876656, - "narHash": "sha256-urnZg6e2JjziBosarDB1MnjPeVqcu3PeSqIpqQKYrdg=", + "lastModified": 1711592024, + "narHash": "sha256-oD4OJ3TRmVrbAuKZWxElRCyCagNCDuhfw2exBmNOy48=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "3ad32bb27c700b59306224e285b66577e3532dfc", + "rev": "aa858717377db2ed8ffd2d44147d907baee656e5", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index c9b3487..e7bf5ab 100644 --- a/flake.nix +++ b/flake.nix @@ -99,7 +99,7 @@ inherit cargoArtifacts src; partitions = 1; partitionType = "count"; - cargoExtraArgs = "--features unstable"; + cargoNextestExtraArgs = "--features unstable"; }; cargoDoc = craneLib.cargoDoc (commonArgs // {inherit cargoArtifacts;}); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index cc3a4ea..08b1110 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ # This file is updated by the `akenji@update-rust-toolchain` GitHub action [toolchain] -channel = "1.76.0" +channel = "1.77.0" components = [] targets = [] profile = "minimal" diff --git a/src/lib.rs b/src/lib.rs index ee91c44..33cfd42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ //! use vt100::Parser; //! //! let mut parser = vt100::Parser::new(24, 80, 0); -//! let pseudo_term = PseudoTerminal::new(&parser.screen()) +//! let pseudo_term = PseudoTerminal::new(parser.screen()) //! .block(Block::default().title("Terminal").borders(Borders::ALL)) //! .style( //! Style::default() @@ -49,10 +49,13 @@ //! sequences, but future versions may introduce support for alternative backends. mod state; +#[cfg(feature = "vt100")] +mod vt100_imp; pub mod widget; #[cfg(feature = "unstable")] pub mod controller; /// Reexport of the vt100 crate to ensure correct version compatibility +#[cfg(feature = "vt100")] pub use vt100; diff --git a/src/state.rs b/src/state.rs index 9ac7465..67def41 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,14 +1,10 @@ -use ratatui::{ - buffer::Buffer, - layout::Rect, - style::{Modifier, Style}, -}; +use ratatui::{buffer::Buffer, layout::Rect}; -use crate::widget::PseudoTerminal; +use crate::widget::{Cell, PseudoTerminal, Screen}; /// Draw the [`Screen`] to the [`Buffer`], /// area is the designated area that the consumer provides -pub fn handle(term: &PseudoTerminal, area: Rect, buf: &mut Buffer) { +pub fn handle(term: &PseudoTerminal, area: Rect, buf: &mut Buffer) { let cols = area.width; let rows = area.height; let col_start = area.x; @@ -30,29 +26,8 @@ pub fn handle(term: &PseudoTerminal, area: Rect, buf: &mut Buffer) { if let Some(screen_cell) = screen.cell(row, col) { if screen_cell.has_contents() { - let fg = screen_cell.fgcolor(); - let bg = screen_cell.bgcolor(); - let cell = buf.get_mut(buf_col, buf_row); - cell.set_symbol(&screen_cell.contents()); - let fg: Color = fg.into(); - let bg: Color = bg.into(); - let mut style = Style::reset(); - if screen_cell.bold() { - style = style.add_modifier(Modifier::BOLD); - } - if screen_cell.italic() { - style = style.add_modifier(Modifier::ITALIC); - } - if screen_cell.underline() { - style = style.add_modifier(Modifier::UNDERLINED); - } - if screen_cell.inverse() { - style = style.add_modifier(Modifier::REVERSED); - } - cell.set_style(style); - cell.set_fg(fg.into()); - cell.set_bg(bg.into()); + screen_cell.apply(cell); } } } @@ -76,94 +51,3 @@ pub fn handle(term: &PseudoTerminal, area: Rect, buf: &mut Buffer) { } } } - -/// Represents a foreground or background color for cells. -/// Intermediate translation layer between -/// [`vt100::Screen`] and [`ratatui::style::Color`] -#[allow(dead_code)] -enum Color { - Reset, - Black, - Red, - Green, - Yellow, - Blue, - Magenta, - Cyan, - Gray, - DarkGray, - LightRed, - LightGreen, - LightYellow, - LightBlue, - LightMagenta, - LightCyan, - White, - Rgb(u8, u8, u8), - Indexed(u8), -} - -impl From for Color { - #[inline] - fn from(value: vt100::Color) -> Self { - match value { - vt100::Color::Default => Self::Reset, - vt100::Color::Idx(i) => Self::Indexed(i), - vt100::Color::Rgb(r, g, b) => Self::Rgb(r, g, b), - } - } -} - -impl From for vt100::Color { - #[inline] - fn from(value: Color) -> Self { - match value { - Color::Reset => Self::Default, - Color::Black => Self::Idx(0), - Color::Red => Self::Idx(1), - Color::Green => Self::Idx(2), - Color::Yellow => Self::Idx(3), - Color::Blue => Self::Idx(4), - Color::Magenta => Self::Idx(5), - Color::Cyan => Self::Idx(6), - Color::Gray => Self::Idx(7), - Color::DarkGray => Self::Idx(8), - Color::LightRed => Self::Idx(9), - Color::LightGreen => Self::Idx(10), - Color::LightYellow => Self::Idx(11), - Color::LightBlue => Self::Idx(12), - Color::LightMagenta => Self::Idx(13), - Color::LightCyan => Self::Idx(14), - Color::White => Self::Idx(15), - Color::Rgb(r, g, b) => Self::Rgb(r, g, b), - Color::Indexed(i) => Self::Idx(i), - } - } -} - -impl From for ratatui::style::Color { - #[inline] - fn from(value: Color) -> Self { - match value { - Color::Reset => Self::Reset, - Color::Black => Self::Black, - Color::Red => Self::Red, - Color::Green => Self::Green, - Color::Yellow => Self::Yellow, - Color::Blue => Self::Blue, - Color::Magenta => Self::Magenta, - Color::Cyan => Self::Cyan, - Color::Gray => Self::Gray, - Color::DarkGray => Self::DarkGray, - Color::LightRed => Self::LightRed, - Color::LightGreen => Self::LightGreen, - Color::LightYellow => Self::LightYellow, - Color::LightBlue => Self::LightBlue, - Color::LightMagenta => Self::LightMagenta, - Color::LightCyan => Self::LightCyan, - Color::White => Self::White, - Color::Rgb(r, g, b) => Self::Rgb(r, g, b), - Color::Indexed(i) => Self::Indexed(i), - } - } -} diff --git a/src/vt100_imp.rs b/src/vt100_imp.rs new file mode 100644 index 0000000..c63c3c2 --- /dev/null +++ b/src/vt100_imp.rs @@ -0,0 +1,151 @@ +use ratatui::style::{Modifier, Style}; + +use crate::widget::{Cell, Screen}; + +impl Screen for vt100::Screen { + type C = vt100::Cell; + + #[inline] + fn cell(&self, row: u16, col: u16) -> Option<&Self::C> { + self.cell(row, col) + } + + #[inline] + fn hide_cursor(&self) -> bool { + self.hide_cursor() + } + + #[inline] + fn cursor_position(&self) -> (u16, u16) { + self.cursor_position() + } +} + +impl Cell for vt100::Cell { + #[inline] + fn has_contents(&self) -> bool { + self.has_contents() + } + + #[inline] + fn apply(&self, cell: &mut ratatui::buffer::Cell) { + fill_buf_cell(self, cell) + } +} + +#[inline] +fn fill_buf_cell(screen_cell: &vt100::Cell, buf_cell: &mut ratatui::buffer::Cell) { + let fg = screen_cell.fgcolor(); + let bg = screen_cell.bgcolor(); + + buf_cell.set_symbol(&screen_cell.contents()); + let fg: Color = fg.into(); + let bg: Color = bg.into(); + let mut style = Style::reset(); + if screen_cell.bold() { + style = style.add_modifier(Modifier::BOLD); + } + if screen_cell.italic() { + style = style.add_modifier(Modifier::ITALIC); + } + if screen_cell.underline() { + style = style.add_modifier(Modifier::UNDERLINED); + } + if screen_cell.inverse() { + style = style.add_modifier(Modifier::REVERSED); + } + buf_cell.set_style(style); + buf_cell.set_fg(fg.into()); + buf_cell.set_bg(bg.into()); +} + +/// Represents a foreground or background color for cells. +/// Intermediate translation layer between +/// [`vt100::Screen`] and [`ratatui::style::Color`] +#[allow(dead_code)] +enum Color { + Reset, + Black, + Red, + Green, + Yellow, + Blue, + Magenta, + Cyan, + Gray, + DarkGray, + LightRed, + LightGreen, + LightYellow, + LightBlue, + LightMagenta, + LightCyan, + White, + Rgb(u8, u8, u8), + Indexed(u8), +} + +impl From for Color { + #[inline] + fn from(value: vt100::Color) -> Self { + match value { + vt100::Color::Default => Self::Reset, + vt100::Color::Idx(i) => Self::Indexed(i), + vt100::Color::Rgb(r, g, b) => Self::Rgb(r, g, b), + } + } +} + +impl From for vt100::Color { + #[inline] + fn from(value: Color) -> Self { + match value { + Color::Reset => Self::Default, + Color::Black => Self::Idx(0), + Color::Red => Self::Idx(1), + Color::Green => Self::Idx(2), + Color::Yellow => Self::Idx(3), + Color::Blue => Self::Idx(4), + Color::Magenta => Self::Idx(5), + Color::Cyan => Self::Idx(6), + Color::Gray => Self::Idx(7), + Color::DarkGray => Self::Idx(8), + Color::LightRed => Self::Idx(9), + Color::LightGreen => Self::Idx(10), + Color::LightYellow => Self::Idx(11), + Color::LightBlue => Self::Idx(12), + Color::LightMagenta => Self::Idx(13), + Color::LightCyan => Self::Idx(14), + Color::White => Self::Idx(15), + Color::Rgb(r, g, b) => Self::Rgb(r, g, b), + Color::Indexed(i) => Self::Idx(i), + } + } +} + +impl From for ratatui::style::Color { + #[inline] + fn from(value: Color) -> Self { + match value { + Color::Reset => Self::Reset, + Color::Black => Self::Black, + Color::Red => Self::Red, + Color::Green => Self::Green, + Color::Yellow => Self::Yellow, + Color::Blue => Self::Blue, + Color::Magenta => Self::Magenta, + Color::Cyan => Self::Cyan, + Color::Gray => Self::Gray, + Color::DarkGray => Self::DarkGray, + Color::LightRed => Self::LightRed, + Color::LightGreen => Self::LightGreen, + Color::LightYellow => Self::LightYellow, + Color::LightBlue => Self::LightBlue, + Color::LightMagenta => Self::LightMagenta, + Color::LightCyan => Self::LightCyan, + Color::White => Self::White, + Color::Rgb(r, g, b) => Self::Rgb(r, g, b), + Color::Indexed(i) => Self::Indexed(i), + } + } +} diff --git a/src/widget.rs b/src/widget.rs index 55127a5..fd36e70 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -4,10 +4,35 @@ use ratatui::{ style::{Color, Modifier, Style}, widgets::{Block, Clear, Widget}, }; -use vt100::Screen; use crate::state; +/// A trait representing a pseudo-terminal screen. +/// +/// Implementing this trait allows for backends other than `vt100` to be used +/// with the `PseudoTerminal` widget. +pub trait Screen { + /// The type of cell this screen contains + type C: Cell; + + /// Returns the cell at the given location if it exists. + fn cell(&self, row: u16, col: u16) -> Option<&Self::C>; + /// Returns whether the terminal should be hidden + fn hide_cursor(&self) -> bool; + /// Returns cursor position of screen. + /// + /// The return value is expected to be (row, column) + fn cursor_position(&self) -> (u16, u16); +} + +/// A trait for representing a single cell on a screen. +pub trait Cell { + /// Whether the cell has any contents that could be rendered to the screen. + fn has_contents(&self) -> bool; + /// Apply the contents and styling of this cell to the provided buffer cell. + fn apply(&self, cell: &mut ratatui::buffer::Cell); +} + /// A widget representing a pseudo-terminal screen. /// /// The `PseudoTerminal` widget displays the contents of a pseudo-terminal screen, @@ -30,7 +55,7 @@ use crate::state; /// use vt100::Parser; /// /// let mut parser = vt100::Parser::new(24, 80, 0); -/// let pseudo_term = PseudoTerminal::new(&parser.screen()) +/// let pseudo_term = PseudoTerminal::new(parser.screen()) /// .block(Block::default().title("Terminal").borders(Borders::ALL)) /// .style( /// Style::default() @@ -40,8 +65,8 @@ use crate::state; /// ); /// ``` #[non_exhaustive] -pub struct PseudoTerminal<'a> { - screen: &'a Screen, +pub struct PseudoTerminal<'a, S> { + screen: &'a S, pub(crate) block: Option>, style: Option