Skip to main content

An opinionated Fortran formatter with Fypp and OpenACC support

Project description

ffmt

An opinionated, idempotent Fortran formatter with first-class Fypp preprocessor and OpenACC/OpenMP directive support.

ffmt is a single-pass formatter written in Rust. It replaces fprettify and produces consistent output on the first run -- no multi-pass convergence needed.

Installation

From PyPI

pip install ffmt

From crates.io

cargo install ffmt

From source

git clone https://github.com/sbryngelson/ffmt
cd ffmt
cargo install --path .

Usage

# Format files in-place
ffmt file.fpp
ffmt src/

# Check mode (CI) -- exit 1 if any file would change
ffmt --check src/

# Show what would change (with color)
ffmt --diff src/

# Both
ffmt --check --diff src/

# Parallel formatting
ffmt -j 8 src/

# Read from stdin, write to stdout (for editor integration)
cat file.fpp | ffmt -
ffmt --stdin-filepath file.fpp - < file.fpp

# Exclude patterns
ffmt --exclude "autogen/**" src/

Stdin/stdout mode

Use - as the path to read from stdin and write formatted output to stdout. This enables editor integration (format-on-save) and piping:

# Format stdin to stdout
echo "INTEGER::x" | ffmt -
# Output: integer :: x

# Check stdin (exit 1 if it would change)
cat file.fpp | ffmt --check --stdin-filepath file.fpp -

# Diff stdin
cat file.fpp | ffmt --diff --stdin-filepath file.fpp -

The --stdin-filepath flag provides a filename for diagnostics.

.gitignore support

ffmt automatically respects .gitignore, .ignore, .fdignore, and .git/info/exclude when walking directories. Files ignored by git are skipped.

Editor integration

Vim/Neovim

" Format on save
autocmd BufWritePost *.fpp,*.f90 silent !ffmt %

" Or use as formatprg
set formatprg=ffmt\ -

VS Code

Add to settings.json:

{
    "[fortran]": {
        "editor.formatOnSave": true
    },
    "fortran.formatting.formatter": "ffmt",
    "fortran.formatting.path": "ffmt",
    "fortran.formatting.args": ["--stdin-filepath", "${file}", "-"]
}

CI integration

GitHub Actions

- uses: sbryngelson/ffmt@v1
  with:
    args: "--check src/"

Or install directly:

- run: pip install ffmt
- run: ffmt --check src/

pre-commit

Add to .pre-commit-config.yaml:

repos:
  - repo: https://github.com/sbryngelson/ffmt
    rev: v0.1.0
    hooks:
      - id: ffmt

Use ffmt-check instead of ffmt for check-only mode (no modifications).

What it does

ffmt formats Fortran free-form source files (.fpp, .f90, .f95, .f03) through a single-pass pipeline:

  1. Indentation -- 4-space indentation based on scope tracking (if/do/subroutine/module/type/...)
  2. Fypp block indentation -- #:if, #:for, #:call, #:def, etc. are indented as nested blocks
  3. Directive indentation -- !$acc, !$omp, !DIR$ lines indented to match surrounding Fortran scope
  4. Whitespace normalization -- consistent spacing around operators, commas, colons, parentheses
  5. Case normalization -- Fortran keywords lowercased (IF -> if, END DO -> end do)

Formatting rules

Indentation:

  • 4 spaces per scope level
  • Fypp blocks (#:if/#:for/#:call/#:def/#:block/#:mute) create scope
  • #ifdef/#endif preprocessor blocks do not change indentation
  • Continuation lines (&) preserve their original spacing
  • !! Doxygen continuation comments preserve their original alignment

Operators -- space each side: ==, /=, <=, >=, <, >, .and., .or., .not., = (assignment), =>, +, - (binary), // (concatenation), ::

Operators -- no spaces: *, /, **

Other:

  • Commas: one space after, none before
  • Array slices: no spaces around : -- a(1:n)
  • Keyword arguments: no spaces around = -- call foo(bar=1)
  • Parentheses: no internal padding -- f(x) not f( x )
  • Trailing whitespace: always stripped
  • Blank lines: 3+ consecutive collapsed to 2
  • Blank lines after !$acc loop directives: removed

Preserved as-is:

  • String literal contents ('...', "...")
  • Inline Fypp expressions (${...}$, @{...}@)
  • Comment contents
  • Doxygen comment alignment (!<, !>, !!)
  • Continuation line structure and alignment

Design

ffmt is opinionated like gofmt -- one style, no configuration file, minimal CLI flags.

Why not fprettify?

  • Not idempotent -- fprettify requires 2-4 passes to converge
  • No directive awareness -- requires a separate indenter script for !$acc/!$omp lines
  • Fragile Fypp support -- treats Fypp lines as "not Fortran," causing inconsistent indentation
  • Unmaintained -- last release circa 2020

Architecture

Source lines -> Join continuations -> Classify (LineKind enum) ->
Track scope depth -> Apply indentation -> Normalize whitespace ->
Normalize case -> Emit formatted lines
Module Responsibility
reader.rs Join & continuations, mark opaque regions (strings, ${...}$, comments)
classifier.rs Classify lines into LineKind enum with disambiguation rules
scope.rs Scope tracking state machine (push/pop on block open/close)
indent.rs Apply 4 * depth leading spaces
whitespace.rs Operator/comma/colon/paren spacing normalization
case_norm.rs Lowercase Fortran keywords outside strings/Fypp/comments
formatter.rs Pipeline orchestrator
cli.rs CLI with stdin/stdout, .gitignore, color, exclude patterns

Exit codes

Code Meaning
0 Success (all files formatted, or no changes needed in --check mode)
1 Files would change (--check mode only)
2 Usage error or I/O failure

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ffmt-0.1.0.tar.gz (37.4 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

ffmt-0.1.0-py3-none-win_amd64.whl (1.3 MB view details)

Uploaded Python 3Windows x86-64

ffmt-0.1.0-py3-none-manylinux_2_34_x86_64.whl (1.7 MB view details)

Uploaded Python 3manylinux: glibc 2.34+ x86-64

ffmt-0.1.0-py3-none-macosx_11_0_arm64.whl (1.5 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

File details

Details for the file ffmt-0.1.0.tar.gz.

File metadata

  • Download URL: ffmt-0.1.0.tar.gz
  • Upload date:
  • Size: 37.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ffmt-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8833e7adaf48f77c94d4b5607c4e81080475f0b91865ea1f372d5ad0dfa4804b
MD5 a50a7667cd60aa94f08b316790461b5a
BLAKE2b-256 f0ece264c141bd093b39b0091f64b946dff0f22f550947472009b52c6ff68cd1

See more details on using hashes here.

Provenance

The following attestation bundles were made for ffmt-0.1.0.tar.gz:

Publisher: release.yml on sbryngelson/ffmt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ffmt-0.1.0-py3-none-win_amd64.whl.

File metadata

  • Download URL: ffmt-0.1.0-py3-none-win_amd64.whl
  • Upload date:
  • Size: 1.3 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ffmt-0.1.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 33d3f02c6970844b875deb4a81a31393c5d771e662d6523f615a61252f66ba0c
MD5 e226a94cc88bab823fbe95b8a0909008
BLAKE2b-256 75cfccdf6a6a30dbec497916754f41c6d61ff1c3fdba80494b055213de3b30ed

See more details on using hashes here.

Provenance

The following attestation bundles were made for ffmt-0.1.0-py3-none-win_amd64.whl:

Publisher: release.yml on sbryngelson/ffmt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ffmt-0.1.0-py3-none-manylinux_2_34_x86_64.whl.

File metadata

  • Download URL: ffmt-0.1.0-py3-none-manylinux_2_34_x86_64.whl
  • Upload date:
  • Size: 1.7 MB
  • Tags: Python 3, manylinux: glibc 2.34+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ffmt-0.1.0-py3-none-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 f8c05ee74183f1775cfff085d87d52c8e4e184bc386f3ea668cd528b8fd2ae71
MD5 8463c609e92675e86ea83e3b641baf07
BLAKE2b-256 49850bc0772be2354f587938ebec9d0ee615f3aaec5a9864eef251687497d9c6

See more details on using hashes here.

Provenance

The following attestation bundles were made for ffmt-0.1.0-py3-none-manylinux_2_34_x86_64.whl:

Publisher: release.yml on sbryngelson/ffmt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ffmt-0.1.0-py3-none-macosx_11_0_arm64.whl.

File metadata

  • Download URL: ffmt-0.1.0-py3-none-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 1.5 MB
  • Tags: Python 3, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ffmt-0.1.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0f3580be0926da37684db3e9b981c2579ff40e6301e35b19a32e57f0329340b4
MD5 07e9a767570d6ee9ccc6ba39831c810e
BLAKE2b-256 31bcb8736e1e305e2383e60d130aa58959537247cbf22a97074eaecb20312953

See more details on using hashes here.

Provenance

The following attestation bundles were made for ffmt-0.1.0-py3-none-macosx_11_0_arm64.whl:

Publisher: release.yml on sbryngelson/ffmt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page