Skip to main content

Ada Language Formatter (ALS-backed, Customizable)

Project description

adafmt โ€” Ada Language Formatter

Version: 0.0.0 License: BSD-3-Clause Copyright: ยฉ 2025 Michael Gardner, A Bit of Help, Inc.

adafmt is an opinionated Ada 2022 formatter that leverages the Ada Language Server (ALS) to provide consistent, modern formatting while maintaining compatibility with earlier Ada versions. Built with extensibility in mind, it supports custom pattern formatting functions that allow teams to enforce project-specific style rules beyond what GNATFORMAT provides. It delivers a robust, production-ready solution for maintaining consistent code style across Ada projects of any size.

Why Use AdaFmt?

๐Ÿš€ Single-Pass Formatting

Unlike tools that require multiple passes to converge on a stable format, adafmt achieves consistent results in a single run. No need to run it three times hoping for convergence.

๐Ÿ“Š Comprehensive Logging

Every formatting transformation is logged in structured JSON Lines format, providing complete visibility into what changed and why. Perfect for auditing and debugging formatting decisions.

โœจ Modern Architecture

Built on the Ada Language Server (ALS) using the Language Server Protocol, avoiding known issues with chaining gnatformat and gnatpp. Get reliable, consistent results every time.

๐ŸŽฏ Extensible Pattern System

Configure custom formatting patterns in a simple JSON file. Override defaults, add project-specific transformations, or enforce team style guides beyond what standard tools provide.

๐Ÿ›ก๏ธ Safe by Default

  • Dry-run mode shows changes before applying them
  • Atomic file updates prevent partial writes
  • Automatic validation catches syntax errors before writing
  • Comprehensive error reporting for troubleshooting

๐Ÿ”ง Additional Benefits

  • CI/CD Integration: Check mode with proper exit codes for automated pipelines
  • Performance Metrics: Detailed statistics on processing time and throughput
  • Cross-Platform: Works on Linux, macOS, and Windows with appropriate ALS
  • Project-Aware: Understands GNAT project files (.gpr) for accurate formatting
  • Flexible Discovery: Smart file finding with include/exclude path support

Features

  • Language Server Protocol (LSP) Integration: Uses the official Ada Language Server for accurate, specification-compliant formatting
  • Multiple UI Modes: Choose from pretty (curses), basic, plain, or headless operation modes
  • Standalone Operation: Works directly with Ada Language Server without external package managers
  • Robust Error Handling: Retry logic for transient failures with configurable timeouts
  • Dry-Run by Default: Preview changes safely before applying them
  • CI/CD Ready: Exit codes and check mode for integration into automated workflows
  • Comprehensive Logging: Structured JSON Lines logging for debugging and auditing
  • Cross-Platform: Works on Linux, macOS, and Windows (with appropriate ALS installation)
  • Syntax Error Detection: Compiler verification to catch GNATFORMAT false positives
  • Performance Statistics: Detailed summary with files processed, time elapsed, and processing rate
  • Atomic File Updates: Safe file writing with automatic backup during updates

Quick Start

Installation

From PyPI (Recommended)

pip install adafmt

From GitHub Releases

Download the appropriate package from the latest release:

Python Wheel (All Platforms)

# Download the wheel file, then:
pip install adafmt-0.0.0-py3-none-any.whl

Python Zipapp (Portable, No Installation)

# Download adafmt.pyz, then run directly:
python3 adafmt.pyz --help

# Or make it executable (Unix-like systems):
chmod +x adafmt.pyz
./adafmt.pyz --help

Standalone Executables (No Python Required)

# Linux
wget https://github.com/abitofhelp/adafmt/releases/latest/download/adafmt-linux-x64
chmod +x adafmt-linux-x64
./adafmt-linux-x64 --help

# macOS
curl -LO https://github.com/abitofhelp/adafmt/releases/latest/download/adafmt-macos-x64
chmod +x adafmt-macos-x64
./adafmt-macos-x64 --help

# Windows (PowerShell)
Invoke-WebRequest -Uri https://github.com/abitofhelp/adafmt/releases/latest/download/adafmt-windows-x64.exe -OutFile adafmt.exe
.\adafmt.exe --help

From Source (Development Mode)

# Clone the repository
git clone https://github.com/abitofhelp/adafmt.git
cd adafmt

# Create virtual environment and install
python3 -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
pip install -e .

# Verify installation
adafmt --version

System Requirements

  • Python 3.8 or higher
  • Ada Language Server (ALS) installed and available in PATH

Requirements

Ada Language Server (ALS)

This tool uses the Ada Language Server, but does not bundle or redistribute it. You must have ada_language_server available on your PATH before running adafmt.

Install ALS

Pick one of the following options:

  • Via Alire (recommended for Ada developers):

    # Install Alire if you don't already have it, then:
    alr search ada_language_server           # discover the crate
    alr get ada_language_server              # fetch locally (buildable project)
    # follow Alire's instructions to build/install; after install it should be on PATH
    
  • Via GNAT Studio: If you have GNAT Studio installed, it includes the Ada Language Server.

  • Via GitHub Releases: Download pre-built binaries from Ada Language Server releases.

Basic Usage

Format Ada Files in a Directory

# Preview changes (dry-run mode)
adafmt --project-path /path/to/project.gpr \
       --include-path /path/to/src

# Apply changes
adafmt --project-path /path/to/project.gpr \
       --include-path /path/to/src \
       --write

Format Specific Files

# Format individual files
adafmt --project-path /path/to/project.gpr \
       main.adb utils.ads types.ads

# Mix files and directories
adafmt --project-path /path/to/project.gpr \
       --include-path /path/to/src \
       special_file.adb

CI/CD Integration

# Check if any files need formatting (exits 1 if changes needed)
adafmt --project-path /path/to/project.gpr \
       --include-path /path/to/src \
       --check

# Use plain output for CI logs
adafmt --project-path /path/to/project.gpr \
       --include-path /path/to/src \
       --ui plain \
       --check

Command-Line Interface

Required Arguments

  • --project-path PATH: Path to your GNAT project file (.gpr)

Optional Arguments

File Selection

  • --include-path PATH: Directory to search for Ada files (can be specified multiple times)
  • --exclude-path PATH: Directory to exclude from search (can be specified multiple times)
  • FILES...: Specific Ada files to format (positional arguments)

Output Options

  • --write: Apply changes to files (default: dry-run mode)
  • --diff / --no-diff: Show/hide unified diffs of changes (default: --diff)
  • --check: Exit with code 1 if any files need formatting

User Interface

  • --ui {off,auto,pretty,basic,plain}: UI mode selection
    • off: No UI, only output diffs
    • auto: Best available UI (default)
    • pretty: Curses UI with progress bar
    • basic: Simple text UI
    • plain: Minimal output for scripts

Advanced Error Handling

  • Smart Error Detection: Distinguishes between syntax errors (prevent formatting) and semantic errors (allow formatting)
  • Compiler Verification: Automatically verifies GNATFORMAT syntax errors to detect false positives
  • Detailed Error Reporting: Comprehensive stderr output with timestamps, error types, and actionable guidance

Advanced Options

  • --preflight {off,warn,safe,kill,kill+clean,aggressive,fail}: Handle existing ALS processes
    • off/none: Skip all checks
    • warn: Report processes and locks only
    • safe (default): Kill ALS processes older than --als-stale-minutes
    • kill: Same as safe
    • kill+clean: Kill stale ALS + remove stale lock files
    • aggressive: Kill ALL user's ALS processes + remove all locks
    • fail: Exit with error if any processes/locks found
  • --als-stale-minutes N: Age threshold for stale ALS processes (default: 30)
  • --init-timeout SECONDS: Timeout for ALS initialization (default: 180.0)
  • --warmup-seconds SECONDS: Time to let ALS warm up (default: 10.0)
  • --format-timeout SECONDS: Timeout per file formatting (default: 60.0)
  • --max-attempts N: Retry attempts for transient errors (default: 2)

Debugging

  • --log-file-path PATH: Write structured logs to JSONL file
  • --stderr-file-path PATH: Capture ALS stderr output to file

Environment Variables

  • ADAFMT_UI_FORCE: Override the UI mode parameter
  • ADAFMT_UI_DEBUG: Enable UI selection debug output

File Discovery

adafmt searches for Ada source files using these rules:

  1. File Extensions: .ads (spec), .adb (body), .ada (either)
  2. Search Behavior:
    • Recursively searches --include-path directories
    • Skips --exclude-path directories and their subdirectories
    • Processes explicitly named files regardless of location
  3. Common Exclusions: Consider excluding:
    • Build directories (obj/, lib/, .build/)
    • Dependencies (alire/, deps/)
    • Generated files (b__*.ad[sb])

Integration Examples

Makefile Integration

.PHONY: format format-check

format:
	adafmt --project-file-path $(PROJECT_GPR) \
	       --include-path src \
	       --exclude-path obj \
	       --write

format-check:
	adafmt --project-file-path $(PROJECT_GPR) \
	       --include-path src \
	       --exclude-path obj \
	       --check

Git Pre-commit Hook

#!/bin/bash
# .git/hooks/pre-commit

# Check Ada files formatting
adafmt --project-file-path project.gpr \
       --include-path src \
       --check \
       --ui plain

if [ $? -ne 0 ]; then
    echo "Error: Ada files need formatting. Run 'make format' and try again."
    exit 1
fi

GitHub Actions

name: Ada Format Check
on: [push, pull_request]

jobs:
  format-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup Ada
        uses: ada-actions/ada-toolchain@v1
        with:
          target: native
          distrib: community

      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.x'

      - name: Install adafmt
        run: |
          pip install adafmt

      - name: Check formatting
        run: |
          adafmt --project-file-path project.gpr \
                 --include-path src \
                 --check \
                 --ui plain

Troubleshooting

Common Issues

"Ada Language Server not found"

  • Ensure ALS is installed: which ada_language_server
  • Add ALS to PATH or ensure it's available in your environment

"Existing ALS processes detected"

  • Use --preflight kill to clean up stuck processes
  • Or manually: pkill -f ada_language_server

Timeout Errors

  • Increase timeouts: --init-timeout 300 --warmup-seconds 20 --format-timeout 120
  • Check ALS stderr: --stderr-file-path als-debug.log

Understanding Error Types

Syntax vs Semantic Errors:

  • Syntax errors: Malformed code structure (missing semicolons, unmatched parentheses, etc.)
    • These prevent formatting - ALS cannot parse the code structure
    • adafmt reports these as failures with error code -32803
  • Semantic errors: Valid syntax but incorrect meaning (undefined types, missing imports, etc.)
    • These do NOT prevent formatting - code structure is valid
    • Examples: "Domain_Event" not declared, invalid prefix in selected component
    • Your code will format successfully but won't compile

When ALS Reports Syntax Errors:

  • adafmt attempts compilation to verify (only for .adb/.ada files)
  • False positives are tracked and shown as yellow warnings
  • Real syntax errors prevent formatting and are reported as failures
  • Failed files show "(details in the stderr log)" on terminal
  • Full error details with timestamps are written to the stderr log file

No Changes Applied

  • Ensure --write flag is used (default is dry-run)
  • Check file permissions and disk space

Debugging

Enable comprehensive logging to diagnose issues:

adafmt --project-file-path /path/to/project.gpr \
       --include-path /path/to/src \
       --log-file-path adafmt-debug.jsonl \
       --stderr-file-path als-stderr.log \
       --ui plain

View logs:

# View structured logs
jq . adafmt-debug.jsonl

# View ALS errors
cat als-stderr.log

Architecture

adafmt follows a modular architecture:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚     CLI     โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚  ALS Client โ”‚โ”€โ”€โ”€โ”€โ–ถโ”‚     ALS     โ”‚
โ”‚   (cli.py)  โ”‚     โ”‚(als_client) โ”‚     โ”‚  (subprocessโ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
       โ”‚                    โ”‚
       โ–ผ                    โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚     TUI     โ”‚     โ”‚    Edits    โ”‚
โ”‚  (tui.py)   โ”‚     โ”‚ (edits.py)  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Project Structure

adafmt/
โ”œโ”€โ”€ src/adafmt/          # Main package source code
โ”‚   โ”œโ”€โ”€ __init__.py      # Package initialization
โ”‚   โ”œโ”€โ”€ cli.py           # Command-line interface and main entry point
โ”‚   โ”œโ”€โ”€ als_client.py    # Ada Language Server async client
โ”‚   โ”œโ”€โ”€ tui.py           # Terminal UI with curses/plain fallback
โ”‚   โ”œโ”€โ”€ file_discovery.py # Ada source file discovery logic
โ”‚   โ”œโ”€โ”€ edits.py         # LSP TextEdit application and diff generation
โ”‚   โ”œโ”€โ”€ utils.py         # Utility functions (atomic write, Alire detection)
โ”‚   โ””โ”€โ”€ logging_jsonl.py # Structured JSON Lines logging
โ”œโ”€โ”€ tests/               # Test suite
โ”‚   โ”œโ”€โ”€ test_*.py        # Unit tests for each module
โ”‚   โ””โ”€โ”€ test_integration.py # Integration tests with ALS
โ”œโ”€โ”€ docs/                # Comprehensive documentation
โ”‚   โ”œโ”€โ”€ api/            # API reference documentation
โ”‚   โ”œโ”€โ”€ guides/          # User and developer guides
โ”‚   โ”œโ”€โ”€ formal/         # Requirements and design documents
โ”‚   โ”œโ”€โ”€ reference/      # Technical references and deep-dives
โ”‚   โ””โ”€โ”€ user/           # User guides and troubleshooting
โ”œโ”€โ”€ scripts/             # Build and release scripts
โ”œโ”€โ”€ pyproject.toml       # Project configuration and dependencies
โ”œโ”€โ”€ Makefile            # Common development tasks
โ””โ”€โ”€ README.md           # This file

Key Components

  • CLI (cli.py): Command-line argument parsing and orchestration
  • ALS Client (als_client.py): Async JSON-RPC client for Language Server Protocol
  • TUI (tui.py): Terminal UI with automatic fallback to simpler modes
  • File Discovery (file_discovery.py): Smart Ada source file detection with exclusion support
  • Edit Engine (edits.py): LSP TextEdit application with unified diff generation
  • Logger (logging_jsonl.py): Structured logging for debugging and auditing
  • Utilities (utils.py): Atomic file writes, process management, preflight checks

Contributing

We welcome contributions! Please see our Developer Documentation for:

  • Setting up a development environment
  • Code style guidelines
  • Testing requirements
  • Submitting pull requests

Documentation

๐Ÿ“š Complete documentation is available in the docs/ directory:

For Users

For Developers

Technical References

License

BSD-3-Clause โ€” see LICENSE for the full text.

ยฉ 2025 Michael Gardner, A Bit of Help, Inc. SPDX-License-Identifier: BSD-3-Clause

Acknowledgments

  • Ada Language Server team for providing a robust LSP implementation
  • Alire community for modern Ada package management
  • Python asyncio contributors for excellent async subprocess support

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

adafmt-1.0.0.tar.gz (68.7 kB view details)

Uploaded Source

Built Distribution

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

adafmt-1.0.0-py3-none-any.whl (72.0 kB view details)

Uploaded Python 3

File details

Details for the file adafmt-1.0.0.tar.gz.

File metadata

  • Download URL: adafmt-1.0.0.tar.gz
  • Upload date:
  • Size: 68.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for adafmt-1.0.0.tar.gz
Algorithm Hash digest
SHA256 eb4786184f67864580e35279bef9e070f7fc8adab1b469baf8e1e051368c326e
MD5 aabbe01afc4c890af2d3f81b686a9aa9
BLAKE2b-256 82cb4099fdf451c7a5ca3823b929efa490a9e90299124747753b4f8ecd82f347

See more details on using hashes here.

File details

Details for the file adafmt-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: adafmt-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 72.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for adafmt-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 909c19ce6c531cb11a91c3890d104253e35dbd303446d65a0e57eeaa8ccdb5d3
MD5 edbfd328c5bce11ff746d0eca65a4a08
BLAKE2b-256 592d24c433a86665668142daac9db3d83f50e85ce8b41dd8612d67967dce7a07

See more details on using hashes here.

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