diff --git a/notes.org b/notes.org index 4572285..442e421 100644 --- a/notes.org +++ b/notes.org @@ -11,3 +11,7 @@ ** Handle list items and comments properly ** Handle verbatim and ignores better ** Only print ignore warnings once +* Logging +** Store all logs on the fly in a vector +** Print them all at the end in an intelligent way +** Avoid panics to make sure we always reach the end diff --git a/src/format.rs b/src/format.rs index 52dc556..19aacac 100644 --- a/src/format.rs +++ b/src/format.rs @@ -1,21 +1,28 @@ use crate::indent::*; +use crate::logging::*; use crate::subs::*; use crate::wrap::*; use crate::Cli; +use log::Level::Error; -const MAX_TRIES: u8 = 10; +const MAX_PASS: usize = 10; -fn apply_passes(file: &str, filename: &str, args: &Cli) -> String { - let mut new_file = apply_indent(file, filename, args); +fn apply_passes( + file: &str, + filename: &str, + args: &Cli, + logs: &mut Vec, +) -> String { + let mut new_file = apply_indent(file, filename, args, logs, Some(1)); let mut finished = false; - let mut tries = 0; + let mut pass = 2; - while needs_wrap(&new_file) && !finished && tries < MAX_TRIES { + while needs_wrap(&new_file) && !finished && pass < MAX_PASS + 2 { let old_file = new_file.clone(); - new_file = wrap(&new_file, filename); + new_file = wrap(&new_file, filename, logs, Some(pass)); new_file = remove_trailing_spaces(&new_file); - new_file = apply_indent(&new_file, filename, args); - tries += 1; + new_file = apply_indent(&new_file, filename, args, logs, Some(pass)); + pass += 1; if new_file == old_file { finished = true; } @@ -23,17 +30,31 @@ fn apply_passes(file: &str, filename: &str, args: &Cli) -> String { // check indents return to zero if new_file.lines().last().unwrap().starts_with(' ') { - log::error!("Indent does not return to zero at end of file"); + //log::error!(""); + record_log( + logs, + Error, + None, + filename.to_string(), + None, + None, + "Indent does not return to zero at end of file.".to_string(), + ); } new_file } -pub fn format_file(file: &str, filename: &str, args: &Cli) -> String { +pub fn format_file( + file: &str, + filename: &str, + args: &Cli, + logs: &mut Vec, +) -> String { let mut new_file = remove_extra_newlines(file); new_file = begin_end_environments_new_line(&new_file); new_file = remove_tabs(&new_file); new_file = remove_trailing_spaces(&new_file); - new_file = apply_passes(&new_file, filename, args); + new_file = apply_passes(&new_file, filename, args, logs); new_file } diff --git a/src/ignore.rs b/src/ignore.rs index b9178c4..b0ac8d5 100644 --- a/src/ignore.rs +++ b/src/ignore.rs @@ -1,4 +1,5 @@ -use crate::colors::*; +use crate::logging::*; +use log::Level::Error; //const IG_STARTS: [&str; 1] = ["\\begin{verbatim}"]; //const IG_ENDS: [&str; 1] = ["\\end{verbatim}"]; @@ -19,9 +20,12 @@ impl Ignore { pub fn get_ignore( line: &str, - i: usize, + linum: usize, ignore: Ignore, filename: &str, + logs: &mut Vec, + pass: Option, + warn: bool, ) -> Ignore { let skip = contains_ignore_skip(line); let start = contains_ignore_start(line); @@ -33,30 +37,28 @@ pub fn get_ignore( if start { block = true } - if end { - log::warn!( - "{}tex-fmt {}{}: {}Line {}. \ - {}No ignore block to end: \ - {}{:.50}", - PINK, - PURPLE, - filename, - WHITE, - i, - YELLOW, - RESET, - line, + if end && warn { + record_log( + logs, + Error, + pass, + filename.to_string(), + Some(linum), + Some(line.to_string()), + "No ignore block to end:".to_string(), ); } } else { // currently in ignore block - if start { - log::warn!( - "Line {}: cannot start ignore block \ - before ending previous block: {}{:.50}", - i, - WHITE, - line + if start && warn { + record_log( + logs, + Error, + pass, + filename.to_string(), + Some(linum), + Some(line.to_string()), + "Cannot start ignore block:".to_string(), ); } if end { diff --git a/src/indent.rs b/src/indent.rs index b546a9e..c8c93ca 100644 --- a/src/indent.rs +++ b/src/indent.rs @@ -1,10 +1,12 @@ -use crate::colors::*; +//use crate::colors::*; use crate::comments::*; use crate::ignore::*; +use crate::logging::*; use crate::parse::*; use crate::regexes::*; use crate::TAB; use core::cmp::max; +use log::Level::{Info, Warn}; const OPENS: [char; 3] = ['(', '[', '{']; const CLOSES: [char; 3] = [')', ']', '}']; @@ -102,8 +104,22 @@ fn get_indent(line: &str, prev_indent: Indent) -> Indent { Indent { actual, visual } } -pub fn apply_indent(file: &str, filename: &str, args: &Cli) -> String { - log::info!("Indenting file"); +pub fn apply_indent( + file: &str, + filename: &str, + args: &Cli, + logs: &mut Vec, + pass: Option, +) -> String { + record_log( + logs, + Info, + pass, + filename.to_string(), + None, + None, + format!("Indent pass {}.", pass.unwrap()), + ); let mut indent = Indent::new(); let mut ignore = Ignore::new(); @@ -114,34 +130,31 @@ pub fn apply_indent(file: &str, filename: &str, args: &Cli) -> String { if RE_VERBATIM_BEGIN.is_match(line) { verbatim_count += 1; } - ignore = get_ignore(line, linum, ignore, filename); + ignore = get_ignore(line, linum, ignore, filename, logs, pass, true); if verbatim_count == 0 && !is_ignored(&ignore) { // calculate indent let comment_index = find_comment_index(line); let line_strip = remove_comment(line, comment_index); indent = get_indent(line_strip, indent); - log::info!( - "Indent line {}: actual = {}, visual = {}:{} {}", - linum, - indent.actual, - indent.visual, - WHITE, - line, + record_log( + logs, + Info, + pass, + filename.to_string(), + Some(linum), + Some(line.to_string()), + format!("Indent: actual = {}, visual = {}", indent.actual, indent.visual), ); if (indent.visual < 0) || (indent.actual < 0) { - log::warn!( - "{}tex-fmt {}{}: {}Line {}. \ - {}Indent is negative: \ - {}{:.50}", - PINK, - PURPLE, - filename, - WHITE, - linum, - YELLOW, - RESET, - line, + record_log( + logs, + Warn, + pass, + filename.to_string(), + Some(linum), + Some(line.to_string()), + "Indent is negative.".to_string(), ); } diff --git a/src/logging.rs b/src/logging.rs index 3c6f7e8..35a027f 100644 --- a/src/logging.rs +++ b/src/logging.rs @@ -5,6 +5,40 @@ use log::Level; use log::Level::{Error, Info, Warn}; use log::LevelFilter; use std::io::Write; +use std::time::Instant; +use std::path::Path; + +#[derive(Debug)] +pub struct Log { + pub level: Level, + pub pass: Option, + pub time: Instant, + pub filename: String, + pub linum: Option, + pub line: Option, + pub message: String, +} + +pub fn record_log( + logs: &mut Vec, + level: Level, + pass: Option, + filename: String, + linum: Option, + line: Option, + message: String, +) { + let log = Log { + level, + pass, + time: Instant::now(), + filename, + linum, + line, + message, + }; + logs.push(log); +} fn get_log_style(log_level: Level) -> String { match log_level { @@ -37,3 +71,45 @@ pub fn init_logger(args: &Cli) { }) .init(); } + +pub fn print_logs(args: &Cli, mut logs: Vec) { + + if get_log_level(args) == LevelFilter::Warn { + let max_pass = &logs.iter().map(|l| l.pass).max().unwrap(); + logs.retain(|l| l.pass == *max_pass || l.pass == None); + } + + logs.sort_by_key(|l| l.time); + + for log in logs { + let linum = match log.linum { + Some(i) => format!("Line {}. ", i), + None => "".to_string(), + }; + + let line = match &log.line { + Some(l) => l.to_string(), + None => "".to_string(), + }; + + let log_string = format!( + "{}tex-fmt {}{}: {}{}{}{} {}{:.50}", + PINK, + PURPLE, + Path::new(&log.filename).file_name().unwrap().to_str().unwrap(), + WHITE, + linum, + YELLOW, + log.message, + RESET, + line, + ); + + match log.level { + Error => log::error!("{}", log_string), + Warn => log::warn!("{}", log_string), + Info => log::info!("{}", log_string), + _ => panic!(), + } + } +} diff --git a/src/main.rs b/src/main.rs index 0b952f7..637a955 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use clap::Parser; +use log::Level::{Error, Info}; #[allow(unused_imports)] use rstest::rstest; #[allow(unused_imports)] @@ -19,7 +20,6 @@ mod regexes; mod subs; mod wrap; mod write; -use crate::colors::*; use crate::format::*; use crate::logging::*; use crate::parse::*; @@ -30,7 +30,6 @@ use crate::write::*; mod tests; fn main() { - // get arguments let mut args = Cli::parse(); if args.debug { args.print = true; @@ -38,25 +37,41 @@ fn main() { }; init_logger(&args); - print_script_name(); for filename in &args.filenames { - if args.verbose { - print_filename(filename); - } - if !check_extension_valid(filename) { - log::error!("File type invalid for {}{}", WHITE, filename); - continue; - }; - - let file = fs::read_to_string(filename).unwrap(); - let new_file = format_file(&file, filename, &args); + let mut logs: Vec = vec![]; + record_log( + &mut logs, + Info, + None, + filename.to_string(), + None, + None, + "Begin indenting.".to_string(), + ); - if args.print { - print_file(&new_file); + let extension_valid = check_extension_valid(filename); + if extension_valid { + let file = fs::read_to_string(filename).unwrap(); + let new_file = format_file(&file, filename, &args, &mut logs); + if args.print { + print_file(&new_file); + } else { + write_file(filename, &new_file); + } } else { - write_file(filename, &new_file); - } + record_log( + &mut logs, + Error, + None, + filename.to_string(), + None, + None, + "File type invalid.".to_string(), + ); + }; + + print_logs(&args, logs); } } diff --git a/src/print.rs b/src/print.rs index e95e747..beccadb 100644 --- a/src/print.rs +++ b/src/print.rs @@ -1,12 +1,12 @@ -use crate::colors::*; +//use crate::colors::*; -pub fn print_script_name() { - println!("{}tex-fmt{}", PINK, RESET); -} +//pub fn print_script_name() { + //println!("{}tex-fmt{}", PINK, RESET); +//} -pub fn print_filename(filename: &str) { - println!("{}tex-fmt {}{}{}", PINK, PURPLE, filename, RESET); -} +//pub fn print_filename(filename: &str) { + //println!("{}{}{}", PURPLE, filename, RESET); +//} pub fn print_file(new_file: &str) { println!("{}", new_file); diff --git a/src/tests.rs b/src/tests.rs index 6e7b8bc..e91332f 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -2,6 +2,7 @@ use crate::apply; use crate::colors::*; use crate::format_file; use crate::fs; +use crate::logging::*; use crate::rstest; use crate::template; use crate::Cli; @@ -29,11 +30,12 @@ fn test_file(#[case] filename: &str, #[case] extension: &str) {} #[apply(test_file)] fn test_in_file(filename: &str, extension: &str) { let args = Cli::new(); + let mut logs: Vec = vec![]; let in_filename = format!("tests/{}_in.{}", filename, extension); let out_filename = format!("tests/{}_out.{}", filename, extension); let in_file = fs::read_to_string(&in_filename).expect(""); let out_file = fs::read_to_string(&out_filename).expect(""); - let fmt_in_file = format_file(&in_file, &in_filename, &args); + let fmt_in_file = format_file(&in_file, &in_filename, &args, &mut logs); assert!(fmt_in_file == out_file, "\n{}Test failed: {}{}{} -> {}{}{}\n\n{}Output:\n{}{}{}\nDesired:\n{}{}", &RED, @@ -54,9 +56,10 @@ fn test_in_file(filename: &str, extension: &str) { #[apply(test_file)] fn test_out_file(filename: &str, extension: &str) { let args = Cli::new(); + let mut logs: Vec = vec![]; let out_filename = format!("tests/{}_out.{}", filename, extension); let out_file = fs::read_to_string(&out_filename).expect(""); - let fmt_out_file = format_file(&out_file, &out_filename, &args); + let fmt_out_file = format_file(&out_file, &out_filename, &args, &mut logs); assert!(fmt_out_file == out_file, "\n{}Test failed: {}{}{} -> {}{}{}\n\n{}Output:\n{}{}{}\nDesired:\n{}{}", &RED, diff --git a/src/wrap.rs b/src/wrap.rs index 773c88b..3665280 100644 --- a/src/wrap.rs +++ b/src/wrap.rs @@ -1,6 +1,7 @@ -use crate::colors::*; +//use crate::colors::*; use crate::comments::*; use crate::ignore::*; +use crate::logging::*; use crate::regexes::*; const WRAP: usize = 80; @@ -31,7 +32,7 @@ fn find_wrap_point(line: &str) -> Option { } fn wrap_line(line: &str) -> String { - log::info!("Wrap long line: {}{}", WHITE, line); + //log::info!("Wrap long line: {}{}", WHITE, line); let mut remaining_line = line.to_string(); let mut new_line = "".to_string(); let mut can_wrap = true; @@ -65,8 +66,8 @@ fn wrap_line(line: &str) -> String { new_line } -pub fn wrap(file: &str, filename: &str) -> String { - log::info!("Wrapping file"); +pub fn wrap(file: &str, filename: &str, logs: &mut Vec, pass: Option) -> String { + //log::info!("Wrapping file"); let mut new_file = "".to_string(); let mut new_line: String; let mut verbatim_count = 0; @@ -75,7 +76,7 @@ pub fn wrap(file: &str, filename: &str) -> String { if RE_VERBATIM_BEGIN.is_match(line) { verbatim_count += 1; } - ignore = get_ignore(line, linum, ignore, filename); + ignore = get_ignore(line, linum, ignore, filename, logs, pass, false); if line_needs_wrap(line) && verbatim_count == 0 && !is_ignored(&ignore) { new_line = wrap_line(line); @@ -92,19 +93,19 @@ pub fn wrap(file: &str, filename: &str) -> String { if needs_wrap(&new_file) { for (linum, line) in new_file.lines().enumerate() { if line_needs_wrap(line) { - log::warn!( - "{}tex-fmt {}{}: {}Line {}. \ - {}Line cannot be wrapped: \ - {}{:.50}", - PINK, - PURPLE, - filename, - WHITE, - linum, - YELLOW, - RESET, - line, - ); + //log::warn!( + //"{}tex-fmt {}{}: {}Line {}. \ + //{}Line cannot be wrapped: \ + //{}{:.50}", + //PINK, + //PURPLE, + //filename, + //WHITE, + //linum, + //YELLOW, + //RESET, + //line, + //); } } } diff --git a/tests/ignore_in.tex b/tests/ignore_in.tex index 0645885..dd28fc1 100644 --- a/tests/ignore_in.tex +++ b/tests/ignore_in.tex @@ -12,4 +12,8 @@ % tex-fmt: on +% tex-fmt: off + +% tex-fmt: off + \end{document} diff --git a/tests/ignore_out.tex b/tests/ignore_out.tex index 0645885..dd28fc1 100644 --- a/tests/ignore_out.tex +++ b/tests/ignore_out.tex @@ -12,4 +12,8 @@ % tex-fmt: on +% tex-fmt: off + +% tex-fmt: off + \end{document}