An unbelieveable amount of changes. too many to count really
This commit is contained in:
parent
ee42bcf67e
commit
4bf376d802
303
src/kak.rs
Normal file
303
src/kak.rs
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
use crate::{get_var, KakMessage};
|
||||||
|
use std::{
|
||||||
|
fmt,
|
||||||
|
fs::{self, File, OpenOptions},
|
||||||
|
io::{BufWriter, Write},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type Selection = String;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
pub struct SelectionWithDesc {
|
||||||
|
pub content: Selection,
|
||||||
|
pub desc: SelectionDesc,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, PartialOrd, Ord, Eq, Debug)]
|
||||||
|
pub struct SelectionDesc {
|
||||||
|
pub left: AnchorPosition,
|
||||||
|
pub right: AnchorPosition,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SelectionDesc {
|
||||||
|
#[must_use]
|
||||||
|
pub 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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub 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_a.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SelectionDesc {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{},{}", self.left, self.right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for SelectionDesc {
|
||||||
|
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 {
|
||||||
|
left: AnchorPosition::from_str(left)?,
|
||||||
|
right: AnchorPosition::from_str(right)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialOrd, PartialEq, Clone, Eq, Ord, Debug)]
|
||||||
|
pub struct AnchorPosition {
|
||||||
|
pub row: usize,
|
||||||
|
pub col: usize,
|
||||||
|
}
|
||||||
|
impl fmt::Display for AnchorPosition {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}.{}", self.row, self.col)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Will return `Err` if command fifo could not be opened, read from, or written to
|
||||||
|
pub fn get_selections() -> Result<Vec<Selection>, KakMessage> {
|
||||||
|
response("%val{selections}")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Will return `Err` if command fifo could not be opened, read from, or written to
|
||||||
|
pub fn get_selections_desc() -> Result<Vec<SelectionDesc>, KakMessage> {
|
||||||
|
response("%val{selections_desc}")?
|
||||||
|
.iter()
|
||||||
|
.map(|sd| SelectionDesc::from_str(sd))
|
||||||
|
.collect::<Result<Vec<_>, KakMessage>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Will return `Err` if command fifo could not be opened, read from, or written to,
|
||||||
|
/// or if `selections.len() != selections_desc.len`
|
||||||
|
pub fn get_selections_with_desc() -> Result<Vec<SelectionWithDesc>, KakMessage> {
|
||||||
|
let mut selections = get_selections()?;
|
||||||
|
let selections_desc = get_selections_desc()?;
|
||||||
|
|
||||||
|
if selections.len() != selections_desc.len() {
|
||||||
|
return Err(KakMessage(
|
||||||
|
"Internal error".to_string(),
|
||||||
|
Some(format!(
|
||||||
|
"Counts for selections={}, selections_desc={}",
|
||||||
|
selections.len(),
|
||||||
|
selections_desc.len()
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kakoune prints selections in file order, but prints selections_desc rotated based on current selection
|
||||||
|
let min_selection = selections_desc.iter().min().ok_or_else(|| {
|
||||||
|
KakMessage(
|
||||||
|
"Internal error".to_string(),
|
||||||
|
Some("No selections in selections_desc".to_string()),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
// Need to rotate selections by primary selection's position in the list
|
||||||
|
match selections_desc.iter().position(|p| p == min_selection) {
|
||||||
|
Some(i) => {
|
||||||
|
selections.rotate_right(i);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(KakMessage(
|
||||||
|
"Internal error".to_string(),
|
||||||
|
Some(format!(
|
||||||
|
"Primary selection {} not found in selection_desc list ({:#?})",
|
||||||
|
min_selection, selections_desc
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selections
|
||||||
|
.into_iter()
|
||||||
|
.zip(selections_desc.into_iter())
|
||||||
|
.map(|(content, desc)| Ok(SelectionWithDesc { content, desc }))
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Will return `Err` if command fifo could not be opened, read from, or written to
|
||||||
|
pub fn set_selections<'a, I, S: 'a>(selections: I) -> Result<(), KakMessage>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = &'a S>,
|
||||||
|
S: AsRef<str> + fmt::Display,
|
||||||
|
{
|
||||||
|
let mut f = open_command_fifo()?;
|
||||||
|
write!(f, "reg '\"'")?;
|
||||||
|
for i in selections {
|
||||||
|
write!(f, " '{}'", i.as_ref().replace('\'', "''"))?;
|
||||||
|
}
|
||||||
|
write!(f, "; exec R;")?;
|
||||||
|
f.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Will return `Err` if command fifo could not be opened, read from, or written to
|
||||||
|
pub fn set_selections_desc<'a, I>(selections: I) -> Result<(), KakMessage>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = &'a SelectionDesc>,
|
||||||
|
{
|
||||||
|
let mut f = open_command_fifo()?;
|
||||||
|
write!(f, "select")?;
|
||||||
|
for i in selections {
|
||||||
|
write!(f, " {}", i)?;
|
||||||
|
}
|
||||||
|
write!(f, ";")?;
|
||||||
|
f.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Will return `Err` if command fifo could not be opened, read from, or written to
|
||||||
|
pub fn send_message(msg: &KakMessage) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let msg_str = msg.0.replace('\'', "''");
|
||||||
|
{
|
||||||
|
let mut f =
|
||||||
|
open_command_fifo().map_err(|e| format!("Could not open command fifo: {:?}", e))?;
|
||||||
|
|
||||||
|
write!(f, "echo '{}';", msg_str)?;
|
||||||
|
write!(f, "echo -debug '{}';", msg_str)?;
|
||||||
|
|
||||||
|
if let Some(debug_msg_str) = &msg.1 {
|
||||||
|
write!(f, "echo -debug '{}';", debug_msg_str.replace('\'', "''"))?;
|
||||||
|
}
|
||||||
|
f.flush()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Will return `Err` if command fifo could not be opened or written to
|
||||||
|
pub fn exec(cmd: &str) -> Result<(), KakMessage> {
|
||||||
|
let mut f = open_command_fifo()?;
|
||||||
|
|
||||||
|
write!(f, "{}", cmd)?;
|
||||||
|
f.flush().map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Will return `Err` if command fifo could not be opened or written to
|
||||||
|
pub fn response(msg: &str) -> Result<Vec<String>, KakMessage> {
|
||||||
|
exec(&format!(
|
||||||
|
"echo -quoting shell -to-file {} -- {msg}",
|
||||||
|
get_var("kak_response_fifo")?
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let selections = shellwords::split(&fs::read_to_string(&get_var("kak_response_fifo")?)?)?;
|
||||||
|
|
||||||
|
Ok(selections)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Will return `Err` if command fifo could not be opened
|
||||||
|
pub fn open_command_fifo() -> Result<BufWriter<File>, KakMessage> {
|
||||||
|
OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.append(true)
|
||||||
|
.open(&get_var("kak_command_fifo")?)
|
||||||
|
.map(BufWriter::new)
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
const SD: SelectionDesc = SelectionDesc {
|
||||||
|
left: AnchorPosition { row: 18, col: 9 },
|
||||||
|
right: AnchorPosition { row: 10, col: 1 },
|
||||||
|
};
|
||||||
|
#[test]
|
||||||
|
fn test_anchor_position() {
|
||||||
|
// Check parsing
|
||||||
|
assert_eq!(SelectionDesc::from_str("18.9,10.1").unwrap(), SD);
|
||||||
|
// Check if multiple parsed ones match
|
||||||
|
assert_eq!(
|
||||||
|
SelectionDesc::from_str("18.9,10.1").unwrap(),
|
||||||
|
SelectionDesc::from_str("18.9,10.1").unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sort() {
|
||||||
|
// Check if sorting works
|
||||||
|
assert_eq!(SD.sort(), SelectionDesc::from_str("10.1,18.9").unwrap());
|
||||||
|
assert_eq!(SD.sort(), SD.sort().sort());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_contains() {
|
||||||
|
assert!(SD.contains(&SD));
|
||||||
|
assert!(SD.contains(&SelectionDesc::from_str("17.9,10.1").unwrap()));
|
||||||
|
assert!(SD.contains(&SelectionDesc::from_str("18.8,10.1").unwrap()));
|
||||||
|
assert!(SD.contains(&SelectionDesc::from_str("18.9,11.1").unwrap()));
|
||||||
|
assert!(SD.contains(&SelectionDesc::from_str("18.9,10.2").unwrap()));
|
||||||
|
assert!(SD.contains(&SelectionDesc::from_str("10.1,17.9").unwrap()));
|
||||||
|
assert!(SD.contains(&SelectionDesc::from_str("10.1,18.8").unwrap()));
|
||||||
|
assert!(SD.contains(&SelectionDesc::from_str("11.1,18.9").unwrap()));
|
||||||
|
assert!(SD.contains(&SelectionDesc::from_str("10.2,18.9").unwrap()));
|
||||||
|
assert!(!SD.contains(&SelectionDesc::from_str("19.9,10.1").unwrap()));
|
||||||
|
assert!(!SD.contains(&SelectionDesc::from_str("18.10,10.1").unwrap()));
|
||||||
|
assert!(!SD.contains(&SelectionDesc::from_str("18.9,9.1").unwrap()));
|
||||||
|
assert!(!SD.contains(&SelectionDesc::from_str("18.9,10.0").unwrap()));
|
||||||
|
assert!(!SD.contains(&SelectionDesc::from_str("10.1,19.9").unwrap()));
|
||||||
|
assert!(!SD.contains(&SelectionDesc::from_str("10.1,18.10").unwrap()));
|
||||||
|
assert!(!SD.contains(&SelectionDesc::from_str("9.1,18.9").unwrap()));
|
||||||
|
assert!(!SD.contains(&SelectionDesc::from_str("10.0,18.9").unwrap()));
|
||||||
|
}
|
||||||
|
}
|
178
src/main.rs
178
src/main.rs
@ -10,19 +10,15 @@
|
|||||||
// #![allow(dead_code, unused_imports)]
|
// #![allow(dead_code, unused_imports)]
|
||||||
|
|
||||||
mod errors;
|
mod errors;
|
||||||
|
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;
|
||||||
use std::{
|
pub use kak::*;
|
||||||
convert::Into,
|
use std::env;
|
||||||
env, fs,
|
|
||||||
fs::{File, OpenOptions},
|
|
||||||
io::{BufWriter, Write},
|
|
||||||
str::FromStr,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(about, version, author)]
|
#[clap(about, version, author)]
|
||||||
@ -38,83 +34,13 @@ 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"])]
|
||||||
MathEval(math_eval::Options),
|
MathEval(math_eval::Options),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, PartialOrd, Ord, Eq, Debug)]
|
|
||||||
pub struct SelectionDesc {
|
|
||||||
left: AnchorPosition,
|
|
||||||
right: AnchorPosition,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for SelectionDesc {
|
|
||||||
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 {
|
|
||||||
left: AnchorPosition::from_str(left)?,
|
|
||||||
right: AnchorPosition::from_str(right)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialOrd, PartialEq, Clone, Eq, Ord, 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_a.right
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let msg = match run() {
|
let msg = match run() {
|
||||||
Ok(msg) => msg,
|
Ok(msg) => msg,
|
||||||
@ -134,23 +60,6 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_message(msg: &KakMessage) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let msg_str = msg.0.replace('\'', "''");
|
|
||||||
{
|
|
||||||
let mut f =
|
|
||||||
open_command_fifo().map_err(|e| format!("Could not open command fifo: {:?}", e))?;
|
|
||||||
|
|
||||||
write!(f, "echo '{}';", msg_str)?;
|
|
||||||
write!(f, "echo -debug '{}';", msg_str)?;
|
|
||||||
|
|
||||||
if let Some(debug_msg_str) = &msg.1 {
|
|
||||||
write!(f, "echo -debug '{}';", debug_msg_str.replace('\'', "''"))?;
|
|
||||||
}
|
|
||||||
f.flush()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run() -> Result<KakMessage, KakMessage> {
|
fn run() -> Result<KakMessage, KakMessage> {
|
||||||
let options = Cli::try_parse().map_err(|e| {
|
let options = Cli::try_parse().map_err(|e| {
|
||||||
KakMessage(
|
KakMessage(
|
||||||
@ -160,49 +69,13 @@ 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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Will return `Err` if command fifo could not be opened or written to
|
|
||||||
pub fn kak_exec(cmd: &str) -> Result<(), KakMessage> {
|
|
||||||
let mut f = open_command_fifo()?;
|
|
||||||
|
|
||||||
write!(f, "{}", cmd)?;
|
|
||||||
f.flush().map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Will return `Err` if command fifo could not be opened or written to
|
|
||||||
pub fn kak_response(msg: &str) -> Result<Vec<String>, KakMessage> {
|
|
||||||
kak_exec(&format!(
|
|
||||||
"echo -quoting shell -to-file {} -- {msg}",
|
|
||||||
get_var("kak_response_fifo")?
|
|
||||||
))?;
|
|
||||||
|
|
||||||
let selections = shellwords::split(&fs::read_to_string(&get_var("kak_response_fifo")?)?)?;
|
|
||||||
|
|
||||||
Ok(selections)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Will return `Err` if command fifo could not be opened
|
|
||||||
pub fn open_command_fifo() -> Result<BufWriter<File>, KakMessage> {
|
|
||||||
OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.append(true)
|
|
||||||
.open(&get_var("kak_command_fifo")?)
|
|
||||||
.map(BufWriter::new)
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Will return `Err` if requested environment variable is not unicode or not present
|
/// Will return `Err` if requested environment variable is not unicode or not present
|
||||||
@ -216,42 +89,3 @@ pub fn get_var(var_name: &str) -> Result<String, KakMessage> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
const sd: SelectionDesc = SelectionDesc {
|
|
||||||
left: AnchorPosition { row: 18, col: 9 },
|
|
||||||
right: AnchorPosition { row: 10, col: 1 },
|
|
||||||
};
|
|
||||||
#[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.1").unwrap(), sd);
|
|
||||||
// Check if multiple parsed ones match
|
|
||||||
assert_eq!(
|
|
||||||
SelectionDesc::from_str("18.9,10.1").unwrap(),
|
|
||||||
SelectionDesc::from_str("18.9,10.1").unwrap()
|
|
||||||
);
|
|
||||||
// Check if sorting works
|
|
||||||
assert_eq!(sd.sort(), SelectionDesc::from_str("10.1,18.9").unwrap());
|
|
||||||
assert_eq!(sd.sort(), sd.sort().sort());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_contains() {
|
|
||||||
assert_true!(sd.contains(sd));
|
|
||||||
assert_false!(sd.contains(SelectionDesc::from_str("17.9,10.1").unwrap()));
|
|
||||||
assert_false!(sd.contains(SelectionDesc::from_str("18.8,10.1").unwrap()));
|
|
||||||
assert_false!(sd.contains(SelectionDesc::from_str("18.9,11.1").unwrap()));
|
|
||||||
assert_false!(sd.contains(SelectionDesc::from_str("18.9,10.2").unwrap()));
|
|
||||||
assert_true!(sd.contains(SelectionDesc::from_str("19.9,10.1").unwrap()));
|
|
||||||
assert_true!(sd.contains(SelectionDesc::from_str("18.10,10.1").unwrap()));
|
|
||||||
assert_true!(sd.contains(SelectionDesc::from_str("18.9,9.1").unwrap()));
|
|
||||||
assert_true!(sd.contains(SelectionDesc::from_str("18.9,10.0").unwrap()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use crate::{kak_response, open_command_fifo, KakMessage};
|
use crate::{get_selections, open_command_fifo, KakMessage};
|
||||||
use evalexpr::{eval, Value};
|
use evalexpr::{eval, Value};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
#[derive(clap::StructOpt, Debug)]
|
#[derive(clap::StructOpt, Debug)]
|
||||||
pub struct Options;
|
pub struct Options;
|
||||||
pub fn math_eval(_options: &Options) -> Result<KakMessage, KakMessage> {
|
pub fn math_eval(_options: &Options) -> Result<KakMessage, KakMessage> {
|
||||||
let selections = kak_response("%val{selections}")?;
|
let selections = get_selections()?;
|
||||||
|
|
||||||
let mut f = open_command_fifo()?;
|
let mut f = open_command_fifo()?;
|
||||||
write!(f, "reg '\"'")?;
|
write!(f, "reg '\"'")?;
|
||||||
|
15
src/shuf.rs
15
src/shuf.rs
@ -1,23 +1,14 @@
|
|||||||
use crate::{kak_response, open_command_fifo, KakMessage};
|
use crate::{get_selections, set_selections, KakMessage};
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
use std::io::Write;
|
|
||||||
#[derive(clap::StructOpt, Debug)]
|
#[derive(clap::StructOpt, Debug)]
|
||||||
pub struct Options;
|
pub struct Options;
|
||||||
pub fn shuf(_options: &Options) -> Result<KakMessage, KakMessage> {
|
pub fn shuf(_options: &Options) -> Result<KakMessage, KakMessage> {
|
||||||
let mut selections = kak_response("%val{selections}")?;
|
let mut selections = get_selections()?;
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
selections.shuffle(&mut rng);
|
selections.shuffle(&mut rng);
|
||||||
|
|
||||||
let mut f = open_command_fifo()?;
|
set_selections(selections.iter())?;
|
||||||
write!(f, "reg '\"'")?;
|
|
||||||
|
|
||||||
for i in &selections {
|
|
||||||
let new_selection = i.replace('\'', "''");
|
|
||||||
write!(f, " '{}'", new_selection)?;
|
|
||||||
}
|
|
||||||
write!(f, " ; exec R;")?;
|
|
||||||
f.flush()?;
|
|
||||||
|
|
||||||
Ok(KakMessage(
|
Ok(KakMessage(
|
||||||
format!("Shuf {} selections", selections.len()),
|
format!("Shuf {} selections", selections.len()),
|
||||||
|
44
src/sort.rs
44
src/sort.rs
@ -1,4 +1,6 @@
|
|||||||
use crate::{kak_response, open_command_fifo, KakMessage, SelectionDesc};
|
use crate::{
|
||||||
|
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, str::FromStr};
|
||||||
@ -41,17 +43,9 @@ 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>(
|
fn get_sortable_selections_subselections<'a, 'b, 'tmp, S: AsRef<str> + std::fmt::Debug + 'a>(
|
||||||
options: &'b Options,
|
options: &'b Options,
|
||||||
selections: &'a [S],
|
selections: Vec<SelectionWithDesc>,
|
||||||
selections_desc: &'tmp [S],
|
subselections: Vec<SelectionWithDesc>,
|
||||||
subselections: &'a [S],
|
|
||||||
subselections_desc: &'tmp [S],
|
|
||||||
) -> Result<Vec<SortableSelection<'a>>, KakMessage> {
|
) -> Result<Vec<SortableSelection<'a>>, KakMessage> {
|
||||||
if selections.len() != selections_desc.len() || subselections.len() != subselections_desc.len()
|
|
||||||
{
|
|
||||||
return Err(KakMessage ("Internal error".to_string(), Some(
|
|
||||||
format!("Counts for selections={}, selections_desc={}, subselections={}, subselections_desc={}",selections.len(),selections_desc.len(),subselections.len(),subselections_desc.len())
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
let mut sortable_selections = selections
|
let mut sortable_selections = selections
|
||||||
.iter()
|
.iter()
|
||||||
.zip(selections_desc.iter())
|
.zip(selections_desc.iter())
|
||||||
@ -126,20 +120,30 @@ 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: Option<(Vec<String>, Vec<String>)> = options
|
let subselections = options
|
||||||
.subselections_register
|
.subselections_register
|
||||||
.map::<Result<(Vec<String>, Vec<String>), KakMessage>, _>(|_c| {
|
.map(|_r| -> Result<Vec<SelectionWithDesc>, KakMessage> {
|
||||||
let subselections = kak_response("%val{selections}")?;
|
let ret = get_selections_with_desc()?;
|
||||||
let subselections_desc = kak_response("%val{selections_desc}")?;
|
kak::exec("exec z")?;
|
||||||
// kak_exec(&format!("exec z",))?;
|
Ok(ret)
|
||||||
Ok((subselections, subselections_desc))
|
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let selections = kak_response("%val{selections}")?;
|
let selections = get_selections_with_desc()?;
|
||||||
|
|
||||||
|
// let subselections: Option<(Vec<String>, Vec<String>)> = 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 {
|
let mut zipped: Vec<SortableSelection<'_>> = match subselections {
|
||||||
Some((ref subselections, ref subselections_desc)) => {
|
Some(subselections) => {
|
||||||
let selections_desc = kak_response("%val{selections_desc}")?;
|
let selections_desc = kak::response("%val{selections_desc}")?;
|
||||||
|
|
||||||
get_sortable_selections_subselections(
|
get_sortable_selections_subselections(
|
||||||
options,
|
options,
|
||||||
|
91
src/uniq.rs
91
src/uniq.rs
@ -1,8 +1,9 @@
|
|||||||
use crate::{kak_response, open_command_fifo, KakMessage};
|
use crate::{
|
||||||
|
get_selections_desc, set_selections, set_selections_desc, KakMessage, SelectionWithDesc,
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{hash_map::DefaultHasher, BTreeSet},
|
collections::{hash_map::DefaultHasher, BTreeSet},
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
io::Write,
|
|
||||||
};
|
};
|
||||||
#[derive(clap::StructOpt, Debug)]
|
#[derive(clap::StructOpt, Debug)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
@ -13,44 +14,68 @@ pub struct Options {
|
|||||||
no_skip_whitespace: bool,
|
no_skip_whitespace: bool,
|
||||||
}
|
}
|
||||||
pub fn uniq(options: &Options) -> Result<KakMessage, KakMessage> {
|
pub fn uniq(options: &Options) -> Result<KakMessage, KakMessage> {
|
||||||
let selections = kak_response("%val{selections}")?;
|
let mut selections = crate::get_selections_with_desc()?;
|
||||||
|
// Sort selections so the first element is the unique one, not an arbitrary one based on primary selection
|
||||||
|
selections.sort_by_key(|s| s.desc.sort());
|
||||||
|
|
||||||
let mut f = open_command_fifo()?;
|
// Set the new selection types
|
||||||
write!(f, "reg '\"'")?;
|
let new_selections: Vec<Option<SelectionWithDesc>> = selections
|
||||||
|
.into_iter()
|
||||||
|
// Create a BTreeSet of hashes of lines. This way, string content is not stored, but uniqueness can be determined
|
||||||
|
.scan(BTreeSet::new(), |state, s| {
|
||||||
|
let key = if options.no_skip_whitespace {
|
||||||
|
s.content.as_str()
|
||||||
|
} else {
|
||||||
|
s.content.trim()
|
||||||
|
};
|
||||||
|
|
||||||
for i in selections.iter().scan(BTreeSet::new(), |state, s| {
|
let key = if options.ignore_case {
|
||||||
let key = if options.no_skip_whitespace {
|
key.to_lowercase()
|
||||||
s
|
} else {
|
||||||
} else {
|
// TODO: Do I really need to clone this?
|
||||||
s.trim()
|
key.to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
let key = if options.ignore_case {
|
let mut hasher = DefaultHasher::new();
|
||||||
key.to_lowercase()
|
key.hash(&mut hasher);
|
||||||
} else {
|
|
||||||
// TODO: Do I really need to clone this?
|
|
||||||
key.to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
// Try inserting to the hash
|
||||||
key.hash(&mut hasher);
|
if state.insert(hasher.finish()) {
|
||||||
|
// True if this is a string we haven't seen before
|
||||||
Some(if state.insert(hasher.finish()) {
|
Some(Some(s))
|
||||||
// True if this is a new line
|
} else {
|
||||||
s
|
// Nothing was inserted because we already saw this string
|
||||||
} else {
|
// Return Some(None) so the iterator can continue
|
||||||
// Nothing was inserted because we already saw this line
|
Some(None)
|
||||||
""
|
}
|
||||||
})
|
})
|
||||||
}) {
|
.collect();
|
||||||
let new_selection = i.replace('\'', "''");
|
|
||||||
write!(f, " '{}'", new_selection)?;
|
// Preallocate so the content and string have the same type, but allocation is not repeated
|
||||||
}
|
// TODO: Do we really have to do this?
|
||||||
write!(f, " ; exec R;")?;
|
let empty_string = String::default();
|
||||||
f.flush()?;
|
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(
|
Ok(KakMessage(
|
||||||
format!("Uniq {} selections", selections.len()),
|
format!("{} unique selections out of {}", new_count, old_count),
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user