Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ unicode-width = "0.2.2"
strip-ansi-escapes = "0.2.1"
strsim = "0.11"
insta = "1"
supports-color = "3"
tempfile = "3"

# Uncomment for local development with facet
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ Ok(())

The entry point of figue is [`builder`] — let yourself be guided from there.

## Color

figue uses [facet-color](https://docs.rs/facet-color) for coloring output.

## Contributing

Run `install/hooks.sh` to install pre-commit and pre-push hooks.
Run `hooks/install.sh` to install pre-commit and pre-push hooks.

## Sponsors

Expand Down
6 changes: 5 additions & 1 deletion README.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ Ok(())

The entry point of figue is [`builder`] — let yourself be guided from there.

## Color

figue uses [facet-color](https://docs.rs/facet-color) for coloring output.

## Contributing

Run `install/hooks.sh` to install pre-commit and pre-push hooks.
Run `hooks/install.sh` to install pre-commit and pre-push hooks.

## Sponsors

Expand Down
1 change: 1 addition & 0 deletions crates/figue/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ ariadne.workspace = true
unicode-width.workspace = true
strip-ansi-escapes.workspace = true
strsim.workspace = true
supports-color.workspace = true

[dev-dependencies]
facet-format = { workspace = true, features = ["tracing"] }
Expand Down
5 changes: 5 additions & 0 deletions crates/figue/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ Ok(())

The entry point of figue is [`builder`] — let yourself be guided from there.

## Color

Color is enabled by default if the terminal supports it. It is disabled when the
[`NO_COLOR`](https://no-color.org) environment variable is set.

## Sponsors

Thanks to all individual sponsors:
Expand Down
5 changes: 5 additions & 0 deletions crates/figue/README.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ Ok(())

The entry point of figue is [`builder`] — let yourself be guided from there.

## Color

Color is enabled by default if the terminal supports it. It is disabled when the
[`NO_COLOR`](https://no-color.org) environment variable is set.

## Sponsors

Thanks to all individual sponsors:
Expand Down
14 changes: 14 additions & 0 deletions crates/figue/src/color.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use std::env::var_os;
use supports_color::Stream;

/// Determine if output should be colored.
///
/// This respects the [`NO_COLOR`](https://no-color.org) and [`FORCE_COLOR`] environment variables.
pub fn should_use_color() -> bool {
// Don't use color when creating snapshots for `insta`. Color isn't disabled for testing in general
// to allow coloring to be tested.
var_os("INSTA_UPDATE").is_none()
&& var_os("INSTA_WORKSPACE").is_none()
&& var_os("INSTA_SNAPSHOT_UPDATE").is_none()
&& supports_color::on(Stream::Stdout).is_some()
}
4 changes: 3 additions & 1 deletion crates/figue/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use std::string::String;
use std::vec::Vec;

use crate::builder::Config;
use crate::color::should_use_color;
use crate::completions::{Shell, generate_completions_for_shape};
use crate::config_value::ConfigValue;
use crate::config_value_parser::{fill_defaults_from_schema, from_config_value};
Expand Down Expand Up @@ -969,7 +970,7 @@ impl ariadne::Cache<()> for NamedSource {
impl DriverReport {
/// Render the report using Ariadne for pretty error display.
pub fn render_pretty(&self) -> String {
use ariadne::{Color, Label, Report, ReportKind, Source};
use ariadne::{Color, Config, Label, Report, ReportKind, Source};

if self.diagnostics.is_empty() {
return String::new();
Expand Down Expand Up @@ -1027,6 +1028,7 @@ impl DriverReport {

let label_message = diagnostic.label.as_deref().unwrap_or(&diagnostic.message);
let report = Report::build(report_kind, span.clone())
.with_config(Config::default().with_color(should_use_color()))
.with_message(&diagnostic.message)
.with_label(
Label::new(span)
Expand Down
102 changes: 76 additions & 26 deletions crates/figue/src/dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{
schema::{ConfigValueSchema, Schema},
};
use owo_colors::OwoColorize;
use owo_colors::Stream::Stdout;
use std::collections::HashMap;
use std::io::Write;
use unicode_width::UnicodeWidthStr;
Expand Down Expand Up @@ -118,7 +119,7 @@ pub(crate) fn dump_config_with_schema(
writeln!(
w,
"Some values were truncated. To show full values, rerun with {}=1",
"FACET_ARGS_BLAST_IT".yellow()
"FACET_ARGS_BLAST_IT".if_supports_color(Stdout, |text| text.yellow())
)
.ok();
}
Expand Down Expand Up @@ -181,12 +182,19 @@ fn write_sources_header(w: &mut impl Write, file_resolution: &FileResolution, sc
};

let colored_path = match path_info.status {
FilePathStatus::Picked => path_str.magenta().to_string(),
_ => path_str.dimmed().to_string(),
FilePathStatus::Picked => path_str
.if_supports_color(Stdout, |text| text.magenta())
.to_string(),
_ => path_str
.if_supports_color(Stdout, |text| text.dimmed())
.to_string(),
};

let colored_status = match path_info.status {
FilePathStatus::Picked => status_label.to_string(),
_ => status_label.dimmed().to_string(),
_ => status_label
.if_supports_color(Stdout, |text| text.dimmed())
.to_string(),
};

writeln!(
Expand All @@ -207,7 +215,13 @@ fn write_sources_header(w: &mut impl Write, file_resolution: &FileResolution, sc
current_source += 1;
let is_last_source = current_source == sources_count;
let branch = if is_last_source { "└─ " } else { "├─ " };
writeln!(w, "{}env {}", branch, format!("${}__*", prefix).yellow()).ok();
writeln!(
w,
"{}env {}",
branch,
format!("${}__*", prefix).if_supports_color(Stdout, |text| text.yellow())
)
.ok();
}

{
Expand All @@ -218,7 +232,7 @@ fn write_sources_header(w: &mut impl Write, file_resolution: &FileResolution, sc
w,
"{}cli {}",
branch,
format!("--{}.*", config_field_name).cyan()
format!("--{}.*", config_field_name).if_supports_color(Stdout, |text| text.cyan())
)
.ok();
}
Expand Down Expand Up @@ -450,37 +464,51 @@ fn build_entry_from_schema(
(ConfigValue::String(sourced), ConfigValueSchema::Leaf(_)) => {
let formatted = if is_sensitive {
format!("🔒 [REDACTED ({} bytes)]", sourced.value.len())
.bright_magenta()
.if_supports_color(Stdout, |text| text.bright_magenta())
.to_string()
} else {
let escaped = sourced.value.replace('\n', "↵");
let (truncated, _) = truncate_middle(&escaped, opts.max_string_length);
truncated.green().to_string()
truncated
.if_supports_color(Stdout, |text| text.green())
.to_string()
};
DumpEntry::leaf(key, formatted, format_provenance(&sourced.provenance))
}
(ConfigValue::Integer(sourced), ConfigValueSchema::Leaf(_)) => DumpEntry::leaf(
key,
sourced.value.to_string().blue().to_string(),
sourced
.value
.if_supports_color(Stdout, |value| value.blue())
.to_string(),
format_provenance(&sourced.provenance),
),
(ConfigValue::Float(sourced), ConfigValueSchema::Leaf(_)) => DumpEntry::leaf(
key,
sourced.value.to_string().bright_blue().to_string(),
sourced
.value
.if_supports_color(Stdout, |value| value.bright_blue())
.to_string(),
format_provenance(&sourced.provenance),
),
(ConfigValue::Bool(sourced), ConfigValueSchema::Leaf(_)) => DumpEntry::leaf(
key,
if sourced.value {
"true".green().to_string()
"true"
.if_supports_color(Stdout, |text| text.green())
.to_string()
} else {
"false".red().to_string()
"false"
.if_supports_color(Stdout, |text| text.red())
.to_string()
},
format_provenance(&sourced.provenance),
),
(ConfigValue::Null(sourced), ConfigValueSchema::Leaf(_)) => DumpEntry::leaf(
key,
"null".bright_black().to_string(),
"null"
.if_supports_color(Stdout, |text| text.bright_black())
.to_string(),
format_provenance(&sourced.provenance),
),

Expand Down Expand Up @@ -511,37 +539,51 @@ fn build_leaf_entry(
ConfigValue::String(sourced) => {
let formatted = if is_sensitive {
format!("🔒 [REDACTED ({} bytes)]", sourced.value.len())
.bright_magenta()
.if_supports_color(Stdout, |text| text.bright_magenta())
.to_string()
} else {
let escaped = sourced.value.replace('\n', "↵");
let (truncated, _) = truncate_middle(&escaped, opts.max_string_length);
truncated.green().to_string()
truncated
.if_supports_color(Stdout, |text| text.green())
.to_string()
};
DumpEntry::leaf(key, formatted, format_provenance(&sourced.provenance))
}
ConfigValue::Integer(sourced) => DumpEntry::leaf(
key,
sourced.value.to_string().blue().to_string(),
sourced
.value
.if_supports_color(Stdout, |text| text.blue())
.to_string(),
format_provenance(&sourced.provenance),
),
ConfigValue::Float(sourced) => DumpEntry::leaf(
key,
sourced.value.to_string().bright_blue().to_string(),
sourced
.value
.if_supports_color(Stdout, |text| text.bright_blue())
.to_string(),
format_provenance(&sourced.provenance),
),
ConfigValue::Bool(sourced) => DumpEntry::leaf(
key,
if sourced.value {
"true".green().to_string()
"true"
.if_supports_color(Stdout, |text| text.green())
.to_string()
} else {
"false".red().to_string()
"false"
.if_supports_color(Stdout, |text| text.red())
.to_string()
},
format_provenance(&sourced.provenance),
),
ConfigValue::Null(sourced) => DumpEntry::leaf(
key,
"null".bright_black().to_string(),
"null"
.if_supports_color(Stdout, |text| text.bright_black())
.to_string(),
format_provenance(&sourced.provenance),
),
ConfigValue::Object(sourced) => {
Expand Down Expand Up @@ -669,9 +711,9 @@ fn render_entries_with_prefix(
"{}{}{} {}{} {}",
full_prefix,
entry.key,
key_pad.bright_black(),
key_pad.if_supports_color(Stdout, |text| text.bright_black()),
line,
val_pad.bright_black(),
val_pad.if_supports_color(Stdout, |text| text.bright_black()),
entry.provenance,
)
.ok();
Expand Down Expand Up @@ -718,17 +760,25 @@ fn render_entries_with_prefix(

fn format_provenance(prov: &Option<Provenance>) -> String {
match prov {
Some(Provenance::Cli { arg, .. }) => arg.cyan().to_string(),
Some(Provenance::Env { var, .. }) => format!("${}", var).yellow().to_string(),
Some(Provenance::Cli { arg, .. }) => arg
.if_supports_color(Stdout, |text| text.cyan())
.to_string(),
Some(Provenance::Env { var, .. }) => format!("${}", var)
.if_supports_color(Stdout, |text| text.yellow())
.to_string(),
Some(Provenance::File { file, offset, .. }) => {
let line_num = calculate_line_number(&file.contents, *offset);
let filename = std::path::Path::new(file.path.as_str())
.file_name()
.and_then(|n| n.to_str())
.unwrap_or(file.path.as_str());
format!("{}:{}", filename, line_num).magenta().to_string()
format!("{}:{}", filename, line_num)
.if_supports_color(Stdout, |text| text.magenta())
.to_string()
}
Some(Provenance::Default) => "DEFAULT".bright_black().to_string(),
Some(Provenance::Default) => "DEFAULT"
.if_supports_color(Stdout, |text| text.bright_black())
.to_string(),
None => String::new(),
}
}
Expand Down
6 changes: 5 additions & 1 deletion crates/figue/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,8 @@ impl fmt::Display for ArgsError {

mod ariadne_impl {
use super::*;
use ariadne::{Color, Label, Report, ReportKind, Source};
use crate::color::should_use_color;
use ariadne::{Color, Config, Label, Report, ReportKind, Source};
use facet_pretty::{PathSegment, format_shape_with_spans};
use std::borrow::Cow;

Expand All @@ -619,6 +620,7 @@ mod ariadne_impl {
// Skip help requests - they're not real errors
if self.is_help_request() {
return Report::build(ReportKind::Custom("Help", Color::Cyan), 0..0)
.with_config(Config::default().with_color(should_use_color()))
.with_message(self.help_text().unwrap_or(""))
.finish();
}
Expand All @@ -631,6 +633,7 @@ mod ariadne_impl {
let span = field_span.key.0..field_span.value.1;

let mut builder = Report::build(ReportKind::Error, span.clone())
.with_config(Config::default().with_color(should_use_color()))
.with_code(self.inner.kind.code())
.with_message(self.inner.kind.label());

Expand Down Expand Up @@ -677,6 +680,7 @@ mod ariadne_impl {
let range = span.start..(span.start + span.len);

let mut builder = Report::build(ReportKind::Error, range.clone())
.with_config(Config::default().with_color(should_use_color()))
.with_code(self.inner.kind.code())
.with_message(self.inner.kind.label());

Expand Down
Loading