diff options
author | Denis Isidoro <denisidoro@users.noreply.github.com> | 2022-07-28 20:11:42 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-28 20:11:42 -0300 |
commit | ebb02e28ec338d55c89c5b2d321d038f982538c5 (patch) | |
tree | da6f4e367a2e0f8c6866c21d0ea609927de817c0 /src | |
parent | f5759f26aac3299ff391be173bdc6c9286fe93d1 (diff) |
Refactor code base (#760)
Diffstat (limited to 'src')
62 files changed, 1466 insertions, 1118 deletions
diff --git a/src/cheatsh.rs b/src/clients/cheatsh.rs index d5ac039..f039b0c 100644 --- a/src/cheatsh.rs +++ b/src/clients/cheatsh.rs @@ -1,10 +1,8 @@ -use crate::parser; -use crate::structures::cheat::VariableMap; +use crate::parser::Parser; +use crate::prelude::*; + use crate::structures::fetcher; -use anyhow::Context; -use anyhow::Result; -use std::collections::HashSet; -use std::process::{self, Command, Stdio}; +use std::process::{self, Command}; fn map_line(line: &str) -> String { line.trim().trim_end_matches(':').to_string() @@ -22,35 +20,6 @@ fn lines(query: &str, markdown: &str) -> impl Iterator<Item = Result<String>> { .into_iter() } -fn read_all(query: &str, cheat: &str, stdin: &mut std::process::ChildStdin) -> Result<Option<VariableMap>> { - let mut variables = VariableMap::new(); - let mut visited_lines = HashSet::new(); - - if cheat.starts_with("Unknown topic.") { - eprintln!( - "`{}` not found in cheatsh. - -Output: -{} -", - query, cheat - ); - process::exit(35) - } - - parser::read_lines( - lines(query, cheat), - "cheat.sh", - 0, - &mut variables, - &mut visited_lines, - stdin, - None, - None, - )?; - Ok(Some(variables)) -} - pub fn fetch(query: &str) -> Result<String> { let args = ["-qO-", &format!("cheat.sh/{}", query)]; @@ -109,12 +78,23 @@ impl Fetcher { } impl fetcher::Fetcher for Fetcher { - fn fetch( - &self, - stdin: &mut std::process::ChildStdin, - _files: &mut Vec<String>, - ) -> Result<Option<VariableMap>> { - let cheat = fetch(&self.query)?; - read_all(&self.query, &cheat, stdin) + fn fetch(&self, parser: &mut Parser) -> Result<bool> { + let cheat = &fetch(&self.query)?; + + if cheat.starts_with("Unknown topic.") { + eprintln!( + "`{}` not found in cheatsh. + +Output: +{} +", + &self.query, cheat + ); + process::exit(35) + } + + parser.read_lines(lines(&self.query, cheat), "cheat.sh", None)?; + + Ok(true) } } diff --git a/src/clients/mod.rs b/src/clients/mod.rs new file mode 100644 index 0000000..1f0dbcd --- /dev/null +++ b/src/clients/mod.rs @@ -0,0 +1,2 @@ +pub mod cheatsh; +pub mod tldr; diff --git a/src/tldr.rs b/src/clients/tldr.rs index adbf5da..f164534 100644 --- a/src/tldr.rs +++ b/src/clients/tldr.rs @@ -1,10 +1,6 @@ -use crate::parser; -use crate::structures::cheat::VariableMap; +use crate::parser::Parser; +use crate::prelude::*; use crate::structures::fetcher; -use anyhow::{Context, Result}; -use regex::Regex; -use std::collections::HashSet; - use std::process::{self, Command, Stdio}; lazy_static! { @@ -58,26 +54,6 @@ fn markdown_lines(query: &str, markdown: &str) -> impl Iterator<Item = Result<St .into_iter() } -fn read_all( - query: &str, - markdown: &str, - stdin: &mut std::process::ChildStdin, -) -> Result<Option<VariableMap>> { - let mut variables = VariableMap::new(); - let mut visited_lines = HashSet::new(); - parser::read_lines( - markdown_lines(query, markdown), - "markdown", - 0, - &mut variables, - &mut visited_lines, - stdin, - None, - None, - )?; - Ok(Some(variables)) -} - pub fn fetch(query: &str) -> Result<String> { let args = [query, "--markdown"]; @@ -148,12 +124,9 @@ impl Fetcher { } impl fetcher::Fetcher for Fetcher { - fn fetch( - &self, - stdin: &mut std::process::ChildStdin, - _files: &mut Vec<String>, - ) -> Result<Option<VariableMap>> { + fn fetch(&self, parser: &mut Parser) -> Result<bool> { let markdown = fetch(&self.query)?; - read_all(&self.query, &markdown, stdin) + parser.read_lines(markdown_lines(&self.query, &markdown), "markdown", None)?; + Ok(true) } } diff --git a/src/actor.rs b/src/commands/core/actor.rs index 306acce..e423afe 100644 --- a/src/actor.rs +++ b/src/commands/core/actor.rs @@ -1,20 +1,15 @@ -use crate::clipboard; +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::config::CONFIG; use crate::env_var; -use crate::extractor; use crate::finder::structures::{Opts as FinderOpts, SuggestionType}; -use crate::finder::Finder; -use crate::fs; -use crate::shell; -use crate::shell::ShellSpawnError; +use crate::prelude::*; +use crate::serializer; use crate::structures::cheat::{Suggestion, VariableMap}; -use crate::writer; -use anyhow::Context; -use anyhow::Result; use shell::EOF; -use std::io::Write; -use std::path::Path; use std::process::Stdio; fn prompt_finder( @@ -128,13 +123,13 @@ fn prompt_finder( opts.suggestion_type = SuggestionType::Disabled; }; - let (output, _, _) = CONFIG + let (output, _) = CONFIG .finder() - .call(opts, |stdin, _| { + .call(opts, |stdin| { stdin .write_all(suggestions.as_bytes()) .context("Could not write to finder's stdin")?; - Ok(None) + Ok(()) }) .context("finder was unable to prompt with suggestions")?; @@ -150,7 +145,10 @@ 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> = writer::VAR_REGEX.find_iter(snippet).map(|m| m.as_str()).collect(); + let variables_found: Vec<&str> = serializer::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 { @@ -213,7 +211,7 @@ pub fn act( ) .context("Failed to replace variables from snippet")?; s = with_absolute_path(s); - s = writer::with_new_lines(s); + s = serializer::with_new_lines(s); s }; diff --git a/src/extractor.rs b/src/commands/core/extractor.rs index 7b6ed3b..56758e8 100644 --- a/src/extractor.rs +++ b/src/commands/core/extractor.rs @@ -1,7 +1,5 @@ -use crate::writer; - -use anyhow::Context; -use anyhow::Result; +use crate::prelude::*; +use crate::serializer; pub type Output<'a> = (&'a str, &'a str, &'a str, &'a str, Option<usize>); @@ -18,7 +16,7 @@ pub fn extract_from_selections(raw_snippet: &str, is_single: bool) -> Result<Out let mut parts = lines .next() .context("No more parts in `selections`")? - .split(writer::DELIMITER) + .split(serializer::DELIMITER) .skip(3); let tags = parts.next().unwrap_or(""); diff --git a/src/commands/core/mod.rs b/src/commands/core/mod.rs new file mode 100644 index 0000000..f212880 --- /dev/null +++ b/src/commands/core/mod.rs @@ -0,0 +1,41 @@ +mod actor; +mod extractor; + +use crate::finder::structures::Opts as FinderOpts; +use crate::parser::Parser; +use crate::prelude::*; +use crate::welcome; + +pub fn main() -> Result<()> { + let config = &CONFIG; + let opts = FinderOpts::snippet_default(); + + let (raw_selection, (variables, files)) = config + .finder() + .call(opts, |writer| { + let fetcher = config.fetcher(); + + let mut parser = Parser::new(writer, true); + + let found_something = fetcher + .fetch(&mut parser) + .context("Failed to parse variables intended for finder")?; + + if !found_something { + welcome::populate_cheatsheet(&mut parser)?; + } + + Ok((Some(parser.variables), fetcher.files())) + }) + .context("Failed getting selection and variables from finder")?; + + let extractions = extractor::extract_from_selections(&raw_selection, config.best_match()); + + if extractions.is_err() { + return main(); + } + + actor::act(extractions, files, variables)?; + + Ok(()) +} diff --git a/src/cheat_variable.rs b/src/commands/func/map.rs index 3f6399d..89e22c5 100644 --- a/src/cheat_variable.rs +++ b/src/commands/func/map.rs @@ -1,8 +1,7 @@ -use crate::shell::{self, ShellSpawnError}; +use crate::common::shell::{self, ShellSpawnError}; +use crate::prelude::*; -use anyhow::Result; - -pub fn map_expand() -> Result<()> { +pub fn expand() -> Result<()> { let cmd = r#"sed -e 's/^.*$/"&"/' | tr '\n' ' '"#; shell::out() .arg(cmd) diff --git a/src/commands/func/mod.rs b/src/commands/func/mod.rs new file mode 100644 index 0000000..383b636 --- /dev/null +++ b/src/commands/func/mod.rs @@ -0,0 +1,65 @@ +mod map; +mod widget; + +use super::core; +use super::temp; +use crate::common::url; +use crate::prelude::*; +use clap::Args; +use clap::Parser; + +const FUNC_POSSIBLE_VALUES: &[&str] = &[ + "url::open", + "welcome", + "widget::last_command", + "map::expand", + "temp", +]; + +impl FromStr for Func { + type Err = &'static str; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "url::open" => Ok(Func::UrlOpen), + "welcome" => Ok(Func::Welcome), + "widget::last_command" => Ok(Func::WidgetLastCommand), + "map::expand" => Ok(Func::MapExpand), + "temp" => Ok(Func::Temp), + _ => Err("no match"), + } + } +} + +#[derive(Debug, Clone, Parser)] +pub enum Func { + UrlOpen, + Welcome, + WidgetLastCommand, + MapExpand, + Temp, +} + +#[derive(Debug, Clone, Args)] +pub struct Input { + /// Function name (example: "url::open") + #[clap(possible_values = FUNC_POSSIBLE_VALUES, ignore_case = true)] + pub func: Func, + /// List of arguments (example: "https://google.com") + pub args: Vec<String>, +} + +impl Runnable for Input { + fn run(&self) -> Result<()> { + let func = &self.func; + let args = self.args.clone(); // TODO + + match func { + Func::UrlOpen => url::open(args), + Func::Welcome => core::main(), + Func::WidgetLastCommand => widget::last_command(), + Func::MapExpand => map::expand(), + Func::Temp => temp::main(), + } + } +} diff --git a/src/commands/func/widget.rs b/src/commands/func/widget.rs new file mode 100644 index 0000000..ff74d74 --- /dev/null +++ b/src/commands/func/widget.rs @@ -0,0 +1,40 @@ +use crate::prelude::*; +use std::io::{self, Read}; + +pub fn last_command() -> Result<()> { + let mut text = String::new(); + io::stdin().read_to_string(&mut text)?; + + let replacements = vec![("||", "ග"), ("|", "ඛ"), ("&&", "ඝ")]; + + let parts = shellwords::split(&text).unwrap_or_else(|_| text.split('|').map(|s| s.to_string()).collect()); + + for p in parts { + for (pattern, escaped) in replacements.clone() { + if p.contains(pattern) && p != pattern && p != format!("{}{}", pattern, pattern) { + let replacement = p.replace(pattern, escaped); + text = text.replace(&p, &replacement); + } + } + } + + let mut extracted = text.clone(); + + for (pattern, _) in replacements.clone() { + let mut new_parts = text.rsplit(pattern); + if let Some(extracted_attempt) = new_parts.next() { + if extracted_attempt.len() <= extracted.len() { + extracted = extracted_attempt.to_string(); + } + } + } + + for (pattern, escaped) in replacements.clone() { + text = text.replace(&escaped, pattern); + extracted = extracted.replace(&escaped, pattern); + } + + println!("{}", extracted.trim_start()); + + Ok(()) +} diff --git a/src/commands/info.rs b/src/commands/info.rs new file mode 100644 index 0000000..6b69f80 --- /dev/null +++ b/src/commands/info.rs @@ -0,0 +1,48 @@ +use clap::Args; + +use crate::filesystem; +use crate::prelude::*; + +const INFO_POSSIBLE_VALUES: &[&str] = &["cheats-example", "cheats-path", "config-path", "config-example"]; + +impl FromStr for Info { + type Err = &'static str; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "cheats-example" => Ok(Info::CheatsExample), + "cheats-path" => Ok(Info::CheatsPath), + "config-example" => Ok(Info::ConfigExample), + "config-path" => Ok(Info::ConfigPath), + _ => Err("no match"), + } + } +} + +#[derive(Debug, Clone, Args)] +pub struct Input { + #[clap(possible_values = INFO_POSSIBLE_VALUES, ignore_case = true)] + pub info: Info, +} + +#[derive(Debug, Clone)] +pub enum Info { + CheatsExample, + CheatsPath, + ConfigPath, + ConfigExample, +} + +impl Runnable for Input { + fn run(&self) -> Result<()> { + let info = &self.info; + + match info { + Info::CheatsExample => println!("{}", include_str!("../../docs/cheat_example.cheat")), + Info::CheatsPath => println!("{}", &filesystem::default_cheat_pathbuf()?.to_string()), + Info::ConfigPath => println!("{}", &filesystem::default_config_pathbuf()?.to_string()), + Info::ConfigExample => println!("{}", include_str!("../../docs/config_file_example.yaml")), + } + Ok(()) + } +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs new file mode 100644 index 0000000..783cbad --- /dev/null +++ b/src/commands/mod.rs @@ -0,0 +1,39 @@ +pub mod core; +pub mod func; +pub mod info; +pub mod preview; +pub mod repo; +pub mod shell; +pub mod temp; + +use crate::commands; +use crate::prelude::*; + +pub fn handle() -> Result<()> { + use crate::config::Command::*; + + match CONFIG.cmd() { + None => commands::core::main(), + + Some(c) => match c { + Preview(input) => input.run(), + + PreviewVarStdin(input) => input.run(), + + PreviewVar(input) => input.run(), + + Widget(input) => input.run().context("Failed to print shell widget code"), + + Fn(input) => input + .run() + .with_context(|| format!("Failed to execute function `{:#?}`", input.func)), + + Info(input) => input + .run() + .with_context(|| format!("Failed to fetch info `{:#?}`", input.info)), + + #[cfg(not(feature = "disable-repo-management"))] + Repo(input) => input.run(), + }, + } +} diff --git a/src/commands/preview/mod.rs b/src/commands/preview/mod.rs new file mode 100644 index 0000000..11fa1fc --- /dev/null +++ b/src/commands/preview/mod.rs @@ -0,0 +1,39 @@ +use crate::prelude::*; +use crate::serializer; +use clap::Args; +use crossterm::style::{style, Stylize}; +use std::process; + +pub mod var; +pub mod var_stdin; + +#[derive(Debug, Clone, Args)] +pub struct Input { + /// Selection line + pub line: String, +} + +fn extract_elements(argstr: &str) -> Result<(&str, &str, &str)> { + let mut parts = argstr.split(serializer::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.")?; + Ok((tags, comment, snippet)) +} + +impl Runnable for Input { + fn run(&self) -> Result<()> { + let line = &self.line; + + let (tags, comment, snippet) = extract_elements(line)?; + + println!( + "{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()), + ); + + process::exit(0) + } +} diff --git a/src/commands/preview/var.rs b/src/commands/preview/var.rs new file mode 100644 index 0000000..e99dcce --- /dev/null +++ b/src/commands/preview/var.rs @@ -0,0 +1,106 @@ +use crate::env_var; +use crate::finder; +use crate::prelude::*; +use crate::serializer; +use clap::Args; +use crossterm::style::style; +use crossterm::style::Stylize; +use std::iter; +use std::process; + +#[derive(Debug, Clone, Args)] +pub struct Input { + /// Selection line + pub selection: String, + /// Query match + pub query: String, + /// Typed text + pub variable: String, +} + +impl Runnable for Input { + fn run(&self) -> Result<()> { + let selection = &self.selection; + let query = &self.query; + let variable = &self.variable; + + let snippet = env_var::must_get(env_var::PREVIEW_INITIAL_SNIPPET); + let tags = env_var::must_get(env_var::PREVIEW_TAGS); + let comment = env_var::must_get(env_var::PREVIEW_COMMENT); + let column = env_var::parse(env_var::PREVIEW_COLUMN); + let delimiter = env_var::get(env_var::PREVIEW_DELIMITER).ok(); + let map = env_var::get(env_var::PREVIEW_MAP).ok(); + + let active_color = CONFIG.tag_color(); + let inactive_color = CONFIG.comment_color(); + + let mut colored_snippet = String::from(&snippet); + let mut visited_vars: HashSet<&str> = HashSet::new(); + + let mut variables = String::from(""); + + println!( + "{comment} {tags}", + comment = style(comment).with(CONFIG.comment_color()), + tags = style(format!("[{}]", tags)).with(CONFIG.tag_color()), + ); + + let bracketed_current_variable = format!("<{}>", variable); + + let bracketed_variables: Vec<&str> = { + if snippet.contains(&bracketed_current_variable) { + serializer::VAR_REGEX + .find_iter(&snippet) + .map(|m| m.as_str()) + .collect() + } else { + iter::once(&bracketed_current_variable) + .map(|s| s.as_str()) + .collect() + } + }; + + for bracketed_variable_name in bracketed_variables { + let variable_name = &bracketed_variable_name[1..bracketed_variable_name.len() - 1]; + + if visited_vars.contains(variable_name) { + continue; + } else { + visited_vars.insert(variable_name); + } + + let is_current = variable_name == variable; + let variable_color = if is_current { active_color } else { inactive_color }; + let env_variable_name = env_var::escape(variable_name); + + let value = if is_current { + let v = selection.trim_matches('\''); |