tmux-copyrat/src/lib.rs

209 lines
6.6 KiB
Rust
Raw Normal View History

2021-03-20 22:54:14 +01:00
pub mod comm;
2020-05-25 23:06:00 +02:00
pub mod error;
pub mod output_destination;
2021-03-19 23:29:36 +01:00
pub mod textbuf;
2020-06-01 15:25:54 +02:00
pub mod ui;
2020-05-25 23:06:00 +02:00
use clap::Clap;
use std::collections::HashMap;
use std::path;
use std::str::FromStr;
use crate::textbuf::alphabet;
use crate::textbuf::regexes;
2020-05-25 23:06:00 +02:00
/// Run copyrat on an input string `buffer`, configured by `Opt`.
///
/// # Note
///
2020-05-27 10:04:42 +02:00
/// Maybe the decision to take ownership of the buffer is a bit bold.
pub fn run(buffer: String, opt: &CliOpt) -> Option<ui::Selection> {
2021-03-19 23:29:36 +01:00
let mut model = textbuf::Model::new(
&buffer,
&opt.alphabet,
2020-06-04 09:45:58 +02:00
opt.use_all_patterns,
&opt.named_patterns,
&opt.custom_patterns,
opt.reverse,
);
2020-05-25 23:06:00 +02:00
2020-05-25 23:32:37 +02:00
let hint_style = match &opt.hint_style {
2020-05-25 23:06:00 +02:00
None => None,
Some(style) => match style {
2020-06-01 15:25:54 +02:00
HintStyleCli::Bold => Some(ui::HintStyle::Bold),
HintStyleCli::Italic => Some(ui::HintStyle::Italic),
HintStyleCli::Underline => Some(ui::HintStyle::Underline),
2020-05-25 23:06:00 +02:00
HintStyleCli::Surround => {
let (open, close) = opt.hint_surroundings;
2020-06-01 15:25:54 +02:00
Some(ui::HintStyle::Surround(open, close))
2020-05-25 23:06:00 +02:00
}
},
};
let default_output_destination = output_destination::OutputDestination::Tmux;
let selection: Option<ui::Selection> = {
2021-03-19 09:00:42 +01:00
let mut ui = ui::ViewController::new(
2020-06-01 09:33:40 +02:00
&mut model,
2020-05-26 08:11:45 +02:00
opt.unique_hint,
2020-06-01 10:06:50 +02:00
opt.focus_wrap_around,
default_output_destination,
2020-05-25 23:06:00 +02:00
&opt.colors,
2020-06-01 15:34:54 +02:00
&opt.hint_alignment,
2020-05-25 23:06:00 +02:00
hint_style,
);
2020-06-01 15:25:54 +02:00
ui.present()
2020-05-25 23:06:00 +02:00
};
2020-05-29 18:43:17 +02:00
selection
2020-05-25 23:06:00 +02:00
}
/// Main configuration, parsed from command line.
#[derive(Clap, Debug)]
#[clap(author, about, version)]
2020-05-27 10:04:42 +02:00
pub struct CliOpt {
2020-05-25 23:06:00 +02:00
/// Alphabet to draw hints from.
///
/// Possible values are "{A}", "{A}-homerow", "{A}-left-hand",
2020-05-30 20:27:32 +02:00
/// "{A}-right-hand", where "{A}" is one of "qwerty", "azerty", "qwertz"
/// "dvorak", "colemak".
///
/// # Examples
///
/// "qwerty", "dvorak-homerow", "azerty-right-hand".
2021-03-13 11:37:20 +01:00
#[clap(short = 'k', long, default_value = "dvorak",
parse(try_from_str = alphabet::parse_alphabet))]
alphabet: alphabet::Alphabet,
2020-05-25 23:06:00 +02:00
2020-06-04 09:45:58 +02:00
/// Use all available regex patterns.
2021-03-13 11:37:20 +01:00
#[clap(short = 'A', long = "--all-patterns")]
2020-06-04 09:45:58 +02:00
use_all_patterns: bool,
/// Pattern names to use ("email", ... see doc).
2021-03-13 11:37:20 +01:00
#[clap(short = 'x', long = "--pattern-name", parse(try_from_str = regexes::parse_pattern_name))]
2020-06-04 09:45:58 +02:00
named_patterns: Vec<regexes::NamedPattern>,
2020-06-04 09:45:58 +02:00
/// Additional regex patterns ("foo*bar", etc).
2021-03-13 11:37:20 +01:00
#[clap(short = 'X', long = "--custom-pattern")]
2020-06-04 09:45:58 +02:00
custom_patterns: Vec<String>,
2020-05-27 10:04:42 +02:00
/// Assign hints starting from the bottom of the screen.
2020-05-25 23:06:00 +02:00
#[clap(short, long)]
reverse: bool,
/// Keep the same hint for identical matches.
#[clap(short, long)]
2020-05-26 08:11:45 +02:00
unique_hint: bool,
2020-05-25 23:06:00 +02:00
2020-05-27 10:04:42 +02:00
#[clap(flatten)]
2021-03-19 23:07:08 +01:00
colors: ui::colors::UiColors,
2020-05-27 10:04:42 +02:00
2020-05-25 23:06:00 +02:00
/// Align hint with its match.
2020-06-04 09:45:58 +02:00
#[clap(long, arg_enum, default_value = "leading")]
2020-06-01 15:25:54 +02:00
hint_alignment: ui::HintAlignment,
2020-05-25 23:06:00 +02:00
2020-06-01 10:06:50 +02:00
/// Move focus back to first/last match.
#[clap(long)]
focus_wrap_around: bool,
2020-05-25 23:06:00 +02:00
/// Optional hint styling.
///
/// Underline or surround the hint for increased visibility.
/// If not provided, only the hint colors will be used.
2021-03-13 11:37:20 +01:00
#[clap(short = 's', long, arg_enum)]
2020-05-25 23:06:00 +02:00
hint_style: Option<HintStyleCli>,
/// Chars surrounding each hint, used with `Surround` style.
#[clap(long, default_value = "{}",
parse(try_from_str = parse_chars))]
hint_surroundings: (char, char),
2020-05-26 08:11:45 +02:00
/// Optional target path where to store the selected matches.
2021-03-13 11:37:20 +01:00
#[clap(short = 'o', long = "output", parse(from_os_str))]
2020-05-25 23:32:37 +02:00
pub target_path: Option<path::PathBuf>,
2020-05-25 23:06:00 +02:00
/// Describes if the uppercased marker should be added to the output,
/// indicating if hint key was uppercased. This is only used by
2020-05-25 23:32:37 +02:00
/// tmux-copyrat, so it is hidden (skipped) from the CLI.
2020-05-25 23:06:00 +02:00
#[clap(skip)]
2020-05-27 10:04:42 +02:00
pub uppercased_marker: bool,
2020-05-25 23:06:00 +02:00
}
/// Type introduced due to parsing limitation,
2020-06-01 15:25:54 +02:00
/// as we cannot directly parse into ui::HintStyle.
2020-05-25 23:06:00 +02:00
#[derive(Debug, Clap)]
enum HintStyleCli {
2020-05-29 16:23:46 +02:00
Bold,
2020-05-28 07:07:51 +02:00
Italic,
2020-05-25 23:06:00 +02:00
Underline,
Surround,
}
2020-05-27 10:04:42 +02:00
impl FromStr for HintStyleCli {
type Err = error::ParseError;
fn from_str(s: &str) -> Result<Self, error::ParseError> {
match s {
"leading" => Ok(HintStyleCli::Underline),
"trailing" => Ok(HintStyleCli::Surround),
_ => Err(error::ParseError::ExpectedString(String::from(
"underline or surround",
))),
}
}
}
2020-05-29 11:50:19 +02:00
/// Try to parse a `&str` into a tuple of `char`s.
2020-05-25 23:06:00 +02:00
fn parse_chars(src: &str) -> Result<(char, char), error::ParseError> {
if src.len() != 2 {
return Err(error::ParseError::ExpectedSurroundingPair);
}
let chars: Vec<char> = src.chars().collect();
Ok((chars[0], chars[1]))
}
2020-05-27 10:04:42 +02:00
impl CliOpt {
/// Try parsing provided options, and update self with the valid values.
pub fn merge_map(
&mut self,
options: &HashMap<String, String>,
) -> Result<(), error::ParseError> {
for (name, value) in options {
match name.as_ref() {
"@copyrat-alphabet" => {
self.alphabet = alphabet::parse_alphabet(value)?;
2020-05-27 10:04:42 +02:00
}
2020-06-04 09:45:58 +02:00
"@copyrat-pattern-name" => {
self.named_patterns = vec![regexes::parse_pattern_name(value)?]
}
"@copyrat-custom-pattern" => self.custom_patterns = vec![String::from(value)],
2020-05-27 10:04:42 +02:00
"@copyrat-reverse" => {
self.reverse = value.parse::<bool>()?;
}
"@copyrat-unique-hint" => {
self.unique_hint = value.parse::<bool>()?;
}
2021-03-19 23:07:08 +01:00
"@copyrat-match-fg" => self.colors.match_fg = ui::colors::parse_color(value)?,
"@copyrat-match-bg" => self.colors.match_bg = ui::colors::parse_color(value)?,
"@copyrat-focused-fg" => self.colors.focused_fg = ui::colors::parse_color(value)?,
"@copyrat-focused-bg" => self.colors.focused_bg = ui::colors::parse_color(value)?,
"@copyrat-hint-fg" => self.colors.hint_fg = ui::colors::parse_color(value)?,
"@copyrat-hint-bg" => self.colors.hint_bg = ui::colors::parse_color(value)?,
2020-05-27 10:04:42 +02:00
"@copyrat-hint-alignment" => {
2020-06-01 15:25:54 +02:00
self.hint_alignment = ui::HintAlignment::from_str(&value)?
2020-05-27 10:04:42 +02:00
}
"@copyrat-hint-style" => self.hint_style = Some(HintStyleCli::from_str(&value)?),
// Ignore unknown options.
_ => (),
}
}
Ok(())
}
}