luff 0.2.1

Print files with formatting
Documentation

luff

Print files with formatting

License: AGPLv3 Rust Version

Features

  • Multiple Output Formats: Markdown blocks, tree view
  • Smart File Discovery: Directory walking with gitignore support
  • Glob Pattern Support: Explicitly ignore files using bash-style patterns
  • Clipboard Integration: Copy output directly to system clipboard
  • Config File Support: YAML configuration with JSON Schema for IDE autocomplete
  • Security Hardened: Protection against path traversal, symlink attacks, resource exhaustion
  • High Performance: Upfront collection with minimal allocations, efficient I/O
  • Well Tested: Unit tests, property-based tests, integration tests, E2E tests
  • Modular Architecture: Clean separation of concerns, easy to extend

Installation

From Source

git clone https://github.com/owlroute/luff.git
cd luff
cargo install --path crates/luff

Development Build

cargo build --release
./target/release/luff --help

Usage

Process Specific Files

Files can be passed as positional arguments or via the -f flag:

luff src/main.rs Cargo.toml
luff -f src/main.rs Cargo.toml

Walk Current Directory

Print all files in the current directory:

luff

Walk from Git Root

Start from the git repository root:

luff -g

Include Ignored Files

Include files that .gitignore would normally skip:

luff --add

Explicitly Ignore Files

Ignore files matching glob patterns (in addition to .gitignore):

luff --ignore "*.log" --ignore "target/**"

Multiple patterns can be passed to a single --ignore flag:

luff --ignore "*.log" "*.tmp" "*.bak"

Include Hidden Files

Include dot-directories and dotfiles:

luff --dotfiles

Override a config file that enables dotfiles:

luff --no-dotfiles

Tree Output Format

Display as a tree structure:

luff --format tree

Limit Directory Depth

Only traverse up to N levels deep:

luff --max-depth 3

Limit File Count

Cap the number of files collected during a directory walk:

luff --max-files 500

Copy to Clipboard

Copy output to system clipboard (also prints to stdout):

luff -c

Clipboard Only (No Stdout)

Copy to clipboard without printing to terminal:

luff -c -S

Use a Config File

Load settings from a YAML config file:

luff --config luff.yaml

Generate Config Schema

Generate a JSON Schema for config file validation and IDE autocomplete:

luff schema
luff schema /tmp/luff.schema.json

Enable Debug Logging

Show detailed information about file processing:

luff -v

Combined Options

# Walk git repo, include ignored files, include dotfiles, debug output
luff -g -a --dotfiles -v

# Process specific files with tree format
luff -f src/*.rs --format tree

# Walk with depth limit and copy to clipboard
luff --max-depth 2 -c

# Clipboard-only mode with tree format
luff --format tree -c -S

Options

Flag Long Description
<FILE>... Process specific files (positional, disables directory walk)
-f --files <FILE>... Process specific files (flag form, disables directory walk)
--config <PATH> Path to config file (YAML format)
-g --git Start from git repository root
--dotfiles Include hidden files/directories
--no-dotfiles Exclude hidden files/directories (overrides config)
-a --add Add files normally ignored by .gitignore
-i --ignore <PATTERN>... Explicitly ignore files matching glob patterns
-c --clip Copy output to clipboard (also prints to stdout unless -S)
-S --suppress-stdout Suppress stdout output (useful with -c for clipboard-only)
-v --verbose Enable debug logging
--format <FORMAT> Output format: markdown (md), tree (default: markdown)
--max-depth <N> Maximum directory depth (0 = unlimited, default: 0)
--max-files <N> Maximum files to collect during walk
--max-clipboard-mb <N> Maximum clipboard size in MB (default: 100, hard limit: 1000)
-h --help Print help information
-V --version Print version information

Subcommands

Command Description
schema Generate JSON Schema for config files (for IDE autocomplete)

Output Modes

Flags Behavior
(none) Stream to stdout (default, low memory)
-c Buffer entire output, write to stdout AND clipboard
-c -S Buffer entire output, copy to clipboard only (no stdout)
-S No output (edge case, allowed but not useful)

Note: Clipboard mode requires buffering the entire output in memory before writing. For large codebases, this may use significant memory. Use --max-clipboard-mb to control the limit.

Architecture

Key Design Decisions

  • Upfront Collection: Directory walker collects file paths at construction to prevent output file inclusion
  • Shell Redirection Protection: Skips empty files created within 2 seconds to avoid including luff > output.txt output
  • Printer Actor: Bounded channel (capacity 16) to a dedicated writer thread for contention-free stdout
  • Zero-cost Abstractions: Walker enum provides unified interface without runtime overhead
  • Security First: All file operations validated against path traversal, size limits, symlink attacks
  • Explicit Error Handling: No panics in library code, Result types throughout

Development

Prerequisites

  • Rust 1.85 or later
  • Git (for git-related functionality)
  • Clipboard support (X11/Wayland on Linux, native on macOS/Windows)

Building

cargo build
cargo build --release
cargo run -- --help

Testing

cargo test
cargo test -- --nocapture
cargo test --test integration_test
cargo test --test e2e_test

Benchmarking

cargo bench
cargo bench walker_small

Code Quality

cargo clippy --all-targets --all-features -- -D warnings
cargo fmt
cargo geiger

Security

This project follows security best practices:

  • Path Validation: All paths validated against traversal attacks
  • Size Limits: Files limited to 100MB to prevent memory exhaustion
  • TOCTOU Protection: File metadata re-checked after open
  • Symlink Safety: Canonicalization with validation
  • Input Sanitization: All user input validated
  • No Unsafe Code: Pure safe Rust (verify with cargo geiger)
  • Root Containment: File list mode prevents access outside project root
  • Timeout Protection: File operations have 5-second timeout to prevent FIFO blocking

Platform Support

  • Linux: Full support (X11 or Wayland required for clipboard)
  • macOS: Full support (native clipboard)
  • Windows: Full support (native clipboard)
  • Headless: Works without clipboard (graceful degradation)