Fix clippy errors
This commit is contained in:
parent
b875e09927
commit
c9974d4d29
@ -644,7 +644,7 @@ After `shuf`:
|
|||||||
Sort selections by regular expression or content
|
Sort selections by regular expression or content
|
||||||
|
|
||||||
* `-S`/`--no-skip-whitespace` - Do not treat trimmed value of selections when sorting (by default, surrounding selection whitespace is trimmed before comparison)
|
* `-S`/`--no-skip-whitespace` - Do not treat trimmed value of selections when sorting (by default, surrounding selection whitespace is trimmed before comparison)
|
||||||
* `-l`/`--lexicographic-sort` - Sort numbers lexicographically (`10 > 2`)
|
* `-L`/`--no-lexicographic-sort` - Do not sort numbers lexicographically (`10 < 2` when `-L` is passed)
|
||||||
* `-r`/`--reverse` - Reverse sorting
|
* `-r`/`--reverse` - Reverse sorting
|
||||||
* `-i`/`--ignore-case` - Ignore case when sorting
|
* `-i`/`--ignore-case` - Ignore case when sorting
|
||||||
* `[REGEX]` - Optional regex comparison key
|
* `[REGEX]` - Optional regex comparison key
|
||||||
|
@ -3,7 +3,9 @@ pub mod types;
|
|||||||
pub use errors::KakError;
|
pub use errors::KakError;
|
||||||
pub use shell_words::ParseError;
|
pub use shell_words::ParseError;
|
||||||
use std::{
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
env,
|
env,
|
||||||
|
fmt::Display,
|
||||||
fs::{self, File, OpenOptions},
|
fs::{self, File, OpenOptions},
|
||||||
io::{BufWriter, Write},
|
io::{BufWriter, Write},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
@ -40,7 +42,7 @@ where
|
|||||||
///
|
///
|
||||||
/// 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
|
||||||
// TODO: Use AsRef
|
// TODO: Use AsRef
|
||||||
pub fn get_selections_desc_unsorted<S>(keys: Option<S>) -> Result<Vec<SelectionDesc>, KakError>
|
pub fn get_selections_desc_unordered<S>(keys: Option<S>) -> Result<Vec<SelectionDesc>, KakError>
|
||||||
where
|
where
|
||||||
S: AsRef<str>,
|
S: AsRef<str>,
|
||||||
{
|
{
|
||||||
@ -63,13 +65,17 @@ where
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
/// Return a vec of SelectionWithDesc. The returned vec is in order according to SelectionDesc
|
||||||
|
///
|
||||||
|
/// For example, if your primary selection is selection 2 of 3, the returned vec's order will be selection 2, 3, then 1
|
||||||
|
///
|
||||||
/// # 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,
|
||||||
/// or if `selections.len() != selections_desc.len`
|
/// or if `selections.len() != selections_desc.len`
|
||||||
pub fn get_selections_with_desc(keys: Option<&'_ str>) -> Result<Vec<SelectionWithDesc>, KakError> {
|
pub fn get_selections_with_desc(keys: Option<&'_ str>) -> Result<Vec<SelectionWithDesc>, KakError> {
|
||||||
let mut selections = get_selections(keys)?;
|
let mut selections = get_selections(keys)?;
|
||||||
let selections_desc = get_selections_desc_unsorted(keys)?;
|
let selections_desc = get_selections_desc_unordered(keys)?;
|
||||||
|
|
||||||
if selections.len() != selections_desc.len() {
|
if selections.len() != selections_desc.len() {
|
||||||
return Err(KakError::KakResponse(format!(
|
return Err(KakError::KakResponse(format!(
|
||||||
@ -109,13 +115,24 @@ pub fn get_selections_with_desc(keys: Option<&'_ str>) -> Result<Vec<SelectionWi
|
|||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a vec of SelectionWithDesc, sorted in file (SelectionDesc) order
|
||||||
|
///
|
||||||
|
/// For example, the returned vec's order will be selection 1, 2, then 3 regardless of the primary selection
|
||||||
|
pub fn get_selections_with_desc_ordered(
|
||||||
|
keys: Option<&'_ str>,
|
||||||
|
) -> Result<Vec<SelectionWithDesc>, KakError> {
|
||||||
|
let mut ret = get_selections_with_desc(keys)?;
|
||||||
|
ret.sort_by_key(|s| s.desc.sort());
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
/// # 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
|
||||||
pub fn set_selections<'a, I, S: 'a>(selections: I) -> Result<(), KakError>
|
pub fn set_selections<'a, I, S: 'a>(selections: I) -> Result<(), KakError>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = S>,
|
I: IntoIterator<Item = S>,
|
||||||
S: AsRef<str>,
|
S: AsRef<str> + Clone + Display,
|
||||||
{
|
{
|
||||||
let mut selections_iter = selections.into_iter().peekable();
|
let mut selections_iter = selections.into_iter().peekable();
|
||||||
if selections_iter.peek().is_none() {
|
if selections_iter.peek().is_none() {
|
||||||
@ -125,7 +142,7 @@ where
|
|||||||
let mut f = open_command_fifo()?;
|
let mut f = open_command_fifo()?;
|
||||||
write!(f, "set-register '\"'")?;
|
write!(f, "set-register '\"'")?;
|
||||||
for i in selections_iter {
|
for i in selections_iter {
|
||||||
write!(f, " '{}'", escape(i))?;
|
write!(f, " '{}'", escape(i.as_ref()))?;
|
||||||
}
|
}
|
||||||
write!(f, "; execute-keys R;")?;
|
write!(f, "; execute-keys R;")?;
|
||||||
f.flush()?;
|
f.flush()?;
|
||||||
@ -135,7 +152,7 @@ where
|
|||||||
/// # 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
|
||||||
pub fn set_selections_desc<'a, I, SD: 'a + std::fmt::Display>(selections: I) -> Result<(), KakError>
|
pub fn set_selections_desc<'a, I, SD: 'a + Display>(selections: I) -> Result<(), KakError>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = SD>,
|
I: IntoIterator<Item = SD>,
|
||||||
SD: AsRef<SelectionDesc>,
|
SD: AsRef<SelectionDesc>,
|
||||||
@ -158,11 +175,11 @@ where
|
|||||||
/// # 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
|
||||||
pub fn display_message<S: AsRef<str>>(
|
pub fn display_message<S: AsRef<str> + Clone + Display>(
|
||||||
message: S,
|
message: S,
|
||||||
debug_message: Option<S>,
|
debug_message: Option<S>,
|
||||||
) -> Result<(), KakError> {
|
) -> Result<(), KakError> {
|
||||||
let msg_str = escape(message);
|
let msg_str = escape(message.as_ref());
|
||||||
{
|
{
|
||||||
let mut f = open_command_fifo()?;
|
let mut f = open_command_fifo()?;
|
||||||
|
|
||||||
@ -170,15 +187,35 @@ pub fn display_message<S: AsRef<str>>(
|
|||||||
write!(f, "echo -debug '{}';", msg_str)?;
|
write!(f, "echo -debug '{}';", msg_str)?;
|
||||||
|
|
||||||
if let Some(debug_msg_str) = &debug_message.as_ref() {
|
if let Some(debug_msg_str) = &debug_message.as_ref() {
|
||||||
write!(f, "echo -debug '{}';", escape(debug_msg_str))?;
|
write!(f, "echo -debug '{}';", escape(debug_msg_str.as_ref()))?;
|
||||||
}
|
}
|
||||||
f.flush()?;
|
f.flush()?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn escape<S: AsRef<str>>(s: S) -> String {
|
/// Escapes a string to be sent to kak by replacing single tick with two single tics
|
||||||
s.as_ref().replace('\'', "''")
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use kakplugin::escape;
|
||||||
|
/// use std::borrow::Cow;
|
||||||
|
///
|
||||||
|
/// assert_eq!(escape("abcd"), "abcd");
|
||||||
|
/// assert_eq!(escape("'ab\\cd'"), "''ab\\cd''");
|
||||||
|
///
|
||||||
|
/// // Will not reallocate for
|
||||||
|
/// assert!(matches!(escape("abcd"), Cow::Borrowed(_)));
|
||||||
|
/// assert!(matches!(escape("ab\\nc\nd"), Cow::Borrowed(_)));
|
||||||
|
/// assert!(matches!(escape("ab'cd"), Cow::Owned(_)));
|
||||||
|
/// ```
|
||||||
|
pub fn escape(s: &str) -> Cow<str> {
|
||||||
|
if s.contains('\'') {
|
||||||
|
Cow::Owned(s.replace('\'', "''"))
|
||||||
|
} else {
|
||||||
|
Cow::Borrowed(s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Errors
|
/// # Errors
|
||||||
@ -221,7 +258,7 @@ where
|
|||||||
execute-keys '{}';
|
execute-keys '{}';
|
||||||
echo -quoting shell -to-file {response_fifo} -- {};
|
echo -quoting shell -to-file {response_fifo} -- {};
|
||||||
}}"#,
|
}}"#,
|
||||||
escape(keys),
|
escape(keys.as_ref()),
|
||||||
msg.as_ref()
|
msg.as_ref()
|
||||||
),
|
),
|
||||||
})?;
|
})?;
|
||||||
|
@ -91,14 +91,14 @@ impl SelectionDesc {
|
|||||||
if self.left < self.right {
|
if self.left < self.right {
|
||||||
// left anchor is first
|
// left anchor is first
|
||||||
Self {
|
Self {
|
||||||
left: self.left.clone(),
|
left: self.left,
|
||||||
right: self.right.clone(),
|
right: self.right,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// right anchor is first
|
// right anchor is first
|
||||||
Self {
|
Self {
|
||||||
left: self.right.clone(),
|
left: self.right,
|
||||||
right: self.left.clone(),
|
right: self.left,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -269,15 +269,15 @@ impl From<&SelectionDesc> for SelectionDesc {
|
|||||||
impl From<&AnchorPosition> for SelectionDesc {
|
impl From<&AnchorPosition> for SelectionDesc {
|
||||||
fn from(ap: &AnchorPosition) -> Self {
|
fn from(ap: &AnchorPosition) -> Self {
|
||||||
Self {
|
Self {
|
||||||
left: ap.clone(),
|
left: *ap,
|
||||||
right: ap.clone(),
|
right: *ap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<SelectionDesc> for SelectionDesc {
|
impl AsRef<SelectionDesc> for SelectionDesc {
|
||||||
fn as_ref(&self) -> &Self {
|
fn as_ref(&self) -> &Self {
|
||||||
&self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -592,7 +592,7 @@ impl Register {
|
|||||||
|
|
||||||
impl AsRef<Register> for Register {
|
impl AsRef<Register> for Register {
|
||||||
fn as_ref(&self) -> &Self {
|
fn as_ref(&self) -> &Self {
|
||||||
&self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,12 +126,12 @@ fn boxed_selections(options: &Options) -> Result<Vec<SelectionDesc>, KakError> {
|
|||||||
.collect::<Vec<SelectionDesc>>())
|
.collect::<Vec<SelectionDesc>>())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a vec of selections_desc of the intersection of the bounding box and the component rows
|
/// Returns a vec of `selections_desc` of the intersection of the bounding box and the component rows
|
||||||
///
|
///
|
||||||
/// This function takes a selection desc, and its whole-row split selections (`<a-x><a-s>`).
|
/// This function takes a selection desc, and its whole-row split selections (`<a-x><a-s>`).
|
||||||
/// For each whole-row (col 1 to max col) selection, it finds the intersection between the min col and max col in `selection_desc`
|
/// For each whole-row (col 1 to max col) selection, it finds the intersection between the min col and max col in `selection_desc`
|
||||||
///
|
///
|
||||||
/// * `selection_desc` - The base (possibly multiline) selection_desc
|
/// * `selection_desc` - The base (possibly multiline) `selection_desc`
|
||||||
/// * `selections_desc_rows` - Vec of above `selection_desc` split by line (`<a-x><a-s>`)
|
/// * `selections_desc_rows` - Vec of above `selection_desc` split by line (`<a-x><a-s>`)
|
||||||
fn to_boxed_selections<SD1, SD2>(
|
fn to_boxed_selections<SD1, SD2>(
|
||||||
selection_desc: SD1,
|
selection_desc: SD1,
|
||||||
@ -154,7 +154,7 @@ where
|
|||||||
|
|
||||||
selections_desc_rows
|
selections_desc_rows
|
||||||
.iter()
|
.iter()
|
||||||
.map(|split_sd| {
|
.filter_map(|split_sd| {
|
||||||
// Find the intersection of <row>.<min_col>,<row>.<max_col>
|
// Find the intersection of <row>.<min_col>,<row>.<max_col>
|
||||||
// If empty, return none. Flatten will not add it to the resulting vec
|
// If empty, return none. Flatten will not add it to the resulting vec
|
||||||
split_sd.as_ref().intersect(SelectionDesc {
|
split_sd.as_ref().intersect(SelectionDesc {
|
||||||
@ -168,7 +168,6 @@ where
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.flatten()
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
src/incr.rs
10
src/incr.rs
@ -1,6 +1,6 @@
|
|||||||
use evalexpr::{eval, Value};
|
use evalexpr::{eval, Value};
|
||||||
use kakplugin::{get_selections, open_command_fifo, set_selections, KakError, Selection};
|
use kakplugin::{get_selections, open_command_fifo, set_selections, KakError, Selection};
|
||||||
use std::io::Write;
|
use std::{borrow::Cow, io::Write};
|
||||||
|
|
||||||
#[derive(clap::StructOpt, Debug)]
|
#[derive(clap::StructOpt, Debug)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
@ -19,14 +19,14 @@ pub fn incr(options: &Options, should_increment: bool) -> Result<String, KakErro
|
|||||||
if should_increment { "+" } else { "-" },
|
if should_increment { "+" } else { "-" },
|
||||||
options.amount
|
options.amount
|
||||||
)) {
|
)) {
|
||||||
Ok(Value::Float(f)) => f.to_string(),
|
Ok(Value::Float(f)) => Cow::Owned(f.to_string()),
|
||||||
Ok(Value::Int(f)) => f.to_string(),
|
Ok(Value::Int(f)) => Cow::Owned(f.to_string()),
|
||||||
Ok(_) => String::from(""),
|
Ok(_) => Cow::Borrowed(""),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Error: {:?}", e);
|
eprintln!("Error: {:?}", e);
|
||||||
err_count = err_count.saturating_add(1);
|
err_count = err_count.saturating_add(1);
|
||||||
// Set the selection to empty
|
// Set the selection to empty
|
||||||
String::from("")
|
Cow::Borrowed("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))?;
|
}))?;
|
||||||
|
@ -42,7 +42,7 @@ pub fn invert(options: &Options) -> Result<String, KakError> {
|
|||||||
get_selections_desc(Some(whole_document_selection_command))?
|
get_selections_desc(Some(whole_document_selection_command))?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
// dd - The full row selectiondesc, spanning from col 1 to the rightmost col, for every row in the file
|
// dd - The full row selectiondesc, spanning from col 1 to the rightmost col, for every row in the file
|
||||||
.map(|dd: SelectionDesc| {
|
.flat_map(|dd: SelectionDesc| {
|
||||||
// For every line, if there are selections to subtract, subtract them all
|
// For every line, if there are selections to subtract, subtract them all
|
||||||
match split_selections_desc
|
match split_selections_desc
|
||||||
.binary_search_by(|sd_search| sd_search.0.cmp(&dd.left.row))
|
.binary_search_by(|sd_search| sd_search.0.cmp(&dd.left.row))
|
||||||
@ -57,7 +57,6 @@ pub fn invert(options: &Options) -> Result<String, KakError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.flatten()
|
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -68,11 +67,11 @@ pub fn invert(options: &Options) -> Result<String, KakError> {
|
|||||||
Ok(format!("Inverted {} selections", count_selections))
|
Ok(format!("Inverted {} selections", count_selections))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Subtract an iterator of `SelectionDesc`s from a given SelectionDesc
|
/// Subtract an iterator of `SelectionDesc`s from a given `SelectionDesc`
|
||||||
///
|
///
|
||||||
/// This returns a `Vec` because splitting in the middle can create two `SelectionDesc`s
|
/// This returns a `Vec` because splitting in the middle can create two `SelectionDesc`s
|
||||||
///
|
///
|
||||||
/// * `selection_desc` - The primary SelectionDesc to be subtracted from
|
/// * `selection_desc` - The primary `SelectionDesc` to be subtracted from
|
||||||
/// * `selections_desc_to_subtract` - `Vec` of `SelectionDesc`s from `sd`. Must be an owned `Vec` because it needs to be sorted
|
/// * `selections_desc_to_subtract` - `Vec` of `SelectionDesc`s from `sd`. Must be an owned `Vec` because it needs to be sorted
|
||||||
fn subtract_all_selections_desc<SD1, SD2>(
|
fn subtract_all_selections_desc<SD1, SD2>(
|
||||||
selection_desc: SD1,
|
selection_desc: SD1,
|
||||||
@ -101,11 +100,11 @@ where
|
|||||||
// TODO: Replace Just with JustLeft and JustRight?
|
// TODO: Replace Just with JustLeft and JustRight?
|
||||||
rightmost_selection_desc = sd.as_ref().clone();
|
rightmost_selection_desc = sd.as_ref().clone();
|
||||||
}
|
}
|
||||||
MaybeSplit::JustTwo(sda, sdb) => {
|
MaybeSplit::JustTwo(selectiondesc_a, selectiondesc_b) => {
|
||||||
// There was a split in the middle of the selection
|
// There was a split in the middle of the selection
|
||||||
// Put the left half into the return vector and keep checking if the right half needs more work
|
// Put the left half into the return vector and keep checking if the right half needs more work
|
||||||
ret.push(sda);
|
ret.push(selectiondesc_a);
|
||||||
rightmost_selection_desc = sdb;
|
rightmost_selection_desc = selectiondesc_b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ mod errors;
|
|||||||
mod incr;
|
mod incr;
|
||||||
mod invert;
|
mod invert;
|
||||||
mod math_eval;
|
mod math_eval;
|
||||||
|
mod pad;
|
||||||
mod set;
|
mod set;
|
||||||
mod shuf;
|
mod shuf;
|
||||||
mod sort;
|
mod sort;
|
||||||
@ -55,6 +56,8 @@ enum Commands {
|
|||||||
Invert(invert::Options),
|
Invert(invert::Options),
|
||||||
#[clap(about = "Evaluate selections as a math expression", visible_aliases = &["bc", "eval"])]
|
#[clap(about = "Evaluate selections as a math expression", visible_aliases = &["bc", "eval"])]
|
||||||
MathEval(math_eval::Options),
|
MathEval(math_eval::Options),
|
||||||
|
#[clap(about = "Pad all selections by some specifier")]
|
||||||
|
Pad(pad::Options),
|
||||||
#[clap(about = "Trim every selection")]
|
#[clap(about = "Trim every selection")]
|
||||||
Trim(trim::Options),
|
Trim(trim::Options),
|
||||||
#[clap(about = "Perform set operations on selections")]
|
#[clap(about = "Perform set operations on selections")]
|
||||||
@ -78,9 +81,8 @@ fn main() {
|
|||||||
let args = env::args().collect::<Vec<_>>();
|
let args = env::args().collect::<Vec<_>>();
|
||||||
eprintln!("Len: {}, args: {:?}", args.len(), args);
|
eprintln!("Len: {}, args: {:?}", args.len(), args);
|
||||||
if args.len() >= 2 && args[1] == "shell-script-candidates" {
|
if args.len() >= 2 && args[1] == "shell-script-candidates" {
|
||||||
match kakplugin::generate_shell_script_candidates(Commands::VARIANTS) {
|
if let Err(e) = kakplugin::generate_shell_script_candidates(Commands::VARIANTS) {
|
||||||
Err(e) => eprintln!("{e:?}"),
|
eprintln!("{e:?}");
|
||||||
Ok(()) => {}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -115,6 +117,7 @@ fn run() -> Result<String, KakError> {
|
|||||||
Commands::Uniq(o) => uniq::uniq(o),
|
Commands::Uniq(o) => uniq::uniq(o),
|
||||||
Commands::Invert(o) => invert::invert(o),
|
Commands::Invert(o) => invert::invert(o),
|
||||||
Commands::MathEval(o) => math_eval::math_eval(o),
|
Commands::MathEval(o) => math_eval::math_eval(o),
|
||||||
|
Commands::Pad(o) => pad::pad(o),
|
||||||
Commands::Trim(o) => trim::trim(o),
|
Commands::Trim(o) => trim::trim(o),
|
||||||
Commands::Set(o) => set::set(o),
|
Commands::Set(o) => set::set(o),
|
||||||
// Commands::Xargs(o) => xargs::xargs(o),
|
// Commands::Xargs(o) => xargs::xargs(o),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use evalexpr::{eval, Value};
|
use evalexpr::{eval, Value};
|
||||||
use kakplugin::{get_selections, open_command_fifo, set_selections, KakError, Selection};
|
use kakplugin::{get_selections, open_command_fifo, set_selections, KakError, Selection};
|
||||||
use std::io::Write;
|
use std::{borrow::Cow, io::Write};
|
||||||
|
|
||||||
#[derive(clap::StructOpt, Debug)]
|
#[derive(clap::StructOpt, Debug)]
|
||||||
pub struct Options;
|
pub struct Options;
|
||||||
@ -10,14 +10,14 @@ pub fn math_eval(_options: &Options) -> Result<String, KakError> {
|
|||||||
let selections = get_selections(None)?;
|
let selections = get_selections(None)?;
|
||||||
|
|
||||||
set_selections(selections.iter().map(|s| match eval(s) {
|
set_selections(selections.iter().map(|s| match eval(s) {
|
||||||
Ok(Value::Float(f)) => f.to_string(),
|
Ok(Value::Float(f)) => Cow::Owned(f.to_string()),
|
||||||
Ok(Value::Int(f)) => f.to_string(),
|
Ok(Value::Int(f)) => Cow::Owned(f.to_string()),
|
||||||
Ok(_) => String::from(""),
|
Ok(_) => Cow::Borrowed(""),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Error: {:?}", e);
|
eprintln!("Error: {:?}", e);
|
||||||
err_count = err_count.saturating_add(1);
|
err_count = err_count.saturating_add(1);
|
||||||
// Set the selection to empty
|
// Set the selection to empty
|
||||||
String::from("")
|
Cow::Borrowed("")
|
||||||
}
|
}
|
||||||
}))?;
|
}))?;
|
||||||
|
|
||||||
|
55
src/pad.rs
Normal file
55
src/pad.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
use crate::utils::split_newlines;
|
||||||
|
use evalexpr::{eval, Value};
|
||||||
|
use kakplugin::{get_selections, open_command_fifo, set_selections, KakError, Selection};
|
||||||
|
use std::{borrow::Cow, io::Write};
|
||||||
|
|
||||||
|
#[derive(clap::StructOpt, Debug)]
|
||||||
|
pub struct Options {
|
||||||
|
#[clap(index = 1, help = "Pad with this char", default_value = "0")]
|
||||||
|
fill: char,
|
||||||
|
#[clap(short, long, help = "Pad on the right instead of the left")]
|
||||||
|
right: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pad(options: &Options) -> Result<String, KakError> {
|
||||||
|
let selections = get_selections(None)?;
|
||||||
|
let selections_trailing_split: Vec<(&str, &str, &str)> = selections
|
||||||
|
.iter()
|
||||||
|
// We don't want leading or trailing newlines to count
|
||||||
|
.map(|s| split_newlines(s))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// The max length of selections with newlines split off
|
||||||
|
let max_len = selections_trailing_split
|
||||||
|
.iter()
|
||||||
|
.map(|(_, s, _)| s.len())
|
||||||
|
.max()
|
||||||
|
.ok_or(KakError::CustomStatic("No selections"))?;
|
||||||
|
|
||||||
|
let mut num_padded: usize = 0;
|
||||||
|
let num_total = selections.len();
|
||||||
|
|
||||||
|
set_selections(selections_trailing_split.iter().zip(selections.iter()).map(
|
||||||
|
|((leading_newlines, s, trailing_newlines), orig_s)| match max_len.checked_sub(s.len()) {
|
||||||
|
Some(0) | None => Cow::Borrowed(orig_s.as_str()),
|
||||||
|
Some(len) => {
|
||||||
|
num_padded += 1;
|
||||||
|
let fill = options.fill.to_string().repeat(len);
|
||||||
|
let mut ret = leading_newlines.to_string();
|
||||||
|
if options.right {
|
||||||
|
ret.push_str(s);
|
||||||
|
ret.push_str(&fill);
|
||||||
|
} else {
|
||||||
|
ret.push_str(&fill);
|
||||||
|
ret.push_str(s);
|
||||||
|
}
|
||||||
|
ret.push_str(trailing_newlines);
|
||||||
|
Cow::Owned(ret)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
))?;
|
||||||
|
|
||||||
|
Ok(format!(
|
||||||
|
"Padded {num_padded} selections ({num_total} total)",
|
||||||
|
))
|
||||||
|
}
|
31
src/set.rs
31
src/set.rs
@ -1,8 +1,8 @@
|
|||||||
// use crate::utils;
|
// use crate::utils;
|
||||||
use clap::ArgEnum;
|
use clap::ArgEnum;
|
||||||
use kakplugin::{
|
use kakplugin::{
|
||||||
get_selections, get_selections_with_desc, set_selections, set_selections_desc, types::Register,
|
get_selections, get_selections_with_desc_ordered, set_selections, set_selections_desc,
|
||||||
KakError, Selection, SelectionWithDesc,
|
types::Register, KakError, Selection, SelectionWithDesc,
|
||||||
};
|
};
|
||||||
use linked_hash_map::LinkedHashMap;
|
use linked_hash_map::LinkedHashMap;
|
||||||
use linked_hash_set::LinkedHashSet;
|
use linked_hash_set::LinkedHashSet;
|
||||||
@ -14,6 +14,7 @@ pub struct Options {
|
|||||||
#[clap(
|
#[clap(
|
||||||
min_values = 1,
|
min_values = 1,
|
||||||
max_values = 3,
|
max_values = 3,
|
||||||
|
allow_hyphen_values = true,
|
||||||
help = "Register operation and operand. Empty register is current selection. Example: 'a-b' or '+b'"
|
help = "Register operation and operand. Empty register is current selection. Example: 'a-b' or '+b'"
|
||||||
)]
|
)]
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
@ -114,8 +115,8 @@ pub fn set(options: &Options) -> Result<String, KakError> {
|
|||||||
|
|
||||||
match &operation {
|
match &operation {
|
||||||
Operation::Compare => compare(
|
Operation::Compare => compare(
|
||||||
&left_register,
|
left_register,
|
||||||
&right_register,
|
right_register,
|
||||||
&result,
|
&result,
|
||||||
&left_ordered_counts,
|
&left_ordered_counts,
|
||||||
&right_ordered_counts,
|
&right_ordered_counts,
|
||||||
@ -127,11 +128,11 @@ pub fn set(options: &Options) -> Result<String, KakError> {
|
|||||||
if left_register == Register::Underscore {
|
if left_register == Register::Underscore {
|
||||||
// If the user asked for an intersection or subtraction from the current selection, we can update selection_descs only
|
// If the user asked for an intersection or subtraction from the current selection, we can update selection_descs only
|
||||||
// For example (current selection) - (contents of register a) allows us to simply deselect some selections
|
// For example (current selection) - (contents of register a) allows us to simply deselect some selections
|
||||||
reduce_selections(&options, &result)?
|
reduce_selections(options, &result)?;
|
||||||
} else {
|
} else {
|
||||||
// The user asked for registers that *aren't* the current selection
|
// The user asked for registers that *aren't* the current selection
|
||||||
// This means either registers don't represent the current selection, or the current selection is on the other side
|
// This means either registers don't represent the current selection, or the current selection is on the other side
|
||||||
print_result(&result)?
|
print_result(&result)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,18 +149,14 @@ pub fn set(options: &Options) -> Result<String, KakError> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reduces selections to those that are in the key_set_operation_result
|
/// Reduces selections to those that are in the `key_set_operation_result`
|
||||||
fn reduce_selections(
|
fn reduce_selections(
|
||||||
options: &Options,
|
options: &Options,
|
||||||
key_set_operation_result: &LinkedHashSet<&Selection>,
|
key_set_operation_result: &LinkedHashSet<&Selection>,
|
||||||
) -> Result<(), KakError> {
|
) -> Result<(), KakError> {
|
||||||
// The registers should have been read in a draft context
|
// The registers should have been read in a draft context
|
||||||
// So the current selection will be unmodified
|
// So the current selection will be unmodified
|
||||||
let selections_with_desc = {
|
let selections_with_desc = get_selections_with_desc_ordered(None)?;
|
||||||
let mut r = get_selections_with_desc(None)?;
|
|
||||||
r.sort_by_key(|s| s.desc.sort());
|
|
||||||
r
|
|
||||||
};
|
|
||||||
|
|
||||||
set_selections_desc(selections_with_desc.into_iter().filter_map(|swd| {
|
set_selections_desc(selections_with_desc.into_iter().filter_map(|swd| {
|
||||||
// Does not matter if the operation was - or &
|
// Does not matter if the operation was - or &
|
||||||
@ -212,8 +209,8 @@ fn print_result(key_set_operation_result: &LinkedHashSet<&Selection>) -> Result<
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn compare(
|
fn compare(
|
||||||
left_register: &Register,
|
left_register: Register,
|
||||||
right_register: &Register,
|
right_register: Register,
|
||||||
key_set_operation_result: &LinkedHashSet<&Selection>,
|
key_set_operation_result: &LinkedHashSet<&Selection>,
|
||||||
left_ordered_counts: &LinkedHashMap<Selection, usize>,
|
left_ordered_counts: &LinkedHashMap<Selection, usize>,
|
||||||
right_ordered_counts: &LinkedHashMap<Selection, usize>,
|
right_ordered_counts: &LinkedHashMap<Selection, usize>,
|
||||||
@ -281,10 +278,10 @@ fn to_ordered_counts(options: &Options, sels: Vec<Selection>) -> LinkedHashMap<S
|
|||||||
if key.is_empty() {
|
if key.is_empty() {
|
||||||
// We don't want to even pretend to look at empty keys
|
// We don't want to even pretend to look at empty keys
|
||||||
continue;
|
continue;
|
||||||
} else {
|
|
||||||
let entry: &mut usize = ret.entry(key).or_insert(0);
|
|
||||||
*entry = entry.saturating_add(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let entry: &mut usize = ret.entry(key).or_insert(0);
|
||||||
|
*entry = entry.saturating_add(1);
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
85
src/sort.rs
85
src/sort.rs
@ -1,7 +1,7 @@
|
|||||||
use alphanumeric_sort::compare_str;
|
use alphanumeric_sort::compare_str;
|
||||||
use kakplugin::{self, get_selections_with_desc, open_command_fifo, KakError, SelectionWithDesc};
|
use kakplugin::{self, get_selections_with_desc, open_command_fifo, KakError, SelectionWithDesc};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::{cmp::Ordering, io::Write};
|
use std::{borrow::Cow, cmp::Ordering, io::Write};
|
||||||
|
|
||||||
#[derive(clap::StructOpt, Debug)]
|
#[derive(clap::StructOpt, Debug)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
@ -16,10 +16,11 @@ pub struct Options {
|
|||||||
// TODO: Can we invert a boolean? This name is terrible
|
// TODO: Can we invert a boolean? This name is terrible
|
||||||
#[clap(short = 'S', long, parse(try_from_str = invert_bool), default_value_t, help = "Do not treat trimmed value of selections when sorting")]
|
#[clap(short = 'S', long, parse(try_from_str = invert_bool), default_value_t, help = "Do not treat trimmed value of selections when sorting")]
|
||||||
no_skip_whitespace: bool,
|
no_skip_whitespace: bool,
|
||||||
#[clap(short, long, help = "Sort numbers lexicographically")]
|
#[clap(short = 'L', long, help = "Do not sort numbers lexicographically")]
|
||||||
lexicographic_sort: bool,
|
no_lexicographic_sort: bool,
|
||||||
#[clap(short, long, help = "Reverse sorting")]
|
#[clap(short, long, help = "Reverse sorting")]
|
||||||
reverse: bool,
|
reverse: bool,
|
||||||
|
// TODO: Allow ignoring case
|
||||||
#[clap(short, long, help = "Ignore case when sorting")]
|
#[clap(short, long, help = "Ignore case when sorting")]
|
||||||
ignore_case: bool,
|
ignore_case: bool,
|
||||||
}
|
}
|
||||||
@ -37,7 +38,7 @@ struct SortableSelection<'a> {
|
|||||||
/// The content of the selection
|
/// The content of the selection
|
||||||
selection: &'a SelectionWithDesc,
|
selection: &'a SelectionWithDesc,
|
||||||
/// The string used to compare the content
|
/// The string used to compare the content
|
||||||
content_comparison: &'a str,
|
content_comparison: Cow<'a, str>,
|
||||||
/// Any subselections
|
/// Any subselections
|
||||||
subselections: Vec<&'a str>,
|
subselections: Vec<&'a str>,
|
||||||
}
|
}
|
||||||
@ -105,18 +106,17 @@ fn to_sortable_selection<'a, 'b>(
|
|||||||
selection: &'a SelectionWithDesc,
|
selection: &'a SelectionWithDesc,
|
||||||
options: &'b Options,
|
options: &'b Options,
|
||||||
) -> SortableSelection<'a> {
|
) -> SortableSelection<'a> {
|
||||||
if options.no_skip_whitespace {
|
SortableSelection {
|
||||||
SortableSelection {
|
selection,
|
||||||
selection,
|
// TODO: Properly use Cow
|
||||||
content_comparison: selection.content.as_str(),
|
content_comparison: crate::utils::get_key(
|
||||||
subselections: vec![],
|
&selection.content,
|
||||||
}
|
!options.no_skip_whitespace,
|
||||||
} else {
|
options.regex.as_ref(),
|
||||||
SortableSelection {
|
options.ignore_case,
|
||||||
selection,
|
)
|
||||||
content_comparison: selection.content.as_str().trim(),
|
.into(),
|
||||||
subselections: vec![],
|
subselections: vec![],
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,25 +147,31 @@ pub fn sort(options: &Options) -> Result<String, KakError> {
|
|||||||
.map(|s| to_sortable_selection(s, options))
|
.map(|s| to_sortable_selection(s, options))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
(Some(regex), None) => {
|
(Some(_regex), None) => {
|
||||||
// Sort based on the regular expression
|
// Sort based on the regular expression
|
||||||
selections
|
selections
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| {
|
.map(|s| to_sortable_selection(s, options))
|
||||||
let mut sortable_selection = to_sortable_selection(s, options);
|
|
||||||
if let Some(regex_match) = (|| {
|
|
||||||
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()
|
.collect()
|
||||||
|
|
||||||
|
// TODO: Figure out if this is fine
|
||||||
|
// selections
|
||||||
|
// .iter()
|
||||||
|
// .map(|s| {
|
||||||
|
// let mut sortable_selection = to_sortable_selection(s, options);
|
||||||
|
// if let Some(regex_match) = (|| {
|
||||||
|
// 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, _) => {
|
(None, _) => {
|
||||||
// Sort based on subselections
|
// Sort based on subselections
|
||||||
@ -178,22 +184,23 @@ pub fn sort(options: &Options) -> Result<String, KakError> {
|
|||||||
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)
|
// 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.no_lexicographic_sort {
|
||||||
a_subselection.cmp(b_subselection)
|
a_subselection.cmp(b_subselection)
|
||||||
} else {
|
} else {
|
||||||
compare_str(a_subselection, b_subselection)
|
compare_str(&a_subselection, &b_subselection)
|
||||||
};
|
};
|
||||||
match comparison {
|
|
||||||
Ordering::Less | Ordering::Greater => return comparison,
|
// If the comparison is not equal, stop here
|
||||||
Ordering::Equal => {}
|
if comparison != Ordering::Equal {
|
||||||
|
return comparison;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, default to comparing the content
|
// Otherwise, default to comparing the content
|
||||||
if options.lexicographic_sort {
|
if options.no_lexicographic_sort {
|
||||||
a.content_comparison.cmp(b.content_comparison)
|
a.content_comparison.cmp(&b.content_comparison)
|
||||||
} else {
|
} else {
|
||||||
compare_str(a.content_comparison, b.content_comparison)
|
compare_str(&a.content_comparison, &b.content_comparison)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
42
src/utils.rs
42
src/utils.rs
@ -52,3 +52,45 @@ pub fn get_hash(
|
|||||||
|
|
||||||
hasher.finish()
|
hasher.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Splits an `&str` into (string_value, trailing_newlines)
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// assert_eq!(split_trailing_newlines("asdf\n"), ("asdf", "\n"));
|
||||||
|
/// assert_eq!(split_trailing_newlines("asdf\n\nhjk\n"), ("asdf\n\nhjk", "\n"));
|
||||||
|
/// assert_eq!(split_trailing_newlines("asdf"), ("asdf", ""));
|
||||||
|
/// assert_eq!(split_trailing_newlines(""), ("", ""));
|
||||||
|
/// ```
|
||||||
|
pub fn split_trailing_newlines<'a>(s: &'a str) -> (&'a str, &'a str) {
|
||||||
|
s.rfind(|c| c != '\n')
|
||||||
|
.map(|idx| s.split_at(idx + 1))
|
||||||
|
.unwrap_or((s, ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Splits an `&str` into (leading_newlines, string_value, trailing_newlines)
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// assert_eq!(split_newlines("asdf\n"), ("", "asdf", "\n"));
|
||||||
|
/// assert_eq!(split_newlines("asdf\n\nhjk\n"), ("", "asdf\n\nhjk", "\n"));
|
||||||
|
/// assert_eq!(split_newlines("\nasdf\n\nhjk\n"), ("\n", "asdf\n\nhjk", "\n"));
|
||||||
|
/// assert_eq!(split_newlines("asdf"), ("", "asdf", ""));
|
||||||
|
/// assert_eq!(split_newlines("\n\n\nasdf"), ("\n\n\n", "asdf", ""));
|
||||||
|
/// assert_eq!(split_newlines(""), ("", "", ""));
|
||||||
|
/// ```
|
||||||
|
pub fn split_newlines<'a>(s: &'a str) -> (&'a str, &'a str, &'a str) {
|
||||||
|
let (leading_newlines, s) = s
|
||||||
|
.find(|c| c != '\n')
|
||||||
|
.map(|idx| s.split_at(idx))
|
||||||
|
.unwrap_or(("", s));
|
||||||
|
|
||||||
|
let (s, trailing_newlines) = s
|
||||||
|
.rfind(|c| c != '\n')
|
||||||
|
.map(|idx| s.split_at(idx + 1))
|
||||||
|
.unwrap_or((s, ""));
|
||||||
|
|
||||||
|
(leading_newlines, s, trailing_newlines)
|
||||||
|
}
|
||||||
|
@ -20,24 +20,22 @@ pub struct Options {
|
|||||||
}
|
}
|
||||||
pub fn xlookup(options: &Options) -> Result<String, KakError> {
|
pub fn xlookup(options: &Options) -> Result<String, KakError> {
|
||||||
let lookup_table = build_lookuptable(options.register)?;
|
let lookup_table = build_lookuptable(options.register)?;
|
||||||
eprintln!("Lookup table: {lookup_table:#?}");
|
|
||||||
|
|
||||||
let selections = get_selections(None)?;
|
let selections = get_selections(None)?;
|
||||||
|
|
||||||
let mut err_count: usize = 0;
|
let mut err_count: usize = 0;
|
||||||
|
|
||||||
set_selections(selections.iter().map(|key| {
|
set_selections(selections.iter().map(|key| {
|
||||||
match lookup_table.get(&get_hash(&key, false, None, false)) {
|
lookup_table
|
||||||
Some(v) => v.to_string(),
|
.get(&get_hash(key, false, None, false))
|
||||||
None => {
|
.map_or_else(
|
||||||
eprintln!(
|
|| {
|
||||||
"Nothing for '{key}' ({})",
|
eprintln!("Key '{key}' not found",);
|
||||||
get_hash(&key, false, None, false)
|
err_count += 1;
|
||||||
);
|
String::from("")
|
||||||
err_count += 1;
|
},
|
||||||
String::from("")
|
ToString::to_string,
|
||||||
}
|
)
|
||||||
}
|
|
||||||
}))?;
|
}))?;
|
||||||
|
|
||||||
Ok(if err_count == 0 {
|
Ok(if err_count == 0 {
|
||||||
@ -52,14 +50,14 @@ pub fn xlookup(options: &Options) -> Result<String, KakError> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_lookuptable(reg: Register) -> Result<BTreeMap<u64, Selection>, KakError> {
|
fn build_lookuptable(register: Register) -> Result<BTreeMap<u64, Selection>, KakError> {
|
||||||
let mut selections = get_selections(Some(&format!("\"{reg}z")))?;
|
let mut selections = get_selections(Some(&format!("\"{register}z")))?;
|
||||||
let mut iter = selections.array_chunks_mut();
|
let mut iter = selections.array_chunks_mut();
|
||||||
let ret = iter.try_fold(BTreeMap::new(), |mut acc, [key, value]| {
|
let ret = iter.try_fold(BTreeMap::new(), |mut acc, [key, value]| {
|
||||||
match acc.entry(get_hash(&key, false, None, false)) {
|
match acc.entry(get_hash(key, false, None, false)) {
|
||||||
Occupied(_) => Err(KakError::Custom(format!("Duplicate key '{key}'"))),
|
Occupied(_) => Err(KakError::Custom(format!("Duplicate key '{key}'"))),
|
||||||
Vacant(v) => {
|
Vacant(v) => {
|
||||||
v.insert(value.to_owned());
|
v.insert(value.clone());
|
||||||
Ok(acc)
|
Ok(acc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user