mirror of
https://github.com/nushell/nushell
synced 2025-01-16 15:14:26 +00:00
Add path separator to char
; Update char to engine-p; List all names of all possible chars (#3470)
* Allow querying the current path separator * Convert char command to engine-p * Wrap char args into struct * Add --list option to char command This lists all the available character names, along with the character and its unicode points.
This commit is contained in:
parent
f075e2459d
commit
5ab4199d71
1 changed files with 167 additions and 93 deletions
|
@ -1,11 +1,108 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_engine::{FromValue, WholeStreamCommand};
|
use nu_engine::{FromValue, WholeStreamCommand};
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
|
||||||
use nu_source::Tagged;
|
use nu_source::Tagged;
|
||||||
|
|
||||||
|
use indexmap::indexmap;
|
||||||
|
use indexmap::map::IndexMap;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
pub struct Char;
|
pub struct Char;
|
||||||
|
|
||||||
|
struct CharArgs {
|
||||||
|
name: Option<Tagged<String>>,
|
||||||
|
rest: Vec<Value>,
|
||||||
|
list: bool,
|
||||||
|
unicode: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref CHAR_MAP: IndexMap<&'static str, String> = indexmap! {
|
||||||
|
// These are some regular characters that either can't used or
|
||||||
|
// it's just easier to use them like this.
|
||||||
|
"newline" => '\n'.to_string(),
|
||||||
|
"enter" => '\n'.to_string(),
|
||||||
|
"nl" => '\n'.to_string(),
|
||||||
|
"tab" => '\t'.to_string(),
|
||||||
|
"sp" => ' '.to_string(),
|
||||||
|
"space" => ' '.to_string(),
|
||||||
|
"pipe" => '|'.to_string(),
|
||||||
|
"left_brace" => '{'.to_string(),
|
||||||
|
"lbrace" => '{'.to_string(),
|
||||||
|
"right_brace" => '}'.to_string(),
|
||||||
|
"rbrace" => '}'.to_string(),
|
||||||
|
"left_paren" => '('.to_string(),
|
||||||
|
"lparen" => '('.to_string(),
|
||||||
|
"right_paren" => ')'.to_string(),
|
||||||
|
"rparen" => ')'.to_string(),
|
||||||
|
"left_bracket" => '['.to_string(),
|
||||||
|
"lbracket" => '['.to_string(),
|
||||||
|
"right_bracket" => ']'.to_string(),
|
||||||
|
"rbracket" => ']'.to_string(),
|
||||||
|
"sep" => std::path::MAIN_SEPARATOR.to_string(),
|
||||||
|
"separator" => std::path::MAIN_SEPARATOR.to_string(),
|
||||||
|
|
||||||
|
// Unicode names came from https://www.compart.com/en/unicode
|
||||||
|
// Private Use Area (U+E000-U+F8FF)
|
||||||
|
// Unicode can't be mixed with Ansi or it will break width calculation
|
||||||
|
"branch" => '\u{e0a0}'.to_string(), //
|
||||||
|
"segment" => '\u{e0b0}'.to_string(), //
|
||||||
|
|
||||||
|
"identical_to" => '\u{2261}'.to_string(), // ≡
|
||||||
|
"hamburger" => '\u{2261}'.to_string(), // ≡
|
||||||
|
"not_identical_to" => '\u{2262}'.to_string(), // ≢
|
||||||
|
"branch_untracked" => '\u{2262}'.to_string(), // ≢
|
||||||
|
"strictly_equivalent_to" => '\u{2263}'.to_string(), // ≣
|
||||||
|
"branch_identical" => '\u{2263}'.to_string(), // ≣
|
||||||
|
|
||||||
|
"upwards_arrow" => '\u{2191}'.to_string(), // ↑
|
||||||
|
"branch_ahead" => '\u{2191}'.to_string(), // ↑
|
||||||
|
"downwards_arrow" => '\u{2193}'.to_string(), // ↓
|
||||||
|
"branch_behind" => '\u{2193}'.to_string(), // ↓
|
||||||
|
"up_down_arrow" => '\u{2195}'.to_string(), // ↕
|
||||||
|
"branch_ahead_behind" => '\u{2195}'.to_string(), // ↕
|
||||||
|
|
||||||
|
"black_right_pointing_triangle" => '\u{25b6}'.to_string(), // ▶
|
||||||
|
"prompt" => '\u{25b6}'.to_string(), // ▶
|
||||||
|
"vector_or_cross_product" => '\u{2a2f}'.to_string(), // ⨯
|
||||||
|
"failed" => '\u{2a2f}'.to_string(), // ⨯
|
||||||
|
"high_voltage_sign" => '\u{26a1}'.to_string(), // ⚡
|
||||||
|
"elevated" => '\u{26a1}'.to_string(), // ⚡
|
||||||
|
"tilde" => '~'.to_string(), // ~
|
||||||
|
"twiddle" => '~'.to_string(), // ~
|
||||||
|
"squiggly" => '~'.to_string(), // ~
|
||||||
|
"home" => '~'.to_string(), // ~
|
||||||
|
"hash" => '#'.to_string(), // #
|
||||||
|
"hashtag" => '#'.to_string(), // #
|
||||||
|
"pound_sign" => '#'.to_string(), // #
|
||||||
|
"sharp" => '#'.to_string(), // #
|
||||||
|
"root" => '#'.to_string(), // #
|
||||||
|
|
||||||
|
// Weather symbols
|
||||||
|
"sun" => "☀️".to_string(),
|
||||||
|
"sunny" => "☀️".to_string(),
|
||||||
|
"sunrise" => "☀️".to_string(),
|
||||||
|
"moon" => "🌛".to_string(),
|
||||||
|
"cloudy" => "☁️".to_string(),
|
||||||
|
"cloud" => "☁️".to_string(),
|
||||||
|
"clouds" => "☁️".to_string(),
|
||||||
|
"rainy" => "🌦️".to_string(),
|
||||||
|
"rain" => "🌦️".to_string(),
|
||||||
|
"foggy" => "🌫️".to_string(),
|
||||||
|
"fog" => "🌫️".to_string(),
|
||||||
|
"mist" => '\u{2591}'.to_string(),
|
||||||
|
"haze" => '\u{2591}'.to_string(),
|
||||||
|
"snowy" => "❄️".to_string(),
|
||||||
|
"snow" => "❄️".to_string(),
|
||||||
|
"thunderstorm" => "🌩️".to_string(),
|
||||||
|
"thunder" => "🌩️".to_string(),
|
||||||
|
|
||||||
|
"bel" => '\x07'.to_string(), // Terminal Bell
|
||||||
|
"backspace" => '\x08'.to_string(), // Backspace
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl WholeStreamCommand for Char {
|
impl WholeStreamCommand for Char {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"char"
|
"char"
|
||||||
|
@ -13,17 +110,18 @@ impl WholeStreamCommand for Char {
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("char")
|
Signature::build("char")
|
||||||
.required(
|
.optional(
|
||||||
"character",
|
"character",
|
||||||
SyntaxShape::Any,
|
SyntaxShape::Any,
|
||||||
"the name of the character to output",
|
"the name of the character to output",
|
||||||
)
|
)
|
||||||
.rest(SyntaxShape::String, "multiple Unicode bytes")
|
.rest(SyntaxShape::String, "multiple Unicode bytes")
|
||||||
|
.switch("list", "List all supported character names", Some('l'))
|
||||||
.switch("unicode", "Unicode string i.e. 1f378", Some('u'))
|
.switch("unicode", "Unicode string i.e. 1f378", Some('u'))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Output special characters (eg. 'newline')."
|
"Output special characters (e.g., 'newline')."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -57,15 +155,32 @@ impl WholeStreamCommand for Char {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
|
let args_tag = args.call_info.name_tag.clone();
|
||||||
let args = args.evaluate_once()?;
|
let args = args.evaluate_once()?;
|
||||||
|
let args = CharArgs {
|
||||||
|
name: args.opt(0)?,
|
||||||
|
rest: args.rest(1)?,
|
||||||
|
list: args.has_flag("list"),
|
||||||
|
unicode: args.has_flag("unicode"),
|
||||||
|
};
|
||||||
|
|
||||||
let name: Tagged<String> = args.req(0)?;
|
if args.list {
|
||||||
let rest: Vec<Value> = args.rest(1)?;
|
Ok(CHAR_MAP
|
||||||
let unicode = args.has_flag("unicode");
|
.iter()
|
||||||
|
.map(move |(name, s)| {
|
||||||
if unicode {
|
let mut dict = TaggedDictBuilder::with_capacity(&args_tag, 2);
|
||||||
if !rest.is_empty() {
|
dict.insert_untagged("name", UntaggedValue::string(*name));
|
||||||
|
dict.insert_untagged("character", UntaggedValue::string(s));
|
||||||
|
let unicode_parts: Vec<String> =
|
||||||
|
s.chars().map(|c| format!("{:x}", c as u32)).collect();
|
||||||
|
dict.insert_untagged("unicode", UntaggedValue::string(unicode_parts.join(" ")));
|
||||||
|
dict.into_value()
|
||||||
|
})
|
||||||
|
.to_output_stream())
|
||||||
|
} else if let Some(name) = args.name {
|
||||||
|
if args.unicode {
|
||||||
|
if !args.rest.is_empty() {
|
||||||
// Setup a new buffer to put all the Unicode bytes in
|
// Setup a new buffer to put all the Unicode bytes in
|
||||||
let mut multi_byte = String::new();
|
let mut multi_byte = String::new();
|
||||||
// Get the first byte
|
// Get the first byte
|
||||||
|
@ -75,7 +190,7 @@ impl WholeStreamCommand for Char {
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
}
|
}
|
||||||
// Get the rest of the bytes
|
// Get the rest of the bytes
|
||||||
for byte_part in rest {
|
for byte_part in args.rest {
|
||||||
let byte_part: Tagged<String> = FromValue::from_value(&byte_part)?;
|
let byte_part: Tagged<String> = FromValue::from_value(&byte_part)?;
|
||||||
let decoded_char = string_to_unicode_char(&byte_part, &byte_part.tag);
|
let decoded_char = string_to_unicode_char(&byte_part, &byte_part.tag);
|
||||||
match decoded_char {
|
match decoded_char {
|
||||||
|
@ -83,15 +198,15 @@ impl WholeStreamCommand for Char {
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(ActionStream::one(ReturnSuccess::value(
|
Ok(OutputStream::one(
|
||||||
UntaggedValue::string(multi_byte).into_value(name.tag),
|
UntaggedValue::string(multi_byte).into_value(name.tag),
|
||||||
)))
|
))
|
||||||
} else {
|
} else {
|
||||||
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
|
let decoded_char = string_to_unicode_char(&name.item, &name.tag);
|
||||||
if let Ok(ch) = decoded_char {
|
if let Ok(ch) = decoded_char {
|
||||||
Ok(ActionStream::one(ReturnSuccess::value(
|
Ok(OutputStream::one(
|
||||||
UntaggedValue::string(ch).into_value(name.tag()),
|
UntaggedValue::string(ch).into_value(name.tag()),
|
||||||
)))
|
))
|
||||||
} else {
|
} else {
|
||||||
Err(ShellError::labeled_error(
|
Err(ShellError::labeled_error(
|
||||||
"error decoding Unicode character",
|
"error decoding Unicode character",
|
||||||
|
@ -103,9 +218,9 @@ impl WholeStreamCommand for Char {
|
||||||
} else {
|
} else {
|
||||||
let special_character = str_to_character(&name.item);
|
let special_character = str_to_character(&name.item);
|
||||||
if let Some(output) = special_character {
|
if let Some(output) = special_character {
|
||||||
Ok(ActionStream::one(ReturnSuccess::value(
|
Ok(OutputStream::one(
|
||||||
UntaggedValue::string(output).into_value(name.tag()),
|
UntaggedValue::string(output).into_value(name.tag()),
|
||||||
)))
|
))
|
||||||
} else {
|
} else {
|
||||||
Err(ShellError::labeled_error(
|
Err(ShellError::labeled_error(
|
||||||
"error finding named character",
|
"error finding named character",
|
||||||
|
@ -114,6 +229,13 @@ impl WholeStreamCommand for Char {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Err(ShellError::labeled_error(
|
||||||
|
"char requires the name of the character",
|
||||||
|
"missing name of the character",
|
||||||
|
&args_tag,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,55 +256,7 @@ fn string_to_unicode_char(s: &str, t: &Tag) -> Result<char, ShellError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn str_to_character(s: &str) -> Option<String> {
|
fn str_to_character(s: &str) -> Option<String> {
|
||||||
match s {
|
CHAR_MAP.get(s).map(|s| s.into())
|
||||||
// These are some regular characters that either can't used or
|
|
||||||
// it's just easier to use them like this.
|
|
||||||
"newline" | "enter" | "nl" => Some("\n".into()),
|
|
||||||
"tab" => Some("\t".into()),
|
|
||||||
"sp" | "space" => Some(" ".into()),
|
|
||||||
"pipe" => Some('|'.to_string()),
|
|
||||||
"left_brace" | "lbrace" => Some('{'.to_string()),
|
|
||||||
"right_brace" | "rbrace" => Some('}'.to_string()),
|
|
||||||
"left_paren" | "lparen" => Some('('.to_string()),
|
|
||||||
"right_paren" | "rparen" => Some(')'.to_string()),
|
|
||||||
"left_bracket" | "lbracket" => Some('['.to_string()),
|
|
||||||
"right_bracket" | "rbracket" => Some(']'.to_string()),
|
|
||||||
|
|
||||||
// Unicode names came from https://www.compart.com/en/unicode
|
|
||||||
// Private Use Area (U+E000-U+F8FF)
|
|
||||||
// Unicode can't be mixed with Ansi or it will break width calculation
|
|
||||||
"branch" => Some('\u{e0a0}'.to_string()), //
|
|
||||||
"segment" => Some('\u{e0b0}'.to_string()), //
|
|
||||||
|
|
||||||
"identical_to" | "hamburger" => Some('\u{2261}'.to_string()), // ≡
|
|
||||||
"not_identical_to" | "branch_untracked" => Some('\u{2262}'.to_string()), // ≢
|
|
||||||
"strictly_equivalent_to" | "branch_identical" => Some('\u{2263}'.to_string()), // ≣
|
|
||||||
|
|
||||||
"upwards_arrow" | "branch_ahead" => Some('\u{2191}'.to_string()), // ↑
|
|
||||||
"downwards_arrow" | "branch_behind" => Some('\u{2193}'.to_string()), // ↓
|
|
||||||
"up_down_arrow" | "branch_ahead_behind" => Some('\u{2195}'.to_string()), // ↕
|
|
||||||
|
|
||||||
"black_right_pointing_triangle" | "prompt" => Some('\u{25b6}'.to_string()), // ▶
|
|
||||||
"vector_or_cross_product" | "failed" => Some('\u{2a2f}'.to_string()), // ⨯
|
|
||||||
"high_voltage_sign" | "elevated" => Some('\u{26a1}'.to_string()), // ⚡
|
|
||||||
"tilde" | "twiddle" | "squiggly" | "home" => Some("~".into()), // ~
|
|
||||||
"hash" | "hashtag" | "pound_sign" | "sharp" | "root" => Some("#".into()), // #
|
|
||||||
|
|
||||||
// Weather symbols
|
|
||||||
"sun" | "sunny" | "sunrise" => Some("☀️".to_string()),
|
|
||||||
"moon" => Some("🌛".to_string()),
|
|
||||||
"cloudy" | "cloud" | "clouds" => Some("☁️".to_string()),
|
|
||||||
"rainy" | "rain" => Some("🌦️".to_string()),
|
|
||||||
"foggy" | "fog" => Some("🌫️".to_string()),
|
|
||||||
"mist" | "haze" => Some("\u{2591}".to_string()),
|
|
||||||
"snowy" | "snow" => Some("❄️".to_string()),
|
|
||||||
"thunderstorm" | "thunder" => Some("🌩️".to_string()),
|
|
||||||
|
|
||||||
"bel" => Some('\x07'.to_string()), // Terminal Bell
|
|
||||||
"backspace" => Some('\x08'.to_string()), // Backspace
|
|
||||||
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Reference in a new issue