Many changes including shuf
This commit is contained in:
parent
04f9c177c5
commit
caa4e82757
60
Cargo.lock
generated
60
Cargo.lock
generated
@ -40,6 +40,12 @@ version = "1.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "3.0.10"
|
version = "3.0.10"
|
||||||
@ -70,6 +76,17 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
@ -107,6 +124,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"alphanumeric-sort",
|
"alphanumeric-sort",
|
||||||
"clap",
|
"clap",
|
||||||
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
"shellwords",
|
"shellwords",
|
||||||
]
|
]
|
||||||
@ -138,6 +156,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-error"
|
name = "proc-macro-error"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@ -180,6 +204,36 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.5.4"
|
version = "1.5.4"
|
||||||
@ -251,6 +305,12 @@ version = "0.9.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@ -13,6 +13,7 @@ regex = "1"
|
|||||||
clap = {version = "3", features = ["derive", "env"]}
|
clap = {version = "3", features = ["derive", "env"]}
|
||||||
alphanumeric-sort = "1"
|
alphanumeric-sort = "1"
|
||||||
shellwords = "1"
|
shellwords = "1"
|
||||||
|
rand = "0.8"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct KakMessage(pub String, pub Option<String>);
|
pub struct KakMessage(pub String, pub Option<String>);
|
||||||
|
|
||||||
@ -21,3 +23,9 @@ impl From<shellwords::MismatchedQuotes> for KakMessage {
|
|||||||
Self("Corrupt kak response".to_string(), Some(err.to_string()))
|
Self("Corrupt kak response".to_string(), Some(err.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ParseIntError> for KakMessage {
|
||||||
|
fn from(err: ParseIntError) -> Self {
|
||||||
|
Self(format!("Could not parse int: {}", err), None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
242
src/main.rs
242
src/main.rs
@ -1,16 +1,20 @@
|
|||||||
#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
|
#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
|
||||||
|
#![allow(dead_code, unused_imports)]
|
||||||
|
|
||||||
mod errors;
|
mod errors;
|
||||||
use alphanumeric_sort::compare_str;
|
mod shuf;
|
||||||
|
mod sort;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use errors::KakMessage;
|
use errors::KakMessage;
|
||||||
use regex::Regex;
|
use shuf::ShufOptions;
|
||||||
|
use sort::SortOptions;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(about, version, author)]
|
#[clap(about, version, author)]
|
||||||
@ -26,32 +30,84 @@ struct Cli {
|
|||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
enum Commands {
|
enum Commands {
|
||||||
// #[clap(flatten)]
|
|
||||||
Sort(SortOptions),
|
Sort(SortOptions),
|
||||||
|
Shuf(ShufOptions),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::StructOpt, Debug)]
|
#[derive(PartialEq, PartialOrd, Debug)]
|
||||||
struct SortOptions {
|
pub struct SelectionDesc {
|
||||||
#[clap(index = 1)]
|
left: AnchorPosition,
|
||||||
regex: Option<String>,
|
right: AnchorPosition,
|
||||||
#[clap(short = 's', long)]
|
|
||||||
subselections_register: Option<char>,
|
|
||||||
// TODO: Can we invert a boolean? This name is terrible
|
|
||||||
#[clap(short = 'S', long)]
|
|
||||||
no_skip_whitespace: bool,
|
|
||||||
#[clap(short, long)]
|
|
||||||
lexicographic_sort: bool,
|
|
||||||
#[clap(short, long)]
|
|
||||||
reverse: bool,
|
|
||||||
#[clap(short, long)]
|
|
||||||
ignore_case: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SortableSelection {
|
impl FromStr for SelectionDesc {
|
||||||
content: &str,
|
type Err = KakMessage;
|
||||||
subselections: Vec<&str>,
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let (left, right) = s.split_once(',').ok_or_else(|| {
|
||||||
|
KakMessage(
|
||||||
|
"Could not parse position".to_string(),
|
||||||
|
Some(format!("Could not parse as position: {}", s)),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
left: AnchorPosition::from_str(left)?,
|
||||||
|
right: AnchorPosition::from_str(right)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialOrd, PartialEq, Clone, Debug)]
|
||||||
|
pub struct AnchorPosition {
|
||||||
|
row: usize,
|
||||||
|
col: usize,
|
||||||
|
}
|
||||||
|
impl FromStr for AnchorPosition {
|
||||||
|
type Err = KakMessage;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let (left, right) = s.split_once('.').ok_or_else(|| {
|
||||||
|
KakMessage(
|
||||||
|
"Could not parse position".to_string(),
|
||||||
|
Some(format!("Could not parse as position: {}", s)),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
Ok(Self {
|
||||||
|
row: usize::from_str(left)?,
|
||||||
|
col: usize::from_str(right)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SelectionDesc {
|
||||||
|
fn sort(&self) -> Self {
|
||||||
|
if self.left < self.right {
|
||||||
|
// left anchor is first
|
||||||
|
Self {
|
||||||
|
left: self.left.clone(),
|
||||||
|
right: self.right.clone(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// right anchor is first
|
||||||
|
Self {
|
||||||
|
left: self.right.clone(),
|
||||||
|
right: self.left.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(&self, b: &Self) -> bool {
|
||||||
|
// Cursor and anchor can be flipped. Set a.0 to be leftmost cursor
|
||||||
|
let sorted_a = self.sort();
|
||||||
|
let sorted_b = b.sort();
|
||||||
|
|
||||||
|
sorted_b.left >= sorted_a.left && sorted_b.right <= sorted_b.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl PartialOrd for SelectionDesc {
|
||||||
|
// fn cmp() {}
|
||||||
|
// }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let msg = match run() {
|
let msg = match run() {
|
||||||
Ok(msg) => msg,
|
Ok(msg) => msg,
|
||||||
@ -91,128 +147,18 @@ fn run() -> Result<KakMessage, KakMessage> {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
match &options.command {
|
match &options.command {
|
||||||
Commands::Sort(sort_options) => sort(sort_options),
|
Commands::Sort(sort_options) => sort::sort(sort_options),
|
||||||
|
Commands::Shuf(shuf_options) => shuf::shuf(shuf_options),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sort_keys_simple<'a>(
|
pub fn kak_exec(cmd: &str) -> Result<(), KakMessage> {
|
||||||
sort_options: &SortOptions,
|
|
||||||
selections: &[&str],
|
|
||||||
) -> Result<Vec<SortableSelection>, KakMessage> {
|
|
||||||
let re = sort_options
|
|
||||||
.regex
|
|
||||||
.as_ref()
|
|
||||||
.map(|r| Regex::new(r))
|
|
||||||
.transpose()
|
|
||||||
.map_err(|_| {
|
|
||||||
format!(
|
|
||||||
"Invalid regular expression: {}",
|
|
||||||
sort_options.regex.as_ref().unwrap_or(&"".to_string())
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(selections
|
|
||||||
.iter()
|
|
||||||
.map(|s| {
|
|
||||||
if sort_options.no_skip_whitespace {
|
|
||||||
SortableSelection{
|
|
||||||
content: s,
|
|
||||||
subselections: vec![s],
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SortableSelection{
|
|
||||||
content: s,
|
|
||||||
subselections: vec![s.trim()],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(|(s, k)| {
|
|
||||||
let captures = re.as_ref()?.captures(k)?;
|
|
||||||
captures
|
|
||||||
.get(1)
|
|
||||||
.or_else(|| captures.get(0))
|
|
||||||
.map(|m| m.as_str())
|
|
||||||
})
|
|
||||||
.collect::<Vec<Option<&str>>>())
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn sort_keys_subselection(sort_options: &SortOptions) -> Result<Vec<(&String, Option<&str>)>, KakMessage> {
|
|
||||||
// let sort_selections = kak_response("%val{selections}")?.iter_mut().map(|a| {
|
|
||||||
// if sort_options.no_skip_whitespace {
|
|
||||||
// a
|
|
||||||
// } else {
|
|
||||||
// a.trim()
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// let sort_selections_desc = kak_response("%val{selections_desc}")?;
|
|
||||||
// kak_exec("z")?;
|
|
||||||
// let selections = kak_response("%val{selections}")?;
|
|
||||||
// let selections_desc = kak_response("%val{selections_desc}")?;
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn sort(sort_options: &SortOptions) -> Result<KakMessage, KakMessage> {
|
|
||||||
// let selections = kak_response("%val{selections}")?;
|
|
||||||
|
|
||||||
// let sort_keys = if let Some(r) = sort_options.subselections_register {
|
|
||||||
// let selections_desc = kak_response("%val{selections_desc}")?;
|
|
||||||
// } else {
|
|
||||||
// };
|
|
||||||
|
|
||||||
let mut zipped = match sort_options.subselections_register {
|
|
||||||
Some(c) => {
|
|
||||||
let selections = kak_response("%val{selections}")?;
|
|
||||||
selections
|
|
||||||
.into_iter()
|
|
||||||
.zip(sort_keys_simple(sort_options, &selections))
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let selections = kak_response("%val{selections}")?;
|
|
||||||
selections.iter().zip(selections.iter().map(|s| s.as_str()))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// let mut zipped = sort_keys_simple(sort_options)?;
|
|
||||||
|
|
||||||
zipped.sort_by(|(a, a_key), (b, b_key)| {
|
|
||||||
let a = a_key.unwrap_or(a);
|
|
||||||
let b = b_key.unwrap_or(b);
|
|
||||||
|
|
||||||
if sort_options.lexicographic_sort {
|
|
||||||
a.cmp(b)
|
|
||||||
} else {
|
|
||||||
compare_str(a, b)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut f = open_command_fifo()?;
|
|
||||||
|
|
||||||
write!(f, "reg '\"'")?;
|
|
||||||
|
|
||||||
let iter: Box<dyn Iterator<Item = _>> = if sort_options.reverse {
|
|
||||||
Box::new(zipped.iter().rev())
|
|
||||||
} else {
|
|
||||||
Box::new(zipped.iter())
|
|
||||||
};
|
|
||||||
|
|
||||||
for i in iter {
|
|
||||||
let new_selection = i.0.replace('\'', "''");
|
|
||||||
write!(f, " '{}'", new_selection)?;
|
|
||||||
}
|
|
||||||
write!(f, " ; exec R;")?;
|
|
||||||
|
|
||||||
Ok(KakMessage(
|
|
||||||
format!("Sorted {} selections", zipped.len()),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kak_exec(cmd: &str) -> Result<(), KakMessage> {
|
|
||||||
let mut f = open_command_fifo()?;
|
let mut f = open_command_fifo()?;
|
||||||
|
|
||||||
write!(f, "{}", cmd).map_err(|e| e.into())
|
write!(f, "{}", cmd).map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kak_response(msg: &str) -> Result<Vec<String>, KakMessage> {
|
pub fn kak_response(msg: &str) -> Result<Vec<String>, KakMessage> {
|
||||||
kak_exec(&format!(
|
kak_exec(&format!(
|
||||||
"echo -quoting shell -to-file {} -- {msg}",
|
"echo -quoting shell -to-file {} -- {msg}",
|
||||||
get_var("kak_response_fifo")?
|
get_var("kak_response_fifo")?
|
||||||
@ -223,7 +169,7 @@ fn kak_response(msg: &str) -> Result<Vec<String>, KakMessage> {
|
|||||||
Ok(selections)
|
Ok(selections)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_command_fifo() -> Result<File, KakMessage> {
|
pub fn open_command_fifo() -> Result<File, KakMessage> {
|
||||||
OpenOptions::new()
|
OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.append(true)
|
.append(true)
|
||||||
@ -231,7 +177,7 @@ fn open_command_fifo() -> Result<File, KakMessage> {
|
|||||||
.map_err(|e| e.into())
|
.map_err(|e| e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_var(var_name: &str) -> Result<String, KakMessage> {
|
pub fn get_var(var_name: &str) -> Result<String, KakMessage> {
|
||||||
env::var(var_name).map_err(|e| match e {
|
env::var(var_name).map_err(|e| match e {
|
||||||
env::VarError::NotPresent => {
|
env::VarError::NotPresent => {
|
||||||
KakMessage(format!("Env var {} is not defined", var_name), None)
|
KakMessage(format!("Env var {} is not defined", var_name), None)
|
||||||
@ -241,3 +187,25 @@ fn get_var(var_name: &str) -> Result<String, KakMessage> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_anchor_position() {
|
||||||
|
let sd = SelectionDesc {
|
||||||
|
left: AnchorPosition { row: 18, col: 9 },
|
||||||
|
right: AnchorPosition { row: 10, col: 0 },
|
||||||
|
};
|
||||||
|
// Check parsing
|
||||||
|
assert_eq!(SelectionDesc::from_str("18.9,10.0").unwrap(), sd);
|
||||||
|
// Check if multiple parsed ones match
|
||||||
|
assert_eq!(
|
||||||
|
SelectionDesc::from_str("18.9,10.0").unwrap(),
|
||||||
|
SelectionDesc::from_str("18.9,10.0").unwrap()
|
||||||
|
);
|
||||||
|
// Check if sorting works
|
||||||
|
assert_eq!(sd.sort(), SelectionDesc::from_str("10.0,18.9").unwrap());
|
||||||
|
assert_eq!(sd.sort(), sd.sort().sort());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
27
src/shuf.rs
Normal file
27
src/shuf.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use crate::KakMessage;
|
||||||
|
use crate::{kak_response, open_command_fifo};
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
use rand::thread_rng;
|
||||||
|
use std::io::Write;
|
||||||
|
#[derive(clap::StructOpt, Debug)]
|
||||||
|
pub struct ShufOptions;
|
||||||
|
pub fn shuf(_shuf_options: &ShufOptions) -> Result<KakMessage, KakMessage> {
|
||||||
|
let mut selections = kak_response("%val{selections}")?;
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
|
selections.shuffle(&mut rng);
|
||||||
|
|
||||||
|
let mut f = open_command_fifo()?;
|
||||||
|
write!(f, "reg '\"'")?;
|
||||||
|
|
||||||
|
for i in selections.iter() {
|
||||||
|
let new_selection = i.replace('\'', "''");
|
||||||
|
write!(f, " '{}'", new_selection)?;
|
||||||
|
}
|
||||||
|
write!(f, " ; exec R;")?;
|
||||||
|
|
||||||
|
Ok(KakMessage(
|
||||||
|
format!("Shuf {} selections", selections.len()),
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
}
|
147
src/sort.rs
Normal file
147
src/sort.rs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
use crate::KakMessage;
|
||||||
|
use crate::{kak_response, open_command_fifo};
|
||||||
|
use alphanumeric_sort::compare_str;
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
use rand::thread_rng;
|
||||||
|
use regex::Regex;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
#[derive(clap::StructOpt, Debug)]
|
||||||
|
pub struct SortOptions {
|
||||||
|
#[clap(index = 1)]
|
||||||
|
regex: Option<Regex>,
|
||||||
|
#[clap(short = 's', long)]
|
||||||
|
subselections_register: Option<char>,
|
||||||
|
// TODO: Can we invert a boolean? This name is terrible
|
||||||
|
#[clap(short = 'S', long)]
|
||||||
|
no_skip_whitespace: bool,
|
||||||
|
#[clap(short, long)]
|
||||||
|
lexicographic_sort: bool,
|
||||||
|
#[clap(short, long)]
|
||||||
|
reverse: bool,
|
||||||
|
#[clap(short, long)]
|
||||||
|
ignore_case: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SortableSelection<'a> {
|
||||||
|
/// The content of the selection
|
||||||
|
content: &'a str,
|
||||||
|
/// The string used to compare the content
|
||||||
|
content_comparison: &'a str,
|
||||||
|
/// Any subselections
|
||||||
|
subselections: Vec<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_sortable_selections_subselections<'a, 'b, 'tmp, S: AsRef<str> + std::fmt::Debug + 'a>(
|
||||||
|
sort_options: &'b SortOptions,
|
||||||
|
selections: &'a [S],
|
||||||
|
sort_selections_desc: &'tmp [S],
|
||||||
|
subselections: &'a [S],
|
||||||
|
subselections_desc: &'tmp [S],
|
||||||
|
) -> Result<Vec<SortableSelection<'a>>, KakMessage> {
|
||||||
|
eprintln!(
|
||||||
|
"All units: {:?}\n{:?}\n{:?}\n{:?}",
|
||||||
|
selections, sort_selections_desc, subselections, subselections_desc,
|
||||||
|
);
|
||||||
|
let mut sortable_selections = selections
|
||||||
|
.iter()
|
||||||
|
.map(|s| to_sortable_selection(s.as_ref(), sort_options))
|
||||||
|
.collect::<Vec<SortableSelection>>();
|
||||||
|
|
||||||
|
// sortable_selections.into_iter().map
|
||||||
|
|
||||||
|
// Ok(vec![])
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_sortable_selection<'a, 'b>(
|
||||||
|
selection: &'a str,
|
||||||
|
sort_options: &'b SortOptions,
|
||||||
|
) -> SortableSelection<'a> {
|
||||||
|
if sort_options.no_skip_whitespace {
|
||||||
|
SortableSelection {
|
||||||
|
content: selection,
|
||||||
|
content_comparison: selection,
|
||||||
|
subselections: vec![],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SortableSelection {
|
||||||
|
content: selection,
|
||||||
|
content_comparison: selection.trim(),
|
||||||
|
subselections: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sort(sort_options: &SortOptions) -> Result<KakMessage, KakMessage> {
|
||||||
|
let subselections: Option<(Vec<String>, Vec<String>)> = sort_options
|
||||||
|
.subselections_register
|
||||||
|
.map::<Result<(Vec<String>, Vec<String>), KakMessage>, _>(|_c| {
|
||||||
|
let subselections = kak_response("%val{selections}")?;
|
||||||
|
let subselections_desc = kak_response("%val{selections_desc}")?;
|
||||||
|
// kak_exec(&format!("exec z",))?;
|
||||||
|
Ok((subselections, subselections_desc))
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
let selections = kak_response("%val{selections}")?;
|
||||||
|
|
||||||
|
let mut zipped: Vec<SortableSelection<'_>> = match subselections {
|
||||||
|
Some((ref subselections, ref subselections_desc)) => {
|
||||||
|
let selections_desc = kak_response("%val{selections_desc}")?;
|
||||||
|
|
||||||
|
get_sortable_selections_subselections(
|
||||||
|
sort_options,
|
||||||
|
&selections,
|
||||||
|
&selections_desc,
|
||||||
|
subselections,
|
||||||
|
subselections_desc,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
None => selections
|
||||||
|
.iter()
|
||||||
|
.map(|s| to_sortable_selection(s, sort_options))
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
zipped.sort_by(|a, b| {
|
||||||
|
for (a_subselection, b_subselection) in a.subselections.iter().zip(b.subselections.iter()) {
|
||||||
|
let comparison = if sort_options.lexicographic_sort {
|
||||||
|
a_subselection.cmp(b_subselection)
|
||||||
|
} else {
|
||||||
|
compare_str(a_subselection, b_subselection)
|
||||||
|
};
|
||||||
|
match comparison {
|
||||||
|
Ordering::Less | Ordering::Greater => return comparison,
|
||||||
|
Ordering::Equal => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sort_options.lexicographic_sort {
|
||||||
|
a.content_comparison.cmp(b.content_comparison)
|
||||||
|
} else {
|
||||||
|
compare_str(a.content_comparison, b.content_comparison)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut f = open_command_fifo()?;
|
||||||
|
|
||||||
|
write!(f, "reg '\"'")?;
|
||||||
|
|
||||||
|
let iter: Box<dyn Iterator<Item = _>> = if sort_options.reverse {
|
||||||
|
Box::new(zipped.iter().rev())
|
||||||
|
} else {
|
||||||
|
Box::new(zipped.iter())
|
||||||
|
};
|
||||||
|
|
||||||
|
for i in iter {
|
||||||
|
let new_selection = i.content.replace('\'', "''");
|
||||||
|
write!(f, " '{}'", new_selection)?;
|
||||||
|
}
|
||||||
|
write!(f, " ; exec R;")?;
|
||||||
|
|
||||||
|
Ok(KakMessage(
|
||||||
|
format!("Sorted {} selections", zipped.len()),
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user