mirror of
https://github.com/denisidoro/navi
synced 2024-11-22 03:23:05 +00:00
Refactor writer packages (#517)
This commit is contained in:
parent
599b0c95bd
commit
d8d0d81368
22 changed files with 252 additions and 300 deletions
|
@ -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<Item = Result<String>> {
|
|||
.into_iter()
|
||||
}
|
||||
|
||||
fn read_all(
|
||||
query: &str,
|
||||
cheat: &str,
|
||||
stdin: &mut std::process::ChildStdin,
|
||||
writer: &mut dyn Writer,
|
||||
) -> Result<Option<VariableMap>> {
|
||||
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();
|
||||
|
||||
|
@ -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<String>,
|
||||
) -> Result<Option<VariableMap>> {
|
||||
let cheat = fetch(&self.query)?;
|
||||
read_all(&self.query, &cheat, stdin, writer)
|
||||
read_all(&self.query, &cheat, stdin)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
pub mod core;
|
||||
pub mod func;
|
||||
pub mod info;
|
||||
pub mod preview;
|
||||
pub mod repo;
|
||||
pub mod shell;
|
|
@ -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<String>,
|
||||
) -> Result<Option<VariableMap>> {
|
||||
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(),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -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<dyn Fetcher> = 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()))
|
||||
}
|
||||
})
|
50
src/handler/mod.rs
Normal file
50
src/handler/mod.rs
Normal file
|
@ -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)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
92
src/handler/preview_var.rs
Normal file
92
src/handler/preview_var.rs
Normal file
|
@ -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)
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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<FinderOpts>)> {
|
|||
|
||||
fn write_cmd(
|
||||
item: &Item,
|
||||
writer: &mut dyn Writer,
|
||||
stdin: &mut std::process::ChildStdin,
|
||||
allowlist: Option<&Vec<String>>,
|
||||
denylist: Option<&Vec<String>>,
|
||||
|
@ -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<u64>,
|
||||
writer: &mut dyn Writer,
|
||||
stdin: &mut std::process::ChildStdin,
|
||||
allowlist: Option<&Vec<String>>,
|
||||
denylist: Option<&Vec<String>>,
|
||||
|
@ -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(())
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<String>,
|
||||
) -> Result<Option<VariableMap>>;
|
||||
}
|
||||
|
|
|
@ -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<Option<VariableMap>> {
|
||||
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<String>,
|
||||
) -> Result<Option<VariableMap>> {
|
||||
let markdown = fetch(&self.query)?;
|
||||
read_all(&self.query, &markdown, stdin, writer)
|
||||
read_all(&self.query, &markdown, stdin)
|
||||
}
|
||||
}
|
||||
|
|
32
src/ui.rs
Normal file
32
src/ui.rs
Normal file
|
@ -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<String> = 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))
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
50
src/writer.rs
Normal file
50
src/writer.rs
Normal file
|
@ -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::<String>())
|
||||
} 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,
|
||||
)
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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<String> = 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::<String>())
|
||||
} 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,
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue