diff --git a/Cargo.lock b/Cargo.lock index 5ba1ed0793..c7d6cea2d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1689,6 +1689,7 @@ dependencies = [ "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", "subprocess 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "sys-info 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", "sysinfo 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index f0789c3375..e47ccfcfe0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,7 @@ tempfile = "3.1.0" image = "0.21.2" semver = "0.9.0" uuid = {version = "0.7.4", features = [ "v4", "serde" ]} +syntect = "3.2.0" [dev-dependencies] pretty_assertions = "0.6.1" diff --git a/assets/themes.bin b/assets/themes.bin new file mode 100644 index 0000000000..23ad913280 Binary files /dev/null and b/assets/themes.bin differ diff --git a/src/cli.rs b/src/cli.rs index 741f6e5739..bde6f770ea 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -494,7 +494,7 @@ fn classify_command( Ok(ClassifiedCommand::Internal(InternalCommand { command, name_span: Some(head.span().clone()), - span_sources: context.span_sources.clone(), + source_map: context.source_map.clone(), args, })) } diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index e9f6599f8b..d13d324f94 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -1,7 +1,9 @@ use crate::commands::command::SinkCommandArgs; +use crate::context::{SourceMap, SpanSource}; use crate::errors::ShellError; use crate::format::GenericView; use crate::prelude::*; +use std::path::Path; pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { if args.input.len() > 0 { @@ -11,6 +13,8 @@ pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { } = args.input[0] { args.ctx.get_sink("binaryview").run(args)?; + } else if is_single_text_value(&args.input) { + view_text_value(&args.input[0], &args.source_map); } else if equal_shapes(&args.input) { args.ctx.get_sink("table").run(args)?; } else { @@ -44,3 +48,76 @@ fn equal_shapes(input: &Vec>) -> bool { true } + +fn is_single_text_value(input: &Vec>) -> bool { + if input.len() != 1 { + return false; + } + if let Spanned { + item: Value::Primitive(Primitive::String(_)), + .. + } = input[0] + { + true + } else { + false + } +} + +fn view_text_value(value: &Spanned, source_map: &SourceMap) { + match value { + Spanned { + item: Value::Primitive(Primitive::String(s)), + span, + } => { + let source = span.source.map(|x| source_map.get(&x)).flatten(); + + if let Some(source) = source { + match source { + SpanSource::File(file) => { + let path = Path::new(file); + match path.extension() { + Some(extension) => { + use syntect::easy::HighlightLines; + use syntect::highlighting::{Style, ThemeSet}; + use syntect::parsing::SyntaxSet; + use syntect::util::{as_24_bit_terminal_escaped, LinesWithEndings}; + + // Load these once at the start of your program + let ps = SyntaxSet::load_defaults_newlines(); + + if let Some(syntax) = + ps.find_syntax_by_extension(extension.to_str().unwrap()) + { + let ts: ThemeSet = syntect::dumps::from_binary(include_bytes!( + "../../assets/themes.bin" + )); + let mut h = + HighlightLines::new(syntax, &ts.themes["OneHalfDark"]); + + for line in LinesWithEndings::from(s) { + let ranges: Vec<(Style, &str)> = h.highlight(line, &ps); + let escaped = + as_24_bit_terminal_escaped(&ranges[..], false); + print!("{}", escaped); + } + } else { + println!("{}", s); + } + } + _ => { + println!("{}", s); + } + } + } + _ => { + println!("{}", s); + } + } + } else { + println!("{}", s); + } + } + _ => {} + } +} diff --git a/src/commands/classified.rs b/src/commands/classified.rs index b6f2fe9757..da4ce706cc 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -1,16 +1,14 @@ use crate::commands::command::Sink; -use crate::context::SpanSource; +use crate::context::SourceMap; use crate::parser::{registry::Args, Span, Spanned, TokenNode}; use crate::prelude::*; use bytes::{BufMut, BytesMut}; use futures::stream::StreamExt; use futures_codec::{Decoder, Encoder, Framed}; use log::{log_enabled, trace}; -use std::collections::HashMap; use std::io::{Error, ErrorKind}; use std::sync::Arc; use subprocess::Exec; -use uuid::Uuid; /// A simple `Codec` implementation that splits up data into lines. pub struct LinesCodec {} @@ -119,7 +117,7 @@ impl SinkCommand { crate struct InternalCommand { crate command: Arc, crate name_span: Option, - crate span_sources: HashMap, + crate source_map: SourceMap, crate args: Args, } @@ -141,7 +139,7 @@ impl InternalCommand { let result = context.run_command( self.command, self.name_span.clone(), - self.span_sources, + self.source_map, self.args, objects, )?; diff --git a/src/commands/command.rs b/src/commands/command.rs index cdd0b1a5c4..5bc2e08778 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -1,3 +1,4 @@ +use crate::context::SourceMap; use crate::context::SpanSource; use crate::errors::ShellError; use crate::object::Value; @@ -8,7 +9,6 @@ use crate::parser::{ use crate::prelude::*; use getset::Getters; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use std::path::PathBuf; use uuid::Uuid; @@ -18,7 +18,7 @@ pub struct CommandArgs { pub host: Arc>, pub env: Arc>, pub name_span: Option, - pub span_sources: HashMap, + pub source_map: SourceMap, pub args: Args, pub input: InputStream, } @@ -53,7 +53,7 @@ impl CommandArgs { pub struct SinkCommandArgs { pub ctx: Context, pub name_span: Option, - pub span_sources: HashMap, + pub source_map: SourceMap, pub args: Args, pub input: Vec>, } diff --git a/src/context.rs b/src/context.rs index 654e8a3a7a..94d15aab56 100644 --- a/src/context.rs +++ b/src/context.rs @@ -17,11 +17,29 @@ pub enum SpanSource { Url(String), File(String), } + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SourceMap(HashMap); + +impl SourceMap { + pub fn insert(&mut self, uuid: Uuid, span_source: SpanSource) { + self.0.insert(uuid, span_source); + } + + pub fn get(&self, uuid: &Uuid) -> Option<&SpanSource> { + self.0.get(uuid) + } + + pub fn new() -> SourceMap { + SourceMap(HashMap::new()) + } +} + #[derive(Clone)] pub struct Context { commands: IndexMap>, sinks: IndexMap>, - crate span_sources: HashMap, + crate source_map: SourceMap, crate host: Arc>, crate env: Arc>, } @@ -31,7 +49,7 @@ impl Context { Ok(Context { commands: indexmap::IndexMap::new(), sinks: indexmap::IndexMap::new(), - span_sources: HashMap::new(), + source_map: SourceMap::new(), host: Arc::new(Mutex::new(crate::env::host::BasicHost)), env: Arc::new(Mutex::new(Environment::basic()?)), }) @@ -50,7 +68,7 @@ impl Context { } pub fn add_span_source(&mut self, uuid: Uuid, span_source: SpanSource) { - self.span_sources.insert(uuid, span_source); + self.source_map.insert(uuid, span_source); } crate fn has_sink(&self, name: &str) -> bool { @@ -71,7 +89,7 @@ impl Context { let command_args = SinkCommandArgs { ctx: self.clone(), name_span, - span_sources: self.span_sources.clone(), + source_map: self.source_map.clone(), args, input, }; @@ -95,7 +113,7 @@ impl Context { &mut self, command: Arc, name_span: Option, - span_sources: HashMap, + source_map: SourceMap, args: Args, input: InputStream, ) -> Result { @@ -103,7 +121,7 @@ impl Context { host: self.host.clone(), env: self.env.clone(), name_span, - span_sources, + source_map, args, input, }; diff --git a/src/lib.rs b/src/lib.rs index 4528be06b6..13a48915ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ #![feature(bind_by_move_pattern_guards)] #![feature(box_syntax)] #![feature(type_ascription)] +#![feature(option_flattening)] #[macro_use] mod prelude; diff --git a/src/main.rs b/src/main.rs index afb0bb32a3..9ad29a625a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,8 +9,8 @@ use log::LevelFilter; use std::error::Error; fn main() -> Result<(), Box> { - let matches = App::new("nu shell") - .version("0.5") + let matches = App::new("nushell") + .version("0.1.3") .arg( Arg::with_name("loglevel") .short("l")