Merge branch 'main' into interrupt
This commit is contained in:
commit
ae2cff8c24
8
.github/workflows/nightly-build.yml
vendored
8
.github/workflows/nightly-build.yml
vendored
|
@ -99,13 +99,13 @@ jobs:
|
|||
extra: msi
|
||||
os: windows-latest
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-22.04
|
||||
- target: x86_64-unknown-linux-musl
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-22.04
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-22.04
|
||||
- target: armv7-unknown-linux-gnueabihf
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-22.04
|
||||
- target: riscv64gc-unknown-linux-gnu
|
||||
os: ubuntu-latest
|
||||
|
||||
|
|
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
|
@ -49,13 +49,13 @@ jobs:
|
|||
extra: msi
|
||||
os: windows-latest
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-22.04
|
||||
- target: x86_64-unknown-linux-musl
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-22.04
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-22.04
|
||||
- target: armv7-unknown-linux-gnueabihf
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-22.04
|
||||
- target: riscv64gc-unknown-linux-gnu
|
||||
os: ubuntu-latest
|
||||
|
||||
|
|
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -2382,9 +2382,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.3"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.5",
|
||||
|
@ -4988,8 +4988,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "reedline"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abf59e4c97b5049ba96b052cdb652368305a2eddcbce9bf1c16f9d003139eeea"
|
||||
source = "git+https://github.com/nushell/reedline?branch=main#480059a3f52cf919341cda88e8c544edd846bc73"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"chrono",
|
||||
|
|
|
@ -197,7 +197,6 @@ nu-protocol = { path = "./crates/nu-protocol", version = "0.95.1" }
|
|||
nu-std = { path = "./crates/nu-std", version = "0.95.1" }
|
||||
nu-system = { path = "./crates/nu-system", version = "0.95.1" }
|
||||
nu-utils = { path = "./crates/nu-utils", version = "0.95.1" }
|
||||
|
||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||
|
||||
crossterm = { workspace = true }
|
||||
|
@ -304,7 +303,7 @@ bench = false
|
|||
# To use a development version of a dependency please use a global override here
|
||||
# changing versions in each sub-crate of the workspace is tedious
|
||||
[patch.crates-io]
|
||||
# reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
|
||||
reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
|
||||
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
|
||||
|
||||
# Run all benchmarks with `cargo bench`
|
||||
|
|
|
@ -333,6 +333,13 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||
.with_quick_completions(config.quick_completions)
|
||||
.with_partial_completions(config.partial_completions)
|
||||
.with_ansi_colors(config.use_ansi_coloring)
|
||||
.with_cwd(Some(
|
||||
engine_state
|
||||
.cwd(None)
|
||||
.unwrap_or_default()
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
))
|
||||
.with_cursor_config(cursor_config);
|
||||
|
||||
perf!("reedline builder", start_time, use_color);
|
||||
|
@ -663,13 +670,14 @@ fn prepare_history_metadata(
|
|||
line_editor: &mut Reedline,
|
||||
) {
|
||||
if !s.is_empty() && line_editor.has_last_command_context() {
|
||||
#[allow(deprecated)]
|
||||
let result = line_editor
|
||||
.update_last_command_context(&|mut c| {
|
||||
c.start_timestamp = Some(chrono::Utc::now());
|
||||
c.hostname = hostname.map(str::to_string);
|
||||
|
||||
c.cwd = Some(StateWorkingSet::new(engine_state).get_cwd());
|
||||
c.cwd = engine_state
|
||||
.cwd(None)
|
||||
.ok()
|
||||
.map(|path| path.to_string_lossy().to_string());
|
||||
c
|
||||
})
|
||||
.into_diagnostic();
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use std::io::{self, Read, Write};
|
||||
|
||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
use nu_protocol::Signals;
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
pub struct Arguments {
|
||||
|
@ -118,15 +121,46 @@ fn into_bits(
|
|||
let cell_paths = call.rest(engine_state, stack, 0)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
|
||||
if let PipelineData::ByteStream(stream, ..) = input {
|
||||
// TODO: in the future, we may want this to stream out, converting each to bytes
|
||||
Ok(Value::binary(stream.into_bytes()?, head).into_pipeline_data())
|
||||
if let PipelineData::ByteStream(stream, metadata) = input {
|
||||
Ok(PipelineData::ByteStream(
|
||||
byte_stream_to_bits(stream, head),
|
||||
metadata,
|
||||
))
|
||||
} else {
|
||||
let args = Arguments { cell_paths };
|
||||
operate(action, args, input, call.head, engine_state.signals())
|
||||
}
|
||||
}
|
||||
|
||||
fn byte_stream_to_bits(stream: ByteStream, head: Span) -> ByteStream {
|
||||
if let Some(mut reader) = stream.reader() {
|
||||
let mut is_first = true;
|
||||
ByteStream::from_fn(
|
||||
head,
|
||||
Signals::empty(),
|
||||
ByteStreamType::String,
|
||||
move |buffer| {
|
||||
let mut byte = [0];
|
||||
if reader.read(&mut byte[..]).err_span(head)? > 0 {
|
||||
// Format the byte as bits
|
||||
if is_first {
|
||||
is_first = false;
|
||||
} else {
|
||||
buffer.push(b' ');
|
||||
}
|
||||
write!(buffer, "{:08b}", byte[0]).expect("format failed");
|
||||
Ok(true)
|
||||
} else {
|
||||
// EOF
|
||||
Ok(false)
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
ByteStream::read(io::empty(), head, Signals::empty(), ByteStreamType::String)
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to_smallest_number_type(num: i64, span: Span) -> Value {
|
||||
if let Some(v) = num.to_i8() {
|
||||
let bytes = v.to_ne_bytes();
|
||||
|
|
13
crates/nu-cmd-extra/tests/commands/bits/into.rs
Normal file
13
crates/nu-cmd-extra/tests/commands/bits/into.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use nu_test_support::nu;
|
||||
|
||||
#[test]
|
||||
fn byte_stream_into_bits() {
|
||||
let result = nu!("[0x[01] 0x[02 03]] | bytes collect | into bits");
|
||||
assert_eq!("00000001 00000010 00000011", result.out);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn byte_stream_into_bits_is_stream() {
|
||||
let result = nu!("[0x[01] 0x[02 03]] | bytes collect | into bits | describe");
|
||||
assert_eq!("string (stream)", result.out);
|
||||
}
|
1
crates/nu-cmd-extra/tests/commands/bits/mod.rs
Normal file
1
crates/nu-cmd-extra/tests/commands/bits/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
mod into;
|
|
@ -1 +1,2 @@
|
|||
mod bits;
|
||||
mod bytes;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use nu_engine::command_prelude::*;
|
||||
use std::str::FromStr;
|
||||
use toml::value::{Datetime, Offset};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FromToml;
|
||||
|
@ -56,6 +56,54 @@ b = [1, 2]' | from toml",
|
|||
}
|
||||
}
|
||||
|
||||
fn convert_toml_datetime_to_value(dt: &Datetime, span: Span) -> Value {
|
||||
match &dt.clone() {
|
||||
toml::value::Datetime {
|
||||
date: Some(_),
|
||||
time: _,
|
||||
offset: _,
|
||||
} => (),
|
||||
_ => return Value::string(dt.to_string(), span),
|
||||
}
|
||||
|
||||
let date = match dt.date {
|
||||
Some(date) => {
|
||||
chrono::NaiveDate::from_ymd_opt(date.year.into(), date.month.into(), date.day.into())
|
||||
}
|
||||
None => Some(chrono::NaiveDate::default()),
|
||||
};
|
||||
|
||||
let time = match dt.time {
|
||||
Some(time) => chrono::NaiveTime::from_hms_nano_opt(
|
||||
time.hour.into(),
|
||||
time.minute.into(),
|
||||
time.second.into(),
|
||||
time.nanosecond,
|
||||
),
|
||||
None => Some(chrono::NaiveTime::default()),
|
||||
};
|
||||
|
||||
let tz = match dt.offset {
|
||||
Some(offset) => match offset {
|
||||
Offset::Z => chrono::FixedOffset::east_opt(0),
|
||||
Offset::Custom { minutes: min } => chrono::FixedOffset::east_opt(min as i32 * 60),
|
||||
},
|
||||
None => chrono::FixedOffset::east_opt(0),
|
||||
};
|
||||
|
||||
let datetime = match (date, time, tz) {
|
||||
(Some(date), Some(time), Some(tz)) => chrono::NaiveDateTime::new(date, time)
|
||||
.and_local_timezone(tz)
|
||||
.earliest(),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
match datetime {
|
||||
Some(datetime) => Value::date(datetime, span),
|
||||
None => Value::string(dt.to_string(), span),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_toml_to_value(value: &toml::Value, span: Span) -> Value {
|
||||
match value {
|
||||
toml::Value::Array(array) => {
|
||||
|
@ -76,13 +124,7 @@ fn convert_toml_to_value(value: &toml::Value, span: Span) -> Value {
|
|||
span,
|
||||
),
|
||||
toml::Value::String(s) => Value::string(s.clone(), span),
|
||||
toml::Value::Datetime(d) => match chrono::DateTime::from_str(&d.to_string()) {
|
||||
Ok(nushell_date) => Value::date(nushell_date, span),
|
||||
// in the unlikely event that parsing goes wrong, this function still returns a valid
|
||||
// nushell date (however the default one). This decision was made to make the output of
|
||||
// this function uniform amongst all eventualities
|
||||
Err(_) => Value::date(chrono::DateTime::default(), span),
|
||||
},
|
||||
toml::Value::Datetime(dt) => convert_toml_datetime_to_value(dt, span),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,32 +155,6 @@ mod tests {
|
|||
test_examples(FromToml {})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_toml_creates_nushell_date() {
|
||||
let toml_date = toml::Value::Datetime(Datetime {
|
||||
date: Option::from(toml::value::Date {
|
||||
year: 1980,
|
||||
month: 10,
|
||||
day: 12,
|
||||
}),
|
||||
time: Option::from(toml::value::Time {
|
||||
hour: 10,
|
||||
minute: 12,
|
||||
second: 44,
|
||||
nanosecond: 0,
|
||||
}),
|
||||
offset: Option::from(toml::value::Offset::Custom { minutes: 120 }),
|
||||
});
|
||||
|
||||
let span = Span::test_data();
|
||||
let reference_date = Value::date(Default::default(), Span::test_data());
|
||||
|
||||
let result = convert_toml_to_value(&toml_date, span);
|
||||
|
||||
//positive test (from toml returns a nushell date)
|
||||
assert_eq!(result.get_type(), reference_date.get_type());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_toml_creates_correct_date() {
|
||||
let toml_date = toml::Value::Datetime(Datetime {
|
||||
|
@ -206,4 +222,113 @@ mod tests {
|
|||
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_toml_datetime_to_value_date_time_offset() {
|
||||
let toml_date = Datetime {
|
||||
date: Option::from(toml::value::Date {
|
||||
year: 2000,
|
||||
month: 1,
|
||||
day: 1,
|
||||
}),
|
||||
time: Option::from(toml::value::Time {
|
||||
hour: 12,
|
||||
minute: 12,
|
||||
second: 12,
|
||||
nanosecond: 0,
|
||||
}),
|
||||
offset: Option::from(toml::value::Offset::Custom { minutes: 120 }),
|
||||
};
|
||||
|
||||
let span = Span::test_data();
|
||||
let reference_date = Value::date(
|
||||
chrono::FixedOffset::east_opt(60 * 120)
|
||||
.unwrap()
|
||||
.with_ymd_and_hms(2000, 1, 1, 12, 12, 12)
|
||||
.unwrap(),
|
||||
span,
|
||||
);
|
||||
|
||||
let result = convert_toml_datetime_to_value(&toml_date, span);
|
||||
|
||||
assert_eq!(result, reference_date);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_toml_datetime_to_value_date_time() {
|
||||
let toml_date = Datetime {
|
||||
date: Option::from(toml::value::Date {
|
||||
year: 2000,
|
||||
month: 1,
|
||||
day: 1,
|
||||
}),
|
||||
time: Option::from(toml::value::Time {
|
||||
hour: 12,
|
||||
minute: 12,
|
||||
second: 12,
|
||||
nanosecond: 0,
|
||||
}),
|
||||
offset: None,
|
||||
};
|
||||
|
||||
let span = Span::test_data();
|
||||
let reference_date = Value::date(
|
||||
chrono::FixedOffset::east_opt(0)
|
||||
.unwrap()
|
||||
.with_ymd_and_hms(2000, 1, 1, 12, 12, 12)
|
||||
.unwrap(),
|
||||
span,
|
||||
);
|
||||
|
||||
let result = convert_toml_datetime_to_value(&toml_date, span);
|
||||
|
||||
assert_eq!(result, reference_date);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_toml_datetime_to_value_date() {
|
||||
let toml_date = Datetime {
|
||||
date: Option::from(toml::value::Date {
|
||||
year: 2000,
|
||||
month: 1,
|
||||
day: 1,
|
||||
}),
|
||||
time: None,
|
||||
offset: None,
|
||||
};
|
||||
|
||||
let span = Span::test_data();
|
||||
let reference_date = Value::date(
|
||||
chrono::FixedOffset::east_opt(0)
|
||||
.unwrap()
|
||||
.with_ymd_and_hms(2000, 1, 1, 0, 0, 0)
|
||||
.unwrap(),
|
||||
span,
|
||||
);
|
||||
|
||||
let result = convert_toml_datetime_to_value(&toml_date, span);
|
||||
|
||||
assert_eq!(result, reference_date);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_toml_datetime_to_value_only_time() {
|
||||
let toml_date = Datetime {
|
||||
date: None,
|
||||
time: Option::from(toml::value::Time {
|
||||
hour: 12,
|
||||
minute: 12,
|
||||
second: 12,
|
||||
nanosecond: 0,
|
||||
}),
|
||||
offset: None,
|
||||
};
|
||||
|
||||
let span = Span::test_data();
|
||||
let reference_date = Value::string(toml_date.to_string(), span);
|
||||
|
||||
let result = convert_toml_datetime_to_value(&toml_date, span);
|
||||
|
||||
assert_eq!(result, reference_date);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::ast::{Assignment, Bits, Boolean, Comparison, Math, Operator};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HelpOperators;
|
||||
|
@ -27,282 +28,148 @@ impl Command for HelpOperators {
|
|||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let op_info = generate_operator_info();
|
||||
let mut recs = vec![];
|
||||
|
||||
for op in op_info {
|
||||
recs.push(Value::record(
|
||||
let mut operators = [
|
||||
Operator::Assignment(Assignment::Assign),
|
||||
Operator::Assignment(Assignment::PlusAssign),
|
||||
Operator::Assignment(Assignment::AppendAssign),
|
||||
Operator::Assignment(Assignment::MinusAssign),
|
||||
Operator::Assignment(Assignment::MultiplyAssign),
|
||||
Operator::Assignment(Assignment::DivideAssign),
|
||||
Operator::Comparison(Comparison::Equal),
|
||||
Operator::Comparison(Comparison::NotEqual),
|
||||
Operator::Comparison(Comparison::LessThan),
|
||||
Operator::Comparison(Comparison::GreaterThan),
|
||||
Operator::Comparison(Comparison::LessThanOrEqual),
|
||||
Operator::Comparison(Comparison::GreaterThanOrEqual),
|
||||
Operator::Comparison(Comparison::RegexMatch),
|
||||
Operator::Comparison(Comparison::NotRegexMatch),
|
||||
Operator::Comparison(Comparison::In),
|
||||
Operator::Comparison(Comparison::NotIn),
|
||||
Operator::Comparison(Comparison::StartsWith),
|
||||
Operator::Comparison(Comparison::EndsWith),
|
||||
Operator::Math(Math::Plus),
|
||||
Operator::Math(Math::Append),
|
||||
Operator::Math(Math::Minus),
|
||||
Operator::Math(Math::Multiply),
|
||||
Operator::Math(Math::Divide),
|
||||
Operator::Math(Math::Modulo),
|
||||
Operator::Math(Math::FloorDivision),
|
||||
Operator::Math(Math::Pow),
|
||||
Operator::Bits(Bits::BitOr),
|
||||
Operator::Bits(Bits::BitXor),
|
||||
Operator::Bits(Bits::BitAnd),
|
||||
Operator::Bits(Bits::ShiftLeft),
|
||||
Operator::Bits(Bits::ShiftRight),
|
||||
Operator::Boolean(Boolean::And),
|
||||
Operator::Boolean(Boolean::Or),
|
||||
Operator::Boolean(Boolean::Xor),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|op| {
|
||||
Value::record(
|
||||
record! {
|
||||
"type" => Value::string(op.op_type, head),
|
||||
"operator" => Value::string(op.operator, head),
|
||||
"name" => Value::string(op.name, head),
|
||||
"description" => Value::string(op.description, head),
|
||||
"precedence" => Value::int(op.precedence, head),
|
||||
"type" => Value::string(op_type(&op), head),
|
||||
"operator" => Value::string(op.to_string(), head),
|
||||
"name" => Value::string(name(&op), head),
|
||||
"description" => Value::string(description(&op), head),
|
||||
"precedence" => Value::int(op.precedence().into(), head),
|
||||
},
|
||||
head,
|
||||
));
|
||||
}
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(Value::list(recs, head).into_pipeline_data())
|
||||
operators.push(Value::record(
|
||||
record! {
|
||||
"type" => Value::string("Boolean", head),
|
||||
"operator" => Value::string("not", head),
|
||||
"name" => Value::string("Not", head),
|
||||
"description" => Value::string("Negates a value or expression.", head),
|
||||
"precedence" => Value::int(0, head),
|
||||
},
|
||||
head,
|
||||
));
|
||||
|
||||
Ok(Value::list(operators, head).into_pipeline_data())
|
||||
}
|
||||
}
|
||||
|
||||
struct OperatorInfo {
|
||||
op_type: String,
|
||||
operator: String,
|
||||
name: String,
|
||||
description: String,
|
||||
precedence: i64,
|
||||
fn op_type(operator: &Operator) -> &'static str {
|
||||
match operator {
|
||||
Operator::Comparison(_) => "Comparison",
|
||||
Operator::Math(_) => "Math",
|
||||
Operator::Boolean(_) => "Boolean",
|
||||
Operator::Bits(_) => "Bitwise",
|
||||
Operator::Assignment(_) => "Assignment",
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_operator_info() -> Vec<OperatorInfo> {
|
||||
vec![
|
||||
OperatorInfo {
|
||||
op_type: "Assignment".into(),
|
||||
operator: "=".into(),
|
||||
name: "Assign".into(),
|
||||
description: "Assigns a value to a variable.".into(),
|
||||
precedence: 10,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Assignment".into(),
|
||||
operator: "+=".into(),
|
||||
name: "PlusAssign".into(),
|
||||
description: "Adds a value to a variable.".into(),
|
||||
precedence: 10,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Assignment".into(),
|
||||
operator: "++=".into(),
|
||||
name: "AppendAssign".into(),
|
||||
description: "Appends a list or a value to a variable.".into(),
|
||||
precedence: 10,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Assignment".into(),
|
||||
operator: "-=".into(),
|
||||
name: "MinusAssign".into(),
|
||||
description: "Subtracts a value from a variable.".into(),
|
||||
precedence: 10,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Assignment".into(),
|
||||
operator: "*=".into(),
|
||||
name: "MultiplyAssign".into(),
|
||||
description: "Multiplies a variable by a value.".into(),
|
||||
precedence: 10,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Assignment".into(),
|
||||
operator: "/=".into(),
|
||||
name: "DivideAssign".into(),
|
||||
description: "Divides a variable by a value.".into(),
|
||||
precedence: 10,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Comparison".into(),
|
||||
operator: "==".into(),
|
||||
name: "Equal".into(),
|
||||
description: "Checks if two values are equal.".into(),
|
||||
precedence: 80,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Comparison".into(),
|
||||
operator: "!=".into(),
|
||||
name: "NotEqual".into(),
|
||||
description: "Checks if two values are not equal.".into(),
|
||||
precedence: 80,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Comparison".into(),
|
||||
operator: "<".into(),
|
||||
name: "LessThan".into(),
|
||||
description: "Checks if a value is less than another.".into(),
|
||||
precedence: 80,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Comparison".into(),
|
||||
operator: "<=".into(),
|
||||
name: "LessThanOrEqual".into(),
|
||||
description: "Checks if a value is less than or equal to another.".into(),
|
||||
precedence: 80,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Comparison".into(),
|
||||
operator: ">".into(),
|
||||
name: "GreaterThan".into(),
|
||||
description: "Checks if a value is greater than another.".into(),
|
||||
precedence: 80,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Comparison".into(),
|
||||
operator: ">=".into(),
|
||||
name: "GreaterThanOrEqual".into(),
|
||||
description: "Checks if a value is greater than or equal to another.".into(),
|
||||
precedence: 80,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Comparison".into(),
|
||||
operator: "=~".into(),
|
||||
name: "RegexMatch".into(),
|
||||
description: "Checks if a value matches a regular expression.".into(),
|
||||
precedence: 80,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Comparison".into(),
|
||||
operator: "!~".into(),
|
||||
name: "NotRegexMatch".into(),
|
||||
description: "Checks if a value does not match a regular expression.".into(),
|
||||
precedence: 80,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Comparison".into(),
|
||||
operator: "in".into(),
|
||||
name: "In".into(),
|
||||
description: "Checks if a value is in a list or string.".into(),
|
||||
precedence: 80,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Comparison".into(),
|
||||
operator: "not-in".into(),
|
||||
name: "NotIn".into(),
|
||||
description: "Checks if a value is not in a list or string.".into(),
|
||||
precedence: 80,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Comparison".into(),
|
||||
operator: "starts-with".into(),
|
||||
name: "StartsWith".into(),
|
||||
description: "Checks if a string starts with another.".into(),
|
||||
precedence: 80,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Comparison".into(),
|
||||
operator: "ends-with".into(),
|
||||
name: "EndsWith".into(),
|
||||
description: "Checks if a string ends with another.".into(),
|
||||
precedence: 80,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Comparison".into(),
|
||||
operator: "not".into(),
|
||||
name: "UnaryNot".into(),
|
||||
description: "Negates a value or expression.".into(),
|
||||
precedence: 0,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Math".into(),
|
||||
operator: "+".into(),
|
||||
name: "Plus".into(),
|
||||
description: "Adds two values.".into(),
|
||||
precedence: 90,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Math".into(),
|
||||
operator: "++".into(),
|
||||
name: "Append".into(),
|
||||
description: "Appends two lists or a list and a value.".into(),
|
||||
precedence: 80,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Math".into(),
|
||||
operator: "-".into(),
|
||||
name: "Minus".into(),
|
||||
description: "Subtracts two values.".into(),
|
||||
precedence: 90,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Math".into(),
|
||||
operator: "*".into(),
|
||||
name: "Multiply".into(),
|
||||
description: "Multiplies two values.".into(),
|
||||
precedence: 95,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Math".into(),
|
||||
operator: "/".into(),
|
||||
name: "Divide".into(),
|
||||
description: "Divides two values.".into(),
|
||||
precedence: 95,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Math".into(),
|
||||
operator: "//".into(),
|
||||
name: "FloorDivision".into(),
|
||||
description: "Divides two values and floors the result.".into(),
|
||||
precedence: 95,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Math".into(),
|
||||
operator: "mod".into(),
|
||||
name: "Modulo".into(),
|
||||
description: "Divides two values and returns the remainder.".into(),
|
||||
precedence: 95,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Math".into(),
|
||||
operator: "**".into(),
|
||||
name: "Pow ".into(),
|
||||
description: "Raises one value to the power of another.".into(),
|
||||
precedence: 100,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Bitwise".into(),
|
||||
operator: "bit-or".into(),
|
||||
name: "BitOr".into(),
|
||||
description: "Performs a bitwise OR on two values.".into(),
|
||||
precedence: 60,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Bitwise".into(),
|
||||
operator: "bit-xor".into(),
|
||||
name: "BitXor".into(),
|
||||
description: "Performs a bitwise XOR on two values.".into(),
|
||||
precedence: 70,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Bitwise".into(),
|
||||
operator: "bit-and".into(),
|
||||
name: "BitAnd".into(),
|
||||
description: "Performs a bitwise AND on two values.".into(),
|
||||
precedence: 75,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Bitwise".into(),
|
||||
operator: "bit-shl".into(),
|
||||
name: "ShiftLeft".into(),
|
||||
description: "Shifts a value left by another.".into(),
|
||||
precedence: 85,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Bitwise".into(),
|
||||
operator: "bit-shr".into(),
|
||||
name: "ShiftRight".into(),
|
||||
description: "Shifts a value right by another.".into(),
|
||||
precedence: 85,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Boolean".into(),
|
||||
operator: "and".into(),
|
||||
name: "And".into(),
|
||||
description: "Checks if two values are true.".into(),
|
||||
precedence: 50,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Boolean".into(),
|
||||
operator: "or".into(),
|
||||
name: "Or".into(),
|
||||
description: "Checks if either value is true.".into(),
|
||||
precedence: 40,
|
||||
},
|
||||
OperatorInfo {
|
||||
op_type: "Boolean".into(),
|
||||
operator: "xor".into(),
|
||||
name: "Xor".into(),
|
||||
description: "Checks if one value is true and the other is false.".into(),
|
||||
precedence: 45,
|
||||
},
|
||||
]
|
||||
fn name(operator: &Operator) -> String {
|
||||
match operator {
|
||||
Operator::Comparison(op) => format!("{op:?}"),
|
||||
Operator::Math(op) => format!("{op:?}"),
|
||||
Operator::Boolean(op) => format!("{op:?}"),
|
||||
Operator::Bits(op) => format!("{op:?}"),
|
||||
Operator::Assignment(op) => format!("{op:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn description(operator: &Operator) -> &'static str {
|
||||
// NOTE: if you have to add an operator here, also add it to the operator list above.
|
||||
match operator {
|
||||
Operator::Comparison(Comparison::Equal) => "Checks if two values are equal.",
|
||||
Operator::Comparison(Comparison::NotEqual) => "Checks if two values are not equal.",
|
||||
Operator::Comparison(Comparison::LessThan) => "Checks if a value is less than another.",
|
||||
Operator::Comparison(Comparison::GreaterThan) => {
|
||||
"Checks if a value is greater than another."
|
||||
}
|
||||
Operator::Comparison(Comparison::LessThanOrEqual) => {
|
||||
"Checks if a value is less than or equal to another."
|
||||
}
|
||||
Operator::Comparison(Comparison::GreaterThanOrEqual) => {
|
||||
"Checks if a value is greater than or equal to another."
|
||||
}
|
||||
Operator::Comparison(Comparison::RegexMatch) => {
|
||||
"Checks if a value matches a regular expression."
|
||||
}
|
||||
Operator::Comparison(Comparison::NotRegexMatch) => {
|
||||
"Checks if a value does not match a regular expression."
|
||||
}
|
||||
Operator::Comparison(Comparison::In) => {
|
||||
"Checks if a value is in a list, is part of a string, or is a key in a record."
|
||||
}
|
||||
Operator::Comparison(Comparison::NotIn) => {
|
||||
"Checks if a value is not in a list, is not part of a string, or is not a key in a record."
|
||||
}
|
||||
Operator::Comparison(Comparison::StartsWith) => "Checks if a string starts with another.",
|
||||
Operator::Comparison(Comparison::EndsWith) => "Checks if a string ends with another.",
|
||||
Operator::Math(Math::Plus) => "Adds two values.",
|
||||
Operator::Math(Math::Append) => {
|
||||
"Appends two lists, a list and a value, two strings, or two binary values."
|
||||
}
|
||||
Operator::Math(Math::Minus) => "Subtracts two values.",
|
||||
Operator::Math(Math::Multiply) => "Multiplies two values.",
|
||||
Operator::Math(Math::Divide) => "Divides two values.",
|
||||
Operator::Math(Math::Modulo) => "Divides two values and returns the remainder.",
|
||||
Operator::Math(Math::FloorDivision) => "Divides two values and floors the result.",
|
||||
Operator::Math(Math::Pow) => "Raises one value to the power of another.",
|
||||
Operator::Boolean(Boolean::And) => "Checks if both values are true.",
|
||||
Operator::Boolean(Boolean::Or) => "Checks if either value is true.",
|
||||
Operator::Boolean(Boolean::Xor) => "Checks if one value is true and the other is false.",
|
||||
Operator::Bits(Bits::BitOr) => "Performs a bitwise OR on two values.",
|
||||
Operator::Bits(Bits::BitXor) => "Performs a bitwise XOR on two values.",
|
||||
Operator::Bits(Bits::BitAnd) => "Performs a bitwise AND on two values.",
|
||||
Operator::Bits(Bits::ShiftLeft) => "Bitwise shifts a value left by another.",
|
||||
Operator::Bits(Bits::ShiftRight) => "Bitwise shifts a value right by another.",
|
||||
Operator::Assignment(Assignment::Assign) => "Assigns a value to a variable.",
|
||||
Operator::Assignment(Assignment::PlusAssign) => "Adds a value to a variable.",
|
||||
Operator::Assignment(Assignment::AppendAssign) => {
|
||||
"Appends a list, a value, a string, or a binary value to a variable."
|
||||
}
|
||||
Operator::Assignment(Assignment::MinusAssign) => "Subtracts a value from a variable.",
|
||||
Operator::Assignment(Assignment::MultiplyAssign) => "Multiplies a variable by a value.",
|
||||
Operator::Assignment(Assignment::DivideAssign) => "Divides a variable by a value.",
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::ViewCommand;
|
||||
use crate::{
|
||||
nu_common::{self, collect_input},
|
||||
views::Preview,
|
||||
views::{Preview, ViewConfig},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use nu_color_config::StyleComputer;
|
||||
|
@ -43,6 +43,7 @@ impl ViewCommand for ExpandCmd {
|
|||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
value: Option<Value>,
|
||||
_: &ViewConfig,
|
||||
) -> Result<Self::View> {
|
||||
if let Some(value) = value {
|
||||
let value_as_string = convert_value_to_string(value, engine_state, stack)?;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::ViewCommand;
|
||||
use crate::views::Preview;
|
||||
use crate::views::{Preview, ViewConfig};
|
||||
use anyhow::Result;
|
||||
use nu_ansi_term::Color;
|
||||
use nu_protocol::{
|
||||
|
@ -99,7 +99,13 @@ impl ViewCommand for HelpCmd {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn spawn(&mut self, _: &EngineState, _: &mut Stack, _: Option<Value>) -> Result<Self::View> {
|
||||
fn spawn(
|
||||
&mut self,
|
||||
_: &EngineState,
|
||||
_: &mut Stack,
|
||||
_: Option<Value>,
|
||||
_: &ViewConfig,
|
||||
) -> Result<Self::View> {
|
||||
Ok(HelpCmd::view())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use crate::views::ViewConfig;
|
||||
|
||||
use super::pager::{Pager, Transition};
|
||||
use anyhow::Result;
|
||||
use nu_protocol::{
|
||||
|
@ -49,5 +51,6 @@ pub trait ViewCommand {
|
|||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
value: Option<Value>,
|
||||
config: &ViewConfig,
|
||||
) -> Result<Self::View>;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ impl ViewCommand for NuCmd {
|
|||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
value: Option<Value>,
|
||||
config: &ViewConfig,
|
||||
) -> Result<Self::View> {
|
||||
let value = value.unwrap_or_default();
|
||||
|
||||
|
@ -62,7 +63,7 @@ impl ViewCommand for NuCmd {
|
|||
return Ok(NuView::Preview(Preview::new(&text)));
|
||||
}
|
||||
|
||||
let mut view = RecordView::new(columns, values);
|
||||
let mut view = RecordView::new(columns, values, config.explore_config.clone());
|
||||
|
||||
if is_record {
|
||||
view.set_top_layer_orientation(Orientation::Left);
|
||||
|
@ -119,11 +120,4 @@ impl View for NuView {
|
|||
NuView::Preview(v) => v.exit(),
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(&mut self, config: ViewConfig<'_>) {
|
||||
match self {
|
||||
NuView::Records(v) => v.setup(config),
|
||||
NuView::Preview(v) => v.setup(config),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::ViewCommand;
|
||||
use crate::{
|
||||
nu_common::collect_input,
|
||||
views::{Orientation, RecordView},
|
||||
views::{Orientation, RecordView, ViewConfig},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use nu_protocol::{
|
||||
|
@ -49,13 +49,14 @@ impl ViewCommand for TableCmd {
|
|||
_: &EngineState,
|
||||
_: &mut Stack,
|
||||
value: Option<Value>,
|
||||
config: &ViewConfig,
|
||||
) -> Result<Self::View> {
|
||||
let value = value.unwrap_or_default();
|
||||
let is_record = matches!(value, Value::Record { .. });
|
||||
|
||||
let (columns, data) = collect_input(value)?;
|
||||
|
||||
let mut view = RecordView::new(columns, data);
|
||||
let mut view = RecordView::new(columns, data, config.explore_config.clone());
|
||||
|
||||
if is_record {
|
||||
view.set_top_layer_orientation(Orientation::Left);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::ViewCommand;
|
||||
use crate::views::TryView;
|
||||
use crate::views::{TryView, ViewConfig};
|
||||
use anyhow::Result;
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack},
|
||||
|
@ -43,9 +43,10 @@ impl ViewCommand for TryCmd {
|
|||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
value: Option<Value>,
|
||||
config: &ViewConfig,
|
||||
) -> Result<Self::View> {
|
||||
let value = value.unwrap_or_default();
|
||||
let mut view = TryView::new(value);
|
||||
let mut view = TryView::new(value, config.explore_config.clone());
|
||||
view.init(self.command.clone());
|
||||
view.try_run(engine_state, stack)?;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ use anyhow::Result;
|
|||
use commands::{ExpandCmd, HelpCmd, NuCmd, QuitCmd, TableCmd, TryCmd};
|
||||
pub use default_context::add_explore_context;
|
||||
pub use explore::Explore;
|
||||
use explore::ExploreConfig;
|
||||
use nu_common::{collect_pipeline, has_simple_value};
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack},
|
||||
|
@ -42,7 +43,7 @@ fn run_pager(
|
|||
if is_binary {
|
||||
p.show_message("For help type :help");
|
||||
|
||||
let view = binary_view(input)?;
|
||||
let view = binary_view(input, config.explore_config)?;
|
||||
return p.run(engine_state, stack, Some(view), commands);
|
||||
}
|
||||
|
||||
|
@ -72,7 +73,7 @@ fn create_record_view(
|
|||
is_record: bool,
|
||||
config: PagerConfig,
|
||||
) -> Option<Page> {
|
||||
let mut view = RecordView::new(columns, data);
|
||||
let mut view = RecordView::new(columns, data, config.explore_config.clone());
|
||||
if is_record {
|
||||
view.set_top_layer_orientation(Orientation::Left);
|
||||
}
|
||||
|
@ -90,14 +91,14 @@ fn help_view() -> Option<Page> {
|
|||
Some(Page::new(HelpCmd::view(), false))
|
||||
}
|
||||
|
||||
fn binary_view(input: PipelineData) -> Result<Page> {
|
||||
fn binary_view(input: PipelineData, config: &ExploreConfig) -> Result<Page> {
|
||||
let data = match input {
|
||||
PipelineData::Value(Value::Binary { val, .. }, _) => val,
|
||||
PipelineData::ByteStream(bs, _) => bs.into_bytes()?,
|
||||
_ => unreachable!("checked beforehand"),
|
||||
};
|
||||
|
||||
let view = BinaryView::new(data);
|
||||
let view = BinaryView::new(data, config);
|
||||
|
||||
Ok(Page::new(view, true))
|
||||
}
|
||||
|
|
|
@ -88,19 +88,41 @@ impl<'a> Pager<'a> {
|
|||
&mut self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
mut view: Option<Page>,
|
||||
view: Option<Page>,
|
||||
commands: CommandRegistry,
|
||||
) -> Result<Option<Value>> {
|
||||
if let Some(page) = &mut view {
|
||||
page.view.setup(ViewConfig::new(
|
||||
self.config.nu_config,
|
||||
self.config.explore_config,
|
||||
self.config.style_computer,
|
||||
self.config.lscolors,
|
||||
))
|
||||
// setup terminal
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen, Clear(ClearType::All))?;
|
||||
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
let mut info = ViewInfo {
|
||||
status: Some(Report::default()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(text) = self.message.take() {
|
||||
info.status = Some(Report::message(text, Severity::Info));
|
||||
}
|
||||
|
||||
run_pager(engine_state, stack, self, view, commands)
|
||||
let result = render_ui(
|
||||
&mut terminal,
|
||||
engine_state,
|
||||
stack,
|
||||
self,
|
||||
&mut info,
|
||||
view,
|
||||
commands,
|
||||
)?;
|
||||
|
||||
// restore terminal
|
||||
disable_raw_mode()?;
|
||||
execute!(io::stdout(), LeaveAlternateScreen)?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,47 +165,6 @@ impl<'a> PagerConfig<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn run_pager(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
pager: &mut Pager,
|
||||
view: Option<Page>,
|
||||
commands: CommandRegistry,
|
||||
) -> Result<Option<Value>> {
|
||||
// setup terminal
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen, Clear(ClearType::All))?;
|
||||
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
let mut info = ViewInfo {
|
||||
status: Some(Report::default()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(text) = pager.message.take() {
|
||||
info.status = Some(Report::message(text, Severity::Info));
|
||||
}
|
||||
|
||||
let result = render_ui(
|
||||
&mut terminal,
|
||||
engine_state,
|
||||
stack,
|
||||
pager,
|
||||
&mut info,
|
||||
view,
|
||||
commands,
|
||||
)?;
|
||||
|
||||
// restore terminal
|
||||
disable_raw_mode()?;
|
||||
execute!(io::stdout(), LeaveAlternateScreen)?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn render_ui(
|
||||
term: &mut Terminal,
|
||||
|
@ -432,15 +413,20 @@ fn run_command(
|
|||
Command::View { mut cmd, stackable } => {
|
||||
// what we do we just replace the view.
|
||||
let value = view_stack.curr_view.as_mut().and_then(|p| p.view.exit());
|
||||
let mut new_view = cmd.spawn(engine_state, stack, value)?;
|
||||
let view_cfg = ViewConfig::new(
|
||||
pager.config.nu_config,
|
||||
pager.config.explore_config,
|
||||
pager.config.style_computer,
|
||||
pager.config.lscolors,
|
||||
);
|
||||
|
||||
let new_view = cmd.spawn(engine_state, stack, value, &view_cfg)?;
|
||||
if let Some(view) = view_stack.curr_view.take() {
|
||||
if !view.stackable {
|
||||
view_stack.stack.push(view);
|
||||
}
|
||||
}
|
||||
|
||||
setup_view(&mut new_view, &pager.config);
|
||||
|
||||
view_stack.curr_view = Some(Page::raw(new_view, stackable));
|
||||
|
||||
Ok(CmdResult::new(false, true, cmd.name().to_owned()))
|
||||
|
@ -448,16 +434,6 @@ fn run_command(
|
|||
}
|
||||
}
|
||||
|
||||
fn setup_view(view: &mut Box<dyn View>, cfg: &PagerConfig<'_>) {
|
||||
let cfg = ViewConfig::new(
|
||||
cfg.nu_config,
|
||||
cfg.explore_config,
|
||||
cfg.style_computer,
|
||||
cfg.lscolors,
|
||||
);
|
||||
view.setup(cfg);
|
||||
}
|
||||
|
||||
fn set_cursor_cmd_bar(f: &mut Frame, area: Rect, pager: &Pager) {
|
||||
if pager.cmd_buf.is_cmd_input {
|
||||
// todo: deal with a situation where we exceed the bar width
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
commands::{SimpleCommand, ViewCommand},
|
||||
views::View,
|
||||
views::{View, ViewConfig},
|
||||
};
|
||||
use anyhow::Result;
|
||||
|
||||
|
@ -78,8 +78,9 @@ where
|
|||
engine_state: &nu_protocol::engine::EngineState,
|
||||
stack: &mut nu_protocol::engine::Stack,
|
||||
value: Option<nu_protocol::Value>,
|
||||
cfg: &ViewConfig,
|
||||
) -> Result<Self::View> {
|
||||
let view = self.0.spawn(engine_state, stack, value)?;
|
||||
let view = self.0.spawn(engine_state, stack, value, cfg)?;
|
||||
Ok(Box::new(view) as Box<dyn View>)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,11 +40,15 @@ struct Settings {
|
|||
}
|
||||
|
||||
impl BinaryView {
|
||||
pub fn new(data: Vec<u8>) -> Self {
|
||||
pub fn new(data: Vec<u8>, cfg: &ExploreConfig) -> Self {
|
||||
let settings = settings_from_config(cfg);
|
||||
// There's gotta be a nicer way of doing this than creating a widget just to count lines
|
||||
let count_rows = BinaryWidget::new(&data, settings.opts, Default::default()).count_lines();
|
||||
|
||||
Self {
|
||||
data,
|
||||
cursor: WindowCursor2D::default(),
|
||||
settings: Settings::default(),
|
||||
cursor: WindowCursor2D::new(count_rows, 1).expect("Failed to create XYCursor"),
|
||||
settings,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,15 +91,6 @@ impl View for BinaryView {
|
|||
// todo: impl Cursor + peek of a value
|
||||
None
|
||||
}
|
||||
|
||||
fn setup(&mut self, cfg: ViewConfig<'_>) {
|
||||
self.settings = settings_from_config(cfg.explore_config);
|
||||
|
||||
let count_rows =
|
||||
BinaryWidget::new(&self.data, self.settings.opts, Default::default()).count_lines();
|
||||
// TODO: refactor View so setup() is fallible and we don't have to panic here
|
||||
self.cursor = WindowCursor2D::new(count_rows, 1).expect("Failed to create XYCursor");
|
||||
}
|
||||
}
|
||||
|
||||
fn create_binary_widget(v: &BinaryView) -> BinaryWidget<'_> {
|
||||
|
|
|
@ -99,8 +99,6 @@ pub trait View {
|
|||
fn exit(&mut self) -> Option<Value> {
|
||||
None
|
||||
}
|
||||
|
||||
fn setup(&mut self, _: ViewConfig<'_>) {}
|
||||
}
|
||||
|
||||
impl View for Box<dyn View> {
|
||||
|
@ -131,8 +129,4 @@ impl View for Box<dyn View> {
|
|||
fn show_data(&mut self, i: usize) -> bool {
|
||||
self.as_mut().show_data(i)
|
||||
}
|
||||
|
||||
fn setup(&mut self, cfg: ViewConfig<'_>) {
|
||||
self.as_mut().setup(cfg)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,14 +36,12 @@ pub struct RecordView {
|
|||
}
|
||||
|
||||
impl RecordView {
|
||||
pub fn new(columns: Vec<String>, records: Vec<Vec<Value>>) -> Self {
|
||||
pub fn new(columns: Vec<String>, records: Vec<Vec<Value>>, cfg: ExploreConfig) -> Self {
|
||||
Self {
|
||||
layer_stack: vec![RecordLayer::new(columns, records)],
|
||||
mode: UIMode::View,
|
||||
orientation: Orientation::Top,
|
||||
// TODO: It's kind of gross how this temporarily has an incorrect/default config.
|
||||
// See if we can pass correct config in through the constructor
|
||||
cfg: ExploreConfig::default(),
|
||||
cfg,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,23 +70,6 @@ impl RecordView {
|
|||
.expect("we guarantee that 1 entry is always in a list")
|
||||
}
|
||||
|
||||
pub fn get_top_layer_orientation(&mut self) -> Orientation {
|
||||
self.get_top_layer().orientation
|
||||
}
|
||||
|
||||
pub fn set_orientation(&mut self, orientation: Orientation) {
|
||||
self.orientation = orientation;
|
||||
|
||||
// we need to reset all indexes as we can't no more use them.
|
||||
self.reset_cursors();
|
||||
}
|
||||
|
||||
fn reset_cursors(&mut self) {
|
||||
for layer in &mut self.layer_stack {
|
||||
layer.reset_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_top_layer_orientation(&mut self, orientation: Orientation) {
|
||||
let layer = self.get_top_layer_mut();
|
||||
layer.orientation = orientation;
|
||||
|
@ -299,11 +280,6 @@ impl View for RecordView {
|
|||
fn exit(&mut self) -> Option<Value> {
|
||||
Some(build_last_value(self))
|
||||
}
|
||||
|
||||
// todo: move the method to Command?
|
||||
fn setup(&mut self, cfg: ViewConfig<'_>) {
|
||||
self.cfg = cfg.explore_config.clone();
|
||||
}
|
||||
}
|
||||
|
||||
fn get_element_info(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use super::{record::RecordView, util::nu_style_to_tui, Layout, Orientation, View, ViewConfig};
|
||||
use crate::{
|
||||
explore::ExploreConfig,
|
||||
nu_common::{collect_pipeline, run_command_with_value},
|
||||
pager::{report::Report, Frame, Transition, ViewInfo},
|
||||
};
|
||||
|
@ -23,17 +24,19 @@ pub struct TryView {
|
|||
table: Option<RecordView>,
|
||||
view_mode: bool,
|
||||
border_color: Style,
|
||||
config: ExploreConfig,
|
||||
}
|
||||
|
||||
impl TryView {
|
||||
pub fn new(input: Value) -> Self {
|
||||
pub fn new(input: Value, config: ExploreConfig) -> Self {
|
||||
Self {
|
||||
input,
|
||||
table: None,
|
||||
immediate: false,
|
||||
border_color: Style::default(),
|
||||
immediate: config.try_reactive,
|
||||
border_color: nu_style_to_tui(config.table.separator_style),
|
||||
view_mode: false,
|
||||
command: String::new(),
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +45,13 @@ impl TryView {
|
|||
}
|
||||
|
||||
pub fn try_run(&mut self, engine_state: &EngineState, stack: &mut Stack) -> Result<()> {
|
||||
let view = run_command(&self.command, &self.input, engine_state, stack)?;
|
||||
let view = run_command(
|
||||
&self.command,
|
||||
&self.input,
|
||||
engine_state,
|
||||
stack,
|
||||
&self.config,
|
||||
)?;
|
||||
self.table = Some(view);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -122,7 +131,6 @@ impl View for TryView {
|
|||
f.render_widget(table_block, table_area);
|
||||
|
||||
if let Some(table) = &mut self.table {
|
||||
table.setup(cfg);
|
||||
let area = Rect::new(
|
||||
area.x + 2,
|
||||
area.y + 4,
|
||||
|
@ -232,20 +240,6 @@ impl View for TryView {
|
|||
fn show_data(&mut self, i: usize) -> bool {
|
||||
self.table.as_mut().map_or(false, |v| v.show_data(i))
|
||||
}
|
||||
|
||||
fn setup(&mut self, config: ViewConfig<'_>) {
|
||||
self.border_color = nu_style_to_tui(config.explore_config.table.separator_style);
|
||||
self.immediate = config.explore_config.try_reactive;
|
||||
|
||||
let mut r = RecordView::new(vec![], vec![]);
|
||||
r.setup(config);
|
||||
|
||||
if let Some(view) = &mut self.table {
|
||||
view.setup(config);
|
||||
view.set_orientation(r.get_top_layer_orientation());
|
||||
view.set_top_layer_orientation(r.get_top_layer_orientation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_command(
|
||||
|
@ -253,6 +247,7 @@ fn run_command(
|
|||
input: &Value,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
config: &ExploreConfig,
|
||||
) -> Result<RecordView> {
|
||||
let pipeline = run_command_with_value(command, input, engine_state, stack)?;
|
||||
|
||||
|
@ -260,7 +255,7 @@ fn run_command(
|
|||
|
||||
let (columns, values) = collect_pipeline(pipeline)?;
|
||||
|
||||
let mut view = RecordView::new(columns, values);
|
||||
let mut view = RecordView::new(columns, values, config.clone());
|
||||
if is_record {
|
||||
view.set_top_layer_orientation(Orientation::Left);
|
||||
}
|
||||
|
|
|
@ -4950,7 +4950,7 @@ pub fn parse_math_expression(
|
|||
let mut expr_stack: Vec<Expression> = vec![];
|
||||
|
||||
let mut idx = 0;
|
||||
let mut last_prec = 1000000;
|
||||
let mut last_prec = u8::MAX;
|
||||
|
||||
let first_span = working_set.get_span_contents(spans[0]);
|
||||
|
||||
|
|
|
@ -27,42 +27,9 @@ impl Expression {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn precedence(&self) -> usize {
|
||||
pub fn precedence(&self) -> u8 {
|
||||
match &self.expr {
|
||||
Expr::Operator(operator) => {
|
||||
use super::operator::*;
|
||||
// Higher precedence binds tighter
|
||||
|
||||
match operator {
|
||||
Operator::Math(Math::Pow) => 100,
|
||||
Operator::Math(Math::Multiply)
|
||||
| Operator::Math(Math::Divide)
|
||||
| Operator::Math(Math::Modulo)
|
||||
| Operator::Math(Math::FloorDivision) => 95,
|
||||
Operator::Math(Math::Plus) | Operator::Math(Math::Minus) => 90,
|
||||
Operator::Bits(Bits::ShiftLeft) | Operator::Bits(Bits::ShiftRight) => 85,
|
||||
Operator::Comparison(Comparison::NotRegexMatch)
|
||||
| Operator::Comparison(Comparison::RegexMatch)
|
||||
| Operator::Comparison(Comparison::StartsWith)
|
||||
| Operator::Comparison(Comparison::EndsWith)
|
||||
| Operator::Comparison(Comparison::LessThan)
|
||||
| Operator::Comparison(Comparison::LessThanOrEqual)
|
||||
| Operator::Comparison(Comparison::GreaterThan)
|
||||
| Operator::Comparison(Comparison::GreaterThanOrEqual)
|
||||
| Operator::Comparison(Comparison::Equal)
|
||||
| Operator::Comparison(Comparison::NotEqual)
|
||||
| Operator::Comparison(Comparison::In)
|
||||
| Operator::Comparison(Comparison::NotIn)
|
||||
| Operator::Math(Math::Append) => 80,
|
||||
Operator::Bits(Bits::BitAnd) => 75,
|
||||
Operator::Bits(Bits::BitXor) => 70,
|
||||
Operator::Bits(Bits::BitOr) => 60,
|
||||
Operator::Boolean(Boolean::And) => 50,
|
||||
Operator::Boolean(Boolean::Xor) => 45,
|
||||
Operator::Boolean(Boolean::Or) => 40,
|
||||
Operator::Assignment(_) => 10,
|
||||
}
|
||||
}
|
||||
Expr::Operator(operator) => operator.precedence(),
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,6 +68,40 @@ pub enum Operator {
|
|||
Assignment(Assignment),
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
pub fn precedence(&self) -> u8 {
|
||||
match self {
|
||||
Self::Math(Math::Pow) => 100,
|
||||
Self::Math(Math::Multiply)
|
||||
| Self::Math(Math::Divide)
|
||||
| Self::Math(Math::Modulo)
|
||||
| Self::Math(Math::FloorDivision) => 95,
|
||||
Self::Math(Math::Plus) | Self::Math(Math::Minus) => 90,
|
||||
Self::Bits(Bits::ShiftLeft) | Self::Bits(Bits::ShiftRight) => 85,
|
||||
Self::Comparison(Comparison::NotRegexMatch)
|
||||
| Self::Comparison(Comparison::RegexMatch)
|
||||
| Self::Comparison(Comparison::StartsWith)
|
||||
| Self::Comparison(Comparison::EndsWith)
|
||||
| Self::Comparison(Comparison::LessThan)
|
||||
| Self::Comparison(Comparison::LessThanOrEqual)
|
||||
| Self::Comparison(Comparison::GreaterThan)
|
||||
| Self::Comparison(Comparison::GreaterThanOrEqual)
|
||||
| Self::Comparison(Comparison::Equal)
|
||||
| Self::Comparison(Comparison::NotEqual)
|
||||
| Self::Comparison(Comparison::In)
|
||||
| Self::Comparison(Comparison::NotIn)
|
||||
| Self::Math(Math::Append) => 80,
|
||||
Self::Bits(Bits::BitAnd) => 75,
|
||||
Self::Bits(Bits::BitXor) => 70,
|
||||
Self::Bits(Bits::BitOr) => 60,
|
||||
Self::Boolean(Boolean::And) => 50,
|
||||
Self::Boolean(Boolean::Xor) => 45,
|
||||
Self::Boolean(Boolean::Or) => 40,
|
||||
Self::Assignment(_) => 10,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Operator {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
@ -95,10 +129,10 @@ impl Display for Operator {
|
|||
Operator::Math(Math::Multiply) => write!(f, "*"),
|
||||
Operator::Math(Math::Divide) => write!(f, "/"),
|
||||
Operator::Math(Math::Modulo) => write!(f, "mod"),
|
||||
Operator::Math(Math::FloorDivision) => write!(f, "fdiv"),
|
||||
Operator::Math(Math::FloorDivision) => write!(f, "//"),
|
||||
Operator::Math(Math::Pow) => write!(f, "**"),
|
||||
Operator::Boolean(Boolean::And) => write!(f, "&&"),
|
||||
Operator::Boolean(Boolean::Or) => write!(f, "||"),
|
||||
Operator::Boolean(Boolean::And) => write!(f, "and"),
|
||||
Operator::Boolean(Boolean::Or) => write!(f, "or"),
|
||||
Operator::Boolean(Boolean::Xor) => write!(f, "xor"),
|
||||
Operator::Bits(Bits::BitOr) => write!(f, "bit-or"),
|
||||
Operator::Bits(Bits::BitXor) => write!(f, "bit-xor"),
|
||||
|
|
|
@ -12,13 +12,17 @@ use tabled::{
|
|||
config::{AlignmentHorizontal, ColoredConfig, Entity, EntityMap, Position},
|
||||
dimension::CompleteDimensionVecRecords,
|
||||
records::{
|
||||
vec_records::{CellInfo, VecRecords},
|
||||
vec_records::{Cell, CellInfo, VecRecords},
|
||||
ExactRecords, PeekableRecords, Records, Resizable,
|
||||
},
|
||||
},
|
||||
settings::{
|
||||
formatting::AlignmentStrategy, object::Segment, peaker::Peaker, themes::ColumnNames, Color,
|
||||
Modify, Padding, Settings, TableOption, Width,
|
||||
formatting::AlignmentStrategy,
|
||||
object::{Columns, Segment},
|
||||
peaker::Peaker,
|
||||
themes::ColumnNames,
|
||||
width::Truncate,
|
||||
Color, Modify, Padding, Settings, TableOption, Width,
|
||||
},
|
||||
Table,
|
||||
};
|
||||
|
@ -216,7 +220,7 @@ fn build_table(
|
|||
}
|
||||
|
||||
let pad = indent.0 + indent.1;
|
||||
let widths = maybe_truncate_columns(&mut data, &cfg.theme, termwidth, pad);
|
||||
let widths = maybe_truncate_columns(&mut data, &cfg, termwidth, pad);
|
||||
if widths.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
@ -269,20 +273,51 @@ fn set_indent(table: &mut Table, left: usize, right: usize) {
|
|||
fn set_border_head(table: &mut Table, with_footer: bool, wctrl: TableWidthCtrl) {
|
||||
if with_footer {
|
||||
let count_rows = table.count_rows();
|
||||
let last_row_index = count_rows - 1;
|
||||
|
||||
// note: funnily last and row must be equal at this point but we do not rely on it just in case.
|
||||
|
||||
let mut first_row = GetRow(0, Vec::new());
|
||||
let mut head_settings = GetRowSettings(0, AlignmentHorizontal::Left, None);
|
||||
let mut last_row = GetRow(last_row_index, Vec::new());
|
||||
|
||||
table.with(&mut first_row);
|
||||
table.with(&mut head_settings);
|
||||
table.with(&mut last_row);
|
||||
|
||||
table.with(
|
||||
Settings::default()
|
||||
.with(wctrl)
|
||||
.with(StripColorFromRow(0))
|
||||
.with(StripColorFromRow(count_rows - 1))
|
||||
.with(HeaderMove((0, 0), true))
|
||||
.with(HeaderMove((count_rows - 1 - 1, count_rows - 1), false)),
|
||||
.with(MoveRowNext::new(0, 0))
|
||||
.with(MoveRowPrev::new(last_row_index - 1, last_row_index))
|
||||
.with(SetLineHeaders::new(
|
||||
0,
|
||||
first_row.1,
|
||||
head_settings.1,
|
||||
head_settings.2.clone(),
|
||||
))
|
||||
.with(SetLineHeaders::new(
|
||||
last_row_index - 1,
|
||||
last_row.1,
|
||||
head_settings.1,
|
||||
head_settings.2,
|
||||
)),
|
||||
);
|
||||
} else {
|
||||
let mut row = GetRow(0, Vec::new());
|
||||
let mut row_opts = GetRowSettings(0, AlignmentHorizontal::Left, None);
|
||||
|
||||
table.with(&mut row);
|
||||
table.with(&mut row_opts);
|
||||
|
||||
table.with(
|
||||
Settings::default()
|
||||
.with(wctrl)
|
||||
.with(StripColorFromRow(0))
|
||||
.with(HeaderMove((0, 0), true)),
|
||||
.with(MoveRowNext::new(0, 0))
|
||||
.with(SetLineHeaders::new(0, row.1, row_opts.1, row_opts.2)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -324,11 +359,14 @@ impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for
|
|||
max: self.max,
|
||||
strategy: self.cfg.trim,
|
||||
width: self.width,
|
||||
do_as_head: self.cfg.header_on_border,
|
||||
}
|
||||
.change(rec, cfg, dim);
|
||||
} else if self.cfg.expand && self.max > total_width {
|
||||
Settings::new(SetDimensions(self.width), Width::increase(self.max))
|
||||
.change(rec, cfg, dim)
|
||||
} else {
|
||||
SetDimensions(self.width).change(rec, cfg, dim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -337,6 +375,7 @@ struct TableTrim {
|
|||
width: Vec<usize>,
|
||||
strategy: TrimStrategy,
|
||||
max: usize,
|
||||
do_as_head: bool,
|
||||
}
|
||||
|
||||
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for TableTrim {
|
||||
|
@ -346,6 +385,44 @@ impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for
|
|||
cfg: &mut ColoredConfig,
|
||||
dims: &mut CompleteDimensionVecRecords<'_>,
|
||||
) {
|
||||
// we already must have been estimated that it's safe to do.
|
||||
// and all dims will be suffitient
|
||||
if self.do_as_head {
|
||||
if recs.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let headers = recs[0].to_owned();
|
||||
for (i, head) in headers.into_iter().enumerate() {
|
||||
let head_width = CellInfo::width(&head);
|
||||
|
||||
match &self.strategy {
|
||||
TrimStrategy::Wrap { try_to_keep_words } => {
|
||||
let mut wrap = Width::wrap(head_width);
|
||||
if *try_to_keep_words {
|
||||
wrap = wrap.keep_words();
|
||||
}
|
||||
|
||||
Modify::new(Columns::single(i))
|
||||
.with(wrap)
|
||||
.change(recs, cfg, dims);
|
||||
}
|
||||
TrimStrategy::Truncate { suffix } => {
|
||||
let mut truncate = Width::truncate(self.max);
|
||||
if let Some(suffix) = suffix {
|
||||
truncate = truncate.suffix(suffix).suffix_try_color(true);
|
||||
}
|
||||
|
||||
Modify::new(Columns::single(i))
|
||||
.with(truncate)
|
||||
.change(recs, cfg, dims);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
match self.strategy {
|
||||
TrimStrategy::Wrap { try_to_keep_words } => {
|
||||
let mut wrap = Width::wrap(self.max).priority::<PriorityMax>();
|
||||
|
@ -460,19 +537,22 @@ fn load_theme(
|
|||
|
||||
fn maybe_truncate_columns(
|
||||
data: &mut NuRecords,
|
||||
theme: &TableTheme,
|
||||
cfg: &NuTableConfig,
|
||||
termwidth: usize,
|
||||
pad: usize,
|
||||
) -> Vec<usize> {
|
||||
const TERMWIDTH_THRESHOLD: usize = 120;
|
||||
|
||||
let truncate = if termwidth > TERMWIDTH_THRESHOLD {
|
||||
let preserve_content = termwidth > TERMWIDTH_THRESHOLD;
|
||||
let truncate = if cfg.header_on_border {
|
||||
truncate_columns_by_head
|
||||
} else if preserve_content {
|
||||
truncate_columns_by_columns
|
||||
} else {
|
||||
truncate_columns_by_content
|
||||
};
|
||||
|
||||
truncate(data, theme, pad, termwidth)
|
||||
truncate(data, &cfg.theme, pad, termwidth)
|
||||
}
|
||||
|
||||
// VERSION where we are showing AS LITTLE COLUMNS AS POSSIBLE but WITH AS MUCH CONTENT AS POSSIBLE.
|
||||
|
@ -627,6 +707,83 @@ fn truncate_columns_by_columns(
|
|||
widths
|
||||
}
|
||||
|
||||
// VERSION where we are showing AS LITTLE COLUMNS AS POSSIBLE but WITH AS MUCH CONTENT AS POSSIBLE.
|
||||
fn truncate_columns_by_head(
|
||||
data: &mut NuRecords,
|
||||
theme: &TableTheme,
|
||||
pad: usize,
|
||||
termwidth: usize,
|
||||
) -> Vec<usize> {
|
||||
const TRAILING_COLUMN_WIDTH: usize = 5;
|
||||
|
||||
let config = get_config(theme, false, None);
|
||||
let mut widths = build_width(&*data, pad);
|
||||
let total_width = get_total_width2(&widths, &config);
|
||||
if total_width <= termwidth {
|
||||
return widths;
|
||||
}
|
||||
|
||||
if data.is_empty() {
|
||||
return widths;
|
||||
}
|
||||
|
||||
let head = &data[0];
|
||||
|
||||
let borders = config.get_borders();
|
||||
let has_vertical = borders.has_vertical();
|
||||
|
||||
let mut width = borders.has_left() as usize + borders.has_right() as usize;
|
||||
let mut truncate_pos = 0;
|
||||
for (i, column_header) in head.iter().enumerate() {
|
||||
let column_header_width = Cell::width(column_header);
|
||||
width += column_header_width;
|
||||
|
||||
if i > 0 {
|
||||
width += has_vertical as usize;
|
||||
}
|
||||
|
||||
if width >= termwidth {
|
||||
width -= column_header_width + (i > 0 && has_vertical) as usize;
|
||||
break;
|
||||
}
|
||||
|
||||
truncate_pos += 1;
|
||||
}
|
||||
|
||||
// we don't need any truncation then (is it possible?)
|
||||
if truncate_pos == head.len() {
|
||||
return widths;
|
||||
}
|
||||
|
||||
if truncate_pos == 0 {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
truncate_columns(data, truncate_pos);
|
||||
widths.truncate(truncate_pos);
|
||||
|
||||
// Append columns with a trailing column
|
||||
|
||||
let min_width = width;
|
||||
|
||||
let diff = termwidth - min_width;
|
||||
let can_trailing_column_be_pushed = diff > TRAILING_COLUMN_WIDTH + has_vertical as usize;
|
||||
|
||||
if !can_trailing_column_be_pushed {
|
||||
if data.count_columns() == 1 {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
truncate_columns(data, data.count_columns() - 1);
|
||||
widths.pop();
|
||||
}
|
||||
|
||||
push_empty_column(data);
|
||||
widths.push(3 + pad);
|
||||
|
||||
widths
|
||||
}
|
||||
|
||||
/// The same as [`tabled::peaker::PriorityMax`] but prioritizes left columns first in case of equal width.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct PriorityMax;
|
||||
|
@ -714,9 +871,8 @@ impl<R> TableOption<R, CompleteDimensionVecRecords<'_>, ColoredConfig> for SetDi
|
|||
}
|
||||
|
||||
// it assumes no spans is used.
|
||||
// todo: Could be replaced by Dimension impl usage
|
||||
fn build_width(records: &NuRecords, pad: usize) -> Vec<usize> {
|
||||
use tabled::grid::records::vec_records::Cell;
|
||||
|
||||
let count_columns = records.count_columns();
|
||||
let mut widths = vec![0; count_columns];
|
||||
for columns in records.iter_rows() {
|
||||
|
@ -729,50 +885,156 @@ fn build_width(records: &NuRecords, pad: usize) -> Vec<usize> {
|
|||
widths
|
||||
}
|
||||
|
||||
struct HeaderMove((usize, usize), bool);
|
||||
struct GetRow(usize, Vec<String>);
|
||||
|
||||
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for HeaderMove {
|
||||
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for &mut GetRow {
|
||||
fn change(
|
||||
self,
|
||||
recs: &mut NuRecords,
|
||||
_: &mut ColoredConfig,
|
||||
_: &mut CompleteDimensionVecRecords<'_>,
|
||||
) {
|
||||
let row = self.0;
|
||||
self.1 = recs[row].iter().map(|c| c.as_ref().to_owned()).collect();
|
||||
}
|
||||
}
|
||||
|
||||
struct GetRowSettings(usize, AlignmentHorizontal, Option<Color>);
|
||||
|
||||
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig>
|
||||
for &mut GetRowSettings
|
||||
{
|
||||
fn change(
|
||||
self,
|
||||
_: &mut NuRecords,
|
||||
cfg: &mut ColoredConfig,
|
||||
_: &mut CompleteDimensionVecRecords<'_>,
|
||||
) {
|
||||
let row = self.0;
|
||||
self.1 = *cfg.get_alignment_horizontal(Entity::Row(row));
|
||||
self.2 = cfg
|
||||
.get_colors()
|
||||
.get_color((row, 0))
|
||||
.cloned()
|
||||
.map(Color::from);
|
||||
}
|
||||
}
|
||||
|
||||
struct SetLineHeaders {
|
||||
line: usize,
|
||||
columns: Vec<String>,
|
||||
alignment: AlignmentHorizontal,
|
||||
color: Option<Color>,
|
||||
}
|
||||
|
||||
impl SetLineHeaders {
|
||||
fn new(
|
||||
line: usize,
|
||||
columns: Vec<String>,
|
||||
alignment: AlignmentHorizontal,
|
||||
color: Option<Color>,
|
||||
) -> Self {
|
||||
Self {
|
||||
line,
|
||||
columns,
|
||||
alignment,
|
||||
color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for SetLineHeaders {
|
||||
fn change(
|
||||
self,
|
||||
recs: &mut NuRecords,
|
||||
cfg: &mut ColoredConfig,
|
||||
dims: &mut CompleteDimensionVecRecords<'_>,
|
||||
) {
|
||||
let (row, line) = self.0;
|
||||
if self.1 {
|
||||
move_header_on_next(recs, cfg, dims, row, line);
|
||||
} else {
|
||||
move_header_on_prev(recs, cfg, dims, row, line);
|
||||
}
|
||||
let mut columns = self.columns;
|
||||
match dims.get_widths() {
|
||||
Some(widths) => {
|
||||
columns = columns
|
||||
.into_iter()
|
||||
.zip(widths.iter().map(|w| w.checked_sub(2).unwrap_or(*w))) // exclude padding; which is generally 2
|
||||
.map(|(s, width)| Truncate::truncate_text(&s, width).into_owned())
|
||||
.collect();
|
||||
}
|
||||
None => {
|
||||
// we don't have widths cached; which means that NO widtds adjustmens were done
|
||||
// which means we are OK to leave columns as they are.
|
||||
//
|
||||
// but we are actually always got to have widths at this point
|
||||
}
|
||||
};
|
||||
|
||||
set_column_names(
|
||||
recs,
|
||||
cfg,
|
||||
dims,
|
||||
columns,
|
||||
self.line,
|
||||
self.alignment,
|
||||
self.color,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn move_header_on_next(
|
||||
recs: &mut NuRecords,
|
||||
cfg: &mut ColoredConfig,
|
||||
dims: &mut CompleteDimensionVecRecords<'_>,
|
||||
struct MoveRowNext {
|
||||
row: usize,
|
||||
line: usize,
|
||||
) {
|
||||
}
|
||||
|
||||
impl MoveRowNext {
|
||||
fn new(row: usize, line: usize) -> Self {
|
||||
Self { row, line }
|
||||
}
|
||||
}
|
||||
|
||||
struct MoveRowPrev {
|
||||
row: usize,
|
||||
line: usize,
|
||||
}
|
||||
|
||||
impl MoveRowPrev {
|
||||
fn new(row: usize, line: usize) -> Self {
|
||||
Self { row, line }
|
||||
}
|
||||
}
|
||||
|
||||
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for MoveRowNext {
|
||||
fn change(
|
||||
self,
|
||||
recs: &mut NuRecords,
|
||||
cfg: &mut ColoredConfig,
|
||||
_: &mut CompleteDimensionVecRecords<'_>,
|
||||
) {
|
||||
row_shift_next(recs, cfg, self.row, self.line);
|
||||
}
|
||||
}
|
||||
|
||||
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for MoveRowPrev {
|
||||
fn change(
|
||||
self,
|
||||
recs: &mut NuRecords,
|
||||
cfg: &mut ColoredConfig,
|
||||
_: &mut CompleteDimensionVecRecords<'_>,
|
||||
) {
|
||||
row_shift_prev(recs, cfg, self.row, self.line);
|
||||
}
|
||||
}
|
||||
|
||||
fn row_shift_next(recs: &mut NuRecords, cfg: &mut ColoredConfig, row: usize, line: usize) {
|
||||
let count_rows = recs.count_rows();
|
||||
let count_columns = recs.count_columns();
|
||||
let has_line = cfg.has_horizontal(line, count_rows);
|
||||
let has_next_line = cfg.has_horizontal(line + 1, count_rows);
|
||||
let align = *cfg.get_alignment_horizontal(Entity::Row(row));
|
||||
let color = cfg
|
||||
.get_colors()
|
||||
.get_color((row, 0))
|
||||
.cloned()
|
||||
.map(Color::from);
|
||||
|
||||
if !has_line && !has_next_line {
|
||||
return;
|
||||
}
|
||||
|
||||
if !has_line {
|
||||
let head = remove_row(recs, row);
|
||||
let _ = remove_row(recs, row);
|
||||
let count_rows = recs.count_rows();
|
||||
set_column_names(recs, cfg, dims, head, line, align, color);
|
||||
shift_alignments_down(cfg, row, count_rows, count_columns);
|
||||
shift_colors_down(cfg, row, count_rows, count_columns);
|
||||
shift_lines_up(cfg, count_rows, &[line + 1]);
|
||||
|
@ -780,47 +1042,31 @@ fn move_header_on_next(
|
|||
return;
|
||||
}
|
||||
|
||||
let head = remove_row(recs, row);
|
||||
let _ = remove_row(recs, row);
|
||||
let count_rows = recs.count_rows();
|
||||
set_column_names(recs, cfg, dims, head, line, align, color);
|
||||
shift_alignments_down(cfg, row, count_rows, count_columns);
|
||||
shift_colors_down(cfg, row, count_rows, count_columns);
|
||||
remove_lines(cfg, count_rows, &[line + 1]);
|
||||
shift_lines_up(cfg, count_rows, &[count_rows]);
|
||||
}
|
||||
|
||||
fn move_header_on_prev(
|
||||
recs: &mut NuRecords,
|
||||
cfg: &mut ColoredConfig,
|
||||
dims: &mut CompleteDimensionVecRecords<'_>,
|
||||
row: usize,
|
||||
line: usize,
|
||||
) {
|
||||
fn row_shift_prev(recs: &mut NuRecords, cfg: &mut ColoredConfig, row: usize, line: usize) {
|
||||
let count_rows = recs.count_rows();
|
||||
let count_columns = recs.count_columns();
|
||||
let has_line = cfg.has_horizontal(line, count_rows);
|
||||
let has_prev_line = cfg.has_horizontal(line - 1, count_rows);
|
||||
let align = *cfg.get_alignment_horizontal(Entity::Row(row));
|
||||
let color = cfg
|
||||
.get_colors()
|
||||
.get_color((row, 0))
|
||||
.cloned()
|
||||
.map(Color::from);
|
||||
|
||||
if !has_line && !has_prev_line {
|
||||
return;
|
||||
}
|
||||
|
||||
if !has_line {
|
||||
let head = remove_row(recs, row);
|
||||
let _ = remove_row(recs, row);
|
||||
// shift_lines_down(table, &[line - 1]);
|
||||
set_column_names(recs, cfg, dims, head, line - 1, align, color);
|
||||
return;
|
||||
}
|
||||
|
||||
let head = remove_row(recs, row);
|
||||
let _ = remove_row(recs, row);
|
||||
let count_rows = count_rows - 1;
|
||||
set_column_names(recs, cfg, dims, head, line - 1, align, color);
|
||||
shift_alignments_down(cfg, row, count_rows, count_columns);
|
||||
shift_colors_down(cfg, row, count_rows, count_columns);
|
||||
remove_lines(cfg, count_rows, &[line - 1]);
|
||||
|
|
Loading…
Reference in New Issue
Block a user