diff --git a/Cargo.toml b/Cargo.toml index 8879b76253..c5d35a65a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,24 +40,24 @@ members = [ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -nu-source = {version = "0.9.0", path = "./crates/nu-source"} -nu-plugin = {version = "0.9.0", path = "./crates/nu-plugin"} -nu-protocol = {version = "0.9.0", path = "./crates/nu-protocol"} -nu-errors = {version = "0.9.0", path = "./crates/nu-errors"} -nu-parser = {version = "0.9.0", path = "./crates/nu-parser"} -nu-value-ext = {version = "0.9.0", path = "./crates/nu-value-ext"} -nu_plugin_average = {version = "0.9.0", path = "./crates/nu_plugin_average", optional=true} -nu_plugin_binaryview = {version = "0.9.0", path = "./crates/nu_plugin_binaryview", optional=true} -nu_plugin_fetch = {version = "0.9.0", path = "./crates/nu_plugin_fetch", optional=true} -nu_plugin_inc = {version = "0.9.0", path = "./crates/nu_plugin_inc", optional=true} -nu_plugin_match = {version = "0.9.0", path = "./crates/nu_plugin_match", optional=true} -nu_plugin_post = {version = "0.9.0", path = "./crates/nu_plugin_post", optional=true} -nu_plugin_ps = {version = "0.9.0", path = "./crates/nu_plugin_ps", optional=true} -nu_plugin_str = {version = "0.9.0", path = "./crates/nu_plugin_str", optional=true} -nu_plugin_sum = {version = "0.9.0", path = "./crates/nu_plugin_sum", optional=true} -nu_plugin_sys = {version = "0.9.0", path = "./crates/nu_plugin_sys", optional=true} -nu_plugin_textview = {version = "0.9.0", path = "./crates/nu_plugin_textview", optional=true} -nu_plugin_tree = {version = "0.9.0", path = "./crates/nu_plugin_tree", optional=true} +nu-source = { version = "0.9.0", path = "./crates/nu-source" } +nu-plugin = { version = "0.9.0", path = "./crates/nu-plugin" } +nu-protocol = { version = "0.9.0", path = "./crates/nu-protocol" } +nu-errors = { version = "0.9.0", path = "./crates/nu-errors" } +nu-parser = { version = "0.9.0", path = "./crates/nu-parser" } +nu-value-ext = { version = "0.9.0", path = "./crates/nu-value-ext" } +nu_plugin_average = { version = "0.9.0", path = "./crates/nu_plugin_average", optional=true } +nu_plugin_binaryview = { version = "0.9.0", path = "./crates/nu_plugin_binaryview", optional=true } +nu_plugin_fetch = { version = "0.9.0", path = "./crates/nu_plugin_fetch", optional=true } +nu_plugin_inc = { version = "0.9.0", path = "./crates/nu_plugin_inc", optional=true } +nu_plugin_match = { version = "0.9.0", path = "./crates/nu_plugin_match", optional=true } +nu_plugin_post = { version = "0.9.0", path = "./crates/nu_plugin_post", optional=true } +nu_plugin_ps = { version = "0.9.0", path = "./crates/nu_plugin_ps", optional=true } +nu_plugin_str = { version = "0.9.0", path = "./crates/nu_plugin_str", optional=true } +nu_plugin_sum = { version = "0.9.0", path = "./crates/nu_plugin_sum", optional=true } +nu_plugin_sys = { version = "0.9.0", path = "./crates/nu_plugin_sys", optional=true } +nu_plugin_textview = { version = "0.9.0", path = "./crates/nu_plugin_textview", optional=true } +nu_plugin_tree = { version = "0.9.0", path = "./crates/nu_plugin_tree", optional=true } nu-macros = { version = "0.9.0", path = "./crates/nu-macros" } query_interface = "0.3.5" @@ -123,9 +123,9 @@ futures-util = "0.3.1" termcolor = "1.1.0" natural = "0.3.0" parking_lot = "0.10.0" -futures-timer = "1.0.2" meval = "0.2" +futures-timer = { version = "1.0.2", optional = true } clipboard = {version = "0.5", optional = true } ptree = {version = "0.2" } starship = { version = "0.33.1", optional = true} @@ -148,10 +148,10 @@ default = ["sys", "ps", "textview", "inc", "str"] stable = ["default", "starship-prompt", "binaryview", "match", "tree", "average", "sum", "post", "fetch", "clipboard"] # Default -sys = ["heim", "battery"] -ps = ["heim"] -textview = ["crossterm", "syntect", "onig_sys", "url"] -inc = ["nu_plugin_inc"] +textview = ["crossterm", "syntect", "onig_sys", "url", "nu_plugin_textview"] +sys = ["heim", "battery", "nu_plugin_sys"] +ps = ["heim", "futures-timer", "nu_plugin_ps"] +inc = ["semver", "nu_plugin_inc"] str = ["nu_plugin_str"] # Stable diff --git a/crates/nu-parser/src/parse/parser.rs b/crates/nu-parser/src/parse/parser.rs index 9e4e6e560f..93905246a3 100644 --- a/crates/nu-parser/src/parse/parser.rs +++ b/crates/nu-parser/src/parse/parser.rs @@ -862,7 +862,7 @@ pub fn delimited_brace(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_square(tokens.item, (left, right), tokens.span), + TokenTreeBuilder::spanned_brace(tokens.item, (left, right), tokens.span), )) } diff --git a/crates/nu_plugin_inc/Cargo.toml b/crates/nu_plugin_inc/Cargo.toml index 03e0d3f399..963791a04b 100644 --- a/crates/nu_plugin_inc/Cargo.toml +++ b/crates/nu_plugin_inc/Cargo.toml @@ -15,6 +15,7 @@ nu-protocol = { path = "../nu-protocol", version = "0.9.0" } nu-source = { path = "../nu-source", version = "0.9.0" } nu-errors = { path = "../nu-errors", version = "0.9.0" } nu-value-ext = { path = "../nu-value-ext", version = "0.9.0" } + semver = "0.9.0" [build-dependencies] diff --git a/crates/nu_plugin_inc/src/lib.rs b/crates/nu_plugin_inc/src/lib.rs index 7a8998e8e5..42f06fdefe 100644 --- a/crates/nu_plugin_inc/src/lib.rs +++ b/crates/nu_plugin_inc/src/lib.rs @@ -1,5 +1,5 @@ mod inc; -mod nu_plugin_inc; +mod nu; pub use inc::Inc; diff --git a/crates/nu_plugin_inc/src/nu_plugin_inc/mod.rs b/crates/nu_plugin_inc/src/nu/mod.rs similarity index 100% rename from crates/nu_plugin_inc/src/nu_plugin_inc/mod.rs rename to crates/nu_plugin_inc/src/nu/mod.rs diff --git a/crates/nu_plugin_inc/src/nu_plugin_inc/tests.rs b/crates/nu_plugin_inc/src/nu/tests.rs similarity index 100% rename from crates/nu_plugin_inc/src/nu_plugin_inc/tests.rs rename to crates/nu_plugin_inc/src/nu/tests.rs diff --git a/crates/nu_plugin_ps/Cargo.toml b/crates/nu_plugin_ps/Cargo.toml index 051eca4485..cae4c000d7 100644 --- a/crates/nu_plugin_ps/Cargo.toml +++ b/crates/nu_plugin_ps/Cargo.toml @@ -6,6 +6,9 @@ edition = "2018" description = "A process list plugin for Nushell" license = "MIT" +[lib] +doctest = false + [dependencies] nu-plugin = { path = "../nu-plugin", version = "0.9.0" } nu-protocol = { path = "../nu-protocol", version = "0.9.0" } diff --git a/crates/nu_plugin_ps/src/lib.rs b/crates/nu_plugin_ps/src/lib.rs new file mode 100644 index 0000000000..07a9ea0733 --- /dev/null +++ b/crates/nu_plugin_ps/src/lib.rs @@ -0,0 +1,4 @@ +mod nu; +mod ps; + +pub use ps::Ps; diff --git a/crates/nu_plugin_ps/src/main.rs b/crates/nu_plugin_ps/src/main.rs index ce265ca7f3..f8b1f880f5 100644 --- a/crates/nu_plugin_ps/src/main.rs +++ b/crates/nu_plugin_ps/src/main.rs @@ -1,94 +1,6 @@ -use futures::executor::block_on; -//use futures::stream::TryStreamExt; - -use futures_util::{StreamExt, TryStreamExt}; -use heim::process::{self as process, Process, ProcessResult}; -use heim::units::{information, ratio, Ratio}; -use std::usize; - -use nu_errors::ShellError; -use nu_plugin::{serve_plugin, Plugin}; -use nu_protocol::{ - CallInfo, ReturnSuccess, ReturnValue, Signature, TaggedDictBuilder, UntaggedValue, Value, -}; -use nu_source::Tag; - -use std::time::Duration; - -struct Ps; -impl Ps { - fn new() -> Ps { - Ps - } -} - -async fn usage(process: Process) -> ProcessResult<(process::Process, Ratio, process::Memory)> { - let usage_1 = process.cpu_usage().await?; - futures_timer::Delay::new(Duration::from_millis(100)).await; - let usage_2 = process.cpu_usage().await?; - - let memory = process.memory().await?; - - Ok((process, usage_2 - usage_1, memory)) -} - -async fn ps(tag: Tag) -> Vec { - let processes = process::processes() - .map_ok(|process| { - // Note that there is no `.await` here, - // as we want to pass the returned future - // into the `.try_buffer_unordered`. - usage(process) - }) - .try_buffer_unordered(usize::MAX); - pin_utils::pin_mut!(processes); - - let mut output = vec![]; - while let Some(res) = processes.next().await { - if let Ok((process, usage, memory)) = res { - let mut dict = TaggedDictBuilder::new(&tag); - dict.insert_untagged("pid", UntaggedValue::int(process.pid())); - if let Ok(name) = process.name().await { - dict.insert_untagged("name", UntaggedValue::string(name)); - } - if let Ok(status) = process.status().await { - dict.insert_untagged("status", UntaggedValue::string(format!("{:?}", status))); - } - dict.insert_untagged("cpu", UntaggedValue::decimal(usage.get::())); - dict.insert_untagged( - "mem", - UntaggedValue::bytes(memory.rss().get::()), - ); - dict.insert_untagged( - "virtual", - UntaggedValue::bytes(memory.vms().get::()), - ); - output.push(dict.into_value()); - } - } - - output -} - -impl Plugin for Ps { - fn config(&mut self) -> Result { - Ok(Signature::build("ps") - .desc("View information about system processes.") - .filter()) - } - - fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { - Ok(block_on(ps(callinfo.name_tag)) - .into_iter() - .map(ReturnSuccess::value) - .collect()) - } - - fn filter(&mut self, _: Value) -> Result, ShellError> { - Ok(vec![]) - } -} +use nu_plugin::serve_plugin; +use nu_plugin_ps::Ps; fn main() { - serve_plugin(&mut Ps::new()); + serve_plugin(&mut Ps::new()) } diff --git a/crates/nu_plugin_ps/src/nu/mod.rs b/crates/nu_plugin_ps/src/nu/mod.rs new file mode 100644 index 0000000000..b1b2ae6270 --- /dev/null +++ b/crates/nu_plugin_ps/src/nu/mod.rs @@ -0,0 +1,25 @@ +use crate::ps::{ps, Ps}; +use nu_errors::ShellError; +use nu_plugin::Plugin; +use nu_protocol::{CallInfo, ReturnSuccess, ReturnValue, Signature, Value}; + +use futures::executor::block_on; + +impl Plugin for Ps { + fn config(&mut self) -> Result { + Ok(Signature::build("ps") + .desc("View information about system processes.") + .filter()) + } + + fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { + Ok(block_on(ps(callinfo.name_tag)) + .into_iter() + .map(ReturnSuccess::value) + .collect()) + } + + fn filter(&mut self, _: Value) -> Result, ShellError> { + Ok(vec![]) + } +} diff --git a/crates/nu_plugin_ps/src/ps.rs b/crates/nu_plugin_ps/src/ps.rs new file mode 100644 index 0000000000..21cce38fbd --- /dev/null +++ b/crates/nu_plugin_ps/src/ps.rs @@ -0,0 +1,66 @@ +use futures_util::{StreamExt, TryStreamExt}; +use heim::process::{self as process, Process, ProcessResult}; +use heim::units::{information, ratio, Ratio}; +use std::usize; + +use nu_protocol::{TaggedDictBuilder, UntaggedValue, Value}; +use nu_source::Tag; + +use std::time::Duration; + +#[derive(Default)] +pub struct Ps; + +impl Ps { + pub fn new() -> Ps { + Ps + } +} + +async fn usage(process: Process) -> ProcessResult<(process::Process, Ratio, process::Memory)> { + let usage_1 = process.cpu_usage().await?; + futures_timer::Delay::new(Duration::from_millis(100)).await; + let usage_2 = process.cpu_usage().await?; + + let memory = process.memory().await?; + + Ok((process, usage_2 - usage_1, memory)) +} + +pub async fn ps(tag: Tag) -> Vec { + let processes = process::processes() + .map_ok(|process| { + // Note that there is no `.await` here, + // as we want to pass the returned future + // into the `.try_buffer_unordered`. + usage(process) + }) + .try_buffer_unordered(usize::MAX); + pin_utils::pin_mut!(processes); + + let mut output = vec![]; + while let Some(res) = processes.next().await { + if let Ok((process, usage, memory)) = res { + let mut dict = TaggedDictBuilder::new(&tag); + dict.insert_untagged("pid", UntaggedValue::int(process.pid())); + if let Ok(name) = process.name().await { + dict.insert_untagged("name", UntaggedValue::string(name)); + } + if let Ok(status) = process.status().await { + dict.insert_untagged("status", UntaggedValue::string(format!("{:?}", status))); + } + dict.insert_untagged("cpu", UntaggedValue::decimal(usage.get::())); + dict.insert_untagged( + "mem", + UntaggedValue::bytes(memory.rss().get::()), + ); + dict.insert_untagged( + "virtual", + UntaggedValue::bytes(memory.vms().get::()), + ); + output.push(dict.into_value()); + } + } + + output +} diff --git a/crates/nu_plugin_str/Cargo.toml b/crates/nu_plugin_str/Cargo.toml index ce81a89950..9b5764709b 100644 --- a/crates/nu_plugin_str/Cargo.toml +++ b/crates/nu_plugin_str/Cargo.toml @@ -15,6 +15,7 @@ nu-protocol = { path = "../nu-protocol", version = "0.9.0" } nu-source = { path = "../nu-source", version = "0.9.0" } nu-errors = { path = "../nu-errors", version = "0.9.0" } nu-value-ext = { path = "../nu-value-ext", version = "0.9.0" } + regex = "1" num-bigint = "0.2.3" diff --git a/crates/nu_plugin_str/src/lib.rs b/crates/nu_plugin_str/src/lib.rs index f55180fbb2..625c337662 100644 --- a/crates/nu_plugin_str/src/lib.rs +++ b/crates/nu_plugin_str/src/lib.rs @@ -1,4 +1,4 @@ -mod nu_plugin_str; +mod nu; mod strutils; pub use strutils::Str; diff --git a/crates/nu_plugin_str/src/nu_plugin_str/mod.rs b/crates/nu_plugin_str/src/nu/mod.rs similarity index 100% rename from crates/nu_plugin_str/src/nu_plugin_str/mod.rs rename to crates/nu_plugin_str/src/nu/mod.rs diff --git a/crates/nu_plugin_str/src/nu_plugin_str/tests.rs b/crates/nu_plugin_str/src/nu/tests.rs similarity index 100% rename from crates/nu_plugin_str/src/nu_plugin_str/tests.rs rename to crates/nu_plugin_str/src/nu/tests.rs diff --git a/crates/nu_plugin_sys/Cargo.toml b/crates/nu_plugin_sys/Cargo.toml index aa8794ded4..9db908583a 100644 --- a/crates/nu_plugin_sys/Cargo.toml +++ b/crates/nu_plugin_sys/Cargo.toml @@ -11,6 +11,7 @@ nu-plugin = { path = "../nu-plugin", version = "0.9.0" } nu-protocol = { path = "../nu-protocol", version = "0.9.0" } nu-source = { path = "../nu-source", version = "0.9.0" } nu-errors = { path = "../nu-errors", version = "0.9.0" } + heim = "0.0.9" futures-preview = { version = "=0.3.0-alpha.19", features = ["compat", "io-compat"] } battery = "0.7.5" diff --git a/crates/nu_plugin_sys/src/lib.rs b/crates/nu_plugin_sys/src/lib.rs new file mode 100644 index 0000000000..40cba13870 --- /dev/null +++ b/crates/nu_plugin_sys/src/lib.rs @@ -0,0 +1,4 @@ +mod nu; +mod sys; + +pub use sys::Sys; diff --git a/crates/nu_plugin_sys/src/main.rs b/crates/nu_plugin_sys/src/main.rs index 9d482b7506..5d4e339595 100644 --- a/crates/nu_plugin_sys/src/main.rs +++ b/crates/nu_plugin_sys/src/main.rs @@ -1,354 +1,5 @@ -use std::ffi::OsStr; - -use futures::executor::block_on; -//use futures::stream::StreamExt; -use futures_util::StreamExt; -use heim::units::{frequency, information, thermodynamic_temperature, time}; -use heim::{disk, host, memory, net, sensors}; -use nu_errors::ShellError; -use nu_plugin::{serve_plugin, Plugin}; -use nu_protocol::{ - CallInfo, ReturnSuccess, ReturnValue, Signature, TaggedDictBuilder, UntaggedValue, Value, -}; -use nu_source::Tag; - -struct Sys; -impl Sys { - fn new() -> Sys { - Sys - } -} - -async fn cpu(tag: Tag) -> Option { - match futures::future::try_join(heim::cpu::logical_count(), heim::cpu::frequency()).await { - Ok((num_cpu, cpu_speed)) => { - let mut cpu_idx = TaggedDictBuilder::with_capacity(tag, 4); - cpu_idx.insert_untagged("cores", UntaggedValue::int(num_cpu)); - - let current_speed = - (cpu_speed.current().get::() as f64 / 1_000_000_000.0 * 100.0) - .round() - / 100.0; - cpu_idx.insert_untagged("current ghz", UntaggedValue::decimal(current_speed)); - - if let Some(min_speed) = cpu_speed.min() { - let min_speed = - (min_speed.get::() as f64 / 1_000_000_000.0 * 100.0).round() - / 100.0; - cpu_idx.insert_untagged("min ghz", UntaggedValue::decimal(min_speed)); - } - - if let Some(max_speed) = cpu_speed.max() { - let max_speed = - (max_speed.get::() as f64 / 1_000_000_000.0 * 100.0).round() - / 100.0; - cpu_idx.insert_untagged("max ghz", UntaggedValue::decimal(max_speed)); - } - - Some(cpu_idx.into_value()) - } - Err(_) => None, - } -} - -async fn mem(tag: Tag) -> Value { - let mut dict = TaggedDictBuilder::with_capacity(tag, 4); - - let (memory_result, swap_result) = - futures::future::join(memory::memory(), memory::swap()).await; - - if let Ok(memory) = memory_result { - dict.insert_untagged( - "total", - UntaggedValue::bytes(memory.total().get::()), - ); - dict.insert_untagged( - "free", - UntaggedValue::bytes(memory.free().get::()), - ); - } - - if let Ok(swap) = swap_result { - dict.insert_untagged( - "swap total", - UntaggedValue::bytes(swap.total().get::()), - ); - dict.insert_untagged( - "swap free", - UntaggedValue::bytes(swap.free().get::()), - ); - } - - dict.into_value() -} - -async fn host(tag: Tag) -> Value { - let mut dict = TaggedDictBuilder::with_capacity(&tag, 6); - - let (platform_result, uptime_result) = - futures::future::join(host::platform(), host::uptime()).await; - - // OS - if let Ok(platform) = platform_result { - dict.insert_untagged("name", UntaggedValue::string(platform.system())); - dict.insert_untagged("release", UntaggedValue::string(platform.release())); - dict.insert_untagged("hostname", UntaggedValue::string(platform.hostname())); - dict.insert_untagged( - "arch", - UntaggedValue::string(platform.architecture().as_str()), - ); - } - - // Uptime - if let Ok(uptime) = uptime_result { - let mut uptime_dict = TaggedDictBuilder::with_capacity(&tag, 4); - - let uptime = uptime.get::().round() as i64; - let days = uptime / (60 * 60 * 24); - let hours = (uptime - days * 60 * 60 * 24) / (60 * 60); - let minutes = (uptime - days * 60 * 60 * 24 - hours * 60 * 60) / 60; - let seconds = uptime % 60; - - uptime_dict.insert_untagged("days", UntaggedValue::int(days)); - uptime_dict.insert_untagged("hours", UntaggedValue::int(hours)); - uptime_dict.insert_untagged("mins", UntaggedValue::int(minutes)); - uptime_dict.insert_untagged("secs", UntaggedValue::int(seconds)); - - dict.insert_value("uptime", uptime_dict); - } - - // Users - let mut users = host::users(); - let mut user_vec = vec![]; - while let Some(user) = users.next().await { - if let Ok(user) = user { - user_vec.push(Value { - value: UntaggedValue::string(user.username()), - tag: tag.clone(), - }); - } - } - let user_list = UntaggedValue::Table(user_vec); - dict.insert_untagged("users", user_list); - - dict.into_value() -} - -async fn disks(tag: Tag) -> Option { - let mut output = vec![]; - let mut partitions = disk::partitions_physical(); - while let Some(part) = partitions.next().await { - if let Ok(part) = part { - let mut dict = TaggedDictBuilder::with_capacity(&tag, 6); - dict.insert_untagged( - "device", - UntaggedValue::string( - part.device() - .unwrap_or_else(|| OsStr::new("N/A")) - .to_string_lossy(), - ), - ); - - dict.insert_untagged("type", UntaggedValue::string(part.file_system().as_str())); - dict.insert_untagged( - "mount", - UntaggedValue::string(part.mount_point().to_string_lossy()), - ); - - if let Ok(usage) = disk::usage(part.mount_point().to_path_buf()).await { - dict.insert_untagged( - "total", - UntaggedValue::bytes(usage.total().get::()), - ); - dict.insert_untagged( - "used", - UntaggedValue::bytes(usage.used().get::()), - ); - dict.insert_untagged( - "free", - UntaggedValue::bytes(usage.free().get::()), - ); - } - - output.push(dict.into_value()); - } - } - - if !output.is_empty() { - Some(UntaggedValue::Table(output)) - } else { - None - } -} - -async fn battery(tag: Tag) -> Option { - let mut output = vec![]; - - if let Ok(manager) = battery::Manager::new() { - if let Ok(batteries) = manager.batteries() { - for battery in batteries { - if let Ok(battery) = battery { - let mut dict = TaggedDictBuilder::new(&tag); - if let Some(vendor) = battery.vendor() { - dict.insert_untagged("vendor", UntaggedValue::string(vendor)); - } - if let Some(model) = battery.model() { - dict.insert_untagged("model", UntaggedValue::string(model)); - } - if let Some(cycles) = battery.cycle_count() { - dict.insert_untagged("cycles", UntaggedValue::int(cycles)); - } - if let Some(time_to_full) = battery.time_to_full() { - dict.insert_untagged( - "mins to full", - UntaggedValue::decimal( - time_to_full.get::(), - ), - ); - } - if let Some(time_to_empty) = battery.time_to_empty() { - dict.insert_untagged( - "mins to empty", - UntaggedValue::decimal( - time_to_empty.get::(), - ), - ); - } - output.push(dict.into_value()); - } - } - } - } - - if !output.is_empty() { - Some(UntaggedValue::Table(output)) - } else { - None - } -} - -async fn temp(tag: Tag) -> Option { - let mut output = vec![]; - - let mut sensors = sensors::temperatures(); - while let Some(sensor) = sensors.next().await { - if let Ok(sensor) = sensor { - let mut dict = TaggedDictBuilder::new(&tag); - dict.insert_untagged("unit", UntaggedValue::string(sensor.unit())); - if let Some(label) = sensor.label() { - dict.insert_untagged("label", UntaggedValue::string(label)); - } - dict.insert_untagged( - "temp", - UntaggedValue::decimal( - sensor - .current() - .get::(), - ), - ); - if let Some(high) = sensor.high() { - dict.insert_untagged( - "high", - UntaggedValue::decimal(high.get::()), - ); - } - if let Some(critical) = sensor.critical() { - dict.insert_untagged( - "critical", - UntaggedValue::decimal( - critical.get::(), - ), - ); - } - - output.push(dict.into_value()); - } - } - - if !output.is_empty() { - Some(UntaggedValue::Table(output)) - } else { - None - } -} - -async fn net(tag: Tag) -> Option { - let mut output = vec![]; - let mut io_counters = net::io_counters(); - while let Some(nic) = io_counters.next().await { - if let Ok(nic) = nic { - let mut network_idx = TaggedDictBuilder::with_capacity(&tag, 3); - network_idx.insert_untagged("name", UntaggedValue::string(nic.interface())); - network_idx.insert_untagged( - "sent", - UntaggedValue::bytes(nic.bytes_sent().get::()), - ); - network_idx.insert_untagged( - "recv", - UntaggedValue::bytes(nic.bytes_recv().get::()), - ); - output.push(network_idx.into_value()); - } - } - if !output.is_empty() { - Some(UntaggedValue::Table(output)) - } else { - None - } -} - -async fn sysinfo(tag: Tag) -> Vec { - let mut sysinfo = TaggedDictBuilder::with_capacity(&tag, 7); - - let (host, cpu, disks, memory, temp) = futures::future::join5( - host(tag.clone()), - cpu(tag.clone()), - disks(tag.clone()), - mem(tag.clone()), - temp(tag.clone()), - ) - .await; - let (net, battery) = futures::future::join(net(tag.clone()), battery(tag.clone())).await; - - sysinfo.insert_value("host", host); - if let Some(cpu) = cpu { - sysinfo.insert_value("cpu", cpu); - } - if let Some(disks) = disks { - sysinfo.insert_untagged("disks", disks); - } - sysinfo.insert_value("mem", memory); - if let Some(temp) = temp { - sysinfo.insert_untagged("temp", temp); - } - if let Some(net) = net { - sysinfo.insert_untagged("net", net); - } - if let Some(battery) = battery { - sysinfo.insert_untagged("battery", battery); - } - - vec![sysinfo.into_value()] -} - -impl Plugin for Sys { - fn config(&mut self) -> Result { - Ok(Signature::build("sys") - .desc("View information about the current system.") - .filter()) - } - - fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { - Ok(block_on(sysinfo(callinfo.name_tag)) - .into_iter() - .map(ReturnSuccess::value) - .collect()) - } - - fn filter(&mut self, _: Value) -> Result, ShellError> { - Ok(vec![]) - } -} +use nu_plugin::serve_plugin; +use nu_plugin_sys::Sys; fn main() { serve_plugin(&mut Sys::new()); diff --git a/crates/nu_plugin_sys/src/nu/mod.rs b/crates/nu_plugin_sys/src/nu/mod.rs new file mode 100644 index 0000000000..b7a908a9cc --- /dev/null +++ b/crates/nu_plugin_sys/src/nu/mod.rs @@ -0,0 +1,25 @@ +use crate::sys::{sysinfo, Sys}; +use nu_errors::ShellError; +use nu_plugin::Plugin; +use nu_protocol::{CallInfo, ReturnSuccess, ReturnValue, Signature, Value}; + +use futures::executor::block_on; + +impl Plugin for Sys { + fn config(&mut self) -> Result { + Ok(Signature::build("sys") + .desc("View information about the current system.") + .filter()) + } + + fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { + Ok(block_on(sysinfo(callinfo.name_tag)) + .into_iter() + .map(ReturnSuccess::value) + .collect()) + } + + fn filter(&mut self, _: Value) -> Result, ShellError> { + Ok(vec![]) + } +} diff --git a/crates/nu_plugin_sys/src/sys.rs b/crates/nu_plugin_sys/src/sys.rs new file mode 100644 index 0000000000..cdb1dc2fa1 --- /dev/null +++ b/crates/nu_plugin_sys/src/sys.rs @@ -0,0 +1,327 @@ +use futures_util::StreamExt; +use heim::units::{frequency, information, thermodynamic_temperature, time}; +use heim::{disk, host, memory, net, sensors}; +use nu_protocol::{TaggedDictBuilder, UntaggedValue, Value}; +use nu_source::Tag; +use std::ffi::OsStr; + +#[derive(Default)] +pub struct Sys; + +impl Sys { + pub fn new() -> Sys { + Sys + } +} + +async fn cpu(tag: Tag) -> Option { + match futures::future::try_join(heim::cpu::logical_count(), heim::cpu::frequency()).await { + Ok((num_cpu, cpu_speed)) => { + let mut cpu_idx = TaggedDictBuilder::with_capacity(tag, 4); + cpu_idx.insert_untagged("cores", UntaggedValue::int(num_cpu)); + + let current_speed = + (cpu_speed.current().get::() as f64 / 1_000_000_000.0 * 100.0) + .round() + / 100.0; + cpu_idx.insert_untagged("current ghz", UntaggedValue::decimal(current_speed)); + + if let Some(min_speed) = cpu_speed.min() { + let min_speed = + (min_speed.get::() as f64 / 1_000_000_000.0 * 100.0).round() + / 100.0; + cpu_idx.insert_untagged("min ghz", UntaggedValue::decimal(min_speed)); + } + + if let Some(max_speed) = cpu_speed.max() { + let max_speed = + (max_speed.get::() as f64 / 1_000_000_000.0 * 100.0).round() + / 100.0; + cpu_idx.insert_untagged("max ghz", UntaggedValue::decimal(max_speed)); + } + + Some(cpu_idx.into_value()) + } + Err(_) => None, + } +} + +async fn mem(tag: Tag) -> Value { + let mut dict = TaggedDictBuilder::with_capacity(tag, 4); + + let (memory_result, swap_result) = + futures::future::join(memory::memory(), memory::swap()).await; + + if let Ok(memory) = memory_result { + dict.insert_untagged( + "total", + UntaggedValue::bytes(memory.total().get::()), + ); + dict.insert_untagged( + "free", + UntaggedValue::bytes(memory.free().get::()), + ); + } + + if let Ok(swap) = swap_result { + dict.insert_untagged( + "swap total", + UntaggedValue::bytes(swap.total().get::()), + ); + dict.insert_untagged( + "swap free", + UntaggedValue::bytes(swap.free().get::()), + ); + } + + dict.into_value() +} + +async fn host(tag: Tag) -> Value { + let mut dict = TaggedDictBuilder::with_capacity(&tag, 6); + + let (platform_result, uptime_result) = + futures::future::join(host::platform(), host::uptime()).await; + + // OS + if let Ok(platform) = platform_result { + dict.insert_untagged("name", UntaggedValue::string(platform.system())); + dict.insert_untagged("release", UntaggedValue::string(platform.release())); + dict.insert_untagged("hostname", UntaggedValue::string(platform.hostname())); + dict.insert_untagged( + "arch", + UntaggedValue::string(platform.architecture().as_str()), + ); + } + + // Uptime + if let Ok(uptime) = uptime_result { + let mut uptime_dict = TaggedDictBuilder::with_capacity(&tag, 4); + + let uptime = uptime.get::().round() as i64; + let days = uptime / (60 * 60 * 24); + let hours = (uptime - days * 60 * 60 * 24) / (60 * 60); + let minutes = (uptime - days * 60 * 60 * 24 - hours * 60 * 60) / 60; + let seconds = uptime % 60; + + uptime_dict.insert_untagged("days", UntaggedValue::int(days)); + uptime_dict.insert_untagged("hours", UntaggedValue::int(hours)); + uptime_dict.insert_untagged("mins", UntaggedValue::int(minutes)); + uptime_dict.insert_untagged("secs", UntaggedValue::int(seconds)); + + dict.insert_value("uptime", uptime_dict); + } + + // Users + let mut users = host::users(); + let mut user_vec = vec![]; + while let Some(user) = users.next().await { + if let Ok(user) = user { + user_vec.push(Value { + value: UntaggedValue::string(user.username()), + tag: tag.clone(), + }); + } + } + let user_list = UntaggedValue::Table(user_vec); + dict.insert_untagged("users", user_list); + + dict.into_value() +} + +async fn disks(tag: Tag) -> Option { + let mut output = vec![]; + let mut partitions = disk::partitions_physical(); + while let Some(part) = partitions.next().await { + if let Ok(part) = part { + let mut dict = TaggedDictBuilder::with_capacity(&tag, 6); + dict.insert_untagged( + "device", + UntaggedValue::string( + part.device() + .unwrap_or_else(|| OsStr::new("N/A")) + .to_string_lossy(), + ), + ); + + dict.insert_untagged("type", UntaggedValue::string(part.file_system().as_str())); + dict.insert_untagged( + "mount", + UntaggedValue::string(part.mount_point().to_string_lossy()), + ); + + if let Ok(usage) = disk::usage(part.mount_point().to_path_buf()).await { + dict.insert_untagged( + "total", + UntaggedValue::bytes(usage.total().get::()), + ); + dict.insert_untagged( + "used", + UntaggedValue::bytes(usage.used().get::()), + ); + dict.insert_untagged( + "free", + UntaggedValue::bytes(usage.free().get::()), + ); + } + + output.push(dict.into_value()); + } + } + + if !output.is_empty() { + Some(UntaggedValue::Table(output)) + } else { + None + } +} + +async fn battery(tag: Tag) -> Option { + let mut output = vec![]; + + if let Ok(manager) = battery::Manager::new() { + if let Ok(batteries) = manager.batteries() { + for battery in batteries { + if let Ok(battery) = battery { + let mut dict = TaggedDictBuilder::new(&tag); + if let Some(vendor) = battery.vendor() { + dict.insert_untagged("vendor", UntaggedValue::string(vendor)); + } + if let Some(model) = battery.model() { + dict.insert_untagged("model", UntaggedValue::string(model)); + } + if let Some(cycles) = battery.cycle_count() { + dict.insert_untagged("cycles", UntaggedValue::int(cycles)); + } + if let Some(time_to_full) = battery.time_to_full() { + dict.insert_untagged( + "mins to full", + UntaggedValue::decimal( + time_to_full.get::(), + ), + ); + } + if let Some(time_to_empty) = battery.time_to_empty() { + dict.insert_untagged( + "mins to empty", + UntaggedValue::decimal( + time_to_empty.get::(), + ), + ); + } + output.push(dict.into_value()); + } + } + } + } + + if !output.is_empty() { + Some(UntaggedValue::Table(output)) + } else { + None + } +} + +async fn temp(tag: Tag) -> Option { + let mut output = vec![]; + + let mut sensors = sensors::temperatures(); + while let Some(sensor) = sensors.next().await { + if let Ok(sensor) = sensor { + let mut dict = TaggedDictBuilder::new(&tag); + dict.insert_untagged("unit", UntaggedValue::string(sensor.unit())); + if let Some(label) = sensor.label() { + dict.insert_untagged("label", UntaggedValue::string(label)); + } + dict.insert_untagged( + "temp", + UntaggedValue::decimal( + sensor + .current() + .get::(), + ), + ); + if let Some(high) = sensor.high() { + dict.insert_untagged( + "high", + UntaggedValue::decimal(high.get::()), + ); + } + if let Some(critical) = sensor.critical() { + dict.insert_untagged( + "critical", + UntaggedValue::decimal( + critical.get::(), + ), + ); + } + + output.push(dict.into_value()); + } + } + + if !output.is_empty() { + Some(UntaggedValue::Table(output)) + } else { + None + } +} + +async fn net(tag: Tag) -> Option { + let mut output = vec![]; + let mut io_counters = net::io_counters(); + while let Some(nic) = io_counters.next().await { + if let Ok(nic) = nic { + let mut network_idx = TaggedDictBuilder::with_capacity(&tag, 3); + network_idx.insert_untagged("name", UntaggedValue::string(nic.interface())); + network_idx.insert_untagged( + "sent", + UntaggedValue::bytes(nic.bytes_sent().get::()), + ); + network_idx.insert_untagged( + "recv", + UntaggedValue::bytes(nic.bytes_recv().get::()), + ); + output.push(network_idx.into_value()); + } + } + if !output.is_empty() { + Some(UntaggedValue::Table(output)) + } else { + None + } +} + +pub async fn sysinfo(tag: Tag) -> Vec { + let mut sysinfo = TaggedDictBuilder::with_capacity(&tag, 7); + + let (host, cpu, disks, memory, temp) = futures::future::join5( + host(tag.clone()), + cpu(tag.clone()), + disks(tag.clone()), + mem(tag.clone()), + temp(tag.clone()), + ) + .await; + let (net, battery) = futures::future::join(net(tag.clone()), battery(tag.clone())).await; + + sysinfo.insert_value("host", host); + if let Some(cpu) = cpu { + sysinfo.insert_value("cpu", cpu); + } + if let Some(disks) = disks { + sysinfo.insert_untagged("disks", disks); + } + sysinfo.insert_value("mem", memory); + if let Some(temp) = temp { + sysinfo.insert_untagged("temp", temp); + } + if let Some(net) = net { + sysinfo.insert_untagged("net", net); + } + if let Some(battery) = battery { + sysinfo.insert_untagged("battery", battery); + } + + vec![sysinfo.into_value()] +} diff --git a/crates/nu_plugin_textview/src/lib.rs b/crates/nu_plugin_textview/src/lib.rs new file mode 100644 index 0000000000..3f6ec8b2af --- /dev/null +++ b/crates/nu_plugin_textview/src/lib.rs @@ -0,0 +1,4 @@ +mod nu; +mod textview; + +pub use textview::TextView; diff --git a/crates/nu_plugin_textview/src/main.rs b/crates/nu_plugin_textview/src/main.rs index 98da9567db..1425efeada 100644 --- a/crates/nu_plugin_textview/src/main.rs +++ b/crates/nu_plugin_textview/src/main.rs @@ -1,304 +1,5 @@ -use crossterm::{ - event::{KeyCode, KeyEvent}, - ExecutableCommand, -}; -use nu_errors::ShellError; -use nu_plugin::{serve_plugin, Plugin}; -use nu_protocol::{outln, CallInfo, Primitive, Signature, UntaggedValue, Value}; -use nu_source::AnchorLocation; - -use syntect::easy::HighlightLines; -use syntect::highlighting::{Style, ThemeSet}; -use syntect::parsing::SyntaxSet; - -use std::io::Write; -use std::path::Path; - -enum DrawCommand { - DrawString(Style, String), - NextLine, -} - -struct TextView; - -impl TextView { - fn new() -> TextView { - TextView - } -} - -impl Plugin for TextView { - fn config(&mut self) -> Result { - Ok(Signature::build("textview").desc("Autoview of text data.")) - } - - fn sink(&mut self, _call_info: CallInfo, input: Vec) { - if !input.is_empty() { - view_text_value(&input[0]); - } - } -} - -fn paint_textview( - draw_commands: &[DrawCommand], - starting_row: usize, - use_color_buffer: bool, -) -> usize { - let size = crossterm::terminal::size().unwrap_or_else(|_| (80, 24)); - - // render - let mut pos = 0; - let width = size.0 as usize; - let height = size.1 as usize - 1; - let mut frame_buffer = vec![]; - - for command in draw_commands { - match command { - DrawCommand::DrawString(style, string) => { - for chr in string.chars() { - if chr == '\t' { - for _ in 0..8 { - frame_buffer.push(( - ' ', - style.foreground.r, - style.foreground.g, - style.foreground.b, - )); - } - pos += 8; - } else { - frame_buffer.push(( - chr, - style.foreground.r, - style.foreground.g, - style.foreground.b, - )); - pos += 1; - } - } - } - DrawCommand::NextLine => { - for _ in 0..(width - pos % width) { - frame_buffer.push((' ', 0, 0, 0)); - } - pos += width - pos % width; - } - } - } - - let num_frame_buffer_rows = frame_buffer.len() / width; - let buffer_needs_scrolling = num_frame_buffer_rows > height; - - // display - let mut ansi_strings = vec![]; - let mut normal_chars = vec![]; - - for c in - &frame_buffer[starting_row * width..std::cmp::min(pos, (starting_row + height) * width)] - { - if use_color_buffer { - ansi_strings.push(ansi_term::Colour::RGB(c.1, c.2, c.3).paint(format!("{}", c.0))); - } else { - normal_chars.push(c.0); - } - } - - if buffer_needs_scrolling { - let _ = std::io::stdout().execute(crossterm::cursor::MoveTo(0, 0)); - } - - if use_color_buffer { - print!("{}", ansi_term::ANSIStrings(&ansi_strings)); - } else { - let s: String = normal_chars.into_iter().collect(); - print!("{}", s); - } - - if buffer_needs_scrolling { - let _ = std::io::stdout().execute(crossterm::cursor::MoveTo(0, size.1)); - print!( - "{}", - ansi_term::Colour::Blue.paint("[ESC to quit, arrow keys to move]") - ); - } - - let _ = std::io::stdout().flush(); - - num_frame_buffer_rows -} - -fn scroll_view_lines_if_needed(draw_commands: Vec, use_color_buffer: bool) { - let mut starting_row = 0; - - if let Ok(_raw) = crossterm::terminal::enable_raw_mode() { - let mut size = crossterm::terminal::size().unwrap_or_else(|_| (80, 24)); - let height = size.1 as usize - 1; - - let mut max_bottom_line = paint_textview(&draw_commands, starting_row, use_color_buffer); - - // Only scroll if needed - if max_bottom_line > height as usize { - let _ = std::io::stdout().execute(crossterm::cursor::Hide); - - loop { - if let Ok(ev) = crossterm::event::read() { - if let crossterm::event::Event::Key(KeyEvent { code, modifiers }) = ev { - match code { - KeyCode::Esc => { - break; - } - KeyCode::Up | KeyCode::Char('k') => { - if starting_row > 0 { - starting_row -= 1; - max_bottom_line = paint_textview( - &draw_commands, - starting_row, - use_color_buffer, - ); - } - } - KeyCode::Down | KeyCode::Char('j') => { - if starting_row < (max_bottom_line - height) { - starting_row += 1; - } - max_bottom_line = - paint_textview(&draw_commands, starting_row, use_color_buffer); - } - KeyCode::Char('b') - if modifiers.contains(crossterm::event::KeyModifiers::CONTROL) => - { - starting_row -= std::cmp::min(height, starting_row); - max_bottom_line = - paint_textview(&draw_commands, starting_row, use_color_buffer); - } - KeyCode::PageUp => { - starting_row -= std::cmp::min(height, starting_row); - max_bottom_line = - paint_textview(&draw_commands, starting_row, use_color_buffer); - } - KeyCode::Char('f') - if modifiers.contains(crossterm::event::KeyModifiers::CONTROL) => - { - if starting_row < (max_bottom_line - height) { - starting_row += height; - - if starting_row > (max_bottom_line - height) { - starting_row = max_bottom_line - height; - } - } - max_bottom_line = - paint_textview(&draw_commands, starting_row, use_color_buffer); - } - KeyCode::PageDown | KeyCode::Char(' ') => { - if starting_row < (max_bottom_line - height) { - starting_row += height; - - if starting_row > (max_bottom_line - height) { - starting_row = max_bottom_line - height; - } - } - max_bottom_line = - paint_textview(&draw_commands, starting_row, use_color_buffer); - } - _ => {} - } - } - } - - if let Ok(new_size) = crossterm::terminal::size() { - if size != new_size { - size = new_size; - let _ = std::io::stdout().execute(crossterm::terminal::Clear( - crossterm::terminal::ClearType::All, - )); - max_bottom_line = - paint_textview(&draw_commands, starting_row, use_color_buffer); - } - } - } - let _ = std::io::stdout().execute(crossterm::cursor::Show); - - let _ = crossterm::terminal::disable_raw_mode(); - } - } - - outln!(""); -} - -fn scroll_view(s: &str) { - let mut v = vec![]; - for line in s.lines() { - v.push(DrawCommand::DrawString(Style::default(), line.to_string())); - v.push(DrawCommand::NextLine); - } - scroll_view_lines_if_needed(v, false); -} - -fn view_text_value(value: &Value) { - let value_anchor = value.anchor(); - if let UntaggedValue::Primitive(Primitive::String(ref s)) = &value.value { - if let Some(source) = value_anchor { - let extension: Option = match source { - AnchorLocation::File(file) => { - let path = Path::new(&file); - path.extension().map(|x| x.to_string_lossy().to_string()) - } - AnchorLocation::Url(url) => { - let url = url::Url::parse(&url); - if let Ok(url) = url { - if let Some(mut segments) = url.path_segments() { - if let Some(file) = segments.next_back() { - let path = Path::new(file); - path.extension().map(|x| x.to_string_lossy().to_string()) - } else { - None - } - } else { - None - } - } else { - None - } - } - //FIXME: this probably isn't correct - AnchorLocation::Source(_source) => None, - }; - - match extension { - Some(extension) => { - // Load these once at the start of your program - let ps: SyntaxSet = - syntect::dumps::from_binary(include_bytes!("assets/syntaxes.bin")); - - if let Some(syntax) = ps.find_syntax_by_extension(&extension) { - let ts: ThemeSet = - syntect::dumps::from_binary(include_bytes!("assets/themes.bin")); - let mut h = HighlightLines::new(syntax, &ts.themes["OneHalfDark"]); - - let mut v = vec![]; - for line in s.lines() { - let ranges: Vec<(Style, &str)> = h.highlight(line, &ps); - - for range in ranges { - v.push(DrawCommand::DrawString(range.0, range.1.to_string())); - } - - v.push(DrawCommand::NextLine); - } - scroll_view_lines_if_needed(v, true); - } else { - scroll_view(s); - } - } - _ => { - scroll_view(s); - } - } - } else { - scroll_view(s); - } - } -} +use nu_plugin::serve_plugin; +use nu_plugin_textview::TextView; fn main() { serve_plugin(&mut TextView::new()); diff --git a/crates/nu_plugin_textview/src/nu/mod.rs b/crates/nu_plugin_textview/src/nu/mod.rs new file mode 100644 index 0000000000..43bc314b74 --- /dev/null +++ b/crates/nu_plugin_textview/src/nu/mod.rs @@ -0,0 +1,16 @@ +use crate::textview::{view_text_value, TextView}; +use nu_errors::ShellError; +use nu_plugin::Plugin; +use nu_protocol::{CallInfo, Signature, Value}; + +impl Plugin for TextView { + fn config(&mut self) -> Result { + Ok(Signature::build("textview").desc("Autoview of text data.")) + } + + fn sink(&mut self, _call_info: CallInfo, input: Vec) { + if !input.is_empty() { + view_text_value(&input[0]); + } + } +} diff --git a/crates/nu_plugin_textview/src/textview.rs b/crates/nu_plugin_textview/src/textview.rs new file mode 100644 index 0000000000..d3bf1be811 --- /dev/null +++ b/crates/nu_plugin_textview/src/textview.rs @@ -0,0 +1,289 @@ +use crossterm::{ + event::{KeyCode, KeyEvent}, + ExecutableCommand, +}; + +use nu_protocol::{Primitive, UntaggedValue, Value}; +use nu_source::AnchorLocation; + +use syntect::easy::HighlightLines; +use syntect::highlighting::{Style, ThemeSet}; +use syntect::parsing::SyntaxSet; + +use std::io::Write; +use std::path::Path; + +enum DrawCommand { + DrawString(Style, String), + NextLine, +} + +#[derive(Default)] +pub struct TextView; + +impl TextView { + pub fn new() -> TextView { + TextView + } +} + +fn paint_textview( + draw_commands: &[DrawCommand], + starting_row: usize, + use_color_buffer: bool, +) -> usize { + let size = crossterm::terminal::size().unwrap_or_else(|_| (80, 24)); + + // render + let mut pos = 0; + let width = size.0 as usize; + let height = size.1 as usize - 1; + let mut frame_buffer = vec![]; + + for command in draw_commands { + match command { + DrawCommand::DrawString(style, string) => { + for chr in string.chars() { + if chr == '\t' { + for _ in 0..8 { + frame_buffer.push(( + ' ', + style.foreground.r, + style.foreground.g, + style.foreground.b, + )); + } + pos += 8; + } else { + frame_buffer.push(( + chr, + style.foreground.r, + style.foreground.g, + style.foreground.b, + )); + pos += 1; + } + } + } + DrawCommand::NextLine => { + for _ in 0..(width - pos % width) { + frame_buffer.push((' ', 0, 0, 0)); + } + pos += width - pos % width; + } + } + } + + let num_frame_buffer_rows = frame_buffer.len() / width; + let buffer_needs_scrolling = num_frame_buffer_rows > height; + + // display + let mut ansi_strings = vec![]; + let mut normal_chars = vec![]; + + for c in + &frame_buffer[starting_row * width..std::cmp::min(pos, (starting_row + height) * width)] + { + if use_color_buffer { + ansi_strings.push(ansi_term::Colour::RGB(c.1, c.2, c.3).paint(format!("{}", c.0))); + } else { + normal_chars.push(c.0); + } + } + + if buffer_needs_scrolling { + let _ = std::io::stdout().execute(crossterm::cursor::MoveTo(0, 0)); + } + + if use_color_buffer { + print!("{}", ansi_term::ANSIStrings(&ansi_strings)); + } else { + let s: String = normal_chars.into_iter().collect(); + print!("{}", s); + } + + if buffer_needs_scrolling { + let _ = std::io::stdout().execute(crossterm::cursor::MoveTo(0, size.1)); + print!( + "{}", + ansi_term::Colour::Blue.paint("[ESC to quit, arrow keys to move]") + ); + } + + let _ = std::io::stdout().flush(); + + num_frame_buffer_rows +} + +fn scroll_view_lines_if_needed(draw_commands: Vec, use_color_buffer: bool) { + let mut starting_row = 0; + + if let Ok(_raw) = crossterm::terminal::enable_raw_mode() { + let mut size = crossterm::terminal::size().unwrap_or_else(|_| (80, 24)); + let height = size.1 as usize - 1; + + let mut max_bottom_line = paint_textview(&draw_commands, starting_row, use_color_buffer); + + // Only scroll if needed + if max_bottom_line > height as usize { + let _ = std::io::stdout().execute(crossterm::cursor::Hide); + + loop { + if let Ok(ev) = crossterm::event::read() { + if let crossterm::event::Event::Key(KeyEvent { code, modifiers }) = ev { + match code { + KeyCode::Esc => { + break; + } + KeyCode::Up | KeyCode::Char('k') => { + if starting_row > 0 { + starting_row -= 1; + max_bottom_line = paint_textview( + &draw_commands, + starting_row, + use_color_buffer, + ); + } + } + KeyCode::Down | KeyCode::Char('j') => { + if starting_row < (max_bottom_line - height) { + starting_row += 1; + } + max_bottom_line = + paint_textview(&draw_commands, starting_row, use_color_buffer); + } + KeyCode::Char('b') + if modifiers.contains(crossterm::event::KeyModifiers::CONTROL) => + { + starting_row -= std::cmp::min(height, starting_row); + max_bottom_line = + paint_textview(&draw_commands, starting_row, use_color_buffer); + } + KeyCode::PageUp => { + starting_row -= std::cmp::min(height, starting_row); + max_bottom_line = + paint_textview(&draw_commands, starting_row, use_color_buffer); + } + KeyCode::Char('f') + if modifiers.contains(crossterm::event::KeyModifiers::CONTROL) => + { + if starting_row < (max_bottom_line - height) { + starting_row += height; + + if starting_row > (max_bottom_line - height) { + starting_row = max_bottom_line - height; + } + } + max_bottom_line = + paint_textview(&draw_commands, starting_row, use_color_buffer); + } + KeyCode::PageDown | KeyCode::Char(' ') => { + if starting_row < (max_bottom_line - height) { + starting_row += height; + + if starting_row > (max_bottom_line - height) { + starting_row = max_bottom_line - height; + } + } + max_bottom_line = + paint_textview(&draw_commands, starting_row, use_color_buffer); + } + _ => {} + } + } + } + + if let Ok(new_size) = crossterm::terminal::size() { + if size != new_size { + size = new_size; + let _ = std::io::stdout().execute(crossterm::terminal::Clear( + crossterm::terminal::ClearType::All, + )); + max_bottom_line = + paint_textview(&draw_commands, starting_row, use_color_buffer); + } + } + } + let _ = std::io::stdout().execute(crossterm::cursor::Show); + + let _ = crossterm::terminal::disable_raw_mode(); + } + } + + println!() +} + +fn scroll_view(s: &str) { + let mut v = vec![]; + for line in s.lines() { + v.push(DrawCommand::DrawString(Style::default(), line.to_string())); + v.push(DrawCommand::NextLine); + } + scroll_view_lines_if_needed(v, false); +} + +pub fn view_text_value(value: &Value) { + let value_anchor = value.anchor(); + if let UntaggedValue::Primitive(Primitive::String(ref s)) = &value.value { + if let Some(source) = value_anchor { + let extension: Option = match source { + AnchorLocation::File(file) => { + let path = Path::new(&file); + path.extension().map(|x| x.to_string_lossy().to_string()) + } + AnchorLocation::Url(url) => { + let url = url::Url::parse(&url); + if let Ok(url) = url { + if let Some(mut segments) = url.path_segments() { + if let Some(file) = segments.next_back() { + let path = Path::new(file); + path.extension().map(|x| x.to_string_lossy().to_string()) + } else { + None + } + } else { + None + } + } else { + None + } + } + //FIXME: this probably isn't correct + AnchorLocation::Source(_source) => None, + }; + + match extension { + Some(extension) => { + // Load these once at the start of your program + let ps: SyntaxSet = + syntect::dumps::from_binary(include_bytes!("assets/syntaxes.bin")); + + if let Some(syntax) = ps.find_syntax_by_extension(&extension) { + let ts: ThemeSet = + syntect::dumps::from_binary(include_bytes!("assets/themes.bin")); + let mut h = HighlightLines::new(syntax, &ts.themes["OneHalfDark"]); + + let mut v = vec![]; + for line in s.lines() { + let ranges: Vec<(Style, &str)> = h.highlight(line, &ps); + + for range in ranges { + v.push(DrawCommand::DrawString(range.0, range.1.to_string())); + } + + v.push(DrawCommand::NextLine); + } + scroll_view_lines_if_needed(v, true); + } else { + scroll_view(s); + } + } + _ => { + scroll_view(s); + } + } + } else { + scroll_view(s); + } + } +} diff --git a/src/plugins/nu_plugin_core_ps.rs b/src/plugins/nu_plugin_core_ps.rs index ce265ca7f3..83b2348264 100644 --- a/src/plugins/nu_plugin_core_ps.rs +++ b/src/plugins/nu_plugin_core_ps.rs @@ -1,93 +1,5 @@ -use futures::executor::block_on; -//use futures::stream::TryStreamExt; - -use futures_util::{StreamExt, TryStreamExt}; -use heim::process::{self as process, Process, ProcessResult}; -use heim::units::{information, ratio, Ratio}; -use std::usize; - -use nu_errors::ShellError; -use nu_plugin::{serve_plugin, Plugin}; -use nu_protocol::{ - CallInfo, ReturnSuccess, ReturnValue, Signature, TaggedDictBuilder, UntaggedValue, Value, -}; -use nu_source::Tag; - -use std::time::Duration; - -struct Ps; -impl Ps { - fn new() -> Ps { - Ps - } -} - -async fn usage(process: Process) -> ProcessResult<(process::Process, Ratio, process::Memory)> { - let usage_1 = process.cpu_usage().await?; - futures_timer::Delay::new(Duration::from_millis(100)).await; - let usage_2 = process.cpu_usage().await?; - - let memory = process.memory().await?; - - Ok((process, usage_2 - usage_1, memory)) -} - -async fn ps(tag: Tag) -> Vec { - let processes = process::processes() - .map_ok(|process| { - // Note that there is no `.await` here, - // as we want to pass the returned future - // into the `.try_buffer_unordered`. - usage(process) - }) - .try_buffer_unordered(usize::MAX); - pin_utils::pin_mut!(processes); - - let mut output = vec![]; - while let Some(res) = processes.next().await { - if let Ok((process, usage, memory)) = res { - let mut dict = TaggedDictBuilder::new(&tag); - dict.insert_untagged("pid", UntaggedValue::int(process.pid())); - if let Ok(name) = process.name().await { - dict.insert_untagged("name", UntaggedValue::string(name)); - } - if let Ok(status) = process.status().await { - dict.insert_untagged("status", UntaggedValue::string(format!("{:?}", status))); - } - dict.insert_untagged("cpu", UntaggedValue::decimal(usage.get::())); - dict.insert_untagged( - "mem", - UntaggedValue::bytes(memory.rss().get::()), - ); - dict.insert_untagged( - "virtual", - UntaggedValue::bytes(memory.vms().get::()), - ); - output.push(dict.into_value()); - } - } - - output -} - -impl Plugin for Ps { - fn config(&mut self) -> Result { - Ok(Signature::build("ps") - .desc("View information about system processes.") - .filter()) - } - - fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { - Ok(block_on(ps(callinfo.name_tag)) - .into_iter() - .map(ReturnSuccess::value) - .collect()) - } - - fn filter(&mut self, _: Value) -> Result, ShellError> { - Ok(vec![]) - } -} +use nu_plugin::serve_plugin; +use nu_plugin_ps::Ps; fn main() { serve_plugin(&mut Ps::new()); diff --git a/src/plugins/nu_plugin_core_sys.rs b/src/plugins/nu_plugin_core_sys.rs index 9d482b7506..5d4e339595 100644 --- a/src/plugins/nu_plugin_core_sys.rs +++ b/src/plugins/nu_plugin_core_sys.rs @@ -1,354 +1,5 @@ -use std::ffi::OsStr; - -use futures::executor::block_on; -//use futures::stream::StreamExt; -use futures_util::StreamExt; -use heim::units::{frequency, information, thermodynamic_temperature, time}; -use heim::{disk, host, memory, net, sensors}; -use nu_errors::ShellError; -use nu_plugin::{serve_plugin, Plugin}; -use nu_protocol::{ - CallInfo, ReturnSuccess, ReturnValue, Signature, TaggedDictBuilder, UntaggedValue, Value, -}; -use nu_source::Tag; - -struct Sys; -impl Sys { - fn new() -> Sys { - Sys - } -} - -async fn cpu(tag: Tag) -> Option { - match futures::future::try_join(heim::cpu::logical_count(), heim::cpu::frequency()).await { - Ok((num_cpu, cpu_speed)) => { - let mut cpu_idx = TaggedDictBuilder::with_capacity(tag, 4); - cpu_idx.insert_untagged("cores", UntaggedValue::int(num_cpu)); - - let current_speed = - (cpu_speed.current().get::() as f64 / 1_000_000_000.0 * 100.0) - .round() - / 100.0; - cpu_idx.insert_untagged("current ghz", UntaggedValue::decimal(current_speed)); - - if let Some(min_speed) = cpu_speed.min() { - let min_speed = - (min_speed.get::() as f64 / 1_000_000_000.0 * 100.0).round() - / 100.0; - cpu_idx.insert_untagged("min ghz", UntaggedValue::decimal(min_speed)); - } - - if let Some(max_speed) = cpu_speed.max() { - let max_speed = - (max_speed.get::() as f64 / 1_000_000_000.0 * 100.0).round() - / 100.0; - cpu_idx.insert_untagged("max ghz", UntaggedValue::decimal(max_speed)); - } - - Some(cpu_idx.into_value()) - } - Err(_) => None, - } -} - -async fn mem(tag: Tag) -> Value { - let mut dict = TaggedDictBuilder::with_capacity(tag, 4); - - let (memory_result, swap_result) = - futures::future::join(memory::memory(), memory::swap()).await; - - if let Ok(memory) = memory_result { - dict.insert_untagged( - "total", - UntaggedValue::bytes(memory.total().get::()), - ); - dict.insert_untagged( - "free", - UntaggedValue::bytes(memory.free().get::()), - ); - } - - if let Ok(swap) = swap_result { - dict.insert_untagged( - "swap total", - UntaggedValue::bytes(swap.total().get::()), - ); - dict.insert_untagged( - "swap free", - UntaggedValue::bytes(swap.free().get::()), - ); - } - - dict.into_value() -} - -async fn host(tag: Tag) -> Value { - let mut dict = TaggedDictBuilder::with_capacity(&tag, 6); - - let (platform_result, uptime_result) = - futures::future::join(host::platform(), host::uptime()).await; - - // OS - if let Ok(platform) = platform_result { - dict.insert_untagged("name", UntaggedValue::string(platform.system())); - dict.insert_untagged("release", UntaggedValue::string(platform.release())); - dict.insert_untagged("hostname", UntaggedValue::string(platform.hostname())); - dict.insert_untagged( - "arch", - UntaggedValue::string(platform.architecture().as_str()), - ); - } - - // Uptime - if let Ok(uptime) = uptime_result { - let mut uptime_dict = TaggedDictBuilder::with_capacity(&tag, 4); - - let uptime = uptime.get::().round() as i64; - let days = uptime / (60 * 60 * 24); - let hours = (uptime - days * 60 * 60 * 24) / (60 * 60); - let minutes = (uptime - days * 60 * 60 * 24 - hours * 60 * 60) / 60; - let seconds = uptime % 60; - - uptime_dict.insert_untagged("days", UntaggedValue::int(days)); - uptime_dict.insert_untagged("hours", UntaggedValue::int(hours)); - uptime_dict.insert_untagged("mins", UntaggedValue::int(minutes)); - uptime_dict.insert_untagged("secs", UntaggedValue::int(seconds)); - - dict.insert_value("uptime", uptime_dict); - } - - // Users - let mut users = host::users(); - let mut user_vec = vec![]; - while let Some(user) = users.next().await { - if let Ok(user) = user { - user_vec.push(Value { - value: UntaggedValue::string(user.username()), - tag: tag.clone(), - }); - } - } - let user_list = UntaggedValue::Table(user_vec); - dict.insert_untagged("users", user_list); - - dict.into_value() -} - -async fn disks(tag: Tag) -> Option { - let mut output = vec![]; - let mut partitions = disk::partitions_physical(); - while let Some(part) = partitions.next().await { - if let Ok(part) = part { - let mut dict = TaggedDictBuilder::with_capacity(&tag, 6); - dict.insert_untagged( - "device", - UntaggedValue::string( - part.device() - .unwrap_or_else(|| OsStr::new("N/A")) - .to_string_lossy(), - ), - ); - - dict.insert_untagged("type", UntaggedValue::string(part.file_system().as_str())); - dict.insert_untagged( - "mount", - UntaggedValue::string(part.mount_point().to_string_lossy()), - ); - - if let Ok(usage) = disk::usage(part.mount_point().to_path_buf()).await { - dict.insert_untagged( - "total", - UntaggedValue::bytes(usage.total().get::()), - ); - dict.insert_untagged( - "used", - UntaggedValue::bytes(usage.used().get::()), - ); - dict.insert_untagged( - "free", - UntaggedValue::bytes(usage.free().get::()), - ); - } - - output.push(dict.into_value()); - } - } - - if !output.is_empty() { - Some(UntaggedValue::Table(output)) - } else { - None - } -} - -async fn battery(tag: Tag) -> Option { - let mut output = vec![]; - - if let Ok(manager) = battery::Manager::new() { - if let Ok(batteries) = manager.batteries() { - for battery in batteries { - if let Ok(battery) = battery { - let mut dict = TaggedDictBuilder::new(&tag); - if let Some(vendor) = battery.vendor() { - dict.insert_untagged("vendor", UntaggedValue::string(vendor)); - } - if let Some(model) = battery.model() { - dict.insert_untagged("model", UntaggedValue::string(model)); - } - if let Some(cycles) = battery.cycle_count() { - dict.insert_untagged("cycles", UntaggedValue::int(cycles)); - } - if let Some(time_to_full) = battery.time_to_full() { - dict.insert_untagged( - "mins to full", - UntaggedValue::decimal( - time_to_full.get::(), - ), - ); - } - if let Some(time_to_empty) = battery.time_to_empty() { - dict.insert_untagged( - "mins to empty", - UntaggedValue::decimal( - time_to_empty.get::(), - ), - ); - } - output.push(dict.into_value()); - } - } - } - } - - if !output.is_empty() { - Some(UntaggedValue::Table(output)) - } else { - None - } -} - -async fn temp(tag: Tag) -> Option { - let mut output = vec![]; - - let mut sensors = sensors::temperatures(); - while let Some(sensor) = sensors.next().await { - if let Ok(sensor) = sensor { - let mut dict = TaggedDictBuilder::new(&tag); - dict.insert_untagged("unit", UntaggedValue::string(sensor.unit())); - if let Some(label) = sensor.label() { - dict.insert_untagged("label", UntaggedValue::string(label)); - } - dict.insert_untagged( - "temp", - UntaggedValue::decimal( - sensor - .current() - .get::(), - ), - ); - if let Some(high) = sensor.high() { - dict.insert_untagged( - "high", - UntaggedValue::decimal(high.get::()), - ); - } - if let Some(critical) = sensor.critical() { - dict.insert_untagged( - "critical", - UntaggedValue::decimal( - critical.get::(), - ), - ); - } - - output.push(dict.into_value()); - } - } - - if !output.is_empty() { - Some(UntaggedValue::Table(output)) - } else { - None - } -} - -async fn net(tag: Tag) -> Option { - let mut output = vec![]; - let mut io_counters = net::io_counters(); - while let Some(nic) = io_counters.next().await { - if let Ok(nic) = nic { - let mut network_idx = TaggedDictBuilder::with_capacity(&tag, 3); - network_idx.insert_untagged("name", UntaggedValue::string(nic.interface())); - network_idx.insert_untagged( - "sent", - UntaggedValue::bytes(nic.bytes_sent().get::()), - ); - network_idx.insert_untagged( - "recv", - UntaggedValue::bytes(nic.bytes_recv().get::()), - ); - output.push(network_idx.into_value()); - } - } - if !output.is_empty() { - Some(UntaggedValue::Table(output)) - } else { - None - } -} - -async fn sysinfo(tag: Tag) -> Vec { - let mut sysinfo = TaggedDictBuilder::with_capacity(&tag, 7); - - let (host, cpu, disks, memory, temp) = futures::future::join5( - host(tag.clone()), - cpu(tag.clone()), - disks(tag.clone()), - mem(tag.clone()), - temp(tag.clone()), - ) - .await; - let (net, battery) = futures::future::join(net(tag.clone()), battery(tag.clone())).await; - - sysinfo.insert_value("host", host); - if let Some(cpu) = cpu { - sysinfo.insert_value("cpu", cpu); - } - if let Some(disks) = disks { - sysinfo.insert_untagged("disks", disks); - } - sysinfo.insert_value("mem", memory); - if let Some(temp) = temp { - sysinfo.insert_untagged("temp", temp); - } - if let Some(net) = net { - sysinfo.insert_untagged("net", net); - } - if let Some(battery) = battery { - sysinfo.insert_untagged("battery", battery); - } - - vec![sysinfo.into_value()] -} - -impl Plugin for Sys { - fn config(&mut self) -> Result { - Ok(Signature::build("sys") - .desc("View information about the current system.") - .filter()) - } - - fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { - Ok(block_on(sysinfo(callinfo.name_tag)) - .into_iter() - .map(ReturnSuccess::value) - .collect()) - } - - fn filter(&mut self, _: Value) -> Result, ShellError> { - Ok(vec![]) - } -} +use nu_plugin::serve_plugin; +use nu_plugin_sys::Sys; fn main() { serve_plugin(&mut Sys::new()); diff --git a/src/plugins/nu_plugin_core_textview.rs b/src/plugins/nu_plugin_core_textview.rs index 9f6860bb1c..1425efeada 100644 --- a/src/plugins/nu_plugin_core_textview.rs +++ b/src/plugins/nu_plugin_core_textview.rs @@ -1,304 +1,5 @@ -use crossterm::{ - event::{KeyCode, KeyEvent}, - ExecutableCommand, -}; -use nu_errors::ShellError; -use nu_plugin::{serve_plugin, Plugin}; -use nu_protocol::{outln, CallInfo, Primitive, Signature, UntaggedValue, Value}; -use nu_source::AnchorLocation; - -use syntect::easy::HighlightLines; -use syntect::highlighting::{Style, ThemeSet}; -use syntect::parsing::SyntaxSet; - -use std::io::Write; -use std::path::Path; - -enum DrawCommand { - DrawString(Style, String), - NextLine, -} - -struct TextView; - -impl TextView { - fn new() -> TextView { - TextView - } -} - -impl Plugin for TextView { - fn config(&mut self) -> Result { - Ok(Signature::build("textview").desc("Autoview of text data.")) - } - - fn sink(&mut self, _call_info: CallInfo, input: Vec) { - if !input.is_empty() { - view_text_value(&input[0]); - } - } -} - -fn paint_textview( - draw_commands: &[DrawCommand], - starting_row: usize, - use_color_buffer: bool, -) -> usize { - let size = crossterm::terminal::size().unwrap_or_else(|_| (80, 24)); - - // render - let mut pos = 0; - let width = size.0 as usize; - let height = size.1 as usize - 1; - let mut frame_buffer = vec![]; - - for command in draw_commands { - match command { - DrawCommand::DrawString(style, string) => { - for chr in string.chars() { - if chr == '\t' { - for _ in 0..8 { - frame_buffer.push(( - ' ', - style.foreground.r, - style.foreground.g, - style.foreground.b, - )); - } - pos += 8; - } else { - frame_buffer.push(( - chr, - style.foreground.r, - style.foreground.g, - style.foreground.b, - )); - pos += 1; - } - } - } - DrawCommand::NextLine => { - for _ in 0..(width - pos % width) { - frame_buffer.push((' ', 0, 0, 0)); - } - pos += width - pos % width; - } - } - } - - let num_frame_buffer_rows = frame_buffer.len() / width; - let buffer_needs_scrolling = num_frame_buffer_rows > height; - - // display - let mut ansi_strings = vec![]; - let mut normal_chars = vec![]; - - for c in - &frame_buffer[starting_row * width..std::cmp::min(pos, (starting_row + height) * width)] - { - if use_color_buffer { - ansi_strings.push(ansi_term::Colour::RGB(c.1, c.2, c.3).paint(format!("{}", c.0))); - } else { - normal_chars.push(c.0); - } - } - - if buffer_needs_scrolling { - let _ = std::io::stdout().execute(crossterm::cursor::MoveTo(0, 0)); - } - - if use_color_buffer { - print!("{}", ansi_term::ANSIStrings(&ansi_strings)); - } else { - let s: String = normal_chars.into_iter().collect(); - print!("{}", s); - } - - if buffer_needs_scrolling { - let _ = std::io::stdout().execute(crossterm::cursor::MoveTo(0, size.1)); - print!( - "{}", - ansi_term::Colour::Blue.paint("[ESC to quit, arrow keys to move]") - ); - } - - let _ = std::io::stdout().flush(); - - num_frame_buffer_rows -} - -fn scroll_view_lines_if_needed(draw_commands: Vec, use_color_buffer: bool) { - let mut starting_row = 0; - - if let Ok(_raw) = crossterm::terminal::enable_raw_mode() { - let mut size = crossterm::terminal::size().unwrap_or_else(|_| (80, 24)); - let height = size.1 as usize - 1; - - let mut max_bottom_line = paint_textview(&draw_commands, starting_row, use_color_buffer); - - // Only scroll if needed - if max_bottom_line > height as usize { - let _ = std::io::stdout().execute(crossterm::cursor::Hide); - - loop { - if let Ok(ev) = crossterm::event::read() { - if let crossterm::event::Event::Key(KeyEvent { code, modifiers }) = ev { - match code { - KeyCode::Esc => { - break; - } - KeyCode::Up | KeyCode::Char('k') => { - if starting_row > 0 { - starting_row -= 1; - max_bottom_line = paint_textview( - &draw_commands, - starting_row, - use_color_buffer, - ); - } - } - KeyCode::Down | KeyCode::Char('j') => { - if starting_row < (max_bottom_line - height) { - starting_row += 1; - } - max_bottom_line = - paint_textview(&draw_commands, starting_row, use_color_buffer); - } - KeyCode::Char('b') - if modifiers.contains(crossterm::event::KeyModifiers::CONTROL) => - { - starting_row -= std::cmp::min(height, starting_row); - max_bottom_line = - paint_textview(&draw_commands, starting_row, use_color_buffer); - } - KeyCode::PageUp => { - starting_row -= std::cmp::min(height, starting_row); - max_bottom_line = - paint_textview(&draw_commands, starting_row, use_color_buffer); - } - KeyCode::Char('f') - if modifiers.contains(crossterm::event::KeyModifiers::CONTROL) => - { - if starting_row < (max_bottom_line - height) { - starting_row += height; - - if starting_row > (max_bottom_line - height) { - starting_row = max_bottom_line - height; - } - } - max_bottom_line = - paint_textview(&draw_commands, starting_row, use_color_buffer); - } - KeyCode::PageDown | KeyCode::Char(' ') => { - if starting_row < (max_bottom_line - height) { - starting_row += height; - - if starting_row > (max_bottom_line - height) { - starting_row = max_bottom_line - height; - } - } - max_bottom_line = - paint_textview(&draw_commands, starting_row, use_color_buffer); - } - _ => {} - } - } - } - - if let Ok(new_size) = crossterm::terminal::size() { - if size != new_size { - size = new_size; - let _ = std::io::stdout().execute(crossterm::terminal::Clear( - crossterm::terminal::ClearType::All, - )); - max_bottom_line = - paint_textview(&draw_commands, starting_row, use_color_buffer); - } - } - } - let _ = std::io::stdout().execute(crossterm::cursor::Show); - - let _ = crossterm::terminal::disable_raw_mode(); - } - } - - outln!(""); -} - -fn scroll_view(s: &str) { - let mut v = vec![]; - for line in s.lines() { - v.push(DrawCommand::DrawString(Style::default(), line.to_string())); - v.push(DrawCommand::NextLine); - } - scroll_view_lines_if_needed(v, false); -} - -fn view_text_value(value: &Value) { - let value_anchor = value.anchor(); - if let UntaggedValue::Primitive(Primitive::String(ref s)) = &value.value { - if let Some(source) = value_anchor { - let extension: Option = match source { - AnchorLocation::File(file) => { - let path = Path::new(&file); - path.extension().map(|x| x.to_string_lossy().to_string()) - } - AnchorLocation::Url(url) => { - let url = url::Url::parse(&url); - if let Ok(url) = url { - if let Some(mut segments) = url.path_segments() { - if let Some(file) = segments.next_back() { - let path = Path::new(file); - path.extension().map(|x| x.to_string_lossy().to_string()) - } else { - None - } - } else { - None - } - } else { - None - } - } - //FIXME: this probably isn't correct - AnchorLocation::Source(_source) => None, - }; - - match extension { - Some(extension) => { - // Load these once at the start of your program - let ps: SyntaxSet = - syntect::dumps::from_binary(include_bytes!("../../assets/syntaxes.bin")); - - if let Some(syntax) = ps.find_syntax_by_extension(&extension) { - let ts: ThemeSet = - syntect::dumps::from_binary(include_bytes!("../../assets/themes.bin")); - let mut h = HighlightLines::new(syntax, &ts.themes["OneHalfDark"]); - - let mut v = vec![]; - for line in s.lines() { - let ranges: Vec<(Style, &str)> = h.highlight(line, &ps); - - for range in ranges { - v.push(DrawCommand::DrawString(range.0, range.1.to_string())); - } - - v.push(DrawCommand::NextLine); - } - scroll_view_lines_if_needed(v, true); - } else { - scroll_view(s); - } - } - _ => { - scroll_view(s); - } - } - } else { - scroll_view(s); - } - } -} +use nu_plugin::serve_plugin; +use nu_plugin_textview::TextView; fn main() { serve_plugin(&mut TextView::new());