Refactor kak code to kakplugin
This commit is contained in:
parent
3d867c7f0d
commit
f1e326fbd7
@ -12,7 +12,43 @@ pub enum KakError {
|
|||||||
KakResponse(String),
|
KakResponse(String),
|
||||||
/// IO Error
|
/// IO Error
|
||||||
Io(std::io::Error),
|
Io(std::io::Error),
|
||||||
|
/// Not yet implemented
|
||||||
|
NotImplemented(&'static str),
|
||||||
|
/// Custom error string
|
||||||
|
Custom(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl KakError {
|
||||||
|
pub fn details(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Self::EnvVarNotSet(e) => e.clone(),
|
||||||
|
Self::EnvVarUnicode(e) => e.clone(),
|
||||||
|
Self::Parse(e) => e.clone(),
|
||||||
|
Self::KakResponse(e) => e.clone(),
|
||||||
|
Self::Io(e) => format!("{e:?}"),
|
||||||
|
Self::NotImplemented(e) => e.to_string(),
|
||||||
|
Self::Custom(s) => s.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for KakError {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"Error: {}",
|
||||||
|
match self {
|
||||||
|
Self::EnvVarNotSet(_) => "env var not set",
|
||||||
|
Self::EnvVarUnicode(_) => "env var not unicode",
|
||||||
|
Self::Parse(_) => "Could not parse",
|
||||||
|
Self::KakResponse(_) => "Invalid kak response",
|
||||||
|
Self::Io(_) => "IO error",
|
||||||
|
Self::NotImplemented(_) => "Not Implemented",
|
||||||
|
Self::Custom(s) => &s,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<std::io::Error> for KakError {
|
impl From<std::io::Error> for KakError {
|
||||||
fn from(e: std::io::Error) -> Self {
|
fn from(e: std::io::Error) -> Self {
|
||||||
Self::Io(e)
|
Self::Io(e)
|
||||||
|
@ -127,7 +127,10 @@ 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 send_message<S: AsRef<str>>(message: S, debug_message: Option<S>) -> Result<(), KakError> {
|
pub fn display_message<S: AsRef<str>>(
|
||||||
|
message: S,
|
||||||
|
debug_message: Option<S>,
|
||||||
|
) -> Result<(), KakError> {
|
||||||
let msg_str = message.as_ref().replace('\'', "''");
|
let msg_str = message.as_ref().replace('\'', "''");
|
||||||
{
|
{
|
||||||
let mut f = open_command_fifo()?;
|
let mut f = open_command_fifo()?;
|
||||||
@ -186,7 +189,7 @@ pub fn open_command_fifo() -> Result<BufWriter<File>, KakError> {
|
|||||||
/// # 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
|
||||||
fn get_var(var_name: &str) -> Result<String, KakError> {
|
pub fn get_var(var_name: &str) -> Result<String, KakError> {
|
||||||
env::var(var_name).map_err(|e| match e {
|
env::var(var_name).map_err(|e| match e {
|
||||||
env::VarError::NotPresent => {
|
env::VarError::NotPresent => {
|
||||||
KakError::EnvVarNotSet(format!("Env var {} is not defined", var_name))
|
KakError::EnvVarNotSet(format!("Env var {} is not defined", var_name))
|
||||||
|
333
src/kak.rs
333
src/kak.rs
@ -1,333 +0,0 @@
|
|||||||
use crate::{get_var, KakMessage};
|
|
||||||
// use shellwords::ShellWordsIterator;
|
|
||||||
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, Eq, Debug)]
|
|
||||||
pub struct SelectionWithSubselections {
|
|
||||||
pub selection: SelectionWithDesc,
|
|
||||||
pub subselections: Vec<SelectionWithDesc>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn get_selections_with_subselections(
|
|
||||||
// register: &str,
|
|
||||||
// ) -> Result<Vec<SelectionWithSubselections>, KakMessage> {
|
|
||||||
// // TODO: Escape register
|
|
||||||
// let subselections = get_selections_with_desc()?;
|
|
||||||
// exec(format!("\"{}z", register.replace('\'', "''")))?;
|
|
||||||
// let selections = get_selections_with_desc()?;
|
|
||||||
|
|
||||||
// for sel in selections {
|
|
||||||
// for i in subselections {}
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// 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 + ?Sized>(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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn response_iter(msg: &str) -> Result<ShellWordsIterator, KakMessage> {
|
|
||||||
// exec(&format!(
|
|
||||||
// "echo -quoting shell -to-file {} -- {msg}",
|
|
||||||
// get_var("kak_response_fifo")?
|
|
||||||
// ))?;
|
|
||||||
|
|
||||||
// Ok(shellwords::split_iter(&fs::read_to_string(&get_var(
|
|
||||||
// "kak_response_fifo",
|
|
||||||
// )?)?))
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// # 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()));
|
|
||||||
}
|
|
||||||
}
|
|
51
src/main.rs
51
src/main.rs
@ -6,9 +6,6 @@
|
|||||||
#![allow(clippy::multiple_crate_versions)]
|
#![allow(clippy::multiple_crate_versions)]
|
||||||
#![allow(clippy::struct_excessive_bools)]
|
#![allow(clippy::struct_excessive_bools)]
|
||||||
|
|
||||||
// #![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
|
|
||||||
// #![allow(dead_code, unused_imports)]
|
|
||||||
|
|
||||||
mod errors;
|
mod errors;
|
||||||
mod kak;
|
mod kak;
|
||||||
mod math_eval;
|
mod math_eval;
|
||||||
@ -19,9 +16,8 @@ mod trim;
|
|||||||
mod uniq;
|
mod uniq;
|
||||||
mod xargs;
|
mod xargs;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use errors::KakMessage;
|
|
||||||
pub use kak::*;
|
pub use kak::*;
|
||||||
use std::env;
|
use kakplugin::{display_message, get_var, KakError};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(about, version, author)]
|
#[clap(about, version, author)]
|
||||||
@ -55,31 +51,22 @@ fn main() {
|
|||||||
panic!("Environment variable kak_command_fifo and kak_response_fifo must be set");
|
panic!("Environment variable kak_command_fifo and kak_response_fifo must be set");
|
||||||
}
|
}
|
||||||
|
|
||||||
let msg = match run() {
|
let (msg, msg_details) = match run() {
|
||||||
Ok(msg) => msg,
|
Ok(msg) => (msg, None),
|
||||||
Err(msg) => {
|
Err(e) => (e.to_string(), Some(e.details())),
|
||||||
// TODO: Do not do a string allocation here
|
|
||||||
eprintln!(
|
|
||||||
"{} (Debug info: {})",
|
|
||||||
msg.0,
|
|
||||||
msg.1.as_ref().map_or("", String::as_str)
|
|
||||||
);
|
|
||||||
msg
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = send_message(&msg) {
|
if let Err(display_error) = display_message(&msg, msg_details.as_ref()) {
|
||||||
println!("{}", e);
|
// If there was an error sending the display message to kakoune, print it out
|
||||||
|
eprintln!(
|
||||||
|
"Error sending message '{msg:?}' (details: '{msg_details:?}') to kak: {display_error:?}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run() -> Result<KakMessage, KakMessage> {
|
fn run() -> Result<String, KakError> {
|
||||||
let options = Cli::try_parse().map_err(|e| {
|
let options =
|
||||||
KakMessage(
|
Cli::try_parse().map_err(|e| KakError::Parse(format!("Argument parse error: {e}")))?;
|
||||||
"Error parsing arguments".to_string(),
|
|
||||||
Some(format!("Could not parse: {}", e)),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
match &options.command {
|
match &options.command {
|
||||||
Commands::Sort(o) => sort::sort(o),
|
Commands::Sort(o) => sort::sort(o),
|
||||||
@ -91,17 +78,3 @@ fn run() -> Result<KakMessage, KakMessage> {
|
|||||||
Commands::Stdin(o) => stdin::stdin(o),
|
Commands::Stdin(o) => stdin::stdin(o),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Will return `Err` if requested environment variable is not unicode or not present
|
|
||||||
pub fn get_var(var_name: &str) -> Result<String, KakMessage> {
|
|
||||||
env::var(var_name).map_err(|e| match e {
|
|
||||||
env::VarError::NotPresent => {
|
|
||||||
KakMessage(format!("Env var {} is not defined", var_name), None)
|
|
||||||
}
|
|
||||||
env::VarError::NotUnicode(_) => {
|
|
||||||
KakMessage(format!("Env var {} is not unicode", var_name), None)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::{get_selections, open_command_fifo, KakMessage};
|
|
||||||
use evalexpr::{eval, Value};
|
use evalexpr::{eval, Value};
|
||||||
|
use kakplugin::{get_selections, open_command_fifo, KakError};
|
||||||
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<String, KakError> {
|
||||||
let selections = get_selections()?;
|
let selections = get_selections()?;
|
||||||
|
|
||||||
let mut f = open_command_fifo()?;
|
let mut f = open_command_fifo()?;
|
||||||
@ -38,8 +38,5 @@ pub fn math_eval(_options: &Options) -> Result<KakMessage, KakMessage> {
|
|||||||
write!(f, " ; exec R;")?;
|
write!(f, " ; exec R;")?;
|
||||||
f.flush()?;
|
f.flush()?;
|
||||||
|
|
||||||
Ok(KakMessage(
|
Ok(format!("MathEval {} selections", selections.len()))
|
||||||
format!("MathEval {} selections", selections.len()),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::{get_selections, set_selections, KakMessage};
|
use kakplugin::{get_selections, set_selections, KakError};
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
#[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<String, KakError> {
|
||||||
let mut selections = get_selections()?;
|
let mut selections = get_selections()?;
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
@ -10,8 +10,5 @@ pub fn shuf(_options: &Options) -> Result<KakMessage, KakMessage> {
|
|||||||
|
|
||||||
set_selections(selections.iter())?;
|
set_selections(selections.iter())?;
|
||||||
|
|
||||||
Ok(KakMessage(
|
Ok(format!("Shuf {} selections", selections.len()))
|
||||||
format!("Shuf {} selections", selections.len()),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
24
src/sort.rs
24
src/sort.rs
@ -1,5 +1,5 @@
|
|||||||
use crate::{get_selections_with_desc, kak, open_command_fifo, KakMessage, SelectionWithDesc};
|
|
||||||
use alphanumeric_sort::compare_str;
|
use alphanumeric_sort::compare_str;
|
||||||
|
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::{cmp::Ordering, io::Write};
|
||||||
|
|
||||||
@ -117,15 +117,15 @@ fn to_sortable_selection<'a, 'b>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sort(options: &Options) -> Result<KakMessage, KakMessage> {
|
pub fn sort(options: &Options) -> Result<String, KakError> {
|
||||||
// subselections is Some if the user requests it in subselections_register
|
// subselections is Some if the user requests it in subselections_register
|
||||||
// It will "exec z" to restore the selections before setting selections
|
// It will "exec z" to restore the selections before setting selections
|
||||||
// If subselections is None, "exec z" is not called
|
// If subselections is None, "exec z" is not called
|
||||||
let subselections: Option<Vec<SelectionWithDesc>> = options
|
let subselections: Option<Vec<SelectionWithDesc>> = options
|
||||||
.subselections_register
|
.subselections_register
|
||||||
.map::<Result<_, KakMessage>, _>(|c| {
|
.map::<Result<_, KakError>, _>(|c| {
|
||||||
let subselections = get_selections_with_desc()?;
|
let subselections = get_selections_with_desc()?;
|
||||||
kak::exec(&format!("exec {}", c))?;
|
kakplugin::exec(&format!("exec {}", c))?;
|
||||||
Ok(subselections)
|
Ok(subselections)
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
@ -133,9 +133,9 @@ pub fn sort(options: &Options) -> Result<KakMessage, KakMessage> {
|
|||||||
|
|
||||||
let mut zipped: Vec<SortableSelection<'_>> = match (&options.regex, &subselections) {
|
let mut zipped: Vec<SortableSelection<'_>> = match (&options.regex, &subselections) {
|
||||||
(Some(_), Some(_)) => {
|
(Some(_), Some(_)) => {
|
||||||
return Err("Cannot pass regex and subselections register"
|
return Err(KakError::Custom(
|
||||||
.to_string()
|
"Cannot pass regex and subselections register".to_string(),
|
||||||
.into())
|
))
|
||||||
}
|
}
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
// Do a regular sort on the content
|
// Do a regular sort on the content
|
||||||
@ -166,9 +166,8 @@ pub fn sort(options: &Options) -> Result<KakMessage, KakMessage> {
|
|||||||
}
|
}
|
||||||
(None, _) => {
|
(None, _) => {
|
||||||
// Sort based on subselections
|
// Sort based on subselections
|
||||||
return Err(KakMessage(
|
return Err(KakError::NotImplemented(
|
||||||
"Not yet implemented".to_string(),
|
"Sort by subselection is not yet implemented",
|
||||||
Some("Sort by subselection is not yet implemented".to_string()),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -212,8 +211,5 @@ pub fn sort(options: &Options) -> Result<KakMessage, KakMessage> {
|
|||||||
write!(f, " ; exec R;")?;
|
write!(f, " ; exec R;")?;
|
||||||
f.flush()?;
|
f.flush()?;
|
||||||
|
|
||||||
Ok(KakMessage(
|
Ok(format!("Sorted {} selections", zipped.len()))
|
||||||
format!("Sorted {} selections", zipped.len()),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
10
src/stdin.rs
10
src/stdin.rs
@ -1,4 +1,4 @@
|
|||||||
use crate::{get_selections_with_desc, set_selections, KakMessage};
|
use kakplugin::{get_selections_with_desc, set_selections, KakError};
|
||||||
use std::{
|
use std::{
|
||||||
io::{BufRead, BufReader, Write},
|
io::{BufRead, BufReader, Write},
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
@ -8,7 +8,7 @@ pub struct Options {
|
|||||||
command: String,
|
command: String,
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
}
|
}
|
||||||
pub fn stdin(options: &Options) -> Result<KakMessage, KakMessage> {
|
pub fn stdin(options: &Options) -> Result<String, KakError> {
|
||||||
let mut child = Command::new(&options.command)
|
let mut child = Command::new(&options.command)
|
||||||
.args(&options.args)
|
.args(&options.args)
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
@ -17,7 +17,7 @@ pub fn stdin(options: &Options) -> Result<KakMessage, KakMessage> {
|
|||||||
.expect("Failed to spawn child process");
|
.expect("Failed to spawn child process");
|
||||||
|
|
||||||
let mut child_stdin = child.stdin.take().expect("Failed to open stdin");
|
let mut child_stdin = child.stdin.take().expect("Failed to open stdin");
|
||||||
let handle = std::thread::spawn(move || -> Result<(), KakMessage> {
|
let handle = std::thread::spawn(move || -> Result<(), KakError> {
|
||||||
for s in get_selections_with_desc()? {
|
for s in get_selections_with_desc()? {
|
||||||
eprintln!("Got selection {}", s.content);
|
eprintln!("Got selection {}", s.content);
|
||||||
write!(child_stdin, "{}\0", s.content)?;
|
write!(child_stdin, "{}\0", s.content)?;
|
||||||
@ -29,7 +29,7 @@ pub fn stdin(options: &Options) -> Result<KakMessage, KakMessage> {
|
|||||||
BufReader::new(child.stdout.take().expect("Failed to get stdout"))
|
BufReader::new(child.stdout.take().expect("Failed to get stdout"))
|
||||||
.split(b'\0')
|
.split(b'\0')
|
||||||
.map(|s| Ok(String::from_utf8_lossy(&s?).into_owned()))
|
.map(|s| Ok(String::from_utf8_lossy(&s?).into_owned()))
|
||||||
.collect::<Result<Vec<_>, KakMessage>>()?
|
.collect::<Result<Vec<_>, KakError>>()?
|
||||||
.iter(),
|
.iter(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ pub fn stdin(options: &Options) -> Result<KakMessage, KakMessage> {
|
|||||||
// TODO: Do not use a string
|
// TODO: Do not use a string
|
||||||
handle
|
handle
|
||||||
.join()
|
.join()
|
||||||
.map_err(|_e| String::from("Could not join background process"))??;
|
.map_err(|_e| KakError::Custom("Could not join background process".to_string()))??;
|
||||||
|
|
||||||
Ok("stdin selections".into())
|
Ok("stdin selections".into())
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{get_selections, open_command_fifo, KakMessage};
|
use kakplugin::{get_selections, open_command_fifo, KakError};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
#[derive(clap::StructOpt, Debug)]
|
#[derive(clap::StructOpt, Debug)]
|
||||||
@ -11,7 +11,7 @@ pub struct Options {
|
|||||||
no_preserve_newline: bool,
|
no_preserve_newline: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trim(options: &Options) -> Result<KakMessage, KakMessage> {
|
pub fn trim(options: &Options) -> Result<String, KakError> {
|
||||||
let selections = get_selections()?;
|
let selections = get_selections()?;
|
||||||
|
|
||||||
let mut f = open_command_fifo()?;
|
let mut f = open_command_fifo()?;
|
||||||
@ -45,11 +45,8 @@ pub fn trim(options: &Options) -> Result<KakMessage, KakMessage> {
|
|||||||
write!(f, " ; exec R;")?;
|
write!(f, " ; exec R;")?;
|
||||||
f.flush()?;
|
f.flush()?;
|
||||||
|
|
||||||
Ok(KakMessage(
|
Ok(format!(
|
||||||
format!(
|
|
||||||
"Trimmed {} selections ({} changed)",
|
"Trimmed {} selections ({} changed)",
|
||||||
num_selections, num_trimmed
|
num_selections, num_trimmed
|
||||||
),
|
|
||||||
None,
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ pub fn uniq(options: &Options) -> Result<String, KakError> {
|
|||||||
s.content.trim()
|
s.content.trim()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// If they requested a regex match, set the key to the string slice of that match
|
||||||
if let Some(regex_match) = (|| {
|
if let Some(regex_match) = (|| {
|
||||||
let captures = options.regex.as_ref()?.captures(key)?;
|
let captures = options.regex.as_ref()?.captures(key)?;
|
||||||
captures
|
captures
|
||||||
@ -67,12 +68,9 @@ pub fn uniq(options: &Options) -> Result<String, KakError> {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Preallocate so the content and string have the same type, but allocation is not repeated
|
|
||||||
// TODO: Do we really have to do this?
|
|
||||||
let empty_string = String::default();
|
|
||||||
set_selections(new_selections.iter().map(|i| match i {
|
set_selections(new_selections.iter().map(|i| match i {
|
||||||
Some(s) => &s.content,
|
Some(s) => &s.content,
|
||||||
None => &empty_string,
|
None => "",
|
||||||
}))?;
|
}))?;
|
||||||
|
|
||||||
// Deselect all `None` strings (all rows that have been seen before)
|
// Deselect all `None` strings (all rows that have been seen before)
|
||||||
|
17
src/xargs.rs
17
src/xargs.rs
@ -1,4 +1,4 @@
|
|||||||
use crate::{get_selections_with_desc, set_selections, KakMessage};
|
use kakplugin::{get_selections_with_desc, set_selections, KakError};
|
||||||
use std::{
|
use std::{
|
||||||
io::{BufRead, BufReader, Write},
|
io::{BufRead, BufReader, Write},
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
@ -7,7 +7,7 @@ use std::{
|
|||||||
pub struct Options {
|
pub struct Options {
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
}
|
}
|
||||||
pub fn xargs(options: &Options) -> Result<KakMessage, KakMessage> {
|
pub fn xargs(options: &Options) -> Result<String, KakError> {
|
||||||
let mut child = Command::new("xargs")
|
let mut child = Command::new("xargs")
|
||||||
.arg("-0")
|
.arg("-0")
|
||||||
.arg("--")
|
.arg("--")
|
||||||
@ -18,7 +18,7 @@ pub fn xargs(options: &Options) -> Result<KakMessage, KakMessage> {
|
|||||||
.expect("Failed to spawn child process");
|
.expect("Failed to spawn child process");
|
||||||
|
|
||||||
let mut stdin = child.stdin.take().expect("Failed to open stdin");
|
let mut stdin = child.stdin.take().expect("Failed to open stdin");
|
||||||
let handle = std::thread::spawn(move || -> Result<(), KakMessage> {
|
let handle = std::thread::spawn(move || -> Result<(), KakError> {
|
||||||
for s in get_selections_with_desc()? {
|
for s in get_selections_with_desc()? {
|
||||||
eprintln!("Got selection {}", s.content);
|
eprintln!("Got selection {}", s.content);
|
||||||
write!(stdin, "{}\0", s.content)?;
|
write!(stdin, "{}\0", s.content)?;
|
||||||
@ -29,10 +29,15 @@ pub fn xargs(options: &Options) -> Result<KakMessage, KakMessage> {
|
|||||||
eprintln!("About t oreadvv");
|
eprintln!("About t oreadvv");
|
||||||
|
|
||||||
set_selections(
|
set_selections(
|
||||||
BufReader::new(child.stdout.take().ok_or("Failed to get stdout")?)
|
BufReader::new(
|
||||||
|
child
|
||||||
|
.stdout
|
||||||
|
.take()
|
||||||
|
.ok_or(KakError::Custom("Failed to get stdout".to_string()))?,
|
||||||
|
)
|
||||||
.split(b'\0')
|
.split(b'\0')
|
||||||
.map(|s| Ok(String::from_utf8_lossy(&s?).into_owned()))
|
.map(|s| Ok(String::from_utf8_lossy(&s?).into_owned()))
|
||||||
.collect::<Result<Vec<_>, KakMessage>>()?
|
.collect::<Result<Vec<_>, KakError>>()?
|
||||||
.iter(),
|
.iter(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -40,7 +45,7 @@ pub fn xargs(options: &Options) -> Result<KakMessage, KakMessage> {
|
|||||||
// TODO: Do not use a string
|
// TODO: Do not use a string
|
||||||
handle
|
handle
|
||||||
.join()
|
.join()
|
||||||
.map_err(|_e| "Could not join background process")??;
|
.map_err(|_e| KakError::Custom("Could not join background process".to_string()))??;
|
||||||
|
|
||||||
Ok("xargs selections".into())
|
Ok("xargs selections".into())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user