diff --git a/src/cheatsh.rs b/src/cheatsh.rs index 747e6c79..d5ac039c 100644 --- a/src/cheatsh.rs +++ b/src/cheatsh.rs @@ -1,7 +1,6 @@ use crate::parser; use crate::structures::cheat::VariableMap; use crate::structures::fetcher; -use crate::writer::Writer; use anyhow::Context; use anyhow::Result; use std::collections::HashSet; @@ -23,12 +22,7 @@ fn lines(query: &str, markdown: &str) -> impl Iterator> { .into_iter() } -fn read_all( - query: &str, - cheat: &str, - stdin: &mut std::process::ChildStdin, - writer: &mut dyn Writer, -) -> Result> { +fn read_all(query: &str, cheat: &str, stdin: &mut std::process::ChildStdin) -> Result> { let mut variables = VariableMap::new(); let mut visited_lines = HashSet::new(); @@ -50,7 +44,6 @@ Output: 0, &mut variables, &mut visited_lines, - writer, stdin, None, None, @@ -119,10 +112,9 @@ impl fetcher::Fetcher for Fetcher { fn fetch( &self, stdin: &mut std::process::ChildStdin, - writer: &mut dyn Writer, _files: &mut Vec, ) -> Result> { let cheat = fetch(&self.query)?; - read_all(&self.query, &cheat, stdin, writer) + read_all(&self.query, &cheat, stdin) } } diff --git a/src/cmds/mod.rs b/src/cmds/mod.rs deleted file mode 100644 index 229a5ee0..00000000 --- a/src/cmds/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod core; -pub mod func; -pub mod info; -pub mod preview; -pub mod repo; -pub mod shell; diff --git a/src/filesystem.rs b/src/filesystem.rs index 769a5bcc..58d28fa2 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -4,7 +4,6 @@ pub use crate::fs::{ use crate::parser; use crate::structures::cheat::VariableMap; use crate::structures::fetcher; -use crate::writer::Writer; use anyhow::Result; use directories_next::BaseDirs; use std::collections::HashSet; @@ -103,7 +102,6 @@ impl fetcher::Fetcher for Fetcher { fn fetch( &self, stdin: &mut std::process::ChildStdin, - writer: &mut dyn Writer, files: &mut Vec, ) -> Result> { let mut variables = VariableMap::new(); @@ -133,7 +131,6 @@ impl fetcher::Fetcher for Fetcher { index, &mut variables, &mut visited_lines, - writer, stdin, self.allowlist.as_ref(), self.denylist.as_ref(), diff --git a/src/handler.rs b/src/handler.rs deleted file mode 100644 index f71df1f8..00000000 --- a/src/handler.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::cmds; -use crate::structures::config::Command::{Fn, Info, Preview, PreviewVar, Repo, Widget}; -use crate::structures::config::{Config, RepoCommand}; -use anyhow::Context; -use anyhow::Result; - -pub fn handle_config(config: Config) -> Result<()> { - match config.cmd.as_ref() { - None => cmds::core::main(config), - - Some(c) => match c { - Preview { line } => cmds::preview::main(&line), - - PreviewVar { - selection, - query, - variable, - } => cmds::preview::main_var(&selection, &query, &variable), - - Widget { shell } => cmds::shell::main(shell).context("Failed to print shell widget code"), - - Fn { func, args } => cmds::func::main(func, args.to_vec()) - .with_context(|| format!("Failed to execute function `{:#?}`", func)), - - Info { info } => { - cmds::info::main(info).with_context(|| format!("Failed to fetch info `{:#?}`", info)) - } - - Repo { cmd } => match cmd { - RepoCommand::Add { uri } => { - cmds::repo::add(uri.clone(), &config.finder) - .with_context(|| format!("Failed to import cheatsheets from `{}`", uri))?; - cmds::core::main(config) - } - RepoCommand::Browse => { - cmds::repo::browse(&config.finder).context("Failed to browse featured cheatsheets")?; - cmds::core::main(config) - } - }, - }, - } -} diff --git a/src/cmds/core.rs b/src/handler/core.rs similarity index 91% rename from src/cmds/core.rs rename to src/handler/core.rs index 0ecbf186..7fa393b8 100644 --- a/src/cmds/core.rs +++ b/src/handler/core.rs @@ -1,18 +1,13 @@ use crate::actor; use crate::cheatsh; - -use crate::writer; - use crate::extractor; use crate::filesystem; use crate::finder::structures::{Opts as FinderOpts, SuggestionType}; use crate::finder::Finder; -use crate::structures::fetcher::Fetcher; - use crate::structures::cheat::VariableMap; - use crate::structures::config::Config; use crate::structures::config::Source; +use crate::structures::fetcher::Fetcher; use crate::tldr; use crate::welcome; use anyhow::Context; @@ -45,8 +40,6 @@ pub fn main(config: Config) -> Result<()> { let (raw_selection, variables, files) = config .finder .call(opts, |stdin, files| { - let mut writer = writer::terminal::Writer::new(); - let fetcher: Box = match config.source() { Source::Cheats(query) => Box::new(cheatsh::Fetcher::new(query)), Source::Tldr(query) => Box::new(tldr::Fetcher::new(query)), @@ -54,13 +47,13 @@ pub fn main(config: Config) -> Result<()> { }; let res = fetcher - .fetch(stdin, &mut writer, files) + .fetch(stdin, files) .context("Failed to parse variables intended for finder")?; if let Some(variables) = res { Ok(Some(variables)) } else { - welcome::populate_cheatsheet(&mut writer, stdin); + welcome::populate_cheatsheet(stdin); Ok(Some(VariableMap::new())) } }) diff --git a/src/cmds/func.rs b/src/handler/func.rs similarity index 100% rename from src/cmds/func.rs rename to src/handler/func.rs diff --git a/src/cmds/info.rs b/src/handler/info.rs similarity index 100% rename from src/cmds/info.rs rename to src/handler/info.rs diff --git a/src/handler/mod.rs b/src/handler/mod.rs new file mode 100644 index 00000000..80c6b0fa --- /dev/null +++ b/src/handler/mod.rs @@ -0,0 +1,50 @@ +pub mod core; +pub mod func; +pub mod info; +pub mod preview; +pub mod preview_var; +pub mod repo; +pub mod shell; + +use crate::handler; +use crate::structures::config::Command::{Fn, Info, Preview, PreviewVar, Repo, Widget}; +use crate::structures::config::{Config, RepoCommand}; +use anyhow::Context; +use anyhow::Result; + +pub fn handle_config(config: Config) -> Result<()> { + match config.cmd.as_ref() { + None => handler::core::main(config), + + Some(c) => match c { + Preview { line } => handler::preview::main(&line), + + PreviewVar { + selection, + query, + variable, + } => handler::preview_var::main(&selection, &query, &variable), + + Widget { shell } => handler::shell::main(shell).context("Failed to print shell widget code"), + + Fn { func, args } => handler::func::main(func, args.to_vec()) + .with_context(|| format!("Failed to execute function `{:#?}`", func)), + + Info { info } => { + handler::info::main(info).with_context(|| format!("Failed to fetch info `{:#?}`", info)) + } + + Repo { cmd } => match cmd { + RepoCommand::Add { uri } => { + handler::repo::add(uri.clone(), &config.finder) + .with_context(|| format!("Failed to import cheatsheets from `{}`", uri))?; + handler::core::main(config) + } + RepoCommand::Browse => { + handler::repo::browse(&config.finder).context("Failed to browse featured cheatsheets")?; + handler::core::main(config) + } + }, + }, + } +} diff --git a/src/cmds/preview.rs b/src/handler/preview.rs similarity index 65% rename from src/cmds/preview.rs rename to src/handler/preview.rs index eb8afd3b..c19ba99e 100644 --- a/src/cmds/preview.rs +++ b/src/handler/preview.rs @@ -1,7 +1,6 @@ +use crate::ui; use crate::writer; - use anyhow::Result; - use std::process; fn extract_elements(argstr: &str) -> (&str, &str, &str) { @@ -14,11 +13,13 @@ fn extract_elements(argstr: &str) -> (&str, &str, &str) { pub fn main(line: &str) -> Result<()> { let (tags, comment, snippet) = extract_elements(line); - writer::terminal::preview(comment, tags, snippet); - process::exit(0) -} -pub fn main_var(selection: &str, query: &str, variable: &str) -> Result<()> { - writer::terminal::preview_var(selection, query, variable); + println!( + "{comment} {tags} \n{snippet}", + comment = ui::style(comment).with(*ui::COMMENT_COLOR), + tags = ui::style(format!("[{}]", tags)).with(*ui::TAG_COLOR), + snippet = ui::style(writer::fix_newlines(snippet)).with(*ui::SNIPPET_COLOR), + ); + process::exit(0) } diff --git a/src/handler/preview_var.rs b/src/handler/preview_var.rs new file mode 100644 index 00000000..6814b360 --- /dev/null +++ b/src/handler/preview_var.rs @@ -0,0 +1,92 @@ +use crate::env_var; +use crate::finder; + +use crate::terminal::style::style; +use crate::ui; +use crate::writer; +use anyhow::Result; + +use std::collections::HashSet; +use std::iter; +use std::process; + +pub fn main(selection: &str, query: &str, variable: &str) -> Result<()> { + 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 = *ui::TAG_COLOR; + let inactive_color = *ui::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(*ui::COMMENT_COLOR), + tags = style(format!("[{}]", tags)).with(*ui::TAG_COLOR), + ); + + let bracketed_current_variable = format!("<{}>", variable); + + let bracketed_variables: Vec<&str> = { + if snippet.contains(&bracketed_current_variable) { + writer::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('\''); + if v.is_empty() { query.trim_matches('\'') } else { v }.to_string() + } else if let Ok(v) = env_var::get(&env_variable_name) { + v + } else { + "".to_string() + }; + + let replacement = format!( + "{variable}", + variable = style(bracketed_variable_name).with(variable_color), + ); + + colored_snippet = colored_snippet.replace(bracketed_variable_name, &replacement); + + variables = format!( + "{variables}\n{variable} = {value}", + variables = variables, + variable = style(variable_name).with(variable_color), + value = finder::process(value, column, delimiter.as_deref(), map.clone()) + .expect("Unable to process value"), + ); + } + + println!("{snippet}", snippet = writer::fix_newlines(&colored_snippet)); + println!("{variables}", variables = variables); + + process::exit(0) +} diff --git a/src/cmds/repo.rs b/src/handler/repo.rs similarity index 100% rename from src/cmds/repo.rs rename to src/handler/repo.rs diff --git a/src/cmds/shell.rs b/src/handler/shell.rs similarity index 100% rename from src/cmds/shell.rs rename to src/handler/shell.rs diff --git a/src/lib.rs b/src/lib.rs index 8eff1bfa..a3b11ed7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,6 @@ extern crate anyhow; mod actor; mod cheatsh; mod clipboard; -mod cmds; mod env_var; mod extractor; mod filesystem; @@ -20,6 +19,7 @@ mod shell; mod structures; mod terminal; mod tldr; +mod ui; mod url; mod welcome; mod writer; diff --git a/src/parser.rs b/src/parser.rs index 289c20aa..bc01b92b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2,7 +2,7 @@ use crate::finder::structures::{Opts as FinderOpts, SuggestionType}; use crate::hash::fnv; use crate::structures::cheat::VariableMap; use crate::structures::item::Item; -use crate::writer::{self, Writer}; +use crate::writer; use anyhow::{Context, Result}; use regex::Regex; use std::collections::HashSet; @@ -104,7 +104,6 @@ fn parse_variable_line(line: &str) -> Result<(&str, &str, Option)> { fn write_cmd( item: &Item, - writer: &mut dyn Writer, stdin: &mut std::process::ChildStdin, allowlist: Option<&Vec>, denylist: Option<&Vec>, @@ -127,7 +126,7 @@ fn write_cmd( } } return stdin - .write_all(writer.write(item).as_bytes()) + .write_all(writer::write(item).as_bytes()) .context("Failed to write command to finder's stdin"); } @@ -146,7 +145,6 @@ pub fn read_lines( file_index: usize, variables: &mut VariableMap, visited_lines: &mut HashSet, - writer: &mut dyn Writer, stdin: &mut std::process::ChildStdin, allowlist: Option<&Vec>, denylist: Option<&Vec>, @@ -171,7 +169,7 @@ pub fn read_lines( } // tag else if line.starts_with('%') { - should_break = write_cmd(&item, writer, stdin, allowlist, denylist).is_err(); + should_break = write_cmd(&item, stdin, allowlist, denylist).is_err(); item.snippet = String::from(""); item.tags = without_prefix(&line); } @@ -185,13 +183,13 @@ pub fn read_lines( } // comment else if line.starts_with('#') { - should_break = write_cmd(&item, writer, stdin, allowlist, denylist).is_err(); + should_break = write_cmd(&item, stdin, allowlist, denylist).is_err(); item.snippet = String::from(""); item.comment = without_prefix(&line); } // variable else if line.starts_with('$') { - should_break = write_cmd(&item, writer, stdin, allowlist, denylist).is_err(); + should_break = write_cmd(&item, stdin, allowlist, denylist).is_err(); item.snippet = String::from(""); let (variable, command, opts) = parse_variable_line(&line).with_context(|| { format!( @@ -218,7 +216,7 @@ pub fn read_lines( } if !should_break { - let _ = write_cmd(&item, writer, stdin, allowlist, denylist); + let _ = write_cmd(&item, stdin, allowlist, denylist); } Ok(()) diff --git a/src/structures/config.rs b/src/structures/config.rs index 5e655e68..0755c870 100644 --- a/src/structures/config.rs +++ b/src/structures/config.rs @@ -1,7 +1,7 @@ -use crate::cmds::func::Func; -use crate::cmds::info::Info; use crate::env_var; use crate::finder::FinderChoice; +use crate::handler::func::Func; +use crate::handler::info::Info; use crate::shell::Shell; use clap::{crate_version, AppSettings, Clap}; use std::str::FromStr; diff --git a/src/structures/fetcher.rs b/src/structures/fetcher.rs index d4e237ca..0eeda521 100644 --- a/src/structures/fetcher.rs +++ b/src/structures/fetcher.rs @@ -1,12 +1,10 @@ use crate::structures::cheat::VariableMap; -use crate::writer::Writer; use anyhow::Result; pub trait Fetcher { fn fetch( &self, stdin: &mut std::process::ChildStdin, - writer: &mut dyn Writer, files: &mut Vec, ) -> Result>; } diff --git a/src/tldr.rs b/src/tldr.rs index 184b019f..f5fe0bca 100644 --- a/src/tldr.rs +++ b/src/tldr.rs @@ -1,7 +1,6 @@ use crate::parser; use crate::structures::cheat::VariableMap; use crate::structures::fetcher; -use crate::writer::Writer; use anyhow::{Context, Result}; use regex::Regex; use std::collections::HashSet; @@ -63,7 +62,6 @@ fn read_all( query: &str, markdown: &str, stdin: &mut std::process::ChildStdin, - writer: &mut dyn Writer, ) -> Result> { let mut variables = VariableMap::new(); let mut visited_lines = HashSet::new(); @@ -73,7 +71,6 @@ fn read_all( 0, &mut variables, &mut visited_lines, - writer, stdin, None, None, @@ -154,10 +151,9 @@ impl fetcher::Fetcher for Fetcher { fn fetch( &self, stdin: &mut std::process::ChildStdin, - writer: &mut dyn Writer, _files: &mut Vec, ) -> Result> { let markdown = fetch(&self.query)?; - read_all(&self.query, &markdown, stdin, writer) + read_all(&self.query, &markdown, stdin) } } diff --git a/src/ui.rs b/src/ui.rs new file mode 100644 index 00000000..d0d5d4b0 --- /dev/null +++ b/src/ui.rs @@ -0,0 +1,32 @@ +use crate::env_var; + +use crate::terminal; +pub use crate::terminal::style::style; +use crate::terminal::style::Color; + +use std::cmp::max; + +fn parse_ansi(varname: &str, default: Color) -> Color { + let value: Option = env_var::parse(varname); + if let Some(v) = value { + if let Some(a) = terminal::parse_ansi(&v) { + return a; + } + } + default +} + +lazy_static! { + pub static ref TAG_COLOR: Color = parse_ansi(env_var::TAG_COLOR, Color::Cyan); + pub static ref COMMENT_COLOR: Color = parse_ansi(env_var::COMMENT_COLOR, Color::Blue); + pub static ref SNIPPET_COLOR: Color = parse_ansi(env_var::SNIPPET_COLOR, Color::White); + pub static ref TAG_WIDTH_PERCENTAGE: u16 = env_var::parse(env_var::TAG_WIDTH).unwrap_or(26); + pub static ref COMMENT_WIDTH_PERCENTAGE: u16 = env_var::parse(env_var::COMMENT_WIDTH).unwrap_or(42); +} + +pub fn get_widths() -> (usize, usize) { + let width = terminal::width(); + let tag_width = max(20, width * *TAG_WIDTH_PERCENTAGE / 100); + let comment_width = max(45, width * *COMMENT_WIDTH_PERCENTAGE / 100); + (usize::from(tag_width), usize::from(comment_width)) +} diff --git a/src/welcome.rs b/src/welcome.rs index 56d235c5..da3a6e91 100644 --- a/src/welcome.rs +++ b/src/welcome.rs @@ -1,14 +1,8 @@ use crate::structures::item::Item; -use crate::writer::Writer; +use crate::writer; use std::io::Write; -fn add_msg( - tags: &str, - comment: &str, - snippet: &str, - writer: &mut dyn Writer, - stdin: &mut std::process::ChildStdin, -) { +fn add_msg(tags: &str, comment: &str, snippet: &str, stdin: &mut std::process::ChildStdin) { let item = Item { tags: tags.to_string(), comment: comment.to_string(), @@ -16,24 +10,22 @@ fn add_msg( file_index: 0, }; stdin - .write_all(writer.write(&item).as_bytes()) + .write_all(writer::write(&item).as_bytes()) .expect("Could not write to fzf's stdin"); } -pub fn populate_cheatsheet(writer: &mut dyn Writer, stdin: &mut std::process::ChildStdin) { +pub fn populate_cheatsheet(stdin: &mut std::process::ChildStdin) { add_msg( "cheatsheets", "Download default cheatsheets", "navi repo add denisidoro/cheats", - writer, stdin, ); add_msg( "cheatsheets", "Browse for cheatsheet repos", "navi repo browse", - writer, stdin, ); - add_msg("more info", "Read --help message", "navi --help", writer, stdin); + add_msg("more info", "Read --help message", "navi --help", stdin); } diff --git a/src/writer.rs b/src/writer.rs new file mode 100644 index 00000000..56bbc14a --- /dev/null +++ b/src/writer.rs @@ -0,0 +1,50 @@ +use crate::structures::item::Item; +use crate::ui; +use regex::Regex; + +const NEWLINE_ESCAPE_CHAR: char = '\x15'; +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) = ui::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::()) + } else { + format!("{:width$}", text, width = length) + } +} + +pub fn write(item: &Item) -> String { + let (tag_width, comment_width) = *COLUMN_WIDTHS; + format!( + "{tags_short}{delimiter}{comment_short}{delimiter}{snippet_short}{delimiter}{tags}{delimiter}{comment}{delimiter}{snippet}{delimiter}{file_index}{delimiter}\n", + tags_short = ui::style(limit_str(&item.tags, tag_width)).with(*ui::TAG_COLOR), + comment_short = ui::style(limit_str(&item.comment, comment_width)).with(*ui::COMMENT_COLOR), + snippet_short = ui::style(fix_newlines(&item.snippet)).with(*ui::SNIPPET_COLOR), + tags = item.tags, + comment = item.comment, + delimiter = DELIMITER, + snippet = &item.snippet, + file_index = item.file_index, + ) +} diff --git a/src/writer/mod.rs b/src/writer/mod.rs deleted file mode 100644 index d3022a5b..00000000 --- a/src/writer/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -pub mod terminal; - -use crate::structures::item::Item; -use regex::Regex; - -const NEWLINE_ESCAPE_CHAR: char = '\x15'; -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 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() - } -} - -pub trait Writer { - fn write(&mut self, item: &Item) -> String; -} diff --git a/src/writer/terminal.rs b/src/writer/terminal.rs deleted file mode 100644 index 7471bc88..00000000 --- a/src/writer/terminal.rs +++ /dev/null @@ -1,160 +0,0 @@ -use crate::env_var; -use crate::finder; -use crate::structures::item::Item; -use crate::terminal; -use crate::terminal::style::{style, Color}; -use crate::writer; -use std::cmp::max; -use std::collections::HashSet; -use std::iter; - -fn parse_ansi(varname: &str, default: Color) -> Color { - let value: Option = env_var::parse(varname); - if let Some(v) = value { - if let Some(a) = terminal::parse_ansi(&v) { - return a; - } - } - default -} - -lazy_static! { - pub static ref TAG_COLOR: Color = parse_ansi(env_var::TAG_COLOR, Color::Cyan); - pub static ref COMMENT_COLOR: Color = parse_ansi(env_var::COMMENT_COLOR, Color::Blue); - pub static ref SNIPPET_COLOR: Color = parse_ansi(env_var::SNIPPET_COLOR, Color::White); - pub static ref TAG_WIDTH_PERCENTAGE: u16 = env_var::parse(env_var::TAG_WIDTH).unwrap_or(26); - pub static ref COMMENT_WIDTH_PERCENTAGE: u16 = env_var::parse(env_var::COMMENT_WIDTH).unwrap_or(42); -} - -pub fn preview(comment: &str, tags: &str, snippet: &str) { - println!( - "{comment} {tags} \n{snippet}", - comment = style(comment).with(*COMMENT_COLOR), - tags = style(format!("[{}]", tags)).with(*TAG_COLOR), - snippet = style(writer::fix_newlines(snippet)).with(*SNIPPET_COLOR), - ); -} -pub fn preview_var(selection: &str, query: &str, variable: &str) { - 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 = *TAG_COLOR; - let inactive_color = *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(*COMMENT_COLOR), - tags = style(format!("[{}]", tags)).with(*TAG_COLOR), - ); - - let bracketed_current_variable = format!("<{}>", variable); - - let bracketed_variables: Vec<&str> = { - if snippet.contains(&bracketed_current_variable) { - writer::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('\''); - if v.is_empty() { query.trim_matches('\'') } else { v }.to_string() - } else if let Ok(v) = env_var::get(&env_variable_name) { - v - } else { - "".to_string() - }; - - let replacement = format!( - "{variable}", - variable = style(bracketed_variable_name).with(variable_color), - ); - - colored_snippet = colored_snippet.replace(bracketed_variable_name, &replacement); - - variables = format!( - "{variables}\n{variable} = {value}", - variables = variables, - variable = style(variable_name).with(variable_color), - value = finder::process(value, column, delimiter.as_deref(), map.clone()) - .expect("Unable to process value"), - ); - } - - println!("{snippet}", snippet = writer::fix_newlines(&colored_snippet)); - println!("{variables}", variables = variables); -} - -fn limit_str(text: &str, length: usize) -> String { - if text.len() > length { - format!("{}…", text.chars().take(length - 1).collect::()) - } else { - format!("{:width$}", text, width = length) - } -} - -fn get_widths() -> (usize, usize) { - let width = terminal::width(); - let tag_width = max(20, width * *TAG_WIDTH_PERCENTAGE / 100); - let comment_width = max(45, width * *COMMENT_WIDTH_PERCENTAGE / 100); - (usize::from(tag_width), usize::from(comment_width)) -} - -pub struct Writer { - tag_width: usize, - comment_width: usize, -} - -impl Writer { - pub fn new() -> Writer { - let (tag_width, comment_width) = get_widths(); - writer::terminal::Writer { - tag_width, - comment_width, - } - } -} - -impl writer::Writer for Writer { - fn write(&mut self, item: &Item) -> String { - 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, self.tag_width)).with(*TAG_COLOR), - comment_short = style(limit_str(&item.comment, self.comment_width)).with(*COMMENT_COLOR), - snippet_short = style(writer::fix_newlines(&item.snippet)).with(*SNIPPET_COLOR), - tags = item.tags, - comment = item.comment, - delimiter = writer::DELIMITER, - snippet = &item.snippet, - file_index = item.file_index, - ) - } -}