Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
WGUNDERWOOD committed May 2, 2024
2 parents c8ac128 + 77a1257 commit 3aec88c
Show file tree
Hide file tree
Showing 28 changed files with 29,164 additions and 28,939 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ target/
result
*.html
*.log
*.svg
perf.data*
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,17 @@ https://mit-license.org/)

A LaTeX formatter written in Rust.

## Formatting style example

Before formatting `example.tex`:
<table width="100%">
<tr>
<td>
<b>Input</b>
</td>
<td>
<b>Output</b>
</td>
</tr>
<tr>
<td>

``` tex
\documentclass{article}
Expand All @@ -29,8 +37,8 @@ E = m c^2
\end{document}
```

After running `tex-fmt example.tex`
</td>
<td>

``` tex
\documentclass{article}
Expand All @@ -48,6 +56,9 @@ After running `tex-fmt example.tex`
\end{document}
```
</td>
</tr>
</table>

## Installation

Expand Down
45 changes: 22 additions & 23 deletions notes.org
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
#+title: tex-fmt
* Tests
** One feature per file
** Benchmark running all tests
*** Give number of files and total line numbers
** Look for problem cases in other documents
** Look for problem cases in other latex documents
* Features
** Commands on own lines
*** Implement this by regex on whole document at start
*** Care about multiple or long arguments
*** Look into bracket matching
*** \begin and \end
*** \section, \section*, \chapter, \subsection etc
*** \author and \title etc
*** \usepackage and \documentclass
*** \label
*** \centering
*** \includegraphics
*** \caption
*** \newpage
*** \appendix
** Fold long lines
** TODO Environments should start and end on new lines
*** New line before begin/end with regex replace
*** Care with comments
** Fold long lines to 80 characters
*** Care with trailing comments
*** No folding in verbatim environments, just warn
** Merge short alphanumeric lines
** Flags
*** Dry run (do not modify files)
*** Print to STDOUT
*** Debug (display log file)
*** -d Dry run
*** -p Print to STDOUT
*** -v Info verbose
*** -vv Debug verbose
** Log file
** Ignore source lines
*** Line-by-line ignore
*** Block ignore
* Bugs
** Better errors including line numbers in source file
** Check multiple begins or ends on the same line
*** This should never happen once new lines are implemented
** Better errors including line numbers in source file
** Check begins or ends with more brackets on the same line
** Handle list items properly
* Structure
** Perform indenting
** While long lines are present
*** Trim long lines
*** Perform indenting
1 change: 1 addition & 0 deletions shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ pkgs.mkShell {
buildInputs = with pkgs; [
rustfmt
clippy
cargo-flamegraph
];
}
53 changes: 53 additions & 0 deletions src/format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use crate::indent::*;
use crate::subs::*;
use crate::TAB;

pub fn format_file(file: String, debug: bool) -> String {
// preformat
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);
let lines: Vec<&str> = new_file.lines().collect();

// set up variables
let n_lines = lines.len();
let mut indent = Indent {
actual: 0,
visual: 0,
};
let mut new_lines = vec!["".to_owned(); n_lines];

// main loop through file
for i in 0..n_lines {
// calculate indent
let line = lines[i];
let line_strip = &remove_comment(line);
indent = get_indent(line_strip, indent);
if !debug {
dbg!(&line);
assert!(indent.actual >= 0, "line {}", i);
assert!(indent.visual >= 0, "line {}", i);
};

// apply indent
let mut new_line = line.trim_start().to_string();
if !new_line.is_empty() {
let n_spaces = indent.visual * TAB;
let spaces: String = (0..n_spaces).map(|_| " ").collect();
new_line.insert_str(0, &spaces);
}
new_lines[i] = new_line
}

// check indents return to zero
if !debug {
assert!(indent.actual == 0);
assert!(indent.visual == 0);
}

// prepare indented file
let mut new_file = new_lines.join("\n");
new_file.push('\n');
new_file
}
94 changes: 94 additions & 0 deletions src/indent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use crate::regexes::*;
use core::cmp::max;

const OPENS: [char; 3] = ['(', '[', '{'];
const CLOSES: [char; 3] = [')', ']', '}'];

#[derive(Debug)]
pub struct Indent {
/// actual running indentation count at end of current line
pub actual: i8,
/// visual indentation of current line
pub visual: i8,
}

/// calculate total indentation change due to current line
pub fn get_diff(line: &str) -> i8 {
// documents get no global indentation
if RE_DOCUMENT_BEGIN.is_match(line) || RE_DOCUMENT_END.is_match(line) {
return 0;
};

// list environments get double indents
let mut diff: i8 = 0;
for re_list_begin in RE_LISTS_BEGIN.iter() {
if re_list_begin.is_match(line) {
diff += 1
};
}
for re_list_end in RE_LISTS_END.iter() {
if re_list_end.is_match(line) {
diff -= 1
};
}

// other environments get single indents
if RE_ENV_BEGIN.is_match(line) {
diff += 1
};
if RE_ENV_END.is_match(line) {
diff -= 1
};

// indent for delimiters
for c in OPENS {
diff += line.chars().filter(|&x| x == c).count() as i8;
}
for c in CLOSES {
diff -= line.chars().filter(|&x| x == c).count() as i8;
}

diff
}

/// calculate dedentation for current line compared to previous
pub fn get_back(line: &str) -> i8 {
// documents get no global indentation
if RE_DOCUMENT_END.is_match(line) {
return 0;
};

// list environments get double indents for indenting items
for re_list_end in RE_LISTS_END.iter() {
if re_list_end.is_match(line) {
return 2;
};
}

// other environments get single indents
if RE_ENV_END.is_match(line) {
return 1;
};

// deindent items to make the rest of item environment appear indented
if RE_ITEM.is_match(line) {
return 1;
};

let mut back: i8 = 0;
let mut cumul: i8 = 0;
for c in line.chars() {
cumul -= OPENS.contains(&c) as i8;
cumul += CLOSES.contains(&c) as i8;
back = max(cumul, back);
}
back
}

pub fn get_indent(line: &str, prev_indent: Indent) -> Indent {
let diff = get_diff(line);
let back = get_back(line);
let actual = prev_indent.actual + diff;
let visual: i8 = prev_indent.actual - back;
Indent { actual, visual }
}
Loading

0 comments on commit 3aec88c

Please sign in to comment.