FSH Formatter
The FSH Formatter automatically formats your FHIR Shorthand files to maintain consistent code style across your project. It leverages a lossless Concrete Syntax Tree (CST) to ensure perfect preservation of comments, blank lines, and all semantic content.
Quick Start
Section titled “Quick Start”Format your FSH files with a single command:
# Format all FSH files in a directorymaki format input/fsh/*.fsh
# Check if files are formatted without modifying themmaki format --check input/fsh/*.fsh
# Show formatting differencesmaki format --diff input/fsh/*.fshKey Features
Section titled “Key Features”Lossless Formatting
Section titled “Lossless Formatting”The formatter uses Rowan-based CST (Concrete Syntax Tree) to ensure:
- All comments preserved - Line comments, block comments, and documentation
- Blank lines maintained - Intentional spacing between definitions
- Perfect reconstruction -
parse(format(source)) == parse(source) - No semantic changes - Only formatting is modified
Configurable Style
Section titled “Configurable Style”Control formatting behavior with configuration options:
- Indent style (spaces or tabs)
- Line width for wrapping
- Rule alignment
- Spacing normalization
- Blank line handling
High Performance
Section titled “High Performance”Built in Rust for speed:
- Formats files in <50ms each
- Parallel processing for multiple files
- Token optimization for 2-5% performance boost
- Efficient memory usage
Formatting Options
Section titled “Formatting Options”Indent Style
Section titled “Indent Style”Control how code is indented:
[format]indent_style = "spaces" # Options: "spaces", "tabs"indent_size = 2 # Number of spaces (when using spaces)Example:
// Before (mixed indentation)Profile: MyProfileParent: Patient* name 1..1 * given 1..1 * family 1..1
// After (consistent 2-space indentation)Profile: MyProfileParent: Patient* name 1..1 * given 1..1 * family 1..1Line Width
Section titled “Line Width”Set maximum line width before wrapping:
[format]line_width = 120Rule Alignment
Section titled “Rule Alignment”Align rules for better readability:
[format]align_rules = trueExample:
// Before (no alignment)Profile: MyProfileParent: Patient* name 1..1 MS* birthDate 1..1 MS* gender 1..1 MS
// After (aligned)Profile: MyProfileParent: Patient* name 1..1 MS* birthDate 1..1 MS* gender 1..1 MSSpacing Normalization
Section titled “Spacing Normalization”Normalize spacing around operators:
[format]normalize_spacing = trueExample:
// Before (inconsistent spacing)Profile:MyProfileParent:PatientId: my-profileTitle:"My Profile"
// After (normalized)Profile: MyProfileParent: PatientId: my-profileTitle: "My Profile"Blank Line Control
Section titled “Blank Line Control”Control blank lines between sections:
[format]preserve_blank_lines = truemax_blank_lines = 2blank_lines_between_groups = 1Configuration File
Section titled “Configuration File”Add formatting options to your maki.json or maki.toml:
{ "format": { "indent_style": "spaces", "indent_size": 2, "line_width": 120, "align_rules": true, "group_rules": false, "sort_rules": false, "normalize_spacing": true, "preserve_blank_lines": true, "max_blank_lines": 2, "blank_lines_between_groups": 1 }}Or in TOML:
[format]indent_style = "spaces"indent_size = 2line_width = 120align_rules = truegroup_rules = falsesort_rules = falsenormalize_spacing = truepreserve_blank_lines = truemax_blank_lines = 2blank_lines_between_groups = 1Default Settings
Section titled “Default Settings”The formatter uses sensible defaults if no configuration is provided:
| Option | Default | Description |
|---|---|---|
indent_style | "spaces" | Use spaces for indentation |
indent_size | 2 | 2 spaces per indent level |
line_width | 120 | Maximum line width |
align_rules | true | Align rule elements |
group_rules | false | Don’t group rules by type |
sort_rules | false | Don’t sort rules |
normalize_spacing | true | Normalize spacing around : and = |
preserve_blank_lines | true | Keep intentional blank lines |
max_blank_lines | 2 | Maximum consecutive blank lines |
blank_lines_between_groups | 1 | Blank lines between rule groups |
Special Cases
Section titled “Special Cases”Multiline Strings
Section titled “Multiline Strings”The formatter preserves multiline string content exactly as written:
Profile: MyProfileParent: PatientDescription: """This is a multi-linedescription that will bepreserved exactly as-is."""Comments
Section titled “Comments”All comment styles are preserved:
// Line comment before profileProfile: MyProfileParent: Patient
// Comment before rule* name 1..1 MS // Inline comment
/* * Block comment * spanning multiple lines */* gender 1..1 MSMapping Multi-line Comments
Section titled “Mapping Multi-line Comments”Mappings with multi-line comments are handled correctly (fixes SUSHI issues #1577, #1576):
Mapping: PatientMappingSource: MyProfileTarget: "http://example.org"* -> "Patient" """This is a multi-linemapping comment."""CLI Usage
Section titled “CLI Usage”Basic Formatting
Section titled “Basic Formatting”# Format a single filemaki format profile.fsh
# Format multiple filesmaki format profile.fsh extension.fsh valueset.fsh
# Format all FSH files in directorymaki format input/fsh/*.fsh
# Format recursivelymaki format **/*.fshCheck Mode
Section titled “Check Mode”Check if files are formatted without modifying them:
maki format --check input/fsh/*.fshExit codes:
0- All files are formatted1- Some files need formatting2- Error occurred
Diff Mode
Section titled “Diff Mode”Show what would change without modifying files:
maki format --diff input/fsh/*.fshExample output:
--- input/fsh/profile.fsh+++ input/fsh/profile.fsh (formatted)@@ -1,5 +1,5 @@-Profile:MyProfile-Parent:Patient+Profile: MyProfile+Parent: Patient * name 1..1 MS-* birthDate 1..1 MS-* gender 1..1 MS+* birthDate 1..1 MS+* gender 1..1 MSCustom Configuration
Section titled “Custom Configuration”Use a specific configuration file:
maki format --config custom-config.json input/fsh/*.fshCI/CD Integration
Section titled “CI/CD Integration”GitHub Actions
Section titled “GitHub Actions”Add formatting checks to your workflow:
name: Format Check
on: [push, pull_request]
jobs: format: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3
- name: Install maki run: cargo install maki
- name: Check formatting run: maki format --check input/fsh/**/*.fshPre-commit Hook
Section titled “Pre-commit Hook”Add to .git/hooks/pre-commit:
#!/bin/bash# Format FSH files before commitmaki format input/fsh/**/*.fshgit add input/fsh/**/*.fshMake it executable:
Section titled “Make it executable:”chmod +x .git/hooks/pre-commitEditor Integration
Section titled “Editor Integration”VS Code
Section titled “VS Code”Add to your settings:
{ "[fsh]": { "editor.formatOnSave": true, "editor.defaultFormatter": "octofhir.maki" }}Command Palette
Section titled “Command Palette”- Open Command Palette (Cmd/Ctrl+Shift+P)
- Search “Format Document”
- Select formatter: maki
Best Practices
Section titled “Best Practices”Format Early and Often
Section titled “Format Early and Often”Format your code regularly to catch style issues early:
# Before committingmaki format --check input/fsh/**/*.fsh
# Or auto-formatmaki format input/fsh/**/*.fshConsistent Team Style
Section titled “Consistent Team Style”Share your maki.json configuration in version control so the entire team uses the same formatting rules.
Combine with Linting
Section titled “Combine with Linting”Use formatting alongside linting for comprehensive code quality:
# Format firstmaki format input/fsh/**/*.fsh
# Then lintmaki lint input/fsh/**/*.fshUse in Pre-commit Hooks
Section titled “Use in Pre-commit Hooks”Automate formatting with git hooks to ensure all commits are formatted:
#!/bin/bashmaki format --check input/fsh/**/*.fshif [ $? -ne 0 ]; then echo "Files are not formatted. Run 'maki format input/fsh/**/*.fsh'" exit 1fiPerformance
Section titled “Performance”The formatter is optimized for speed:
- Single files: <50ms
- Large projects: Parallel processing
- Token optimization: 2-5% performance boost
- Memory efficient: Streaming processing
Benchmark Results
Section titled “Benchmark Results”Typical formatting performance on real-world projects:
| Project Size | Files | Time | Throughput |
|---|---|---|---|
| Small | 10 files | ~50ms | 200 files/sec |
| Medium | 100 files | ~300ms | 330 files/sec |
| Large | 1000 files | ~2s | 500 files/sec |
Troubleshooting
Section titled “Troubleshooting”Formatting Doesn’t Match Expected Output
Section titled “Formatting Doesn’t Match Expected Output”Check your configuration file:
# Verify config is loadedmaki format --check --verbose input/fsh/profile.fshPerformance Issues
Section titled “Performance Issues”For large projects, use parallel processing (automatic with multiple files):
# Formats files in parallelmaki format input/fsh/**/*.fshPreserve Specific Formatting
Section titled “Preserve Specific Formatting”If you need to preserve specific formatting in a section, use comments:
// maki-format-off* name 1..1 MS* custom formatting here// maki-format-onNote: Format control comments are planned for a future release.
Related Features
Section titled “Related Features”- Automatic Fixes - Combine formatting with rule fixes
- CI/CD Integration - Automate formatting checks
- Editor Integration - Format on save
Technical Details
Section titled “Technical Details”Lossless CST
Section titled “Lossless CST”The formatter uses Rowan-based Concrete Syntax Tree:
- Green Tree: Immutable, position-independent storage with all trivia
- Red Tree: Dynamic view with parent pointers for efficient traversal
- Lossless Property:
parse(format(parse(source))) == parse(source)
Token Optimization
Section titled “Token Optimization”Based on optimizations from Ruff and Biome formatters:
- Token variant: Static keywords/operators (fast path)
- Text variant: Dynamic content from source (slow path)
- 70-85% fast path usage: High keyword density in FSH
- 2-5% performance improvement: Proven optimization strategy
SUSHI Compatibility
Section titled “SUSHI Compatibility”The formatter addresses known SUSHI formatting issues:
- #1569: Preserves triple-quote endings
- #1577, #1576: Handles mapping multi-line delimiters
- #693: Accepts missing whitespace in input
Future Enhancements
Section titled “Future Enhancements”Planned improvements for future releases:
- Smart line breaking: Intelligent wrapping of long lines
- Custom formatting rules: User-defined formatting plugins
- Format on save: LSP integration for automatic formatting
- Diff-aware formatting: Only format changed lines
- Format control comments: Selectively disable formatting
- Rule grouping: Group rules by type (metadata, constraints, flags)
- Rule sorting: Alphabetical or custom sorting within groups