use crate::args::Args;
use crate::comments::find_comment_index;
use crate::format::{Pattern, State};
use crate::logging::{record_line_log, Log};
use crate::regexes::VERBS;
use log::Level;
use log::LevelFilter;
use std::path::Path;
pub const TEXT_LINE_START: &str = "";
pub const COMMENT_LINE_START: &str = "% ";
#[must_use]
pub fn needs_wrap(
line: &str,
indent_length: usize,
args: &Args,
state: &State,
) -> bool {
args.wrap
&& (line.chars().count() + indent_length > args.wraplen)
&& !(state.table.visual && args.format_tables)
}
fn is_wrap_point(
i_byte: usize,
c: char,
prev_c: Option<char>,
inside_verb: bool,
line_len: usize,
args: &Args,
) -> bool {
args.wrap_chars.contains(&c)
&& prev_c != Some('\\')
&& !inside_verb
&& (i_byte + 1 < line_len)
}
fn get_verb_end(verb_byte_start: Option<usize>, line: &str) -> Option<usize> {
verb_byte_start.map(|v| {
line[v..]
.match_indices(['|', '+'])
.nth(1)
.map(|(i, _)| i + v)
})?
}
fn is_inside_verb(
i_byte: usize,
contains_verb: bool,
verb_start: Option<usize>,
verb_end: Option<usize>,
) -> bool {
if contains_verb {
(verb_start.unwrap() <= i_byte) && (i_byte <= verb_end.unwrap())
} else {
false
}
}
fn find_wrap_point(
line: &str,
indent_length: usize,
args: &Args,
pattern: &Pattern,
) -> Option<usize> {
let mut wrap_point: Option<usize> = None;
let mut prev_c: Option<char> = None;
let contains_verb =
pattern.contains_verb && VERBS.iter().any(|x| line.contains(x));
let verb_start: Option<usize> = contains_verb
.then(|| VERBS.iter().filter_map(|&x| line.find(x)).min().unwrap());
let verb_end = get_verb_end(verb_start, line);
let mut after_non_percent = verb_start == Some(0);
let wrap_boundary = args.wrapmin - indent_length;
let line_len = line.len();
for (i_char, (i_byte, c)) in line.char_indices().enumerate() {
if i_char >= wrap_boundary && wrap_point.is_some() {
break;
}
let inside_verb =
is_inside_verb(i_byte, contains_verb, verb_start, verb_end);
if is_wrap_point(i_byte, c, prev_c, inside_verb, line_len, args) {
if after_non_percent {
let wrap_byte = i_byte + c.len_utf8() - 1;
if wrap_byte + 1 < line_len {
wrap_point = Some(wrap_byte);
}
}
} else if c != '%' {
after_non_percent = true;
}
prev_c = Some(c);
}
wrap_point
}
pub fn apply_wrap<'a>(
line: &'a str,
indent_length: usize,
state: &State,
file: &Path,
args: &Args,
logs: &mut Vec<Log>,
pattern: &Pattern,
) -> Option<[&'a str; 3]> {
if args.verbosity == LevelFilter::Trace {
record_line_log(
logs,
Level::Trace,
file,
state.linum_new,
state.linum_old,
line,
"Wrapping long line.",
);
}
let wrap_point = find_wrap_point(line, indent_length, args, pattern);
let comment_index = find_comment_index(line, pattern);
match wrap_point {
Some(p) if p <= args.wraplen => {}
_ => {
record_line_log(
logs,
Level::Warn,
file,
state.linum_new,
state.linum_old,
line,
"Line cannot be wrapped.",
);
}
}
wrap_point.map(|p| {
let this_line = &line[0..=p];
let next_line_start = comment_index.map_or("", |c| {
if p > c {
COMMENT_LINE_START
} else {
TEXT_LINE_START
}
});
let next_line = &line[p + 1..];
[this_line, next_line_start, next_line]
})
}