Skip to main content

ffmt/
lib.rs

1pub mod reader;
2pub mod classifier;
3pub mod scope;
4pub mod whitespace;
5pub mod case_norm;
6pub mod keyword_norm;
7pub mod formatter;
8pub mod config;
9pub mod align;
10pub mod unicode;
11pub mod cli;
12pub mod lsp;
13
14pub use config::Config;
15
16/// Match a dot-delimited operator/literal starting at `pos` in `bytes`.
17/// Returns `(end_position, is_operator)` where `is_operator` distinguishes
18/// `.true.`/`.false.` (literals, not operators) from `.and.`/`.eq.` etc.
19pub fn match_dot_token(bytes: &[u8], pos: usize) -> Option<(usize, bool)> {
20    if bytes[pos] != b'.' {
21        return None;
22    }
23    let len = bytes.len();
24    let mut end = pos + 1;
25    while end < len && bytes[end].is_ascii_alphabetic() {
26        end += 1;
27    }
28    if end >= len || bytes[end] != b'.' {
29        return None;
30    }
31    end += 1;
32
33    let word: String = bytes[pos + 1..end - 1]
34        .iter()
35        .map(|&b| (b as char).to_ascii_lowercase())
36        .collect();
37
38    match word.as_str() {
39        "eq" | "ne" | "lt" | "le" | "gt" | "ge" | "and" | "or" | "not" | "eqv" | "neqv" => {
40            Some((end, true)) // operator
41        }
42        "true" | "false" => {
43            Some((end, false)) // literal
44        }
45        _ => None,
46    }
47}
48
49/// Format a Fortran source string using default config.
50pub fn format_string(source: &str) -> String {
51    formatter::format(source)
52}
53
54/// Format with a specific config.
55pub fn format_string_with_config(source: &str, config: &Config) -> String {
56    formatter::format_with_config(source, config, None)
57}
58
59/// Format only lines within a range (1-based inclusive).
60pub fn format_range(source: &str, start: usize, end: usize) -> String {
61    formatter::format_with_range(source, Some((start, end)))
62}
63
64/// Format a file in-place using default config. Returns true if changed.
65pub fn format_file(path: &std::path::Path) -> std::io::Result<bool> {
66    let source = std::fs::read_to_string(path)?;
67    let formatted = format_string(&source);
68    if formatted == source {
69        return Ok(false);
70    }
71    std::fs::write(path, &formatted)?;
72    Ok(true)
73}