Handle ctrl-c in uniq and uniq-by (#7478)

A partial fix for #7477. `uniq` can be slow sometimes, so we should
check `ctrl-c` when it's running.

Tested on [this
file](https://home.treasury.gov/system/files/276/yield-curve-rates-1990-2021.csv),
I ran `open yield-curve-rates-1990-2021.csv | uniq` and confirmed that I
can now cancel the operation.

Future work is needed to figure out why `uniq` is so slow.
This commit is contained in:
Reilly Wood 2022-12-14 11:31:54 -08:00 committed by GitHub
parent e0bf17930b
commit 80a69224f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 19 additions and 6 deletions

View File

@ -1,3 +1,4 @@
use crate::input_handler::ctrl_c_was_pressed;
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{ use nu_protocol::{
@ -213,27 +214,30 @@ fn generate_results_with_count(head: Span, uniq_values: Vec<ValueCounter>) -> Ve
} }
pub fn uniq( pub fn uniq(
_engine_state: &EngineState, engine_state: &EngineState,
_stack: &mut Stack, _stack: &mut Stack,
call: &Call, call: &Call,
input: Vec<Value>, input: Vec<Value>,
item_mapper: Box<dyn Fn(ItemMapperState) -> ValueCounter>, item_mapper: Box<dyn Fn(ItemMapperState) -> ValueCounter>,
metadata: Option<PipelineMetadata>, metadata: Option<PipelineMetadata>,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> { ) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let ctrlc = engine_state.ctrlc.clone();
let head = call.head; let head = call.head;
let flag_show_count = call.has_flag("count"); let flag_show_count = call.has_flag("count");
let flag_show_repeated = call.has_flag("repeated"); let flag_show_repeated = call.has_flag("repeated");
let flag_ignore_case = call.has_flag("ignore-case"); let flag_ignore_case = call.has_flag("ignore-case");
let flag_only_uniques = call.has_flag("unique"); let flag_only_uniques = call.has_flag("unique");
// let metadata = input.metadata();
let mut uniq_values = input let mut uniq_values = input
.into_iter() .into_iter()
.map(|item| { .map_while(|item| {
item_mapper(ItemMapperState { if ctrl_c_was_pressed(&ctrlc) {
return None;
}
Some(item_mapper(ItemMapperState {
item, item,
flag_ignore_case, flag_ignore_case,
}) }))
}) })
.fold(Vec::<ValueCounter>::new(), |mut counter, item| { .fold(Vec::<ValueCounter>::new(), |mut counter, item| {
match counter match counter

View File

@ -1,6 +1,6 @@
use nu_protocol::ast::CellPath; use nu_protocol::ast::CellPath;
use nu_protocol::{PipelineData, ShellError, Span, Value}; use nu_protocol::{PipelineData, ShellError, Span, Value};
use std::sync::atomic::AtomicBool; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
pub trait CmdArgument { pub trait CmdArgument {
@ -71,3 +71,12 @@ where
} }
} }
} }
// Helper method to avoid boilerplate every time we check ctrl+c
pub fn ctrl_c_was_pressed(ctrlc: &Option<Arc<AtomicBool>>) -> bool {
if let Some(ctrlc) = ctrlc {
ctrlc.load(Ordering::SeqCst)
} else {
false
}
}