Fix clippy errors

This commit is contained in:
Austen Adler 2022-10-02 09:57:37 -04:00
parent b875e09927
commit c9974d4d29
13 changed files with 253 additions and 116 deletions

View File

@ -644,7 +644,7 @@ After `shuf`:
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)
* `-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
* `-i`/`--ignore-case` - Ignore case when sorting
* `[REGEX]` - Optional regex comparison key

View File

@ -3,7 +3,9 @@ pub mod types;
pub use errors::KakError;
pub use shell_words::ParseError;
use std::{
borrow::Cow,
env,
fmt::Display,
fs::{self, File, OpenOptions},
io::{BufWriter, Write},
str::FromStr,
@ -40,7 +42,7 @@ where
///
/// Will return `Err` if command fifo could not be opened, read from, or written to
// 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
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
///
/// 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(keys: Option<&'_ str>) -> Result<Vec<SelectionWithDesc>, KakError> {
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() {
return Err(KakError::KakResponse(format!(
@ -109,13 +115,24 @@ pub fn get_selections_with_desc(keys: Option<&'_ str>) -> Result<Vec<SelectionWi
.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
///
/// 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>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
S: AsRef<str> + Clone + Display,
{
let mut selections_iter = selections.into_iter().peekable();
if selections_iter.peek().is_none() {
@ -125,7 +142,7 @@ where
let mut f = open_command_fifo()?;
write!(f, "set-register '\"'")?;
for i in selections_iter {
write!(f, " '{}'", escape(i))?;
write!(f, " '{}'", escape(i.as_ref()))?;
}
write!(f, "; execute-keys R;")?;
f.flush()?;
@ -135,7 +152,7 @@ where
/// # Errors
///
/// 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
I: IntoIterator<Item = SD>,
SD: AsRef<SelectionDesc>,
@ -158,11 +175,11 @@ where
/// # Errors
///
/// 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,
debug_message: Option<S>,
) -> Result<(), KakError> {
let msg_str = escape(message);
let msg_str = escape(message.as_ref());
{
let mut f = open_command_fifo()?;
@ -170,15 +187,35 @@ pub fn display_message<S: AsRef<str>>(
write!(f, "echo -debug '{}';", msg_str)?;
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()?;
}
Ok(())
}
pub fn escape<S: AsRef<str>>(s: S) -> String {
s.as_ref().replace('\'', "''")
/// Escapes a string to be sent to kak by replacing single tick with two single tics
///
/// # 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
@ -221,7 +258,7 @@ where
execute-keys '{}';
echo -quoting shell -to-file {response_fifo} -- {};
}}"#,
escape(keys),
escape(keys.as_ref()),
msg.as_ref()
),
})?;

View File

@ -91,14 +91,14 @@ impl SelectionDesc {
if self.left < self.right {
// left anchor is first
Self {
left: self.left.clone(),
right: self.right.clone(),
left: self.left,
right: self.right,
}
} else {
// right anchor is first
Self {
left: self.right.clone(),
right: self.left.clone(),
left: self.right,
right: self.left,
}
}
}
@ -269,15 +269,15 @@ impl From<&SelectionDesc> for SelectionDesc {
impl From<&AnchorPosition> for SelectionDesc {
fn from(ap: &AnchorPosition) -> Self {
Self {
left: ap.clone(),
right: ap.clone(),
left: *ap,
right: *ap,
}
}
}
impl AsRef<SelectionDesc> for SelectionDesc {
fn as_ref(&self) -> &Self {
&self
self
}
}
@ -592,7 +592,7 @@ impl Register {
impl AsRef<Register> for Register {
fn as_ref(&self) -> &Self {
&self
self
}
}

View File

@ -126,12 +126,12 @@ fn boxed_selections(options: &Options) -> Result<Vec<SelectionDesc>, KakError> {
.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>`).
/// 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>`)
fn to_boxed_selections<SD1, SD2>(
selection_desc: SD1,
@ -154,7 +154,7 @@ where
selections_desc_rows
.iter()
.map(|split_sd| {
.filter_map(|split_sd| {
// Find the intersection of <row>.<min_col>,<row>.<max_col>
// If empty, return none. Flatten will not add it to the resulting vec
split_sd.as_ref().intersect(SelectionDesc {
@ -168,7 +168,6 @@ where
},
})
})
.flatten()
.collect()
}

View File

@ -1,6 +1,6 @@
use evalexpr::{eval, Value};
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)]
pub struct Options {
@ -19,14 +19,14 @@ pub fn incr(options: &Options, should_increment: bool) -> Result<String, KakErro
if should_increment { "+" } else { "-" },
options.amount
)) {
Ok(Value::Float(f)) => f.to_string(),
Ok(Value::Int(f)) => f.to_string(),
Ok(_) => String::from(""),
Ok(Value::Float(f)) => Cow::Owned(f.to_string()),
Ok(Value::Int(f)) => Cow::Owned(f.to_string()),
Ok(_) => Cow::Borrowed(""),
Err(e) => {
eprintln!("Error: {:?}", e);
err_count = err_count.saturating_add(1);
// Set the selection to empty
String::from("")
Cow::Borrowed("")
}
}
}))?;

View File

@ -42,7 +42,7 @@ pub fn invert(options: &Options) -> Result<String, KakError> {
get_selections_desc(Some(whole_document_selection_command))?
.into_iter()
// 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
match split_selections_desc
.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()
};
@ -68,11 +67,11 @@ pub fn invert(options: &Options) -> Result<String, KakError> {
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
///
/// * `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
fn subtract_all_selections_desc<SD1, SD2>(
selection_desc: SD1,
@ -101,11 +100,11 @@ where
// TODO: Replace Just with JustLeft and JustRight?
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
// Put the left half into the return vector and keep checking if the right half needs more work
ret.push(sda);
rightmost_selection_desc = sdb;
ret.push(selectiondesc_a);
rightmost_selection_desc = selectiondesc_b;
}
}
}

View File

@ -16,6 +16,7 @@ mod errors;
mod incr;
mod invert;
mod math_eval;
mod pad;
mod set;
mod shuf;
mod sort;
@ -55,6 +56,8 @@ enum Commands {
Invert(invert::Options),
#[clap(about = "Evaluate selections as a math expression", visible_aliases = &["bc", "eval"])]
MathEval(math_eval::Options),
#[clap(about = "Pad all selections by some specifier")]
Pad(pad::Options),
#[clap(about = "Trim every selection")]
Trim(trim::Options),
#[clap(about = "Perform set operations on selections")]
@ -78,9 +81,8 @@ fn main() {
let args = env::args().collect::<Vec<_>>();
eprintln!("Len: {}, args: {:?}", args.len(), args);
if args.len() >= 2 && args[1] == "shell-script-candidates" {
match kakplugin::generate_shell_script_candidates(Commands::VARIANTS) {
Err(e) => eprintln!("{e:?}"),
Ok(()) => {}
if let Err(e) = kakplugin::generate_shell_script_candidates(Commands::VARIANTS) {
eprintln!("{e:?}");
}
return;
}
@ -115,6 +117,7 @@ fn run() -> Result<String, KakError> {
Commands::Uniq(o) => uniq::uniq(o),
Commands::Invert(o) => invert::invert(o),
Commands::MathEval(o) => math_eval::math_eval(o),
Commands::Pad(o) => pad::pad(o),
Commands::Trim(o) => trim::trim(o),
Commands::Set(o) => set::set(o),
// Commands::Xargs(o) => xargs::xargs(o),

View File

@ -1,6 +1,6 @@
use evalexpr::{eval, Value};
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)]
pub struct Options;
@ -10,14 +10,14 @@ pub fn math_eval(_options: &Options) -> Result<String, KakError> {
let selections = get_selections(None)?;
set_selections(selections.iter().map(|s| match eval(s) {
Ok(Value::Float(f)) => f.to_string(),
Ok(Value::Int(f)) => f.to_string(),
Ok(_) => String::from(""),
Ok(Value::Float(f)) => Cow::Owned(f.to_string()),
Ok(Value::Int(f)) => Cow::Owned(f.to_string()),
Ok(_) => Cow::Borrowed(""),
Err(e) => {
eprintln!("Error: {:?}", e);
err_count = err_count.saturating_add(1);
// Set the selection to empty
String::from("")
Cow::Borrowed("")
}
}))?;

55
src/pad.rs Normal file
View 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)",
))
}

View File

@ -1,8 +1,8 @@
// use crate::utils;
use clap::ArgEnum;
use kakplugin::{
get_selections, get_selections_with_desc, set_selections, set_selections_desc, types::Register,
KakError, Selection, SelectionWithDesc,
get_selections, get_selections_with_desc_ordered, set_selections, set_selections_desc,
types::Register, KakError, Selection, SelectionWithDesc,
};
use linked_hash_map::LinkedHashMap;
use linked_hash_set::LinkedHashSet;
@ -14,6 +14,7 @@ pub struct Options {
#[clap(
min_values = 1,
max_values = 3,
allow_hyphen_values = true,
help = "Register operation and operand. Empty register is current selection. Example: 'a-b' or '+b'"
)]
args: Vec<String>,
@ -114,8 +115,8 @@ pub fn set(options: &Options) -> Result<String, KakError> {
match &operation {
Operation::Compare => compare(
&left_register,
&right_register,
left_register,
right_register,
&result,
&left_ordered_counts,
&right_ordered_counts,
@ -127,11 +128,11 @@ pub fn set(options: &Options) -> Result<String, KakError> {
if left_register == Register::Underscore {
// 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
reduce_selections(&options, &result)?
reduce_selections(options, &result)?;
} else {
// 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
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(
options: &Options,
key_set_operation_result: &LinkedHashSet<&Selection>,
) -> Result<(), KakError> {
// The registers should have been read in a draft context
// So the current selection will be unmodified
let selections_with_desc = {
let mut r = get_selections_with_desc(None)?;
r.sort_by_key(|s| s.desc.sort());
r
};
let selections_with_desc = get_selections_with_desc_ordered(None)?;
set_selections_desc(selections_with_desc.into_iter().filter_map(|swd| {
// Does not matter if the operation was - or &
@ -212,8 +209,8 @@ fn print_result(key_set_operation_result: &LinkedHashSet<&Selection>) -> Result<
}
fn compare(
left_register: &Register,
right_register: &Register,
left_register: Register,
right_register: Register,
key_set_operation_result: &LinkedHashSet<&Selection>,
left_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() {
// We don't want to even pretend to look at empty keys
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
}

View File

@ -1,7 +1,7 @@
use alphanumeric_sort::compare_str;
use kakplugin::{self, get_selections_with_desc, open_command_fifo, KakError, SelectionWithDesc};
use regex::Regex;
use std::{cmp::Ordering, io::Write};
use std::{borrow::Cow, cmp::Ordering, io::Write};
#[derive(clap::StructOpt, Debug)]
pub struct Options {
@ -16,10 +16,11 @@ pub struct Options {
// 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")]
no_skip_whitespace: bool,
#[clap(short, long, help = "Sort numbers lexicographically")]
lexicographic_sort: bool,
#[clap(short = 'L', long, help = "Do not sort numbers lexicographically")]
no_lexicographic_sort: bool,
#[clap(short, long, help = "Reverse sorting")]
reverse: bool,
// TODO: Allow ignoring case
#[clap(short, long, help = "Ignore case when sorting")]
ignore_case: bool,
}
@ -37,7 +38,7 @@ struct SortableSelection<'a> {
/// The content of the selection
selection: &'a SelectionWithDesc,
/// The string used to compare the content
content_comparison: &'a str,
content_comparison: Cow<'a, str>,
/// Any subselections
subselections: Vec<&'a str>,
}
@ -105,18 +106,17 @@ fn to_sortable_selection<'a, 'b>(
selection: &'a SelectionWithDesc,
options: &'b Options,
) -> SortableSelection<'a> {
if options.no_skip_whitespace {
SortableSelection {
selection,
content_comparison: selection.content.as_str(),
subselections: vec![],
}
} else {
SortableSelection {
selection,
content_comparison: selection.content.as_str().trim(),
subselections: vec![],
}
SortableSelection {
selection,
// TODO: Properly use Cow
content_comparison: crate::utils::get_key(
&selection.content,
!options.no_skip_whitespace,
options.regex.as_ref(),
options.ignore_case,
)
.into(),
subselections: vec![],
}
}
@ -147,25 +147,31 @@ pub fn sort(options: &Options) -> Result<String, KakError> {
.map(|s| to_sortable_selection(s, options))
.collect()
}
(Some(regex), None) => {
(Some(_regex), None) => {
// Sort based on the regular expression
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
})
.map(|s| to_sortable_selection(s, options))
.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, _) => {
// Sort based on subselections
@ -178,22 +184,23 @@ pub fn sort(options: &Options) -> Result<String, KakError> {
zipped.sort_by(|a, b| {
// First, try sorting by subselection. This won't iterate anything if either is None (regex and default mode)
for (a_subselection, b_subselection) in a.subselections.iter().zip(b.subselections.iter()) {
let comparison = if options.lexicographic_sort {
let comparison = if options.no_lexicographic_sort {
a_subselection.cmp(b_subselection)
} else {
compare_str(a_subselection, b_subselection)
compare_str(&a_subselection, &b_subselection)
};
match comparison {
Ordering::Less | Ordering::Greater => return comparison,
Ordering::Equal => {}
// If the comparison is not equal, stop here
if comparison != Ordering::Equal {
return comparison;
}
}
// Otherwise, default to comparing the content
if options.lexicographic_sort {
a.content_comparison.cmp(b.content_comparison)
if options.no_lexicographic_sort {
a.content_comparison.cmp(&b.content_comparison)
} else {
compare_str(a.content_comparison, b.content_comparison)
compare_str(&a.content_comparison, &b.content_comparison)
}
});

View File

@ -52,3 +52,45 @@ pub fn get_hash(
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)
}

View File

@ -20,24 +20,22 @@ pub struct Options {
}
pub fn xlookup(options: &Options) -> Result<String, KakError> {
let lookup_table = build_lookuptable(options.register)?;
eprintln!("Lookup table: {lookup_table:#?}");
let selections = get_selections(None)?;
let mut err_count: usize = 0;
set_selections(selections.iter().map(|key| {
match lookup_table.get(&get_hash(&key, false, None, false)) {
Some(v) => v.to_string(),
None => {
eprintln!(
"Nothing for '{key}' ({})",
get_hash(&key, false, None, false)
);
err_count += 1;
String::from("")
}
}
lookup_table
.get(&get_hash(key, false, None, false))
.map_or_else(
|| {
eprintln!("Key '{key}' not found",);
err_count += 1;
String::from("")
},
ToString::to_string,
)
}))?;
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> {
let mut selections = get_selections(Some(&format!("\"{reg}z")))?;
fn build_lookuptable(register: Register) -> Result<BTreeMap<u64, Selection>, KakError> {
let mut selections = get_selections(Some(&format!("\"{register}z")))?;
let mut iter = selections.array_chunks_mut();
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}'"))),
Vacant(v) => {
v.insert(value.to_owned());
v.insert(value.clone());
Ok(acc)
}
}