Skip to content

shakfu/reformat

Repository files navigation

reformat

A modular code transformation framework for applying code transformations to code in a set of source code files.

Organized as a Cargo workspace:

  • reformat-core: Core transformation library
  • reformat-cli: Command-line interface
  • reformat-plugins: Plugin system (foundation)

Features

Default Command (Quick Processing)

  • Run all common transformations in a single pass with reformat <path>
  • Combines three operations efficiently:
    1. Rename files to lowercase
    2. Transform task emojis to text alternatives
    3. Remove trailing whitespace
  • 3x faster than running individual commands separately
  • Perfect for quick project cleanup: reformat -r src/

Case Format Conversion

  • Convert between 6 case formats: camelCase, PascalCase, snake_case, SCREAMING_SNAKE_CASE, kebab-case, and SCREAMING-KEBAB-CASE
  • Process single files or entire directories (with recursive option)
  • Dry-run mode to preview changes
  • Filter files by glob patterns
  • Filter which words to convert using regex patterns
  • Add prefix/suffix to converted identifiers
  • Support for multiple file extensions (.c, .h, .py, .md, .js, .ts, .java, .cpp, .hpp)

Whitespace Cleaning

  • Remove trailing whitespace from files
  • Preserve line endings and file structure
  • Recursive directory processing
  • Extension filtering with sensible defaults
  • Dry-run mode to preview changes
  • Automatically skips hidden files and build directories

Emoji Transformation

  • Replace task completion emojis with text alternatives (✅ → [x], ☐ → [ ], etc.)
  • Replace status indicator emojis (🟡 → [yellow], 🟢 → [green], 🔴 → [red])
  • Remove non-task emojis from code and documentation
  • Smart replacements for common task tracking symbols
  • Configurable behavior (replace task emojis, remove others, or both)
  • Support for markdown, documentation, and source files

File Grouping

  • Organize files by common prefix into subdirectories
  • Automatically detect file prefixes based on separator character
  • Optional prefix stripping from filenames after moving
  • Suffix-based splitting (--from-suffix): Split at the LAST separator for multi-part prefixes
  • Preview mode to see what groups would be created
  • Configurable minimum file count for group creation
  • Recursive processing for nested directories
  • Broken reference detection: Automatically scan codebase for references to moved files
  • Interactive fix workflow: Review and apply fixes for broken references
  • Change tracking with changes.json and fixes.json output files

Presets

  • Define reusable transformation pipelines in a reformat.json config file
  • Run named presets with reformat -p <preset-name> <path>
  • Chain any combination of steps: rename, emojis, clean, convert, group
  • Per-step configuration overrides (case transforms, file extensions, separators, etc.)
  • Dry-run mode applies to all steps in the preset
  • Step validation with clear error messages for unknown steps

Logging & UI

  • Multi-level verbosity control (-v, -vv, -vvv)
  • Quiet mode for silent operation (-q)
  • File logging for debugging (--log-file)
  • Progress spinners with indicatif
  • Automatic operation timing
  • Color-coded console output

Installation

Install from crates.io:

cargo install reformat

Or install from the workspace:

cargo install --path reformat-cli

Or build from source:

cargo build --release -p reformat

The binary will be at ./target/release/reformat

Library Usage

Add to your Cargo.toml:

[dependencies]
reformat-core = "0.1.4"

Case Conversion

use reformat_core::{CaseConverter, CaseFormat};

let converter = CaseConverter::new(
    CaseFormat::CamelCase,    // from
    CaseFormat::SnakeCase,    // to
    None,                     // file_extensions
    false,                    // recursive
    false,                    // dry_run
    String::new(),            // prefix
    String::new(),            // suffix
    None,                     // strip_prefix
    None,                     // strip_suffix
    None,                     // replace_prefix_from
    None,                     // replace_prefix_to
    None,                     // replace_suffix_from
    None,                     // replace_suffix_to
    None,                     // glob_pattern
    None,                     // word_filter
)?;

converter.process_directory(std::path::Path::new("src"))?;

Whitespace Cleaning

use reformat_core::{WhitespaceCleaner, WhitespaceOptions};

let mut options = WhitespaceOptions::default();
options.dry_run = false;
options.recursive = true;

let cleaner = WhitespaceCleaner::new(options);
let (files_cleaned, lines_cleaned) = cleaner.process(std::path::Path::new("src"))?;
println!("Cleaned {} lines in {} files", lines_cleaned, files_cleaned);

Combined Processing (Default Command)

use reformat_core::{CombinedProcessor, CombinedOptions};

let mut options = CombinedOptions::default();
options.recursive = true;
options.dry_run = false;

let processor = CombinedProcessor::new(options);
let stats = processor.process(std::path::Path::new("src"))?;

println!("Files renamed: {}", stats.files_renamed);
println!("Emojis transformed: {} files ({} changes)",
         stats.files_emoji_transformed, stats.emoji_changes);
println!("Whitespace cleaned: {} files ({} lines)",
         stats.files_whitespace_cleaned, stats.whitespace_lines_cleaned);

File Grouping

use reformat_core::{FileGrouper, GroupOptions};

let mut options = GroupOptions::default();
options.strip_prefix = true;  // Remove prefix from filenames
options.from_suffix = false;  // Set true to split at LAST separator
options.min_count = 2;        // Require at least 2 files to create a group
options.dry_run = false;

let grouper = FileGrouper::new(options);
let stats = grouper.process(std::path::Path::new("templates"))?;

println!("Directories created: {}", stats.dirs_created);
println!("Files moved: {}", stats.files_moved);
println!("Files renamed: {}", stats.files_renamed);

Quick Start

Default Command (Recommended)

The fastest way to clean up your code:

# Process directory (non-recursive)
reformat <path>

# Process recursively
reformat -r <path>

# Preview changes without modifying files
reformat -d <path>

What it does:

  1. Renames files to lowercase
  2. Transforms task emojis: ✅ → [x], ☐ → [ ]
  3. Removes trailing whitespace

Example:

# Clean up an entire project directory
reformat -r src/

# Preview changes first
reformat -d -r docs/

# Process a single file
reformat README.md

Output:

Renamed '/tmp/TestFile.txt' -> '/tmp/testfile.txt'
Transformed emojis in '/tmp/testfile.txt'
Cleaned 2 lines in '/tmp/testfile.txt'
Processed files:
  - Renamed: 1 file(s)
  - Emoji transformations: 1 file(s) (1 changes)
  - Whitespace cleaned: 1 file(s) (2 lines)

Usage

Case Conversion

Basic conversion (using subcommand):

reformat convert --from-camel --to-snake myfile.py

Recursive directory conversion:

reformat convert --from-snake --to-camel -r src/

Dry run (preview changes):

reformat convert --from-camel --to-kebab --dry-run mydir/

Add prefix to all converted identifiers:

reformat convert --from-camel --to-snake --prefix "old_" myfile.py

Filter files by pattern:

reformat convert --from-camel --to-snake -r --glob "*test*.py" src/

Only convert specific identifiers:

reformat convert --from-camel --to-snake --word-filter "^get.*" src/

Whitespace Cleaning

Clean all default file types in current directory:

reformat clean .

Clean with dry-run to preview changes:

reformat clean --dry-run src/

Clean only specific file types:

reformat clean -e .py -e .rs src/

Clean a single file:

reformat clean myfile.py

Emoji Transformation

Replace task emojis with text in markdown files:

reformat emojis docs/

Process with dry-run to preview changes:

reformat emojis --dry-run README.md

Only replace task emojis, keep other emojis:

reformat emojis --replace-task --no-remove-other docs/

Process specific file types:

reformat emojis -e .md -e .txt project/

File Grouping

Organize files by common prefix into subdirectories:

# Preview what groups would be created
reformat group --preview templates/

# Dry run to see what would happen
reformat group --dry-run templates/

# Group files (keep original filenames)
reformat group templates/

# Group files and strip prefix from filenames
reformat group --strip-prefix templates/

# Group by suffix (split at LAST separator) - for multi-part prefixes
reformat group --from-suffix templates/

# Process subdirectories recursively
reformat group -r templates/

# Use custom separator (e.g., hyphen)
reformat group -s '-' templates/

# Require at least 3 files to create a group
reformat group -m 3 templates/

Example transformation with --strip-prefix (splits at FIRST separator):

Before:                          After:
templates/                       templates/
├── wbs_create.tmpl             ├── wbs/
├── wbs_delete.tmpl             │   ├── create.tmpl
├── wbs_list.tmpl               │   ├── delete.tmpl
├── work_package_create.tmpl    │   └── list.tmpl
├── work_package_delete.tmpl    ├── work/
└── other.txt                   │   ├── package_create.tmpl
                                │   └── package_delete.tmpl
                                └── other.txt

Example transformation with --from-suffix (splits at LAST separator):

Before:                                    After:
templates/                                 templates/
├── activity_relationships_list.tmpl      ├── activity_relationships/
├── activity_relationships_create.tmpl    │   ├── list.tmpl
├── activity_relationships_delete.tmpl    │   ├── create.tmpl
├── user_profile_edit.tmpl                │   └── delete.tmpl
├── user_profile_view.tmpl                ├── user_profile/
└── other.txt                             │   ├── edit.tmpl
                                          │   └── view.tmpl
                                          └── other.txt

Broken Reference Detection

After grouping files, reformat can scan your codebase for broken references:

# Interactive mode (default) - prompts for scanning
reformat group --strip-prefix templates/

# Output:
# Grouping complete:
#   - Directories created: 2
#   - Files moved: 5
# 
# Changes recorded to: changes.json
# 
# Would you like to scan for broken references? [y/N]: y
# Enter directories to scan: src
# 
# Found 3 broken reference(s).
# Proposed fixes written to: fixes.json
# 
# Review fixes.json and apply changes? [y/N]: y
# Fixed 3 reference(s) in 2 file(s).
# Non-interactive mode with automatic scanning
reformat group --strip-prefix --no-interactive --scope src templates/

# Skip reference scanning entirely
reformat group --strip-prefix --no-interactive templates/

Generated files:

  • changes.json - Record of all file operations (for auditing)
  • fixes.json - Proposed reference fixes (review before applying)

Presets

Define reusable transformation pipelines in a reformat.json file in your project root:

{
  "code": {
    "steps": ["rename", "emojis", "clean"],
    "rename": {
      "case_transform": "lowercase",
      "space_replace": "hyphen"
    },
    "emojis": {
      "replace_task_emojis": true,
      "remove_other_emojis": false,
      "file_extensions": [".md", ".txt"]
    },
    "clean": {
      "remove_trailing": true,
      "file_extensions": [".rs", ".py"]
    }
  },
  "templates": {
    "steps": ["group", "clean"],
    "group": {
      "separator": "_",
      "min_count": 3,
      "strip_prefix": true
    }
  }
}

Run a preset:

reformat -p code src/

# Dry-run to preview changes
reformat -p code -d src/

# Run a different preset
reformat -p templates web/templates/

Available step configuration options:

Step Options
rename case_transform (lowercase/uppercase/capitalize), space_replace (underscore/hyphen), recursive, include_symlinks
emojis replace_task_emojis, remove_other_emojis, file_extensions, recursive
clean remove_trailing, file_extensions, recursive
convert from_format, to_format, file_extensions, recursive, prefix, suffix, glob, word_filter
group separator, min_count, strip_prefix, from_suffix, recursive

Steps without explicit configuration use sensible defaults.

Logging and Debugging

Control output verbosity:

# Info level output (-v)
reformat -v convert --from-camel --to-snake src/

# Debug level output (-vv)
reformat -vv clean src/

# Silent mode (errors only)
reformat -q convert --from-camel --to-snake src/

# Log to file
reformat --log-file debug.log -v convert --from-camel --to-snake src/

Output example with -v:

2025-10-10T00:15:08.927Z [INFO] Converting from CamelCase to SnakeCase
2025-10-10T00:15:08.927Z [INFO] Target path: /tmp/test.py
2025-10-10T00:15:08.927Z [INFO] Recursive: false, Dry run: false
Converted '/tmp/test.py'
2025-10-10T00:15:08.931Z [INFO] Conversion completed successfully
2025-10-10T00:15:08.931Z [INFO] run_convert(), Elapsed=4.089125ms

Case Format Options

  • --from-camel / --to-camel - camelCase (firstName, lastName)
  • --from-pascal / --to-pascal - PascalCase (FirstName, LastName)
  • --from-snake / --to-snake - snake_case (first_name, last_name)
  • --from-screaming-snake / --to-screaming-snake - SCREAMING_SNAKE_CASE (FIRST_NAME, LAST_NAME)
  • --from-kebab / --to-kebab - kebab-case (first-name, last-name)
  • --from-screaming-kebab / --to-screaming-kebab - SCREAMING-KEBAB-CASE (FIRST-NAME, LAST-NAME)

Examples

Case Conversion Examples

Convert Python file from camelCase to snake_case:

reformat convert --from-camel --to-snake main.py

Convert C++ project from snake_case to PascalCase:

reformat convert --from-snake --to-pascal -r -e .cpp -e .hpp src/

Preview converting JavaScript getters to snake_case:

reformat convert --from-camel --to-snake --word-filter "^get.*" -d src/

Whitespace Cleaning Examples

Clean trailing whitespace from entire project:

reformat clean -r .

Clean only Python files in src directory:

reformat clean -e .py src/

Preview what would be cleaned without making changes:

reformat clean --dry-run .

Emoji Transformation Examples

Transform task emojis in documentation:

reformat emojis -r docs/

Example transformation:

Before:
- Task done ✅
- Task pending ☐
- Warning ⚠ issue
- 🟡 In progress
- 🟢 Complete
- 🔴 Blocked

After:
- Task done [x]
- Task pending [ ]
- Warning [!] issue
- [yellow] In progress
- [green] Complete
- [red] Blocked

Process only markdown files:

reformat emojis -e .md README.md

File Grouping Examples

Organize template files by prefix (split at first separator):

reformat group --strip-prefix web/templates/

Organize files with multi-part prefixes (split at last separator):

# activity_relationships_list.tmpl -> activity_relationships/list.tmpl
reformat group --from-suffix web/templates/

Preview groups without making changes:

reformat group --preview web/templates/

Example output:

Found 2 potential group(s):

  wbs (3 files):
    - wbs_create.tmpl
    - wbs_delete.tmpl
    - wbs_list.tmpl

  work (2 files):
    - work_package_create.tmpl
    - work_package_delete.tmpl

Group files with hyphen separator:

reformat group -s '-' --strip-prefix components/

Recursively organize nested directories:

reformat group -r --strip-prefix src/

Group files and automatically scan for broken references:

reformat group --strip-prefix --scope src templates/

Example changes.json:

{
  "operation": "group",
  "timestamp": "2026-01-15T16:30:00+00:00",
  "base_dir": "/project/templates",
  "changes": [
    {"type": "directory_created", "path": "wbs"},
    {"type": "file_moved", "from": "wbs_create.tmpl", "to": "wbs/create.tmpl"}
  ]
}

Example fixes.json:

{
  "generated_from": "changes.json",
  "fixes": [
    {
      "file": "src/handler.go",
      "line": 15,
      "context": "template.ParseFiles(\"wbs_create.tmpl\")",
      "old_reference": "wbs_create.tmpl",
      "new_reference": "wbs/create.tmpl"
    }
  ]
}

Preset Examples

Run a multi-step cleanup preset:

# Define in reformat.json, then run:
reformat -p code src/

# Output:
#   rename: 3 file(s) renamed
#   emojis: 2 file(s), 5 change(s)
#   clean: 4 file(s), 12 line(s) cleaned
# Preset 'code' complete.

Preview preset changes without modifying files:

reformat -p code -d src/

Case conversion preset:

{
  "snake-to-camel": {
    "steps": ["convert"],
    "convert": {
      "from_format": "snake",
      "to_format": "camel",
      "file_extensions": [".py"],
      "recursive": true
    }
  }
}
reformat -p snake-to-camel src/

License

MIT License. See LICENSE for details.

About

A code transformer in rust

Topics

Resources

License

Stars

Watchers

Forks

Contributors