Compare commits
5
Commits
ab3a818507
...
b9474ce6ef
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9474ce6ef
|
||
|
|
d328310880
|
||
|
|
5eb77ab467
|
||
|
|
a2c4f05c8c
|
||
|
|
89fadcca15
|
@@ -207,7 +207,7 @@ pub(in crate::config) const DEFAULT_USER_INTERACTION_INSTRUCTIONS: &str = indoc!
|
|||||||
## User Interaction
|
## User Interaction
|
||||||
|
|
||||||
You have built-in tools to interact with the user directly:
|
You have built-in tools to interact with the user directly:
|
||||||
- `user__ask --question \"...\" --options [\"A\", \"B\", \"C\"]`: Present a selection prompt. Returns the chosen option.
|
- `user__select --question \"...\" --options [\"A\", \"B\", \"C\"]`: Present a single-select list of named options. Use this — not `user__confirm` — whenever there are 2+ named options. Returns the chosen option.
|
||||||
- `user__confirm --question \"...\"`: Ask a yes/no question. Returns \"yes\" or \"no\".
|
- `user__confirm --question \"...\"`: Ask a yes/no question. Returns \"yes\" or \"no\".
|
||||||
- `user__input --question \"...\"`: Request free-form text input from the user.
|
- `user__input --question \"...\"`: Request free-form text input from the user.
|
||||||
- `user__checkbox --question \"...\" --options [\"A\", \"B\", \"C\"]`: Multi-select prompt. Returns an array of selected options.
|
- `user__checkbox --question \"...\" --options [\"A\", \"B\", \"C\"]`: Multi-select prompt. Returns an array of selected options.
|
||||||
|
|||||||
@@ -4784,7 +4784,7 @@ mod tests {
|
|||||||
let fns = ctx.select_functions(&role).unwrap();
|
let fns = ctx.select_functions(&role).unwrap();
|
||||||
let names: Vec<&str> = fns.iter().map(|f| f.name.as_str()).collect();
|
let names: Vec<&str> = fns.iter().map(|f| f.name.as_str()).collect();
|
||||||
assert!(names.contains(&"todo__init"));
|
assert!(names.contains(&"todo__init"));
|
||||||
assert!(names.contains(&"user__ask"));
|
assert!(names.contains(&"user__select"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -4848,7 +4848,7 @@ mod tests {
|
|||||||
|
|
||||||
let fns = ctx.select_functions(&role).unwrap();
|
let fns = ctx.select_functions(&role).unwrap();
|
||||||
let names: Vec<&str> = fns.iter().map(|f| f.name.as_str()).collect();
|
let names: Vec<&str> = fns.iter().map(|f| f.name.as_str()).collect();
|
||||||
assert!(names.contains(&"user__ask"));
|
assert!(names.contains(&"user__select"));
|
||||||
assert!(!names.contains(&"skill__list"));
|
assert!(!names.contains(&"skill__list"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4933,7 +4933,7 @@ mod tests {
|
|||||||
"teammate tools must survive an agent tool filter, got: {names:?}"
|
"teammate tools must survive an agent tool filter, got: {names:?}"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
names.contains(&"user__ask"),
|
names.contains(&"user__select"),
|
||||||
"user__ tools must survive an agent tool filter, got: {names:?}"
|
"user__ tools must survive an agent tool filter, got: {names:?}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-14
@@ -150,7 +150,9 @@ pub async fn eval_tool_calls(
|
|||||||
let dup_msg = format!("{{\"tool_call_loop_alert\":{}}}", msg.trim());
|
let dup_msg = format!("{{\"tool_call_loop_alert\":{}}}", msg.trim());
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
warning_text(format!("{}: ⚠️ Tool-call loop detected! ⚠️", call.name).as_str())
|
muted_warning_text(
|
||||||
|
format!("{}: ⚠️ Tool-call loop detected! ⚠️", call.name).as_str()
|
||||||
|
)
|
||||||
);
|
);
|
||||||
let val = json!(dup_msg);
|
let val = json!(dup_msg);
|
||||||
output.push(ToolResult::new(call, val));
|
output.push(ToolResult::new(call, val));
|
||||||
@@ -1088,10 +1090,8 @@ impl ToolCall {
|
|||||||
|
|
||||||
cmd_args.push(json_data.to_string());
|
cmd_args.push(json_data.to_string());
|
||||||
|
|
||||||
let prompt = format!("Call {cmd_name} {}", cmd_args.join(" "));
|
|
||||||
|
|
||||||
if *IS_STDOUT_TERMINAL && current_depth == 0 {
|
if *IS_STDOUT_TERMINAL && current_depth == 0 {
|
||||||
println!("{}", dimmed_text(&prompt));
|
println!("{}", format_call_log(&cmd_name, &cmd_args, &json_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = match cmd_name.as_str() {
|
let output = match cmd_name.as_str() {
|
||||||
@@ -1100,7 +1100,7 @@ impl ToolCall {
|
|||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
let error_msg = format!("MCP search failed: {e}");
|
let error_msg = format!("MCP search failed: {e}");
|
||||||
eprintln!("{}", warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
eprintln!("{}", muted_warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
||||||
json!({"tool_call_error": error_msg})
|
json!({"tool_call_error": error_msg})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1109,7 +1109,7 @@ impl ToolCall {
|
|||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
let error_msg = format!("MCP describe failed: {e}");
|
let error_msg = format!("MCP describe failed: {e}");
|
||||||
eprintln!("{}", warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
eprintln!("{}", muted_warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
||||||
json!({"tool_call_error": error_msg})
|
json!({"tool_call_error": error_msg})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1118,21 +1118,21 @@ impl ToolCall {
|
|||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
let error_msg = format!("MCP tool invocation failed: {e}");
|
let error_msg = format!("MCP tool invocation failed: {e}");
|
||||||
eprintln!("{}", warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
eprintln!("{}", muted_warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
||||||
json!({"tool_call_error": error_msg})
|
json!({"tool_call_error": error_msg})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ if cmd_name.starts_with(TODO_FUNCTION_PREFIX) => {
|
_ if cmd_name.starts_with(TODO_FUNCTION_PREFIX) => {
|
||||||
todo::handle_todo_tool(ctx, &cmd_name, &json_data).unwrap_or_else(|e| {
|
todo::handle_todo_tool(ctx, &cmd_name, &json_data).unwrap_or_else(|e| {
|
||||||
let error_msg = format!("Todo tool failed: {e}");
|
let error_msg = format!("Todo tool failed: {e}");
|
||||||
eprintln!("{}", warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
eprintln!("{}", muted_warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
||||||
json!({"tool_call_error": error_msg})
|
json!({"tool_call_error": error_msg})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ if cmd_name.starts_with(MEMORY_FUNCTION_PREFIX) => {
|
_ if cmd_name.starts_with(MEMORY_FUNCTION_PREFIX) => {
|
||||||
memory::handle_memory_tool(ctx, &cmd_name, &json_data).unwrap_or_else(|e| {
|
memory::handle_memory_tool(ctx, &cmd_name, &json_data).unwrap_or_else(|e| {
|
||||||
let error_msg = format!("Memory tool failed: {e}");
|
let error_msg = format!("Memory tool failed: {e}");
|
||||||
eprintln!("{}", warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
eprintln!("{}", muted_warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
||||||
json!({"tool_call_error": error_msg})
|
json!({"tool_call_error": error_msg})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1141,7 +1141,7 @@ impl ToolCall {
|
|||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
let error_msg = format!("Skill tool failed: {e}");
|
let error_msg = format!("Skill tool failed: {e}");
|
||||||
eprintln!("{}", warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
eprintln!("{}", muted_warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
||||||
json!({"tool_call_error": error_msg})
|
json!({"tool_call_error": error_msg})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1150,7 +1150,7 @@ impl ToolCall {
|
|||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
let error_msg = format!("Supervisor tool failed: {e}");
|
let error_msg = format!("Supervisor tool failed: {e}");
|
||||||
eprintln!("{}", warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
eprintln!("{}", muted_warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
||||||
json!({"tool_call_error": error_msg})
|
json!({"tool_call_error": error_msg})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1159,7 +1159,7 @@ impl ToolCall {
|
|||||||
.await
|
.await
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
let error_msg = format!("User interaction failed: {e}");
|
let error_msg = format!("User interaction failed: {e}");
|
||||||
eprintln!("{}", warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
eprintln!("{}", muted_warning_text(&format!("⚠️ {error_msg} ⚠️")));
|
||||||
json!({"tool_call_error": error_msg})
|
json!({"tool_call_error": error_msg})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1419,7 +1419,10 @@ pub fn run_llm_function(
|
|||||||
let stderr = String::from_utf8_lossy(&stderr_bytes).trim().to_string();
|
let stderr = String::from_utf8_lossy(&stderr_bytes).trim().to_string();
|
||||||
let stdout = String::from_utf8_lossy(&stdout_bytes).trim().to_string();
|
let stdout = String::from_utf8_lossy(&stdout_bytes).trim().to_string();
|
||||||
let tool_error_message = format!("Tool call '{command_name}' exited with code {exit_code}");
|
let tool_error_message = format!("Tool call '{command_name}' exited with code {exit_code}");
|
||||||
eprintln!("{}", warning_text(&format!("⚠️ {tool_error_message} ⚠️")));
|
eprintln!(
|
||||||
|
"{}",
|
||||||
|
muted_warning_text(&format!("⚠️ {tool_error_message} ⚠️"))
|
||||||
|
);
|
||||||
let mut error_json = json!({"tool_call_error": tool_error_message});
|
let mut error_json = json!({"tool_call_error": tool_error_message});
|
||||||
if !stderr.is_empty() {
|
if !stderr.is_empty() {
|
||||||
error_json["stderr"] = json!(stderr);
|
error_json["stderr"] = json!(stderr);
|
||||||
@@ -1552,6 +1555,47 @@ impl ToolCallTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format_call_log(cmd_name: &str, cmd_args: &[String], json_data: &serde_json::Value) -> String {
|
||||||
|
if *NO_COLOR {
|
||||||
|
return format!("Call {cmd_name} {}", cmd_args.join(" "));
|
||||||
|
}
|
||||||
|
let prefix_args = &cmd_args[..cmd_args.len().saturating_sub(1)];
|
||||||
|
let prefix = if prefix_args.is_empty() {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
format!("{} ", dimmed_text(&prefix_args.join(" ")))
|
||||||
|
};
|
||||||
|
format!(
|
||||||
|
"{}{} {}{}",
|
||||||
|
dimmed_text("Call "),
|
||||||
|
cyan_bold_text(cmd_name),
|
||||||
|
prefix,
|
||||||
|
format_json_colored_keys(json_data),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_json_colored_keys(value: &serde_json::Value) -> String {
|
||||||
|
let serde_json::Value::Object(map) = value else {
|
||||||
|
return dimmed_text(&value.to_string());
|
||||||
|
};
|
||||||
|
if map.is_empty() {
|
||||||
|
return dimmed_text("{}");
|
||||||
|
}
|
||||||
|
let pairs: Vec<String> = map
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
let key = magenta_text(&format!("\"{k}\""));
|
||||||
|
format!("{}{}", key, dimmed_text(&format!(": {v}")))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
format!(
|
||||||
|
"{}{}{}",
|
||||||
|
dimmed_text("{"),
|
||||||
|
pairs.join(&dimmed_text(", ")),
|
||||||
|
dimmed_text("}")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -1836,7 +1880,7 @@ mod tests {
|
|||||||
fn functions_append_user_interaction_adds_declarations() {
|
fn functions_append_user_interaction_adds_declarations() {
|
||||||
let mut f = Functions::default();
|
let mut f = Functions::default();
|
||||||
f.append_user_interaction_functions();
|
f.append_user_interaction_functions();
|
||||||
assert!(f.contains("user__ask"));
|
assert!(f.contains("user__select"));
|
||||||
assert!(f.contains("user__confirm"));
|
assert!(f.contains("user__confirm"));
|
||||||
assert!(f.contains("user__input"));
|
assert!(f.contains("user__input"));
|
||||||
assert!(f.contains("user__checkbox"));
|
assert!(f.contains("user__checkbox"));
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ const CUSTOM_MULTI_CHOICE_ANSWER_OPTION: &str = "Other (custom)";
|
|||||||
pub fn user_interaction_function_declarations() -> Vec<FunctionDeclaration> {
|
pub fn user_interaction_function_declarations() -> Vec<FunctionDeclaration> {
|
||||||
vec![
|
vec![
|
||||||
FunctionDeclaration {
|
FunctionDeclaration {
|
||||||
name: format!("{USER_FUNCTION_PREFIX}ask"),
|
name: format!("{USER_FUNCTION_PREFIX}select"),
|
||||||
description: "Ask the user to select one option from a list. Returns the selected option. Indicate the recommended choice if there is one.".to_string(),
|
description: "Present a list of named options and ask the user to pick exactly one. \
|
||||||
|
Indicate the recommended choice if there is one. \
|
||||||
|
Use this — not `confirm` — whenever there are 2+ named options to choose \
|
||||||
|
between. Returns the selected option.".to_string(),
|
||||||
parameters: JsonSchema {
|
parameters: JsonSchema {
|
||||||
type_value: Some("object".to_string()),
|
type_value: Some("object".to_string()),
|
||||||
properties: Some(IndexMap::from([
|
properties: Some(IndexMap::from([
|
||||||
@@ -50,7 +53,9 @@ pub fn user_interaction_function_declarations() -> Vec<FunctionDeclaration> {
|
|||||||
},
|
},
|
||||||
FunctionDeclaration {
|
FunctionDeclaration {
|
||||||
name: format!("{USER_FUNCTION_PREFIX}confirm"),
|
name: format!("{USER_FUNCTION_PREFIX}confirm"),
|
||||||
description: "Ask the user a yes/no question. Returns \"yes\" or \"no\".".to_string(),
|
description: "Ask a genuinely binary yes/no question with no other choices. Do NOT \
|
||||||
|
use for \"A or B?\" situations — use `select` instead. Returns \"yes\" \
|
||||||
|
or \"no\".".to_string(),
|
||||||
parameters: JsonSchema {
|
parameters: JsonSchema {
|
||||||
type_value: Some("object".to_string()),
|
type_value: Some("object".to_string()),
|
||||||
properties: Some(IndexMap::from([(
|
properties: Some(IndexMap::from([(
|
||||||
@@ -68,7 +73,8 @@ pub fn user_interaction_function_declarations() -> Vec<FunctionDeclaration> {
|
|||||||
},
|
},
|
||||||
FunctionDeclaration {
|
FunctionDeclaration {
|
||||||
name: format!("{USER_FUNCTION_PREFIX}input"),
|
name: format!("{USER_FUNCTION_PREFIX}input"),
|
||||||
description: "Ask the user for free-form text input. Returns the text entered.".to_string(),
|
description: "Collect free-form text from the user when no predefined options exist. \
|
||||||
|
Returns the text entered.".to_string(),
|
||||||
parameters: JsonSchema {
|
parameters: JsonSchema {
|
||||||
type_value: Some("object".to_string()),
|
type_value: Some("object".to_string()),
|
||||||
properties: Some(IndexMap::from([(
|
properties: Some(IndexMap::from([(
|
||||||
@@ -86,7 +92,9 @@ pub fn user_interaction_function_declarations() -> Vec<FunctionDeclaration> {
|
|||||||
},
|
},
|
||||||
FunctionDeclaration {
|
FunctionDeclaration {
|
||||||
name: format!("{USER_FUNCTION_PREFIX}checkbox"),
|
name: format!("{USER_FUNCTION_PREFIX}checkbox"),
|
||||||
description: "Ask the user to select one or more options from a list. Returns an array of selected options.".to_string(),
|
description: "Ask the user to pick one or more options from a list (multi-select). \
|
||||||
|
Use when multiple answers are valid simultaneously. Returns an array \
|
||||||
|
of selected options.".to_string(),
|
||||||
parameters: JsonSchema {
|
parameters: JsonSchema {
|
||||||
type_value: Some("object".to_string()),
|
type_value: Some("object".to_string()),
|
||||||
properties: Some(IndexMap::from([
|
properties: Some(IndexMap::from([
|
||||||
@@ -139,7 +147,7 @@ pub async fn handle_user_tool(
|
|||||||
|
|
||||||
fn handle_direct(action: &str, args: &Value) -> Result<Value> {
|
fn handle_direct(action: &str, args: &Value) -> Result<Value> {
|
||||||
match action {
|
match action {
|
||||||
"ask" => handle_direct_ask(args),
|
"select" => handle_direct_ask(args),
|
||||||
"confirm" => handle_direct_confirm(args),
|
"confirm" => handle_direct_confirm(args),
|
||||||
"input" => handle_direct_input(args),
|
"input" => handle_direct_input(args),
|
||||||
"checkbox" => handle_direct_checkbox(args),
|
"checkbox" => handle_direct_checkbox(args),
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ impl ApprovalNodeExecutor {
|
|||||||
&json!({ "question": question, "options": node.options }),
|
&json!({ "question": question, "options": node.options }),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context("user__ask failed")?;
|
.context("user__select failed")?;
|
||||||
|
|
||||||
if let Some(err) = response.get("error").and_then(Value::as_str) {
|
if let Some(err) = response.get("error").and_then(Value::as_str) {
|
||||||
bail!("Approval interaction failed: {err}");
|
bail!("Approval interaction failed: {err}");
|
||||||
|
|||||||
+5
-1
@@ -210,7 +210,11 @@ async fn main() -> Result<()> {
|
|||||||
{
|
{
|
||||||
let app = &*ctx.app.config;
|
let app = &*ctx.app.config;
|
||||||
if app.highlight {
|
if app.highlight {
|
||||||
set_global_render_config(prompt_theme(app.render_options()?)?)
|
let render_opts = app.render_options()?;
|
||||||
|
if let Some(ref theme) = render_opts.theme {
|
||||||
|
utils::init_tool_colors(theme);
|
||||||
|
}
|
||||||
|
set_global_render_config(prompt_theme(render_opts)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+64
-5
@@ -31,9 +31,12 @@ use anyhow::{Context, Result};
|
|||||||
use fancy_regex::Regex;
|
use fancy_regex::Regex;
|
||||||
use fuzzy_matcher::{FuzzyMatcher, skim::SkimMatcherV2};
|
use fuzzy_matcher::{FuzzyMatcher, skim::SkimMatcherV2};
|
||||||
use is_terminal::IsTerminal;
|
use is_terminal::IsTerminal;
|
||||||
|
use nu_ansi_term::Color;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::sync::LazyLock;
|
use std::sync::{LazyLock, OnceLock};
|
||||||
use std::{cmp, env, path::PathBuf, process};
|
use std::{cmp, env, path::PathBuf, process};
|
||||||
|
use syntect::highlighting::{Highlighter, Theme};
|
||||||
|
use syntect::parsing::Scope;
|
||||||
|
|
||||||
pub static CODE_BLOCK_RE: LazyLock<Regex> =
|
pub static CODE_BLOCK_RE: LazyLock<Regex> =
|
||||||
LazyLock::new(|| Regex::new(r"(?ms)```\w*(.*)```").unwrap());
|
LazyLock::new(|| Regex::new(r"(?ms)```\w*(.*)```").unwrap());
|
||||||
@@ -48,6 +51,33 @@ pub static NO_COLOR: LazyLock<bool> = LazyLock::new(|| {
|
|||||||
|| !*IS_STDOUT_TERMINAL
|
|| !*IS_STDOUT_TERMINAL
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static TOOL_DIM_COLOR: OnceLock<Color> = OnceLock::new();
|
||||||
|
static TOOL_FN_COLOR: OnceLock<Color> = OnceLock::new();
|
||||||
|
static TOOL_KEY_COLOR: OnceLock<Color> = OnceLock::new();
|
||||||
|
static TOOL_WARN_COLOR: OnceLock<Color> = OnceLock::new();
|
||||||
|
|
||||||
|
pub fn init_tool_colors(theme: &Theme) {
|
||||||
|
fn resolve(theme: &Theme, scope_str: &str) -> Option<Color> {
|
||||||
|
let scope = Scope::new(scope_str).ok()?;
|
||||||
|
let style = Highlighter::new(theme).style_mod_for_stack(&[scope]);
|
||||||
|
let fg = style.foreground.or(theme.settings.foreground)?;
|
||||||
|
let mute = |ch: u8| -> u8 { ((ch as u16 + 128) / 2) as u8 };
|
||||||
|
Some(Color::Rgb(mute(fg.r), mute(fg.g), mute(fg.b)))
|
||||||
|
}
|
||||||
|
if let Some(c) = resolve(theme, "comment") {
|
||||||
|
let _ = TOOL_DIM_COLOR.set(c);
|
||||||
|
}
|
||||||
|
if let Some(c) = resolve(theme, "support.function") {
|
||||||
|
let _ = TOOL_FN_COLOR.set(c);
|
||||||
|
}
|
||||||
|
if let Some(c) = resolve(theme, "constant.numeric") {
|
||||||
|
let _ = TOOL_KEY_COLOR.set(c);
|
||||||
|
}
|
||||||
|
if let Some(c) = resolve(theme, "string") {
|
||||||
|
let _ = TOOL_WARN_COLOR.set(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn now() -> String {
|
pub fn now() -> String {
|
||||||
chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, false)
|
chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, false)
|
||||||
}
|
}
|
||||||
@@ -141,14 +171,22 @@ pub fn indent_text<T: ToString>(s: T, size: usize) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn error_text(input: &str) -> String {
|
pub fn error_text(input: &str) -> String {
|
||||||
color_text(input, nu_ansi_term::Color::Red)
|
color_text(input, Color::Red)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn warning_text(input: &str) -> String {
|
pub fn warning_text(input: &str) -> String {
|
||||||
color_text(input, nu_ansi_term::Color::Yellow)
|
color_text(input, Color::Yellow)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn color_text(input: &str, color: nu_ansi_term::Color) -> String {
|
pub fn muted_warning_text(input: &str) -> String {
|
||||||
|
if *NO_COLOR {
|
||||||
|
return input.to_string();
|
||||||
|
}
|
||||||
|
let color = TOOL_WARN_COLOR.get().copied().unwrap_or(Color::Fixed(136));
|
||||||
|
color.paint(input).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn color_text(input: &str, color: Color) -> String {
|
||||||
if *NO_COLOR {
|
if *NO_COLOR {
|
||||||
return input.to_string();
|
return input.to_string();
|
||||||
}
|
}
|
||||||
@@ -162,7 +200,28 @@ pub fn dimmed_text(input: &str) -> String {
|
|||||||
if *NO_COLOR {
|
if *NO_COLOR {
|
||||||
return input.to_string();
|
return input.to_string();
|
||||||
}
|
}
|
||||||
nu_ansi_term::Style::new().dimmed().paint(input).to_string()
|
let color = TOOL_DIM_COLOR.get().copied().unwrap_or(Color::Fixed(243));
|
||||||
|
color.paint(input).to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cyan_bold_text(input: &str) -> String {
|
||||||
|
if *NO_COLOR {
|
||||||
|
return input.to_string();
|
||||||
|
}
|
||||||
|
let color = TOOL_FN_COLOR.get().copied().unwrap_or(Color::Fixed(73));
|
||||||
|
nu_ansi_term::Style::new()
|
||||||
|
.fg(color)
|
||||||
|
.bold()
|
||||||
|
.paint(input)
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn magenta_text(input: &str) -> String {
|
||||||
|
if *NO_COLOR {
|
||||||
|
return input.to_string();
|
||||||
|
}
|
||||||
|
let color = TOOL_KEY_COLOR.get().copied().unwrap_or(Color::Fixed(133));
|
||||||
|
color.paint(input).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn multiline_text(input: &str) -> String {
|
pub fn multiline_text(input: &str) -> String {
|
||||||
|
|||||||
Reference in New Issue
Block a user