mirror of
https://github.com/denisidoro/navi
synced 2024-11-24 20:43:06 +00:00
Minor code base improvements (#767)
This commit is contained in:
parent
8b78d54863
commit
9d862344e6
19 changed files with 242 additions and 233 deletions
|
@ -1,5 +1,5 @@
|
|||
use crate::prelude::*;
|
||||
use std::process::{self, Command, Stdio};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
lazy_static! {
|
||||
pub static ref VAR_TLDR_REGEX: Regex = Regex::new(r"\{\{(.*?)\}\}").expect("Invalid regex");
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use super::extractor;
|
||||
use crate::common::clipboard;
|
||||
use crate::common::fs;
|
||||
use crate::common::shell;
|
||||
use crate::common::shell::ShellSpawnError;
|
||||
use crate::config::Action;
|
||||
use crate::deser;
|
||||
use crate::env_var;
|
||||
use crate::finder::structures::{Opts as FinderOpts, SuggestionType};
|
||||
use crate::prelude::*;
|
||||
use crate::serializer;
|
||||
use crate::structures::cheat::{Suggestion, VariableMap};
|
||||
use crate::structures::item::Item;
|
||||
use shell::EOF;
|
||||
use std::process::Stdio;
|
||||
|
||||
|
@ -145,10 +145,7 @@ fn unique_result_count(results: &[&str]) -> usize {
|
|||
|
||||
fn replace_variables_from_snippet(snippet: &str, tags: &str, variables: VariableMap) -> Result<String> {
|
||||
let mut interpolated_snippet = String::from(snippet);
|
||||
let variables_found: Vec<&str> = serializer::VAR_REGEX
|
||||
.find_iter(snippet)
|
||||
.map(|m| m.as_str())
|
||||
.collect();
|
||||
let variables_found: Vec<&str> = deser::VAR_REGEX.find_iter(snippet).map(|m| m.as_str()).collect();
|
||||
let variable_count = unique_result_count(&variables_found);
|
||||
|
||||
for bracketed_variable_name in variables_found {
|
||||
|
@ -187,11 +184,20 @@ pub fn with_absolute_path(snippet: String) -> String {
|
|||
}
|
||||
|
||||
pub fn act(
|
||||
extractions: Result<extractor::Output>,
|
||||
extractions: Result<(&str, Item)>,
|
||||
files: Vec<String>,
|
||||
variables: Option<VariableMap>,
|
||||
) -> Result<()> {
|
||||
let (key, tags, comment, snippet, file_index) = extractions.unwrap();
|
||||
let (
|
||||
key,
|
||||
Item {
|
||||
tags,
|
||||
comment,
|
||||
snippet,
|
||||
file_index,
|
||||
..
|
||||
},
|
||||
) = extractions.unwrap();
|
||||
|
||||
if key == "ctrl-o" {
|
||||
edit::edit_file(Path::new(&files[file_index.expect("No files found")]))
|
||||
|
@ -205,13 +211,13 @@ pub fn act(
|
|||
|
||||
let interpolated_snippet = {
|
||||
let mut s = replace_variables_from_snippet(
|
||||
snippet,
|
||||
tags,
|
||||
&snippet,
|
||||
&tags,
|
||||
variables.expect("No variables received from finder"),
|
||||
)
|
||||
.context("Failed to replace variables from snippet")?;
|
||||
s = with_absolute_path(s);
|
||||
s = serializer::with_new_lines(s);
|
||||
s = deser::with_new_lines(s);
|
||||
s
|
||||
};
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
use crate::prelude::*;
|
||||
use crate::serializer;
|
||||
|
||||
pub type Output<'a> = (&'a str, &'a str, &'a str, &'a str, Option<usize>);
|
||||
|
||||
pub fn extract_from_selections(raw_snippet: &str, is_single: bool) -> Result<Output> {
|
||||
let mut lines = raw_snippet.split('\n');
|
||||
let key = if is_single {
|
||||
"enter"
|
||||
} else {
|
||||
lines
|
||||
.next()
|
||||
.context("Key was promised but not present in `selections`")?
|
||||
};
|
||||
|
||||
let mut parts = lines
|
||||
.next()
|
||||
.context("No more parts in `selections`")?
|
||||
.split(serializer::DELIMITER)
|
||||
.skip(3);
|
||||
|
||||
let tags = parts.next().unwrap_or("");
|
||||
let comment = parts.next().unwrap_or("");
|
||||
let snippet = parts.next().unwrap_or("");
|
||||
let file_index = parts.next().unwrap_or("").parse().ok();
|
||||
Ok((key, tags, comment, snippet, file_index))
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
mod actor;
|
||||
mod extractor;
|
||||
|
||||
use crate::clients::cheatsh;
|
||||
use crate::clients::{cheatsh, tldr};
|
||||
use crate::config::Source;
|
||||
use crate::deser;
|
||||
use crate::filesystem;
|
||||
use crate::finder::structures::Opts as FinderOpts;
|
||||
use crate::parser::Parser;
|
||||
|
@ -32,7 +32,7 @@ pub fn init(fetcher: Box<dyn Fetcher>) -> Result<()> {
|
|||
})
|
||||
.context("Failed getting selection and variables from finder")?;
|
||||
|
||||
let extractions = extractor::extract_from_selections(&raw_selection, config.best_match());
|
||||
let extractions = deser::terminal::read(&raw_selection, config.best_match());
|
||||
|
||||
if extractions.is_err() {
|
||||
return init(fetcher);
|
||||
|
@ -51,7 +51,7 @@ pub fn get_fetcher() -> Result<Box<dyn Fetcher>> {
|
|||
Ok(fetcher)
|
||||
}
|
||||
Source::Tldr(query) => {
|
||||
let lines = cheatsh::call(&query)?;
|
||||
let lines = tldr::call(&query)?;
|
||||
let fetcher = Box::new(StaticFetcher::new(lines));
|
||||
Ok(fetcher)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::prelude::*;
|
|||
use clap::Args;
|
||||
use clap::Parser;
|
||||
|
||||
const FUNC_POSSIBLE_VALUES: &[&str] = &[
|
||||
const POSSIBLE_VALUES: &[&str] = &[
|
||||
"url::open",
|
||||
"welcome",
|
||||
"widget::last_command",
|
||||
|
@ -43,7 +43,7 @@ pub enum Func {
|
|||
#[derive(Debug, Clone, Args)]
|
||||
pub struct Input {
|
||||
/// Function name (example: "url::open")
|
||||
#[clap(possible_values = FUNC_POSSIBLE_VALUES, ignore_case = true)]
|
||||
#[clap(possible_values = POSSIBLE_VALUES, ignore_case = true)]
|
||||
pub func: Func,
|
||||
/// List of arguments (example: "https://google.com")
|
||||
pub args: Vec<String>,
|
||||
|
@ -63,3 +63,10 @@ impl Runnable for Input {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_possible_values() {
|
||||
for v in POSSIBLE_VALUES {
|
||||
assert!(Func::from_str(v).is_ok())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use clap::Args;
|
|||
use crate::filesystem;
|
||||
use crate::prelude::*;
|
||||
|
||||
const INFO_POSSIBLE_VALUES: &[&str] = &["cheats-example", "cheats-path", "config-path", "config-example"];
|
||||
const POSSIBLE_VALUES: &[&str] = &["cheats-example", "cheats-path", "config-path", "config-example"];
|
||||
|
||||
impl FromStr for Info {
|
||||
type Err = &'static str;
|
||||
|
@ -21,7 +21,7 @@ impl FromStr for Info {
|
|||
|
||||
#[derive(Debug, Clone, Args)]
|
||||
pub struct Input {
|
||||
#[clap(possible_values = INFO_POSSIBLE_VALUES, ignore_case = true)]
|
||||
#[clap(possible_values = POSSIBLE_VALUES, ignore_case = true)]
|
||||
pub info: Info,
|
||||
}
|
||||
|
||||
|
@ -46,3 +46,10 @@ impl Runnable for Input {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_possible_values() {
|
||||
for v in POSSIBLE_VALUES {
|
||||
assert!(Info::from_str(v).is_ok())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::deser;
|
||||
use crate::prelude::*;
|
||||
use crate::serializer;
|
||||
use clap::Args;
|
||||
use crossterm::style::{style, Stylize};
|
||||
use std::process;
|
||||
|
@ -14,7 +14,7 @@ pub struct Input {
|
|||
}
|
||||
|
||||
fn extract_elements(argstr: &str) -> Result<(&str, &str, &str)> {
|
||||
let mut parts = argstr.split(serializer::DELIMITER).skip(3);
|
||||
let mut parts = argstr.split(deser::terminal::DELIMITER).skip(3);
|
||||
let tags = parts.next().context("No `tags` element provided.")?;
|
||||
let comment = parts.next().context("No `comment` element provided.")?;
|
||||
let snippet = parts.next().context("No `snippet` element provided.")?;
|
||||
|
@ -31,7 +31,7 @@ impl Runnable for Input {
|
|||
"{comment} {tags} \n{snippet}",
|
||||
comment = style(comment).with(CONFIG.comment_color()),
|
||||
tags = style(format!("[{}]", tags)).with(CONFIG.tag_color()),
|
||||
snippet = style(serializer::fix_newlines(snippet)).with(CONFIG.snippet_color()),
|
||||
snippet = style(deser::fix_newlines(snippet)).with(CONFIG.snippet_color()),
|
||||
);
|
||||
|
||||
process::exit(0)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::deser;
|
||||
use crate::env_var;
|
||||
use crate::finder;
|
||||
use crate::prelude::*;
|
||||
use crate::serializer;
|
||||
use clap::Args;
|
||||
use crossterm::style::style;
|
||||
use crossterm::style::Stylize;
|
||||
|
@ -49,10 +49,7 @@ impl Runnable for Input {
|
|||
|
||||
let bracketed_variables: Vec<&str> = {
|
||||
if snippet.contains(&bracketed_current_variable) {
|
||||
serializer::VAR_REGEX
|
||||
.find_iter(&snippet)
|
||||
.map(|m| m.as_str())
|
||||
.collect()
|
||||
deser::VAR_REGEX.find_iter(&snippet).map(|m| m.as_str()).collect()
|
||||
} else {
|
||||
iter::once(&bracketed_current_variable)
|
||||
.map(|s| s.as_str())
|
||||
|
@ -98,7 +95,7 @@ impl Runnable for Input {
|
|||
);
|
||||
}
|
||||
|
||||
println!("{snippet}", snippet = serializer::fix_newlines(&colored_snippet));
|
||||
println!("{snippet}", snippet = deser::fix_newlines(&colored_snippet));
|
||||
println!("{variables}", variables = variables);
|
||||
|
||||
process::exit(0)
|
||||
|
|
|
@ -3,7 +3,7 @@ use clap::Args;
|
|||
use crate::common::shell::Shell;
|
||||
use crate::prelude::*;
|
||||
|
||||
const WIDGET_POSSIBLE_VALUES: &[&str] = &["bash", "zsh", "fish", "elvish"];
|
||||
const POSSIBLE_VALUES: &[&str] = &["bash", "zsh", "fish", "elvish"];
|
||||
|
||||
impl FromStr for Shell {
|
||||
type Err = &'static str;
|
||||
|
@ -21,7 +21,7 @@ impl FromStr for Shell {
|
|||
|
||||
#[derive(Debug, Clone, Args)]
|
||||
pub struct Input {
|
||||
#[clap(possible_values = WIDGET_POSSIBLE_VALUES, ignore_case = true, default_value = "bash")]
|
||||
#[clap(possible_values = POSSIBLE_VALUES, ignore_case = true, default_value = "bash")]
|
||||
pub shell: Shell,
|
||||
}
|
||||
|
||||
|
@ -41,3 +41,10 @@ impl Runnable for Input {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_possible_values() {
|
||||
for v in POSSIBLE_VALUES {
|
||||
assert!(Shell::from_str(v).is_ok())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@ use crate::commands::core::get_fetcher;
|
|||
use crate::common::shell::{self, ShellSpawnError};
|
||||
use crate::finder::structures::Opts as FinderOpts;
|
||||
use crate::parser::Parser;
|
||||
use crate::{prelude::*, serializer};
|
||||
use crate::{deser, prelude::*};
|
||||
use std::io::{self, Write};
|
||||
|
||||
pub fn main() -> Result<()> {
|
||||
let config = &CONFIG;
|
||||
let _config = &CONFIG;
|
||||
let _opts = FinderOpts::snippet_default();
|
||||
|
||||
let fetcher = get_fetcher()?;
|
||||
|
@ -22,7 +22,7 @@ pub fn main() -> Result<()> {
|
|||
|
||||
let variables = parser.variables;
|
||||
let item_str = String::from_utf8(buf)?;
|
||||
let item = serializer::raycast_deser(&item_str)?;
|
||||
let item = deser::raycast::read(&item_str)?;
|
||||
dbg!(&item);
|
||||
|
||||
let x = variables.get_suggestion(&item.tags, "local_branch").expect("foo");
|
||||
|
@ -49,7 +49,7 @@ pub fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
pub fn _main0() -> Result<()> {
|
||||
let config = &CONFIG;
|
||||
let _config = &CONFIG;
|
||||
|
||||
let fetcher = get_fetcher()?;
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use crate::commands;
|
||||
use crate::finder::FinderChoice;
|
||||
use crate::finder::{self, FinderChoice};
|
||||
use crate::prelude::*;
|
||||
use clap::{crate_version, AppSettings, Parser, Subcommand};
|
||||
|
||||
const FINDER_POSSIBLE_VALUES: &[&str] = &["fzf", "skim"];
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(after_help = "\x1b[0;33mMORE INFO:\x1b[0;0m
|
||||
Please refer to \x1b[0;32mhttps://github.com/denisidoro/navi\x1b[0;0m
|
||||
|
@ -80,7 +78,7 @@ pub(super) struct ClapConfig {
|
|||
pub fzf_overrides_var: Option<String>,
|
||||
|
||||
/// Finder application to use
|
||||
#[clap(long, possible_values = FINDER_POSSIBLE_VALUES, ignore_case = true)]
|
||||
#[clap(long, possible_values = finder::POSSIBLE_VALUES, ignore_case = true)]
|
||||
pub finder: Option<FinderChoice>,
|
||||
|
||||
#[clap(subcommand)]
|
||||
|
@ -127,38 +125,3 @@ pub enum Action {
|
|||
Print,
|
||||
Execute,
|
||||
}
|
||||
|
||||
/*
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_widget_possible_values() {
|
||||
for v in WIDGET_POSSIBLE_VALUES {
|
||||
assert!(Shell::from_str(v).is_ok())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_info_possible_values() {
|
||||
for v in INFO_POSSIBLE_VALUES {
|
||||
assert!(Info::from_str(v).is_ok())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_func_possible_values() {
|
||||
for v in FUNC_POSSIBLE_VALUES {
|
||||
assert!(Func::from_str(v).is_ok())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_finder_possible_values() {
|
||||
for v in FINDER_POSSIBLE_VALUES {
|
||||
assert!(FinderChoice::from_str(v).is_ok())
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
34
src/deser/mod.rs
Normal file
34
src/deser/mod.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
pub mod raycast;
|
||||
pub mod terminal;
|
||||
|
||||
const NEWLINE_ESCAPE_CHAR: char = '\x15';
|
||||
pub const LINE_SEPARATOR: &str = " \x15 ";
|
||||
|
||||
lazy_static! {
|
||||
pub static ref NEWLINE_REGEX: Regex = Regex::new(r"\\\s+").expect("Invalid regex");
|
||||
pub static ref VAR_REGEX: Regex = Regex::new(r"\\?<(\w[\w\d\-_]*)>").expect("Invalid regex");
|
||||
}
|
||||
|
||||
pub fn with_new_lines(txt: String) -> String {
|
||||
txt.replace(LINE_SEPARATOR, "\n")
|
||||
}
|
||||
|
||||
pub fn fix_newlines(txt: &str) -> String {
|
||||
if txt.contains(NEWLINE_ESCAPE_CHAR) {
|
||||
(*NEWLINE_REGEX)
|
||||
.replace_all(txt.replace(LINE_SEPARATOR, " ").as_str(), "")
|
||||
.to_string()
|
||||
} else {
|
||||
txt.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn limit_str(text: &str, length: usize) -> String {
|
||||
if text.len() > length {
|
||||
format!("{}…", text.chars().take(length - 1).collect::<String>())
|
||||
} else {
|
||||
format!("{:width$}", text, width = length)
|
||||
}
|
||||
}
|
51
src/deser/raycast.rs
Normal file
51
src/deser/raycast.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
use super::*;
|
||||
use crate::structures::item::Item;
|
||||
|
||||
const FIELD_SEP_ESCAPE_CHAR: char = '\x16';
|
||||
|
||||
pub fn write(item: &Item) -> String {
|
||||
format!(
|
||||
"{hash}{delimiter}{tags}{delimiter}{comment}{delimiter}{icon}{delimiter}{snippet}\n",
|
||||
hash = item.hash(),
|
||||
tags = item.tags,
|
||||
comment = item.comment,
|
||||
delimiter = FIELD_SEP_ESCAPE_CHAR,
|
||||
icon = item.icon.clone().unwrap_or_default(),
|
||||
snippet = &item.snippet.trim_end_matches(LINE_SEPARATOR),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn read(line: &str) -> Result<Item> {
|
||||
let mut parts = line.split(FIELD_SEP_ESCAPE_CHAR);
|
||||
let hash: u64 = parts
|
||||
.next()
|
||||
.context("no hash")?
|
||||
.parse()
|
||||
.context("hash not a u64")?;
|
||||
let tags = parts.next().context("no tags")?.into();
|
||||
let comment = parts.next().context("no comment")?.into();
|
||||
let icon_str = parts.next().context("no icon")?;
|
||||
let snippet = parts.next().context("no snippet")?.into();
|
||||
|
||||
let icon = if icon_str.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(icon_str.into())
|
||||
};
|
||||
|
||||
let item = Item {
|
||||
tags,
|
||||
comment,
|
||||
icon,
|
||||
snippet,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if item.hash() != hash {
|
||||
dbg!(&item.hash());
|
||||
dbg!(hash);
|
||||
Err(anyhow!("Incorrect hash"))
|
||||
} else {
|
||||
Ok(item)
|
||||
}
|
||||
}
|
79
src/deser/terminal.rs
Normal file
79
src/deser/terminal.rs
Normal file
|
@ -0,0 +1,79 @@
|
|||
use super::*;
|
||||
use crate::common::terminal;
|
||||
use crate::structures::item::Item;
|
||||
use crossterm::style::{style, Stylize};
|
||||
use std::cmp::max;
|
||||
|
||||
pub fn get_widths() -> (usize, usize, usize) {
|
||||
let width = terminal::width();
|
||||
let tag_width_percentage = max(
|
||||
CONFIG.tag_min_width(),
|
||||
width * CONFIG.tag_width_percentage() / 100,
|
||||
);
|
||||
let comment_width_percentage = max(
|
||||
CONFIG.comment_min_width(),
|
||||
width * CONFIG.comment_width_percentage() / 100,
|
||||
);
|
||||
let snippet_width_percentage = max(
|
||||
CONFIG.snippet_min_width(),
|
||||
width * CONFIG.snippet_width_percentage() / 100,
|
||||
);
|
||||
(
|
||||
usize::from(tag_width_percentage),
|
||||
usize::from(comment_width_percentage),
|
||||
usize::from(snippet_width_percentage),
|
||||
)
|
||||
}
|
||||
|
||||
pub const DELIMITER: &str = r" ⠀";
|
||||
|
||||
lazy_static! {
|
||||
pub static ref COLUMN_WIDTHS: (usize, usize, usize) = get_widths();
|
||||
}
|
||||
|
||||
pub fn write(item: &Item) -> String {
|
||||
let (tag_width_percentage, comment_width_percentage, snippet_width_percentage) = *COLUMN_WIDTHS;
|
||||
format!(
|
||||
"{tags_short}{delimiter}{comment_short}{delimiter}{snippet_short}{delimiter}{tags}{delimiter}{comment}{delimiter}{snippet}{delimiter}{file_index}{delimiter}\n",
|
||||
tags_short = style(limit_str(&item.tags, tag_width_percentage)).with(CONFIG.tag_color()),
|
||||
comment_short = style(limit_str(&item.comment, comment_width_percentage)).with(CONFIG.comment_color()),
|
||||
snippet_short = style(limit_str(&fix_newlines(&item.snippet), snippet_width_percentage)).with(CONFIG.snippet_color()),
|
||||
tags = item.tags,
|
||||
comment = item.comment,
|
||||
delimiter = DELIMITER,
|
||||
snippet = &item.snippet.trim_end_matches(LINE_SEPARATOR),
|
||||
file_index = item.file_index.unwrap_or(0),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn read(raw_snippet: &str, is_single: bool) -> Result<(&str, Item)> {
|
||||
let mut lines = raw_snippet.split('\n');
|
||||
let key = if is_single {
|
||||
"enter"
|
||||
} else {
|
||||
lines
|
||||
.next()
|
||||
.context("Key was promised but not present in `selections`")?
|
||||
};
|
||||
|
||||
let mut parts = lines
|
||||
.next()
|
||||
.context("No more parts in `selections`")?
|
||||
.split(DELIMITER)
|
||||
.skip(3);
|
||||
|
||||
let tags = parts.next().unwrap_or("").into();
|
||||
let comment = parts.next().unwrap_or("").into();
|
||||
let snippet = parts.next().unwrap_or("").into();
|
||||
let file_index = parts.next().unwrap_or("").parse().ok();
|
||||
|
||||
let item = Item {
|
||||
tags,
|
||||
comment,
|
||||
snippet,
|
||||
file_index,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok((key, item))
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
use crate::deser;
|
||||
use crate::prelude::*;
|
||||
use crate::serializer;
|
||||
|
||||
use std::io::Write;
|
||||
use std::process::{self, Output};
|
||||
use std::process::{Command, Stdio};
|
||||
|
@ -17,6 +16,8 @@ pub enum FinderChoice {
|
|||
Skim,
|
||||
}
|
||||
|
||||
pub const POSSIBLE_VALUES: &[&str] = &["fzf", "skim"];
|
||||
|
||||
impl FromStr for FinderChoice {
|
||||
type Err = &'static str;
|
||||
|
||||
|
@ -78,7 +79,7 @@ impl FinderChoice {
|
|||
"--with-nth",
|
||||
"1,2,3",
|
||||
"--delimiter",
|
||||
serializer::DELIMITER.to_string().as_str(),
|
||||
deser::terminal::DELIMITER.to_string().as_str(),
|
||||
"--ansi",
|
||||
"--bind",
|
||||
format!("ctrl-j:down,ctrl-k:up{}", bindings).as_str(),
|
||||
|
@ -189,3 +190,10 @@ impl FinderChoice {
|
|||
Ok((output, return_value))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_possible_values() {
|
||||
for v in POSSIBLE_VALUES {
|
||||
assert!(FinderChoice::from_str(v).is_ok())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,14 +7,13 @@ mod clients;
|
|||
mod commands;
|
||||
mod common;
|
||||
mod config;
|
||||
mod deser;
|
||||
mod env_var;
|
||||
mod filesystem;
|
||||
mod finder;
|
||||
mod parser;
|
||||
mod prelude;
|
||||
mod serializer;
|
||||
mod structures;
|
||||
mod terminal;
|
||||
mod welcome;
|
||||
|
||||
pub use commands::handle;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::common::fs;
|
||||
use crate::deser;
|
||||
use crate::finder::structures::{Opts as FinderOpts, SuggestionType};
|
||||
use crate::prelude::*;
|
||||
use crate::serializer;
|
||||
use crate::structures::cheat::VariableMap;
|
||||
use crate::structures::item::Item;
|
||||
use std::io::Write;
|
||||
|
@ -162,9 +162,9 @@ fn gen_lists(tag_rules: &str) -> FilterOpts {
|
|||
impl<'a> Parser<'a> {
|
||||
pub fn new(writer: &'a mut dyn Write, is_terminal: bool) -> Self {
|
||||
let write_fn = if is_terminal {
|
||||
serializer::write
|
||||
deser::terminal::write
|
||||
} else {
|
||||
serializer::write_raw
|
||||
deser::raycast::write
|
||||
};
|
||||
|
||||
let filter = match CONFIG.tag_rules() {
|
||||
|
@ -256,7 +256,7 @@ impl<'a> Parser<'a> {
|
|||
// blank
|
||||
if line.is_empty() {
|
||||
if !(&item.snippet).is_empty() {
|
||||
item.snippet.push_str(serializer::LINE_SEPARATOR);
|
||||
item.snippet.push_str(deser::LINE_SEPARATOR);
|
||||
}
|
||||
}
|
||||
// tag
|
||||
|
@ -309,7 +309,7 @@ impl<'a> Parser<'a> {
|
|||
// snippet
|
||||
else {
|
||||
if !(&item.snippet).is_empty() {
|
||||
item.snippet.push_str(serializer::LINE_SEPARATOR);
|
||||
item.snippet.push_str(deser::LINE_SEPARATOR);
|
||||
}
|
||||
item.snippet.push_str(&line);
|
||||
}
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
use crate::common::terminal;
|
||||
use crate::prelude::*;
|
||||
use crate::structures::item::Item;
|
||||
use crossterm::style::{style, Stylize};
|
||||
use std::cmp::max;
|
||||
|
||||
pub fn get_widths() -> (usize, usize, usize) {
|
||||
let width = terminal::width();
|
||||
let tag_width_percentage = max(
|
||||
CONFIG.tag_min_width(),
|
||||
width * CONFIG.tag_width_percentage() / 100,
|
||||
);
|
||||
let comment_width_percentage = max(
|
||||
CONFIG.comment_min_width(),
|
||||
width * CONFIG.comment_width_percentage() / 100,
|
||||
);
|
||||
let snippet_width_percentage = max(
|
||||
CONFIG.snippet_min_width(),
|
||||
width * CONFIG.snippet_width_percentage() / 100,
|
||||
);
|
||||
(
|
||||
usize::from(tag_width_percentage),
|
||||
usize::from(comment_width_percentage),
|
||||
usize::from(snippet_width_percentage),
|
||||
)
|
||||
}
|
||||
|
||||
const NEWLINE_ESCAPE_CHAR: char = '\x15';
|
||||
const FIELD_SEP_ESCAPE_CHAR: char = '\x16';
|
||||
pub const LINE_SEPARATOR: &str = " \x15 ";
|
||||
pub const DELIMITER: &str = r" ⠀";
|
||||
|
||||
lazy_static! {
|
||||
pub static ref NEWLINE_REGEX: Regex = Regex::new(r"\\\s+").expect("Invalid regex");
|
||||
pub static ref VAR_REGEX: Regex = Regex::new(r"\\?<(\w[\w\d\-_]*)>").expect("Invalid regex");
|
||||
pub static ref COLUMN_WIDTHS: (usize, usize, usize) = get_widths();
|
||||
}
|
||||
|
||||
pub fn with_new_lines(txt: String) -> String {
|
||||
txt.replace(LINE_SEPARATOR, "\n")
|
||||
}
|
||||
|
||||
pub fn fix_newlines(txt: &str) -> String {
|
||||
if txt.contains(NEWLINE_ESCAPE_CHAR) {
|
||||
(*NEWLINE_REGEX)
|
||||
.replace_all(txt.replace(LINE_SEPARATOR, " ").as_str(), "")
|
||||
.to_string()
|
||||
} else {
|
||||
txt.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn limit_str(text: &str, length: usize) -> String {
|
||||
if text.len() > length {
|
||||
format!("{}…", text.chars().take(length - 1).collect::<String>())
|
||||
} else {
|
||||
format!("{:width$}", text, width = length)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(item: &Item) -> String {
|
||||
let (tag_width_percentage, comment_width_percentage, snippet_width_percentage) = *COLUMN_WIDTHS;
|
||||
format!(
|
||||
"{tags_short}{delimiter}{comment_short}{delimiter}{snippet_short}{delimiter}{tags}{delimiter}{comment}{delimiter}{snippet}{delimiter}{file_index}{delimiter}\n",
|
||||
tags_short = style(limit_str(&item.tags, tag_width_percentage)).with(CONFIG.tag_color()),
|
||||
comment_short = style(limit_str(&item.comment, comment_width_percentage)).with(CONFIG.comment_color()),
|
||||
snippet_short = style(limit_str(&fix_newlines(&item.snippet), snippet_width_percentage)).with(CONFIG.snippet_color()),
|
||||
tags = item.tags,
|
||||
comment = item.comment,
|
||||
delimiter = DELIMITER,
|
||||
snippet = &item.snippet.trim_end_matches(LINE_SEPARATOR),
|
||||
file_index = item.file_index.unwrap_or(0),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn write_raw(item: &Item) -> String {
|
||||
format!(
|
||||
"{hash}{delimiter}{tags}{delimiter}{comment}{delimiter}{icon}{delimiter}{snippet}\n",
|
||||
hash = item.hash(),
|
||||
tags = item.tags,
|
||||
comment = item.comment,
|
||||
delimiter = FIELD_SEP_ESCAPE_CHAR,
|
||||
icon = item.icon.clone().unwrap_or_default(),
|
||||
snippet = &item.snippet.trim_end_matches(LINE_SEPARATOR),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn raycast_deser(line: &str) -> Result<Item> {
|
||||
let mut parts = line.split(FIELD_SEP_ESCAPE_CHAR);
|
||||
let hash: u64 = parts
|
||||
.next()
|
||||
.context("no hash")?
|
||||
.parse()
|
||||
.context("hash not a u64")?;
|
||||
let tags = parts.next().context("no tags")?.into();
|
||||
let comment = parts.next().context("no comment")?.into();
|
||||
let icon_str = parts.next().context("no icon")?;
|
||||
let snippet = parts.next().context("no snippet")?.into();
|
||||
|
||||
let icon = if icon_str.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(icon_str.into())
|
||||
};
|
||||
|
||||
let item = Item {
|
||||
tags,
|
||||
comment,
|
||||
icon,
|
||||
snippet,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if item.hash() != hash {
|
||||
dbg!(&item.hash());
|
||||
dbg!(hash);
|
||||
Err(anyhow!("Incorrect hash"))
|
||||
} else {
|
||||
Ok(item)
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
|
Loading…
Reference in a new issue