feat: add ratatui-macros crate

This crate previously lived at https://github.com/ratatui/ratatui-macros
and is now part of the ratatui mono-repo.
This commit is contained in:
Josh McKinney 2025-02-05 12:16:18 -08:00
commit d4415204e1
No known key found for this signature in database
GPG Key ID: 722287396A903BC5
18 changed files with 1383 additions and 2 deletions

73
Cargo.lock generated
View File

@ -883,6 +883,12 @@ dependencies = [
"syn 2.0.98",
]
[[package]]
name = "dissimilar"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d"
[[package]]
name = "document-features"
version = "0.2.10"
@ -2536,6 +2542,7 @@ dependencies = [
"rand_chacha 0.9.0",
"ratatui-core",
"ratatui-crossterm",
"ratatui-macros",
"ratatui-termion",
"ratatui-termwiz",
"ratatui-widgets",
@ -2587,6 +2594,15 @@ dependencies = [
"rstest",
]
[[package]]
name = "ratatui-macros"
version = "0.7.0-alpha.0"
dependencies = [
"ratatui-core",
"ratatui-widgets",
"trybuild",
]
[[package]]
name = "ratatui-termion"
version = "0.1.0-alpha.1"
@ -2996,6 +3012,15 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_spanned"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
dependencies = [
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
@ -3250,6 +3275,12 @@ dependencies = [
"syn 2.0.98",
]
[[package]]
name = "target-triple"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078"
[[package]]
name = "tempfile"
version = "3.16.0"
@ -3264,6 +3295,15 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "terminfo"
version = "0.8.0"
@ -3505,11 +3545,26 @@ dependencies = [
"tokio",
]
[[package]]
name = "toml"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
@ -3518,6 +3573,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02a8b472d1a3d7c18e2d61a489aee3453fd9031c33e4f55bd533f4a7adca1bee"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
@ -3660,6 +3717,22 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "trybuild"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b812699e0c4f813b872b373a4471717d9eb550da14b311058a4d9cf4173cbca6"
dependencies = [
"dissimilar",
"glob",
"serde",
"serde_derive",
"serde_json",
"target-triple",
"termcolor",
"toml",
]
[[package]]
name = "typenum"
version = "1.17.0"

View File

@ -7,6 +7,7 @@ default-members = [
"ratatui-crossterm",
# this is not included as it doesn't compile on windows
# "ratatui-termion",
"ratatui-macros",
"ratatui-termwiz",
"ratatui-widgets",
"examples/apps/*",
@ -37,6 +38,7 @@ pretty_assertions = "1.4.1"
ratatui = { path = "ratatui", version = "0.30.0-alpha.1" }
ratatui-core = { path = "ratatui-core", version = "0.1.0-alpha.2" }
ratatui-crossterm = { path = "ratatui-crossterm", version = "0.1.0-alpha.1" }
ratatui-macros = { path = "ratatui-macros", version = "0.7.0-alpha.0" }
ratatui-termion = { path = "ratatui-termion", version = "0.1.0-alpha.1" }
ratatui-termwiz = { path = "ratatui-termwiz", version = "0.1.0-alpha.1" }
ratatui-widgets = { path = "ratatui-widgets", version = "0.3.0-alpha.1" }

View File

@ -0,0 +1,2 @@
format_macro_bodies = true
format_macro_matchers = true

117
ratatui-macros/CHANGELOG.md Normal file
View File

@ -0,0 +1,117 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.6.0](https://github.com/ratatui/ratatui-macros/compare/v0.5.0...v0.6.0) - 2024-10-21
### Other
- *(deps)* bump the cargo-dependencies group with 2 updates ([#73](https://github.com/ratatui/ratatui-macros/pull/73))
- *(deps)* bump ratatui from 0.28.0 to 0.28.1 in the cargo-dependencies group ([#70](https://github.com/ratatui/ratatui-macros/pull/70))
## [0.5.0] - 2024-08-12
### 🐛 Bug Fixes
- Bump version to 0.5.0
## [0.4.4](https://github.com/ratatui-org/ratatui-macros/compare/v0.4.3...v0.4.4) - 2024-08-09
### Other
- *(deps)* bump ratatui to 0.28.0 ([#66](https://github.com/ratatui-org/ratatui-macros/pull/66))
- *(deps)* bump trybuild from 1.0.98 to 1.0.99 in the cargo-dependencies group ([#65](https://github.com/ratatui-org/ratatui-macros/pull/65))
- *(deps)* bump trybuild from 1.0.97 to 1.0.98 in the cargo-dependencies group ([#62](https://github.com/ratatui-org/ratatui-macros/pull/62))
## [0.4.3](https://github.com/ratatui-org/ratatui-macros/compare/v0.4.2...v0.4.3) - 2024-07-22
### Added
- allow span macro to accept a bare expression ([#61](https://github.com/ratatui-org/ratatui-macros/pull/61))
### Other
- *(deps)* bump trybuild from 1.0.96 to 1.0.97 in the cargo-dependencies group ([#59](https://github.com/ratatui-org/ratatui-macros/pull/59))
## [0.4.2](https://github.com/ratatui-org/ratatui-macros/compare/v0.4.1...v0.4.2) - 2024-06-29
### Added
- Use `::ratatui` instead of `ratatui` ([#54](https://github.com/ratatui-org/ratatui-macros/pull/54))
- Add row! macro ([#52](https://github.com/ratatui-org/ratatui-macros/pull/52))
### Other
- Update README with row! documentation ([#56](https://github.com/ratatui-org/ratatui-macros/pull/56))
- Make doc examples shorter by removing duplicate imports ([#55](https://github.com/ratatui-org/ratatui-macros/pull/55))
## [0.4.1](https://github.com/ratatui-org/ratatui-macros/compare/v0.4.0...v0.4.1) - 2024-06-24
### Other
- *(deps)* bump ratatui from 0.26.3 to 0.27.0 in the cargo-dependencies group ([#51](https://github.com/ratatui-org/ratatui-macros/pull/51))
- Update dependabot.yml to group dependencies ([#50](https://github.com/ratatui-org/ratatui-macros/pull/50))
- *(deps)* bump ratatui from 0.26.2 to 0.26.3 ([#48](https://github.com/ratatui-org/ratatui-macros/pull/48))
- *(deps)* bump trybuild from 1.0.95 to 1.0.96 ([#47](https://github.com/ratatui-org/ratatui-macros/pull/47))
## [0.4.0](https://github.com/ratatui-org/ratatui-macros/compare/v0.3.1...v0.4.0) - 2024-05-15
### Added
- *(layout)* [**breaking**] Use `*=` instead of `=*` ([#45](https://github.com/ratatui-org/ratatui-macros/pull/45))
## [0.3.1](https://github.com/ratatui-org/ratatui-macros/compare/v0.3.0...v0.3.1) - 2024-05-13
### Added
- Better error messages for `span!` macro ([#43](https://github.com/ratatui-org/ratatui-macros/pull/43))
### Fixed
- downgrade ratatui to 0.26.2 ([#41](https://github.com/ratatui-org/ratatui-macros/pull/41))
### Other
- Update authors to ratatui developers ([#44](https://github.com/ratatui-org/ratatui-macros/pull/44))
## [0.3.0](https://github.com/ratatui-org/ratatui-macros/compare/v0.2.4...v0.3.0) - 2024-05-09
### Added
- Use release-plz ([#38](https://github.com/ratatui-org/ratatui-macros/pull/38))
- Add text! macro ([#36](https://github.com/ratatui-org/ratatui-macros/pull/36))
- Add fill constraint ([#34](https://github.com/ratatui-org/ratatui-macros/pull/34))
- [**breaking**] Remove color `palette!` macro ([#32](https://github.com/ratatui-org/ratatui-macros/pull/32))
- Replace raw! and styled! with span! macro ([#30](https://github.com/ratatui-org/ratatui-macros/pull/30))
- Add `line!` attribute macro ([#29](https://github.com/ratatui-org/ratatui-macros/pull/29))
- *(text)* add raw! and styled! macros ([#4](https://github.com/ratatui-org/ratatui-macros/pull/4))
### Fixed
- Update repo url in Cargo.toml ([#39](https://github.com/ratatui-org/ratatui-macros/pull/39))
### Other
- Use `.areas(area)` instead of `.split(area).to_vec().try_into().unwrap()` ([#37](https://github.com/ratatui-org/ratatui-macros/pull/37))
- Update README.md with short description of span and line macros ([#33](https://github.com/ratatui-org/ratatui-macros/pull/33))
- format using cargo +nightly fmt ([#31](https://github.com/ratatui-org/ratatui-macros/pull/31))
- *(deps)* bump ratatui from 0.27.0-alpha.3 to 0.27.0-alpha.5 ([#27](https://github.com/ratatui-org/ratatui-macros/pull/27))
- *(deps)* bump trybuild from 1.0.91 to 1.0.93 ([#28](https://github.com/ratatui-org/ratatui-macros/pull/28))
- *(deps)* bump ratatui from 0.27.0-alpha.2 to 0.27.0-alpha.3 ([#24](https://github.com/ratatui-org/ratatui-macros/pull/24))
- *(deps)* bump trybuild from 1.0.90 to 1.0.91 ([#23](https://github.com/ratatui-org/ratatui-macros/pull/23))
- *(deps)* bump trybuild from 1.0.88 to 1.0.90 ([#20](https://github.com/ratatui-org/ratatui-macros/pull/20))
- *(deps)* bump ratatui from 0.27.0-alpha.0 to 0.27.0-alpha.2 ([#22](https://github.com/ratatui-org/ratatui-macros/pull/22))
- *(deps)* bump mio from 0.8.10 to 0.8.11 ([#18](https://github.com/ratatui-org/ratatui-macros/pull/18))
- *(deps)* bump ratatui from 0.26.0-alpha.1 to 0.27.0-alpha.0 ([#19](https://github.com/ratatui-org/ratatui-macros/pull/19))
- add cargo husky pre-commit hook ([#8](https://github.com/ratatui-org/ratatui-macros/pull/8))
- Create dependabot.yml ([#7](https://github.com/ratatui-org/ratatui-macros/pull/7))
- use rust cache to cache deps ([#6](https://github.com/ratatui-org/ratatui-macros/pull/6))
- readme tweaks ([#5](https://github.com/ratatui-org/ratatui-macros/pull/5))
- Update README.md
- Update README.md
- Update README.md
- Update README.md
- Add link to ratatui

18
ratatui-macros/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "ratatui-macros"
version = "0.7.0-alpha.0"
edition = "2021"
authors = ["The Ratatui Developers"]
description = "Macros for Ratatui"
license = "MIT"
repository = "https://github.com/ratatui/ratatui"
documentation = "https://docs.rs/ratatui-macros"
keywords = ["ratatui", "macros", "tui", "ui"]
categories = ["command-line-interface"]
[dependencies]
ratatui-core.workspace = true
ratatui-widgets.workspace = true
[dev-dependencies]
trybuild = { version = "1.0.103", features = ["diff"] }

20
ratatui-macros/LICENSE Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2024 Dheepak Krishnamurthy
Copyright (c) 2025 The Ratatui Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

200
ratatui-macros/README.md Normal file
View File

@ -0,0 +1,200 @@
# Ratatui Macros
[![Crates.io badge]][ratatui_macros crate] [![License badge]](./LICENSE)
[![Docs.rs badge]][API Docs] [![CI Badge]][CI Status]
[![Crate Downloads badge]][ratatui_macros crate]
`ratatui-macros` is a Rust crate that provides easy-to-use macros for simplifying boilerplate
associated with creating UI using [Ratatui].
This is an experimental playground for us to explore macros that would be useful to have in Ratatui
proper.
## Features
- Constraint-based Layouts: Easily define layout constraints such as fixed, percentage, minimum, and
maximum sizes, as well as ratios.
- Directional Layouts: Specify layouts as either horizontal or vertical with simple macro commands.
- Span and Line macros: Make it easier to create spans and lines with styling.
## Getting Started
To use `ratatui-macros` in your Rust project, add it as a dependency in your `Cargo.toml`:
```shell
cargo add ratatui-macros
```
Then, import the macros in your Rust file:
```rust
use ratatui_macros::{
constraint,
constraints,
horizontal,
vertical,
span,
line,
};
```
### Layout
If you are new to Ratatui, check out the [Layout concepts] article on the Ratatui website before proceeding.
Use the `constraints!` macro to define layout constraints:
```rust
# use ratatui_core::layout::Constraint;
use ratatui_macros::constraints;
assert_eq!(
constraints![==50, ==30%, >=3, <=1, ==1/2, *=1],
[
Constraint::Length(50),
Constraint::Percentage(30),
Constraint::Min(3),
Constraint::Max(1),
Constraint::Ratio(1, 2),
Constraint::Fill(1),
]
)
```
```rust
# use ratatui_core::layout::Constraint;
use ratatui_macros::constraints;
assert_eq!(
constraints![==1/4; 4],
[
Constraint::Ratio(1, 4),
Constraint::Ratio(1, 4),
Constraint::Ratio(1, 4),
Constraint::Ratio(1, 4),
]
)
```
Use the `constraint!` macro to define individual constraints:
```rust
# use ratatui_core::layout::Constraint;
use ratatui_macros::constraint;
assert_eq!(
constraint!(==50),
Constraint::Length(50),
)
```
Create vertical and horizontal layouts using the `vertical!` and `horizontal!` macros:
```rust
# use ratatui_core::layout::Rect;
use ratatui_macros::{vertical, horizontal};
let area = Rect { x: 0, y: 0, width: 10, height: 10 };
let [main, bottom] = vertical![==100%, >=3].areas(area);
assert_eq!(bottom.y, 7);
assert_eq!(bottom.height, 3);
let [left, main, right] = horizontal![>=3, ==100%, >=3].areas(area);
assert_eq!(left.width, 3);
assert_eq!(right.width, 3);
```
## Spans
The `span!` macro create raw and styled `Span`s. They each take a format string and arguments.
`span!` accepts as the first parameter any value that can be converted to a `Style` followed by a
`;` followed by the format string and arguments.
```rust
# use ratatui_core::style::{Color, Modifier, Style, Stylize};
use ratatui_macros::span;
let name = "world!";
let raw_greeting = span!("hello {name}");
let styled_greeting = span!(Style::new().green(); "hello {name}");
let styled_greeting = span!(Color::Green; "hello {name}");
let styled_greeting = span!(Modifier::BOLD; "hello {name}");
```
## Line
The `line!` macro creates a `Line` that contains a sequence of spans. It is similar to the `vec!`
macro.
```rust
use ratatui_macros::line;
let name = "world!";
let line = line!["hello", format!("{name}")];
let line = line!["bye"; 2];
```
## Text
The `text!` macro creates a `Text` that contains a sequence of lines. It is similar to the `vec!`
macro.
```rust
use ratatui_macros::{span, line, text};
let name = "world!";
let text = text!["hello", format!("{name}")];
let text = text!["bye"; 2];
```
It is even possible to use `span!` and `line!` in the `text!` macro:
```rust
# use ratatui_core::style::{Modifier, Stylize};
use ratatui_macros::{span, line, text};
let name = "Bye!!!";
let text = text![line!["hello", "world".bold()], span!(Modifier::BOLD; "{name}")];
```
## Row
The `row!` macro creates a `Row` that contains a sequence of `Cell`. It is similar to the `vec!`
macro. A `Row` represents a sequence of `Cell`s in a single row of a table.
```rust
use ratatui_macros::row;
let rows = [
row!["hello", "world"],
row!["goodbye", "world"],
];
```
It is even possible to use `span!`, `line!` and `text!` in the `row!` macro:
```rust
# use ratatui_core::style::{Modifier, Stylize};
use ratatui_macros::{span, line, text, row};
let name = "Bye!!!";
let text = row![text![line!["hello", "world".bold()]], span!(Modifier::BOLD; "{name}")];
```
## Contributing
Contributions to `ratatui-macros` are welcome! Whether it's submitting a bug report, a feature
request, or a pull request, all forms of contributions are valued and appreciated.
[Crates.io badge]: https://img.shields.io/crates/v/ratatui-macros?logo=rust&style=flat-square
[License badge]: https://img.shields.io/crates/l/ratatui-macros
[CI Badge]:
https://img.shields.io/github/actions/workflow/status/ratatui-org/ratatui-macros/ci.yml?logo=github&style=flat-square
[Docs.rs badge]: https://img.shields.io/docsrs/ratatui-macros?logo=rust&style=flat-square
[Crate Downloads badge]: https://img.shields.io/crates/d/ratatui-macros?logo=rust&style=flat-square
[ratatui_macros crate]: https://crates.io/crates/ratatui-macros
[API Docs]: https://docs.rs/ratatui-macros
[CI Status]: https://github.com/kdheepak/ratatui-macros/actions
[Ratatui]: https://github.com/ratatui-org/ratatui
[Layout concepts]: https://ratatui.rs/concepts/layout

View File

@ -0,0 +1,197 @@
/// Creates a single constraint.
///
/// If creating an array of constraints, you probably want to use
/// [`constraints!`] instead.
///
/// # Examples
///
/// ```
/// # use ratatui_core::layout::Constraint;
/// use ratatui_macros::constraint;
/// assert_eq!(constraint!(>= 3 + 4), Constraint::Min(7));
/// assert_eq!(constraint!(<= 3 + 4), Constraint::Max(7));
/// assert_eq!(constraint!(== 1 / 3), Constraint::Ratio(1, 3));
/// assert_eq!(constraint!(== 3), Constraint::Length(3));
/// assert_eq!(constraint!(== 10 %), Constraint::Percentage(10));
/// assert_eq!(constraint!(*= 1), Constraint::Fill(1));
/// ```
#[macro_export]
macro_rules! constraint {
(== $token:tt %) => {
$crate::ratatui_core::layout::Constraint::Percentage($token)
};
(>= $expr:expr) => {
$crate::ratatui_core::layout::Constraint::Min($expr)
};
(<= $expr:expr) => {
$crate::ratatui_core::layout::Constraint::Max($expr)
};
(== $num:tt / $denom:tt) => {
$crate::ratatui_core::layout::Constraint::Ratio($num as u32, $denom as u32)
};
(== $expr:expr) => {
$crate::ratatui_core::layout::Constraint::Length($expr)
};
(*= $expr:expr) => {
$crate::ratatui_core::layout::Constraint::Fill($expr)
};
}
/// Creates an array of constraints.
///
/// See [`constraint!`] for more information.
///
/// If you want to solve the constraints, see
/// [`vertical!`] and [`horizontal!`] macros.
///
/// # Examples
///
/// ```rust
/// use ratatui_macros::constraints;
/// assert_eq!(constraints![==5, ==30%, >=3, <=1, ==1/2].len(), 5);
/// assert_eq!(constraints![==5; 5].len(), 5);
/// ```
///
/// ```rust
/// # use ratatui_core::layout::Constraint;
/// # use ratatui_macros::constraints;
/// assert_eq!(
/// constraints![==50, ==30%, >=3, <=1, ==1/2, *=1],
/// [
/// Constraint::Length(50),
/// Constraint::Percentage(30),
/// Constraint::Min(3),
/// Constraint::Max(1),
/// Constraint::Ratio(1, 2),
/// Constraint::Fill(1),
/// ]
/// )
/// ```
#[macro_export]
macro_rules! constraints {
// Note: this implementation forgoes speed for the sake of simplicity. Adding variations of the
// comma and semicolon rules for each constraint type would be faster, but would result in a lot
// of duplicated code.
// Cannot start the constraints macro with a ,
([ , $($rest:tt)* ] -> () []) => {
compile_error!("No rules expected the token `,` while trying to match the end of the macro")
};
// Comma finishes a constraint element, so parse it and continue.
// When a comma is encountered, it marks the end of a constraint element, so this rule is responsible
// for parsing the constraint expression up to the comma and continuing the parsing process.
// It accumulated the $partial contains a Constraint and is parsed using a separate $crate::constraint! macro.
// The constraint is then appended to the list of parsed constraints.
//
// [ , $($rest:tt)* ] -> In the rule matcher, this pattern matches a comma followed
// by the rest of the tokens. The comma signals the end of
// the current constraint element.
// ($($partial:tt)*) -> In the rule matcher, this contains the partial tokens
// accumulated so far for the current constraint element.
// [$($parsed:tt)* ] -> This contains the constraints that have been successfully
// parsed so far.
// $crate::constraint!($($partial)*) -> This macro call parses and expands the accumulated
// partial tokens into a single Constraint expression.
// [$($parsed)* $crate::constraint!(...)] -> Appends the newly parsed constraint to the list of
// already parsed constraints.
([ , $($rest:tt)* ] -> ($($partial:tt)*) [ $($parsed:tt)* ]) => {
$crate::constraints!([$($rest)*] -> () [$($parsed)* $crate::constraint!($($partial)*) ,])
};
// Semicolon indicates that there's repetition. The trailing comma is required because the 'entrypoint'
// rule adds a trailing comma.
// This rule is triggered when a semicolon is encountered, indicating that there is repetition of
// constraints. It handles the repetition logic by parsing the count and generating an array of
// constraints using the $crate::constraint! macro.
//
// [ ; $count:expr , ] -> In the rule matcher, this pattern matches a semicolon
// followed by an expression representing the count, and a
// trailing comma.
// ($($partial:tt)*) -> In the rule matcher, this contains the partial tokens
// accumulated so far for the current constraint element.
// This represents everything before the ;
// [] -> There will be no existed parsed constraints when using ;
// $crate::constraint!($($partial)*) -> This macro call parses and expands the accumulated
// partial tokens into a single Constraint expression.
// [$crate::constraint!(...) ; $count] -> Generates an array of constraints by repeating the
// parsed constraint count number of times.
([ ; $count:expr , ] -> ($($partial:tt)*) []) => {
[$crate::constraint!($($partial)*); $count]
};
// Pull the first token (which can't be a comma or semicolon) onto the accumulator.
// if first token is a comma or semicolon, previous rules will match before this rule
//
// [ $head:tt $($rest:tt)* ] -> In the rule matcher, this pulls a single `head` token
// out of the previous rest, and puts
// the remaining into `rest`
// [ $($rest)* ] -> This is what is fed back into the `constraints!` macro
// as the first segment for the match rule
//
// ($($partial:tt)*) -> In the rule matcher, this contains previous partial
// tokens that will make up a `Constraint` expression
// ($($partial)* $head) -> This combines head with the previous partial tokens
// i.e. this is the accumulated tokens
//
// [ $($parsed:tt)* ] -> In the rule matcher, this contains all parsed exprs
// [$($parsed)* ] -> These are passed on to the next match untouched.
([ $head:tt $($rest:tt)* ] -> ($($partial:tt)*) [ $($parsed:tt)* ]) => {
$crate::constraints!([$($rest)*] -> ($($partial)* $head) [$($parsed)* ])
};
// This rule is triggered when there are no more input tokens to process. It signals the end of the
// macro invocation and outputs the parsed constraints as a final array.
([$(,)?] -> () [ $( $parsed:tt )* ]) => {
[$($parsed)*]
};
// Entrypoint where there's no comma at the end.
// We add a comma to make sure there's always a trailing comma.
// Right-hand side will accumulate the actual `Constraint` literals.
($( $constraint:tt )+) => {
$crate::constraints!([ $($constraint)+ , ] -> () [])
};
}
/// Creates a vertical layout with specified constraints.
///
/// It accepts a series of constraints and applies them to create a vertical layout. The constraints
/// can include fixed sizes, minimum and maximum sizes, percentages, and ratios.
///
/// See [`constraint!`] or [`constraints!`] for more information.
///
/// # Examples
///
/// ```
/// // Vertical layout with a fixed size and a percentage constraint
/// use ratatui_macros::vertical;
/// vertical![== 50, == 30%];
/// ```
#[macro_export]
macro_rules! vertical {
($( $constraint:tt )+) => {
$crate::ratatui_core::layout::Layout::vertical($crate::constraints!( $($constraint)+ ))
};
}
/// Creates a horizontal layout with specified constraints.
///
/// It takes a series of constraints and applies them to create a horizontal layout. The constraints
/// can include fixed sizes, minimum and maximum sizes, percentages, and ratios.
///
/// See [`constraint!`] or [`constraints!`] for more information.
///
/// # Examples
///
/// ```
/// // Horizontal layout with a ratio constraint and a minimum size constraint
/// use ratatui_macros::horizontal;
/// horizontal![== 1/3, >= 100];
/// ```
#[macro_export]
macro_rules! horizontal {
($( $constraint:tt )+) => {
$crate::ratatui_core::layout::Layout::horizontal($crate::constraints!( $($constraint)+ ))
};
}

10
ratatui-macros/src/lib.rs Normal file
View File

@ -0,0 +1,10 @@
#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
mod layout;
mod line;
mod row;
mod span;
mod text;
// Re-export the core crate to use the types in macros
pub use ratatui_core;

View File

@ -0,0 +1,97 @@
/// A macro for creating a [`Line`] using vec! syntax.
///
/// `line!` is similar to the [`vec!`] macro, but it returns a [`Line`] instead of a `Vec`.
///
/// # Examples
///
/// * Create a [`Line`] containing a vector of [`Span`]s:
///
/// ```rust
/// # use ratatui_core::style::Stylize;
/// use ratatui_macros::line;
///
/// let line = line!["hello", "world"];
/// let line = line!["hello".red(), "world".red().bold()];
/// ```
///
/// * Create a [`Line`] from a given [`Span`] repeated some amount of times:
///
/// ```rust
/// # use ratatui_macros::line;
/// let line = line!["hello"; 2];
/// ```
///
/// * Use [`span!`] macro inside [`line!`] macro for formatting.
///
/// ```rust
/// # use ratatui_core::style::Modifier;
/// use ratatui_macros::{line, span};
///
/// let line = line![span!("hello {}", "world"), span!(Modifier::BOLD; "goodbye {}", "world")];
/// ```
///
/// [`Line`]: crate::text::Line
/// [`Span`]: crate::text::Span
#[macro_export]
macro_rules! line {
() => {
$crate::ratatui_core::text::Line::default()
};
($span:expr; $n:expr) => {
$crate::ratatui_core::text::Line::from(vec![$span.into(); $n])
};
($($span:expr),+ $(,)?) => {{
$crate::ratatui_core::text::Line::from(vec![
$(
$span.into(),
)+
])
}};
}
#[cfg(test)]
mod tests {
use ratatui_core::text::{Line, Span};
#[test]
fn line_literal() {
let line = line!["hello", "world"];
assert_eq!(line, Line::from(vec!["hello".into(), "world".into()]));
}
#[test]
fn line_raw_instead_of_literal() {
let line = line![Span::raw("hello"), "world"];
assert_eq!(line, Line::from(vec!["hello".into(), "world".into()]));
}
#[test]
fn line_vec_count_syntax() {
let line = line!["hello"; 2];
assert_eq!(line, Line::from(vec!["hello".into(), "hello".into()]));
}
#[test]
fn line_vec_count_syntax_with_span() {
let line = line![crate::span!("hello"); 2];
assert_eq!(line, Line::from(vec!["hello".into(), "hello".into()]));
}
#[test]
fn line_empty() {
let line = line![];
assert_eq!(line, Line::default());
}
#[test]
fn line_single_span() {
let line = line![Span::raw("foo")];
assert_eq!(line, Line::from(vec!["foo".into()]));
}
#[test]
fn line_repeated_span() {
let line = line![Span::raw("foo"); 2];
assert_eq!(line, Line::from(vec!["foo".into(), "foo".into()]));
}
}

139
ratatui-macros/src/row.rs Normal file
View File

@ -0,0 +1,139 @@
/// A macro for creating a [`Row`] using vec! syntax.
///
/// `row!` is similar to the [`vec!`] macro, but it returns a [`Row`] instead of a `Vec`.
///
/// # Examples
///
/// * Create a [`Row`] containing a vector of [`Cell`]s:
///
/// ```rust
/// # use ratatui_core::style::Stylize;
/// use ratatui_macros::row;
///
/// let row = row!["hello", "world"];
/// let row = row!["hello".red(), "world".red().bold()];
/// ```
///
/// * Create an empty [`Row`]:
///
/// ```rust
/// # use ratatui_macros::row;
/// let empty_row = row![];
/// ```
///
/// * Create a [`Row`] from a given [`Cell`] repeated some amount of times:
///
/// ```rust
/// # use ratatui_macros::row;
/// let row = row!["hello"; 2];
/// ```
///
/// * Use [`text!`], [`line!`] or [`span!`] macro inside [`row!`] macro.
///
/// ```rust
/// # use ratatui_core::style::{Modifier};
/// use ratatui_macros::{row, line, text, span};
///
/// let row = row![
/// line!["hello", "world"], span!(Modifier::BOLD; "goodbye {}", "world"),
/// text!["hello", "world"],
/// ];
/// ```
///
/// [`Row`]: crate::widgets::Row
/// [`Cell`]: crate::widgets::Cell
#[macro_export]
macro_rules! row {
() => {
::ratatui_widgets::table::Row::default()
};
($cell:expr; $n:expr) => {
::ratatui_widgets::table::Row::new(vec![::ratatui_widgets::table::Cell::from($cell); $n])
};
($($cell:expr),+ $(,)?) => {{
::ratatui_widgets::table::Row::new(vec![
$(
::ratatui_widgets::table::Cell::from($cell),
)+
])
}};
}
#[cfg(test)]
mod tests {
use ratatui_core::text::Text;
use ratatui_widgets::table::{Cell, Row};
#[test]
fn row_literal() {
let row = row!["hello", "world"];
assert_eq!(
row,
Row::new(vec![Cell::from("hello"), Cell::from("world")])
);
}
#[test]
fn row_empty() {
let row = row![];
assert_eq!(row, Row::default());
}
#[test]
fn row_single_cell() {
let row = row![Cell::from("foo")];
assert_eq!(row, Row::new(vec![Cell::from("foo")]));
}
#[test]
fn row_repeated_cell() {
let row = row![Cell::from("foo"); 2];
assert_eq!(row, Row::new(vec![Cell::from("foo"), Cell::from("foo")]));
}
#[test]
fn row_explicit_use_of_span_and_line() {
let row = row![crate::line!("hello"), crate::span!["world"]];
assert_eq!(
row,
Row::new(vec![Cell::from("hello"), Cell::from("world")])
);
}
#[test]
fn row_vec_count_syntax() {
let row = row!["hello"; 2];
assert_eq!(
row,
Row::new(vec![Cell::from("hello"), Cell::from("hello")])
);
}
#[test]
fn multiple_rows() {
use crate::text;
let rows = [
row!["Find File", text!["ctrl+f"].right_aligned()],
row!["Open recent", text!["ctrl+r"].right_aligned()],
row!["Open config", text!["ctrl+k"].right_aligned()],
];
assert_eq!(
rows,
[
Row::new([
Cell::from("Find File"),
Cell::from(Text::raw("ctrl+f").right_aligned()),
]),
Row::new([
Cell::from("Open recent"),
Cell::from(Text::raw("ctrl+r").right_aligned()),
]),
Row::new([
Cell::from("Open config"),
Cell::from(Text::raw("ctrl+k").right_aligned()),
]),
]
);
}
}

245
ratatui-macros/src/span.rs Normal file
View File

@ -0,0 +1,245 @@
/// A macro for creating a [`Span`] using formatting syntax.
///
/// `span!` is similar to the [`format!`] macro, but it returns a [`Span`] instead of a `String`. In
/// addition, it also accepts an expression for the first argument, which will be converted to a
/// string using the [`format!`] macro.
///
/// If semicolon follows the first argument, then the first argument is a [`Style`] and a styled
/// [`Span`] will be created. Otherwise, the [`Span`] will be created as a raw span (i.e. with style
/// set to `Style::default()`).
///
/// # Examples
///
/// ```rust
/// # use ratatui_core::style::{Color, Modifier, Style, Stylize};
/// use ratatui_macros::span;
///
/// let content = "content";
///
/// // expression
/// let span = span!(content);
///
/// // format string
/// let span = span!("test content");
/// let span = span!("test {}", "content");
/// let span = span!("{} {}", "test", "content");
/// let span = span!("test {content}");
/// let span = span!("test {content}", content = "content");
///
/// // with format specifiers
/// let span = span!("test {:4}", 123);
/// let span = span!("test {:04}", 123);
///
/// let style = Style::new().green();
///
/// // styled expression
/// let span = span!(style; content);
///
/// // styled format string
/// let span = span!(style; "test content");
/// let span = span!(style; "test {}", "content");
/// let span = span!(style; "{} {}", "test", "content");
/// let span = span!(style; "test {content}");
/// let span = span!(style; "test {content}", content = "content");
///
/// // accepts any type that is convertible to Style
/// let span = span!(Style::new().green(); "test {content}");
/// let span = span!(Color::Green; "test {content}");
/// let span = span!(Modifier::BOLD; "test {content}");
///
/// // with format specifiers
/// let span = span!(style; "test {:4}", 123);
/// let span = span!(style; "test {:04}", 123);
/// ```
///
/// # Note
///
/// The first parameter must be a formatting specifier followed by a comma OR anything that can be
/// converted into a [`Style`] followed by a semicolon.
///
/// For example, the following will fail to compile:
///
/// ```compile_fail
/// # use ratatui::prelude::*;
/// # use ratatui_macros::span;
/// let span = span!(Modifier::BOLD, "hello world");
/// ```
///
/// But this will work:
///
/// ```rust
/// # use ratatui_core::style::{Modifier};
/// # use ratatui_macros::span;
/// let span = span!(Modifier::BOLD; "hello world");
/// ```
///
/// The following will fail to compile:
///
/// ```compile_fail
/// # use ratatui::prelude::*;
/// # use ratatui_macros::span;
/// let span = span!("hello", "world");
/// ```
///
/// But this will work:
///
/// ```rust
/// # use ratatui_macros::span;
/// let span = span!("hello {}", "world");
/// ```
///
/// [`Color`]: crate::style::Color
/// [`Style`]: crate::style::Style
/// [`Span`]: crate::text::Span
/// [`Style`]: crate::style::Style
#[macro_export]
macro_rules! span {
($string:literal) => {
$crate::ratatui_core::text::Span::raw(format!($string))
};
($string:literal, $($arg:tt)*) => {
$crate::ratatui_core::text::Span::raw(format!($string, $($arg)*))
};
($expr:expr) => {
$crate::ratatui_core::text::Span::raw(format!("{}", $expr))
};
($style:expr, $($arg:tt)*) => {
compile_error!("first parameter must be a formatting specifier followed by a comma OR a `Style` followed by a semicolon")
};
($style:expr; $string:literal) => {
$crate::ratatui_core::text::Span::styled(format!($string), $style)
};
($style:expr; $string:literal, $($arg:tt)*) => {
$crate::ratatui_core::text::Span::styled(format!($string, $($arg)*), $style)
};
($style:expr; $expr:expr) => {
$crate::ratatui_core::text::Span::styled(format!("{}", $expr), $style)
};
}
#[cfg(test)]
mod tests {
use ratatui_core::{
style::{Color, Modifier, Style, Stylize},
text::Span,
};
#[test]
fn raw() {
let test = "test";
let content = "content";
let number = 123;
// literal
let span = span!("test content");
assert_eq!(span, Span::raw("test content"));
// string
let span = span!("test {}", "content");
assert_eq!(span, Span::raw("test content"));
// string variable
let span = span!("test {}", content);
assert_eq!(span, Span::raw("test content"));
// string variable in the format string
let span = span!("test {content}");
assert_eq!(span, Span::raw("test content"));
// named variable
let span = span!("test {content}", content = "content");
assert_eq!(span, Span::raw("test content"));
// named variable pointing at a local variable
let span = span!("test {content}", content = content);
assert_eq!(span, Span::raw("test content"));
// two strings
let span = span!("{} {}", "test", "content");
assert_eq!(span, Span::raw("test content"));
// two string variables
let span = span!("{test} {content}");
assert_eq!(span, Span::raw("test content"));
// a number
let span = span!("test {number}");
assert_eq!(span, Span::raw("test 123"));
// a number with a format specifier
let span = span!("test {number:04}");
assert_eq!(span, Span::raw("test 0123"));
// directly pass a number expression
let span = span!(number);
assert_eq!(span, Span::raw("123"));
// directly pass a string expression
let span = span!(test);
assert_eq!(span, Span::raw("test"));
}
#[test]
fn styled() {
const STYLE: Style = Style::new().fg(Color::Green);
let test = "test";
let content = "content";
let number = 123;
// literal
let span = span!(STYLE; "test content");
assert_eq!(span, Span::styled("test content", STYLE));
// string
let span = span!(STYLE; "test {}", "content");
assert_eq!(span, Span::styled("test content", STYLE));
// string variable
let span = span!(STYLE; "test {}", content);
assert_eq!(span, Span::styled("test content", STYLE));
// string variable in the format string
let span = span!(STYLE; "test {content}");
assert_eq!(span, Span::styled("test content", STYLE));
// named variable
let span = span!(STYLE; "test {content}", content = "content");
assert_eq!(span, Span::styled("test content", STYLE));
// named variable pointing at a local variable
let span = span!(STYLE; "test {content}", content = content);
assert_eq!(span, Span::styled("test content", STYLE));
// two strings
let span = span!(STYLE; "{} {}", "test", "content");
assert_eq!(span, Span::styled("test content", STYLE));
// two string variables
let span = span!(STYLE; "{test} {content}");
assert_eq!(span, Span::styled("test content", STYLE));
// a number
let span = span!(STYLE; "test {number}");
assert_eq!(span, Span::styled("test 123", STYLE));
// a number with a format specifier
let span = span!(STYLE; "test {number:04}");
assert_eq!(span, Span::styled("test 0123", STYLE));
// accepts any type that is convertible to Style
let span = span!(Color::Green; "test {content}");
assert_eq!(span, Span::styled("test content", STYLE));
let span = span!(Modifier::BOLD; "test {content}");
assert_eq!(span, Span::styled("test content", Style::new().bold()));
// directly pass a number expression
let span = span!(STYLE; number);
assert_eq!(span, Span::styled("123", STYLE));
// directly pass a string expression
let span = span!(STYLE; test);
assert_eq!(span, Span::styled("test", STYLE));
}
}

View File

@ -0,0 +1,71 @@
/// A macro for creating a [`Text`] using vec! syntax.
///
/// `text!` is similar to the [`vec!`] macro, but it returns a [`Text`] instead of a `Vec`.
///
/// # Examples
///
/// * Create a [`Text`] containing a vector of [`Line`]s:
///
/// ```rust
/// # use ratatui_core::style::Stylize;
/// use ratatui_macros::text;
///
/// let text = text!["hello", "world"];
/// let text = text!["hello".red(), "world".red().bold()];
/// ```
///
/// * Create a [`text`] from a given [`Line`] repeated some amount of times:
///
/// ```rust
/// # use ratatui_macros::text;
/// let text = text!["hello"; 2];
/// ```
///
/// * Use [`line!`] or [`span!`] macro inside [`text!`] macro.
///
/// ```rust
/// # use ratatui_core::style::{Modifier};
/// use ratatui_macros::{line, text, span};
///
/// let text = text![line!["hello", "world"], span!(Modifier::BOLD; "goodbye {}", "world")];
/// ```
///
/// [`Text`]: crate::text::Text
/// [`Line`]: crate::text::Line
/// [`Span`]: crate::text::Span
#[macro_export]
macro_rules! text {
() => {
ratatui_core::text::Text::default()
};
($line:expr; $n:expr) => {
ratatui_core::text::Text::from(vec![$line.into(); $n])
};
($($line:expr),+ $(,)?) => {{
ratatui_core::text::Text::from(vec![
$(
$line.into(),
)+
])
}};
}
#[cfg(test)]
mod tests {
use ratatui_core::text::Text;
#[test]
fn text() {
// literal
let text = text!["hello", "world"];
assert_eq!(text, Text::from(vec!["hello".into(), "world".into()]));
// explicit use of span and line
let text = text![crate::line!("hello"), crate::span!["world"]];
assert_eq!(text, Text::from(vec!["hello".into(), "world".into()]));
// vec count syntax
let text = text!["hello"; 2];
assert_eq!(text, Text::from(vec!["hello".into(), "hello".into()]));
}
}

View File

@ -0,0 +1,104 @@
use ratatui_core::layout::{Constraint, Rect};
use ratatui_macros::{constraints, horizontal, vertical};
#[test]
fn layout_constraints_macro() {
let rect = Rect {
x: 0,
y: 0,
width: 10,
height: 10,
};
let [rect1, rect2] = vertical![==7, <=3].split(rect).to_vec().try_into().unwrap();
assert_eq!(rect1, Rect::new(0, 0, 10, 7));
assert_eq!(rect2, Rect::new(0, 7, 10, 3));
let [rect1, rect2] = horizontal![==7, <=3]
.split(rect)
.to_vec()
.try_into()
.unwrap();
assert_eq!(rect1, Rect::new(0, 0, 7, 10));
assert_eq!(rect2, Rect::new(7, 0, 3, 10));
let one = 1;
let two = 2;
let ten = 10;
let zero = 0;
let [a, b, c, d, e, f] = horizontal![==one, >=one, <=one, == 1 / two, == ten %, >=zero]
.split(rect)
.to_vec()
.try_into()
.unwrap();
assert_eq!(a, Rect::new(0, 0, 1, 10));
assert_eq!(b, Rect::new(1, 0, 1, 10));
assert_eq!(c, Rect::new(2, 0, 1, 10));
assert_eq!(d, Rect::new(3, 0, 5, 10));
assert_eq!(e, Rect::new(8, 0, 1, 10));
assert_eq!(f, Rect::new(9, 0, 1, 10));
let one = 1;
let two = 2;
let ten = 10;
let zero = 0;
let [a, b, c, d, e, f] = horizontal![
== one*one, // expr allowed here
>= one+zero, // expr allowed here
<= one-zero, // expr allowed here
== 1/two, // only single token allowed in numerator and denominator
== ten%, // only single token allowed before %
>= zero // no trailing comma
]
.split(rect)
.to_vec()
.try_into()
.unwrap();
assert_eq!(a, Rect::new(0, 0, 1, 10));
assert_eq!(b, Rect::new(1, 0, 1, 10));
assert_eq!(c, Rect::new(2, 0, 1, 10));
assert_eq!(d, Rect::new(3, 0, 5, 10));
assert_eq!(e, Rect::new(8, 0, 1, 10));
assert_eq!(f, Rect::new(9, 0, 1, 10));
let [a, b, c, d, e] = constraints![ >=0, ==1, <=5, ==10%, ==1/2 ];
assert_eq!(a, Constraint::Min(0));
assert_eq!(b, Constraint::Length(1));
assert_eq!(c, Constraint::Max(5));
assert_eq!(d, Constraint::Percentage(10));
assert_eq!(e, Constraint::Ratio(1, 2));
let [a, b, c, d, e] = constraints![ >=0; 5 ];
assert_eq!(a, Constraint::Min(0));
assert_eq!(b, Constraint::Min(0));
assert_eq!(c, Constraint::Min(0));
assert_eq!(d, Constraint::Min(0));
assert_eq!(e, Constraint::Min(0));
let [a, b, c, d, e] = constraints![ <=0; 5 ];
assert_eq!(a, Constraint::Max(0));
assert_eq!(b, Constraint::Max(0));
assert_eq!(c, Constraint::Max(0));
assert_eq!(d, Constraint::Max(0));
assert_eq!(e, Constraint::Max(0));
let [a, b] = constraints![ ==0; 2 ];
assert_eq!(a, Constraint::Length(0));
assert_eq!(b, Constraint::Length(0));
let [a, b] = constraints![ == 50%; 2 ];
assert_eq!(a, Constraint::Percentage(50));
assert_eq!(b, Constraint::Percentage(50));
let [a, b] = constraints![ == 1/2; 2 ];
assert_eq!(a, Constraint::Ratio(1, 2));
assert_eq!(b, Constraint::Ratio(1, 2));
}
#[test]
fn fails() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/ui/fails.rs");
}

View File

@ -0,0 +1,20 @@
use ratatui_core::layout::Constraint;
use ratatui_macros::{constraints, span};
fn main() {
constraints![,];
// TODO: Make this compiler error pass
let [a, b] = constraints![
== 1/2,
== 2,
];
assert_eq!(a, Constraint::Ratio(1, 2));
assert_eq!(b, Constraint::Length(2));
let [a, b, c] = constraints![ == 1, == 10%, == 2; 4];
let _ = span!(Modifier::BOLD, "hello world");
let _ = span!("hello", "hello world");
}

View File

@ -0,0 +1,58 @@
error: No rules expected the token `,` while trying to match the end of the macro
--> tests/ui/fails.rs:5:5
|
5 | constraints![,];
| ^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `$crate::constraints` which comes from the expansion of the macro `constraints` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unexpected end of macro invocation
--> tests/ui/fails.rs:8:18
|
8 | let [a, b] = constraints![
| __________________^
9 | | == 1/2,
10 | | == 2,
11 | | ];
| |_____^ missing tokens in macro arguments
|
note: while trying to match `==`
--> src/layout.rs
|
| (== $token:tt %) => {
| ^^
= note: this error originates in the macro `$crate::constraints` which comes from the expansion of the macro `constraints` (in Nightly builds, run with -Z macro-backtrace for more info)
error: no rules expected `;`
--> tests/ui/fails.rs:15:53
|
15 | let [a, b, c] = constraints![ == 1, == 10%, == 2; 4];
| ^ no rules expected this token in macro call
|
note: while trying to match `%`
--> src/layout.rs
|
| (== $token:tt %) => {
| ^
error: first parameter must be a formatting specifier followed by a comma OR a `Style` followed by a semicolon
--> tests/ui/fails.rs:17:13
|
17 | let _ = span!(Modifier::BOLD, "hello world");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `span` (in Nightly builds, run with -Z macro-backtrace for more info)
error: argument never used
--> tests/ui/fails.rs:19:28
|
19 | let _ = span!("hello", "hello world");
| ------- ^^^^^^^^^^^^^ argument never used
| |
| formatting specifier missing
error[E0527]: pattern requires 2 elements but array has 3
--> tests/ui/fails.rs:8:9
|
8 | let [a, b] = constraints![
| ^^^^^^ expected 3 elements

View File

@ -25,8 +25,9 @@ rustdoc-args = ["--cfg", "docsrs"]
#!
## By default, we enable the crossterm backend as this is a reasonable choice for most applications
## as it is supported on Linux/Mac/Windows systems. We also enable the `underline-color` feature
## which allows you to set the underline color of text.
default = ["crossterm", "underline-color", "all-widgets"]
## which allows you to set the underline color of text and the `macros` feature which provides
## some useful macros.
default = ["crossterm", "underline-color", "all-widgets", "macros"]
#! Generally an application will only use one backend, so you should only enable one of the following features:
## enables the [`CrosstermBackend`](backend::CrosstermBackend) backend and adds a dependency on [`crossterm`].
crossterm = ["dep:ratatui-crossterm"]
@ -52,6 +53,10 @@ scrolling-regions = [
"ratatui-termwiz?/scrolling-regions",
]
## enables the [`macros`](macros) module which provides some useful macros for creating spans,
## lines, text, and layouts
macros = ["dep:ratatui-macros"]
## enables all widgets.
all-widgets = ["widget-calendar"]
@ -102,6 +107,7 @@ itertools.workspace = true
palette = { version = "0.7.6", optional = true }
ratatui-core = { workspace = true }
ratatui-crossterm = { workspace = true, optional = true }
ratatui-macros = { workspace = true, optional = true }
ratatui-termwiz = { workspace = true, optional = true }
ratatui-widgets = { workspace = true }
serde = { workspace = true, optional = true }

View File

@ -335,6 +335,8 @@ pub use ratatui_core::{
/// re-export the `crossterm` crate so that users don't have to add it as a dependency
#[cfg(feature = "crossterm")]
pub use ratatui_crossterm::crossterm;
#[cfg(feature = "macros")]
pub use ratatui_macros as macros;
/// re-export the `termion` crate so that users don't have to add it as a dependency
#[cfg(all(not(windows), feature = "termion"))]
pub use ratatui_termion::termion;