vibefpmgui.formatter
Formatting Fortran files in the editor with fprettify.
fprettify reads the editor's
text on stdin and writes the formatted text to stdout, so unsaved changes are
formatted too and the result is one undoable edit. It runs in the folder of
the file, so a .fprettify.rc there (or in a parent folder, or ~)
applies. Like fortitude, it only understands free-form Fortran.
1"""Formatting Fortran files in the editor with fprettify. 2 3`fprettify <https://github.com/fortran-lang/fprettify>`_ reads the editor's 4text on stdin and writes the formatted text to stdout, so unsaved changes are 5formatted too and the result is one undoable edit. It runs in the folder of 6the file, so a ``.fprettify.rc`` there (or in a parent folder, or ``~``) 7applies. Like fortitude, it only understands free-form Fortran. 8""" 9 10from __future__ import annotations 11 12import os 13import shutil 14import subprocess 15from pathlib import Path 16 17from .console import NO_WINDOW 18from .lint import LINT_EXTS 19 20FORMAT_EXTS = LINT_EXTS 21 22 23def executable() -> str | None: 24 return shutil.which("fprettify") 25 26 27def can_format(path: Path) -> bool: 28 return path.suffix.lower() in FORMAT_EXTS 29 30 31def format_text(text: str, path: Path, timeout: float = 60) -> tuple[str | None, str]: 32 """(formatted text, "") or (None, why it failed).""" 33 program = executable() 34 if program is None: 35 return None, "fprettify was not found on the PATH." 36 # fprettify's own regexes trigger SyntaxWarnings on newer Pythons. 37 env = dict(os.environ, PYTHONWARNINGS="ignore") 38 try: 39 out = subprocess.run([program, "-"], input=text, capture_output=True, text=True, 40 encoding="utf-8", errors="replace", cwd=path.parent, 41 timeout=timeout, env=env, check=False, **NO_WINDOW) 42 except (OSError, subprocess.SubprocessError) as exc: 43 return None, str(exc) 44 if out.returncode != 0: 45 lines = [line for line in out.stderr.splitlines() if line.strip()] 46 return None, lines[-1] if lines else f"fprettify exited with code {out.returncode}" 47 if text.strip() and not out.stdout.strip(): 48 return None, "fprettify returned no text." 49 return out.stdout, ""
FORMAT_EXTS =
{'.f23', '.f08', '.f95', '.f90', '.f03', '.f18'}
def
executable() -> str | None:
def
can_format(path: pathlib.Path) -> bool:
def
format_text( text: str, path: pathlib.Path, timeout: float = 60) -> tuple[str | None, str]:
32def format_text(text: str, path: Path, timeout: float = 60) -> tuple[str | None, str]: 33 """(formatted text, "") or (None, why it failed).""" 34 program = executable() 35 if program is None: 36 return None, "fprettify was not found on the PATH." 37 # fprettify's own regexes trigger SyntaxWarnings on newer Pythons. 38 env = dict(os.environ, PYTHONWARNINGS="ignore") 39 try: 40 out = subprocess.run([program, "-"], input=text, capture_output=True, text=True, 41 encoding="utf-8", errors="replace", cwd=path.parent, 42 timeout=timeout, env=env, check=False, **NO_WINDOW) 43 except (OSError, subprocess.SubprocessError) as exc: 44 return None, str(exc) 45 if out.returncode != 0: 46 lines = [line for line in out.stderr.splitlines() if line.strip()] 47 return None, lines[-1] if lines else f"fprettify exited with code {out.returncode}" 48 if text.strip() and not out.stdout.strip(): 49 return None, "fprettify returned no text." 50 return out.stdout, ""
(formatted text, "") or (None, why it failed).