Start work on potential iterator approach

This commit is contained in:
Austen Adler 2022-03-12 18:29:47 -05:00
parent 92de282027
commit ddcdd2b487
3 changed files with 115 additions and 0 deletions

View File

@ -13,6 +13,7 @@ keywords = ["cli", "kakoune"]
regex = "1"
clap = {version = "3", features = ["derive", "env"]}
alphanumeric-sort = "1"
# shellwords = {version = "1", path = "../../../git/rust-shellwords/"}
shellwords = "1"
rand = "0.8"
evalexpr = "7"

View File

@ -1,4 +1,5 @@
use crate::{get_var, KakMessage};
// use shellwords::ShellWordsIterator;
use std::{
fmt,
fs::{self, File, OpenOptions},
@ -261,6 +262,17 @@ pub fn response(msg: &str) -> Result<Vec<String>, KakMessage> {
Ok(selections)
}
// pub fn response_iter(msg: &str) -> Result<ShellWordsIterator, KakMessage> {
// exec(&format!(
// "echo -quoting shell -to-file {} -- {msg}",
// get_var("kak_response_fifo")?
// ))?;
// Ok(shellwords::split_iter(&fs::read_to_string(&get_var(
// "kak_response_fifo",
// )?)?))
// }
/// # Errors
///
/// Will return `Err` if command fifo could not be opened

102
src/xargs.rs Normal file
View File

@ -0,0 +1,102 @@
use crate::{
get_selections_desc, set_selections, set_selections_desc, KakMessage, SelectionWithDesc,
};
use regex::Regex;
use std::{
collections::{hash_map::DefaultHasher, BTreeSet},
hash::{Hash, Hasher},
};
#[derive(clap::StructOpt, Debug)]
pub struct Options {
}
pub fn xargs(options: &Options) -> Result<KakMessage, KakMessage> {
let mut child = Command::new("xargs")
.args(["-0"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn().expect("Failed to spawn child process");
let mut stdin = child.stdin.take().expect("Failed to open stdin");
std::thread::spawn(move || {
for s in crate::get_selections_with_desc()? {
stdin.write_all(s.selection).expect("Failed to write to stdin");
stdin.write_all('\0').expect("Failed to write to stdin");
}
})
// Sort selections so the first element is the xargsue one, not an arbitrary one based on primary selection
selections.sort_by_key(|s| s.desc.sort());
// Set the new selection types
let new_selections: Vec<Option<SelectionWithDesc>> = selections
.into_iter()
// Create a BTreeSet of hashes of selections. This way, string content is not stored, but xargsueness can be determined
.scan(BTreeSet::new(), |state, s| {
// Strip whitespace if requested
let mut key = if options.no_skip_whitespace {
s.content.as_str()
} else {
s.content.trim()
};
if let Some(regex_match) = (|| {
let captures = options.regex.as_ref()?.captures(key)?;
captures
.get(1)
.or_else(|| captures.get(0))
.map(|m| m.as_str())
})() {
key = regex_match;
}
// Ignore case if requested
let key = if options.ignore_case {
key.to_lowercase()
} else {
// TODO: Do I really need to clone this?
key.to_string()
};
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
// Try inserting to the hash
if state.insert(hasher.finish()) {
// True if this is a string we haven't seen before
Some(Some(s))
} else {
// Nothing was inserted because we already saw this string
// Return Some(None) so the iterator can continue
Some(None)
}
})
.collect();
// Preallocate so the content and string have the same type, but allocation is not repeated
// TODO: Do we really have to do this?
let empty_string = String::default();
set_selections(new_selections.iter().map(|i| match i {
Some(s) => &s.content,
None => &empty_string,
}))?;
// Deselect all `None` strings (all rows that have been seen before)
let mut new_selections_desc = get_selections_desc()?;
new_selections_desc.sort();
set_selections_desc(
// Refresh seelections_desc because positions have changed
new_selections_desc
.iter()
.zip(new_selections.iter())
// If the string was emptied (None), then do not set `sd`
.filter_map(|(sd, s)| if s.is_some() { Some(sd) } else { None }),
)?;
let old_count = new_selections.len();
let new_count = new_selections.iter().flatten().count();
Ok(KakMessage(
format!("{} xargs selections out of {}", new_count, old_count),
None,
))
}