Refactor sorting selections

This commit is contained in:
Austen Adler 2022-03-09 21:25:50 -05:00
parent 4bf376d802
commit 9b847eff0d
3 changed files with 130 additions and 95 deletions

View File

@ -14,6 +14,12 @@ pub struct SelectionWithDesc {
pub desc: SelectionDesc, pub desc: SelectionDesc,
} }
#[derive(PartialEq, Eq, Debug)]
pub struct SelectionWithSubselections {
pub selection: SelectionWithDesc,
pub subselections: Vec<SelectionWithDesc>,
}
#[derive(PartialEq, PartialOrd, Ord, Eq, Debug)] #[derive(PartialEq, PartialOrd, Ord, Eq, Debug)]
pub struct SelectionDesc { pub struct SelectionDesc {
pub left: AnchorPosition, pub left: AnchorPosition,
@ -115,6 +121,18 @@ pub fn get_selections_desc() -> Result<Vec<SelectionDesc>, KakMessage> {
.collect::<Result<Vec<_>, KakMessage>>() .collect::<Result<Vec<_>, KakMessage>>()
} }
// pub fn get_selections_with_subselections(
// register: &str,
// ) -> Result<Vec<SelectionWithSubselections>, KakMessage> {
// // TODO: Escape register
// let subselections = get_selections_with_desc()?;
// exec(format!("\"{}z", register.replace('\'', "''")))?;
// let selections = get_selections_with_desc()?;
// for sel in selections {
// for i in subselections {}
// }
// }
/// # Errors /// # Errors
/// ///
/// Will return `Err` if command fifo could not be opened, read from, or written to, /// Will return `Err` if command fifo could not be opened, read from, or written to,

View File

@ -13,7 +13,7 @@ mod errors;
mod kak; mod kak;
mod math_eval; mod math_eval;
mod shuf; mod shuf;
// mod sort; mod sort;
mod uniq; mod uniq;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use errors::KakMessage; use errors::KakMessage;
@ -34,7 +34,7 @@ struct Cli {
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
enum Commands { enum Commands {
// Sort(sort::Options), Sort(sort::Options),
Shuf(shuf::Options), Shuf(shuf::Options),
Uniq(uniq::Options), Uniq(uniq::Options),
#[clap(visible_aliases = &["bc", "eval"])] #[clap(visible_aliases = &["bc", "eval"])]
@ -69,7 +69,7 @@ fn run() -> Result<KakMessage, KakMessage> {
})?; })?;
match &options.command { match &options.command {
// Commands::Sort(o) => sort::sort(o), Commands::Sort(o) => sort::sort(o),
Commands::Shuf(o) => shuf::shuf(o), Commands::Shuf(o) => shuf::shuf(o),
Commands::Uniq(o) => uniq::uniq(o), Commands::Uniq(o) => uniq::uniq(o),
Commands::MathEval(o) => math_eval::math_eval(o), Commands::MathEval(o) => math_eval::math_eval(o),

View File

@ -1,9 +1,7 @@
use crate::{ use crate::{get_selections_with_desc, kak, open_command_fifo, KakMessage, SelectionWithDesc};
get_selections_with_desc, kak, open_command_fifo, KakMessage, SelectionDesc, SelectionWithDesc,
};
use alphanumeric_sort::compare_str; use alphanumeric_sort::compare_str;
use regex::Regex; use regex::Regex;
use std::{cmp::Ordering, io::Write, str::FromStr}; use std::{cmp::Ordering, io::Write};
#[derive(clap::StructOpt, Debug)] #[derive(clap::StructOpt, Debug)]
pub struct Options { pub struct Options {
@ -33,7 +31,7 @@ fn invert_bool(s: &str) -> Result<bool, &'static str> {
struct SortableSelection<'a> { struct SortableSelection<'a> {
/// The content of the selection /// The content of the selection
content: &'a str, selection: &'a SelectionWithDesc,
/// The string used to compare the content /// The string used to compare the content
content_comparison: &'a str, content_comparison: &'a str,
/// Any subselections /// Any subselections
@ -41,77 +39,78 @@ struct SortableSelection<'a> {
} }
/// Gets a Vec of sortable selections with a given list of subselections and descriptions /// Gets a Vec of sortable selections with a given list of subselections and descriptions
fn get_sortable_selections_subselections<'a, 'b, 'tmp, S: AsRef<str> + std::fmt::Debug + 'a>( /// TODO: Implement sort by subselection
options: &'b Options, // fn get_sortable_selections_subselections<'a, 'b, 'tmp, S: AsRef<str> + std::fmt::Debug + 'a>(
selections: Vec<SelectionWithDesc>, // options: &'b Options,
subselections: Vec<SelectionWithDesc>, // selections: Vec<SelectionWithDesc>,
) -> Result<Vec<SortableSelection<'a>>, KakMessage> { // subselections: Vec<SelectionWithDesc>,
let mut sortable_selections = selections // ) -> Result<Vec<SortableSelection<'a>>, KakMessage> {
.iter() // let mut sortable_selections = selections
.zip(selections_desc.iter()) // .iter()
.map(|(s, sd)| { // .zip(selections_desc.iter())
Ok(( // .map(|(s, sd)| {
to_sortable_selection(s.as_ref(), options), // Ok((
SelectionDesc::from_str(sd.as_ref())?, // to_sortable_selection(s.as_ref(), options),
)) // SelectionDesc::from_str(sd.as_ref())?,
}) // ))
.collect::<Result<Vec<(SortableSelection, SelectionDesc)>, KakMessage>>()?; // })
// .collect::<Result<Vec<(SortableSelection, SelectionDesc)>, KakMessage>>()?;
let mut subselections = subselections // let mut subselections = subselections
.iter() // .iter()
.zip(subselections_desc.iter()) // .zip(subselections_desc.iter())
// Bind selection with associated description // // Bind selection with associated description
// Sort descriptions so left is always <= right // // Sort descriptions so left is always <= right
.map(|(s, sd)| Ok((s.as_ref(), SelectionDesc::from_str(sd.as_ref())?.sort()))) // .map(|(s, sd)| Ok((s.as_ref(), SelectionDesc::from_str(sd.as_ref())?.sort())))
.collect::<Result<Vec<(&str, SelectionDesc)>, KakMessage>>()?; // .collect::<Result<Vec<(&str, SelectionDesc)>, KakMessage>>()?;
// Sort subselections by description // // Sort subselections by description
subselections.sort_by(|(_, ssd_a), (_, ssd_b)| ssd_a.cmp(ssd_b)); // subselections.sort_by(|(_, ssd_a), (_, ssd_b)| ssd_a.cmp(ssd_b));
// For each selection, check if they contain any subselections // // For each selection, check if they contain any subselections
// If so, add them to the subselections vector // // If so, add them to the subselections vector
// TODO: This is O(n^2), but can be made more efficient since subselections is sorted // // TODO: This is O(n^2), but can be made more efficient since subselections is sorted
for (s, s_desc) in &mut sortable_selections { // for (s, s_desc) in &mut sortable_selections {
for i in &subselections { // for i in &subselections {
if s_desc.contains(&i.1) { // if s_desc.contains(&i.1) {
s.subselections.push(i.0); // s.subselections.push(i.0);
} // }
} // }
} // }
sortable_selections.sort_by(|(a, _), (b, _)| { // sortable_selections.sort_by(|(a, _), (b, _)| {
// First, check if there are any subselection comparisons to be made // // First, check if there are any subselection comparisons to be made
// If one has more subselections than the other, stop comparing // // If one has more subselections than the other, stop comparing
for (a_subsel, b_subsel) in a.subselections.iter().zip(b.subselections.iter()) { // for (a_subsel, b_subsel) in a.subselections.iter().zip(b.subselections.iter()) {
match a_subsel.cmp(b_subsel) { // match a_subsel.cmp(b_subsel) {
// These subselections are equal, so we can't do anything // // These subselections are equal, so we can't do anything
Ordering::Equal => continue, // Ordering::Equal => continue,
// We found a difference, so return the comparison // // We found a difference, so return the comparison
o => return o, // o => return o,
} // }
} // }
// No subselections mismatched, so compare the (possibly trimmed) content // // No subselections mismatched, so compare the (possibly trimmed) content
a.content_comparison.cmp(b.content_comparison) // a.content_comparison.cmp(b.content_comparison)
}); // });
Ok(sortable_selections.into_iter().map(|(s, _)| s).collect()) // Ok(sortable_selections.into_iter().map(|(s, _)| s).collect())
} // }
fn to_sortable_selection<'a, 'b>( fn to_sortable_selection<'a, 'b>(
selection: &'a str, selection: &'a SelectionWithDesc,
options: &'b Options, options: &'b Options,
) -> SortableSelection<'a> { ) -> SortableSelection<'a> {
if options.no_skip_whitespace { if options.no_skip_whitespace {
SortableSelection { SortableSelection {
content: selection, selection: &selection,
content_comparison: selection, content_comparison: selection.content.as_str(),
subselections: vec![], subselections: vec![],
} }
} else { } else {
SortableSelection { SortableSelection {
content: selection, selection: &selection,
content_comparison: selection.trim(), content_comparison: selection.content.as_str().trim(),
subselections: vec![], subselections: vec![],
} }
} }
@ -120,46 +119,63 @@ fn to_sortable_selection<'a, 'b>(
pub fn sort(options: &Options) -> Result<KakMessage, KakMessage> { pub fn sort(options: &Options) -> Result<KakMessage, KakMessage> {
eprintln!("Got sort options: {:?}", options); eprintln!("Got sort options: {:?}", options);
let subselections = options // subselections is Some if the user requests it in subselections_register
// It will "exec z" to restore the selections before setting selections
// If subselections is None, "exec z" is not called
let subselections: Option<Vec<SelectionWithDesc>> = options
.subselections_register .subselections_register
.map(|_r| -> Result<Vec<SelectionWithDesc>, KakMessage> { .map::<Result<_, KakMessage>, _>(|c| {
let ret = get_selections_with_desc()?; let subselections = get_selections_with_desc()?;
kak::exec("exec z")?; kak::exec(&format!("exec {}", c))?;
Ok(ret) Ok(subselections)
}) })
.transpose()?; .transpose()?;
let selections = get_selections_with_desc()?; let selections = get_selections_with_desc()?;
// let subselections: Option<(Vec<String>, Vec<String>)> = options let mut zipped: Vec<SortableSelection<'_>> = match (&options.regex, &subselections) {
// .subselections_register (Some(_), Some(_)) => {
// .map::<Result<(Vec<String>, Vec<String>), KakMessage>, _>(|_c| { return Err("Cannot pass regex and subselections register"
// let subselections = kak::response("%val{selections}")?; .to_string()
// let subselections_desc = kak::response("%val{selections_desc}")?; .into())
// // kak_exec(&format!("exec z",))?; }
// Ok((subselections, subselections_desc)) (None, None) => {
// }) // Do a regular sort on the content
// .transpose()?; selections
// let selections = kak::response("%val{selections}")?; .iter()
.map(|s| to_sortable_selection(s, options))
let mut zipped: Vec<SortableSelection<'_>> = match subselections { .collect()
Some(subselections) => { }
let selections_desc = kak::response("%val{selections_desc}")?; (Some(regex), None) => {
// Sort based on the regular expression
get_sortable_selections_subselections( selections
options, .iter()
&selections, .map(|s| {
&selections_desc, let mut sortable_selection = to_sortable_selection(s, options);
subselections, if let Some(regex_match) = (|| {
subselections_desc, let captures = regex.captures(sortable_selection.content_comparison)?;
)? captures
.get(1)
.or_else(|| captures.get(0))
.map(|m| m.as_str())
})() {
sortable_selection.content_comparison = regex_match;
}
sortable_selection
})
.collect()
}
(None, _) => {
// Sort based on subselections
return Err(KakMessage(
"Not yet implemented".to_string(),
Some("Sort by subselection is not yet implemented".to_string()),
));
} }
None => selections
.iter()
.map(|s| to_sortable_selection(s, options))
.collect(),
}; };
zipped.sort_by(|a, b| { zipped.sort_by(|a, b| {
// First, try sorting by subselection. This won't iterate anything if either is None (regex and default mode)
for (a_subselection, b_subselection) in a.subselections.iter().zip(b.subselections.iter()) { for (a_subselection, b_subselection) in a.subselections.iter().zip(b.subselections.iter()) {
let comparison = if options.lexicographic_sort { let comparison = if options.lexicographic_sort {
a_subselection.cmp(b_subselection) a_subselection.cmp(b_subselection)
@ -172,6 +188,7 @@ pub fn sort(options: &Options) -> Result<KakMessage, KakMessage> {
} }
} }
// Otherwise, default to comparing the content
if options.lexicographic_sort { if options.lexicographic_sort {
a.content_comparison.cmp(b.content_comparison) a.content_comparison.cmp(b.content_comparison)
} else { } else {
@ -190,7 +207,7 @@ pub fn sort(options: &Options) -> Result<KakMessage, KakMessage> {
}; };
for i in iter { for i in iter {
let new_selection = i.content.replace('\'', "''"); let new_selection = i.selection.content.replace('\'', "''");
write!(f, " '{}'", new_selection)?; write!(f, " '{}'", new_selection)?;
} }
write!(f, " ; exec R;")?; write!(f, " ; exec R;")?;