Refactor writer packages (#517)

This commit is contained in:
Denis Isidoro 2021-04-16 09:23:53 -03:00 committed by GitHub
parent 599b0c95bd
commit d8d0d81368
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 252 additions and 300 deletions

View file

@ -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)
}
}

View file

@ -1,6 +0,0 @@
pub mod core;
pub mod func;
pub mod info;
pub mod preview;
pub mod repo;
pub mod shell;

View file

@ -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(),

View file

@ -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)
}
},
},
}
}

View file

@ -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
View 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)
}
},
},
}
}

View file

@ -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)
}

View 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)
}

View file

@ -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;

View file

@ -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(())

View file

@ -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;

View file

@ -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>>;
}

View file

@ -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
View 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))
}

View file

@ -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
View 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,
)
}

View file

@ -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;
}

View file

@ -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,
)
}
}