Skip to content

Commit

Permalink
Improve performance by caching pattern matches
Browse files Browse the repository at this point in the history
  • Loading branch information
WGUNDERWOOD committed Oct 1, 2024
1 parent b5c2b97 commit 92a56b9
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 29 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ tex-fmt is over a thousand times faster than latexindent.

| **Files** | **Lines** | **Size** | **tex-fmt** | **latexindent** | **latexindent -m** |
| --- | --- | --- | --- | --- | --- |
| 49 | 94k | 3.5M | **0.085s** | 99s [x1169] | 130s [x1541] |
| 49 | 94k | 3.5M | **0.077s** | 99s [x1286] | 130s [x1688] |

## Contribution

Expand Down
30 changes: 27 additions & 3 deletions src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::ignore::*;
use crate::indent::*;
use crate::logging::*;
use crate::parse::*;
use crate::regexes::{ENV_BEGIN, ENV_END, ITEM};
use crate::subs::*;
use crate::verbatim::*;
use crate::wrap::*;
Expand Down Expand Up @@ -33,10 +34,12 @@ pub fn format_file(

loop {
if let Some((linum_old, mut line)) = queue.pop() {
let pattern = Pattern::new(&line);
let temp_state: State;
(line, temp_state) =
apply_indent(&line, linum_old, &state, logs, file, args);
if needs_env_new_line(&line, &temp_state) {
(line, temp_state) = apply_indent(
&line, linum_old, &state, logs, file, args, &pattern,
);
if needs_env_new_line(&line, &temp_state, &pattern) {
let env_lines =
put_env_new_line(&line, &temp_state, file, args, logs);
if env_lines.is_some() {
Expand Down Expand Up @@ -110,6 +113,27 @@ impl State {
}
}

/// Record whether a line contains certain patterns to avoid recomputing
pub struct Pattern {
/// Whether a begin environment pattern is present
pub contains_env_begin: bool,
/// Whether an end environment pattern is present
pub contains_env_end: bool,
/// Whether an item pattern is present
pub contains_item: bool,
}

impl Pattern {
/// Check if a string contains patterns
pub fn new(s: &str) -> Self {
Self {
contains_env_begin: s.contains(ENV_BEGIN),
contains_env_end: s.contains(ENV_END),
contains_item: s.contains(ITEM),
}
}
}

/// Ensure that the indentation returns to zero at the end of the file
fn indents_return_to_zero(text: &str) -> bool {
!text.lines().last().unwrap_or_default().starts_with(' ')
Expand Down
32 changes: 15 additions & 17 deletions src/indent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@ impl Indent {
}

/// Calculate total indentation change due to the current line
fn get_diff(line: &str, contains_env_end: bool) -> i8 {
fn get_diff(line: &str, pattern: &Pattern) -> i8 {
// list environments get double indents
let mut diff: i8 = 0;

// other environments get single indents
if line.contains(ENV_BEGIN) {
if pattern.contains_env_begin && line.contains(ENV_BEGIN) {
// double check here as pattern might have matched inside comment
// documents get no global indentation
if line.contains(DOC_BEGIN) {
return 0;
Expand All @@ -50,7 +51,8 @@ fn get_diff(line: &str, contains_env_end: bool) -> i8 {
LISTS_BEGIN.iter().filter(|&r| line.contains(r)).count(),
)
.unwrap();
} else if contains_env_end {
} else if pattern.contains_env_end && line.contains(ENV_END) {
// double check here as pattern might have matched inside comment
// documents get no global indentation
if line.contains(DOC_END) {
return 0;
Expand All @@ -72,7 +74,7 @@ fn get_diff(line: &str, contains_env_end: bool) -> i8 {
}

/// Calculate dedentation for the current line
fn get_back(line: &str, contains_env_end: bool) -> i8 {
fn get_back(line: &str, pattern: &Pattern) -> i8 {
let mut back: i8 = 0;
let mut cumul: i8 = 0;

Expand All @@ -84,7 +86,7 @@ fn get_back(line: &str, contains_env_end: bool) -> i8 {
}

// other environments get single indents
if contains_env_end {
if pattern.contains_env_end {
// documents get no global indentation
if line.contains(DOC_END) {
return 0;
Expand All @@ -99,23 +101,17 @@ fn get_back(line: &str, contains_env_end: bool) -> i8 {
};

// deindent items to make the rest of item environment appear indented
if line.contains(ITEM) {
if pattern.contains_item && line.contains(ITEM) {
back += 1;
};

back
}

/// Check if a line contains an environment end
fn check_contains_env_end(line: &str) -> bool {
line.contains(ENV_END)
}

/// Calculate indentation properties of the current line
fn get_indent(line: &str, prev_indent: &Indent) -> Indent {
let contains_env_end = check_contains_env_end(line);
let diff = get_diff(line, contains_env_end);
let back = get_back(line, contains_env_end);
fn get_indent(line: &str, prev_indent: &Indent, pattern: &Pattern) -> Indent {
let diff = get_diff(line, pattern);
let back = get_back(line, pattern);
let actual = prev_indent.actual + diff;
let visual = prev_indent.actual - back;
Indent { actual, visual }
Expand All @@ -129,19 +125,21 @@ pub fn apply_indent(
logs: &mut Vec<Log>,
file: &str,
args: &Cli,
pattern: &Pattern,
) -> (String, State) {
let mut new_line = line.to_string();
let mut new_state = state.clone();
new_state.linum_old = linum_old;

new_state.ignore = get_ignore(line, &new_state, logs, file, true);
new_state.verbatim = get_verbatim(line, &new_state, logs, file, true);
new_state.verbatim =
get_verbatim(line, &new_state, logs, file, true, pattern);

if !new_state.verbatim.visual && !new_state.ignore.visual {
// calculate indent
let comment_index = find_comment_index(line);
let line_strip = &remove_comment(line, comment_index);
let mut indent = get_indent(line_strip, &state.indent);
let mut indent = get_indent(line_strip, &state.indent, pattern);
new_state.indent = indent.clone();
if args.trace {
record_line_log(
Expand Down
12 changes: 8 additions & 4 deletions src/subs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@ pub fn remove_trailing_spaces(text: &str) -> String {
}

/// Check if environment should be split onto a new line
pub fn needs_env_new_line(line: &str, state: &State) -> bool {
pub fn needs_env_new_line(
line: &str,
state: &State,
pattern: &Pattern,
) -> bool {
!state.verbatim.visual
&& !state.ignore.visual
&& (line.contains(ENV_BEGIN)
|| line.contains(ENV_END)
|| line.contains(ITEM))
&& (pattern.contains_env_begin
|| pattern.contains_env_end
|| pattern.contains_item)
&& (RE_ENV_BEGIN_SHARED_LINE.is_match(line)
|| RE_ENV_END_SHARED_LINE.is_match(line)
|| RE_ITEM_SHARED_LINE.is_match(line))
Expand Down
9 changes: 5 additions & 4 deletions src/verbatim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ pub fn get_verbatim(
logs: &mut Vec<Log>,
file: &str,
warn: bool,
pattern: &Pattern,
) -> Verbatim {
let diff = get_verbatim_diff(line);
let diff = get_verbatim_diff(line, pattern);
let actual = state.verbatim.actual + diff;
let visual = actual > 0 && state.verbatim.actual > 0;

Expand All @@ -52,12 +53,12 @@ pub fn get_verbatim(
}

/// Calculate total verbatim depth change
fn get_verbatim_diff(line: &str) -> i8 {
if line.contains(ENV_BEGIN)
fn get_verbatim_diff(line: &str, pattern: &Pattern) -> i8 {
if pattern.contains_env_begin
&& VERBATIMS_BEGIN.iter().any(|r| line.contains(r))
{
1
} else if line.contains(ENV_END)
} else if pattern.contains_env_end
&& VERBATIMS_END.iter().any(|r| line.contains(r))
{
-1
Expand Down

0 comments on commit 92a56b9

Please sign in to comment.