Skip to main content

A lightweight, syntax-aware diff tool for Python with tree-sitter integration

Project description

Linediff

PyPI version Python 3.8+ License: MIT CI Downloads

A lightweight, syntax-aware diff tool for Python with tree-sitter integration. Linediff understands code structure using tree-sitter parsers, providing more meaningful diffs than traditional line-based tools.

Downloads

  • Install with PyPI: pip install linediff
  • Tracking via pepy

Table of Contents

Features

  • Syntax-aware diffing: Uses tree-sitter parsers to understand code structure
  • Multiple display modes: unified, side-by-side, and inline views
  • Git integration: Can be used as external diff tool
  • Multi-language support: Python, JavaScript, JSON, HTML, CSS, Rust, Go, Java
  • Automatic language detection: Based on file extensions
  • Robust fallbacks: Falls back to line-based diffing when needed
  • Check-only mode: For CI/CD pipelines (exit code indicates if files differ)
  • Color output: ANSI color codes for better readability
  • Stdin support: Can read diff input from stdin

🚀 Quick Start

Get Linediff in Seconds

From PyPI (Easiest Way):

pip install linediff

From Source (For Contributors):

git clone https://github.com/OthmaneBlial/linediff.git
cd linediff
pip install -e .

Supercharge with Syntax Parsers

Unlock the full power of syntax-aware diffing:

# Get all language parsers
pip install linediff[tree-sitter]

# Or pick your favorites
pip install tree-sitter-python tree-sitter-javascript tree-sitter-json

🎯 Usage

Basic Diff Magic

Compare any two files with intelligent diffing:

linediff file1.py file2.py

See the difference? It's not just lines – it's code structure! 🎉

Git Integration

Linediff can be used as Git's external diff tool:

Global Setup

git config --global diff.external linediff

Per-Language Configuration

git config diff.python.command "linediff --language python"
git config diff.python.binary false

Project Configuration

Add to .gitattributes:

*.py diff=python
*.js diff=javascript
*.json diff=json

Check-Only Mode

For CI/CD pipelines, use --check-only to check if files differ without output:

linediff --check-only file1.py file2.py
echo $?  # 0 = identical, 1 = different

Language Override

Override automatic language detection:

linediff --language javascript file1.txt file2.txt

Display Modes

Choose how your diffs are displayed:

Unified (default) - Traditional diff format:

linediff file1.py file2.py

Side-by-side - See changes next to each other with color coding:

linediff --display side-by-side file1.py file2.py

Inline - Changes highlighted with ANSI colors:

linediff --display inline file1.py file2.py

Reading from Stdin

Pipe diff output through linediff:

git diff | linediff

Or provide content via stdin with separator:

cat > /tmp/diff_input << 'EOF'
old content here
---
new content here
EOF

linediff < /tmp/diff_input

🌍 Language Support

Supported Languages:

Language Extensions
Python .py, .pyw, .pyi
JavaScript .js, .jsx, .ts, .tsx, .mjs
JSON .json, .jsonc
HTML .html, .htm, .xml
CSS .css, .scss, .sass, .less
Rust .rs
Go .go
Java .java

For unsupported languages, Linediff falls back to line-based diffing using Python's difflib.

🎨 See Linediff in Action

Python Function Evolution

Before:

def calculate_total(items):
    total = 0
    for item in items:
        total += item.price
    return total

After:

def calculate_total(items, tax_rate=0.08):
    """Calculate total with tax."""
    subtotal = sum(item.price for item in items)
    tax = subtotal * tax_rate
    return subtotal + tax

Diff output:

--- old.py
+++ new.py
@@ -1,5 +1,6 @@
-def calculate_total(items):
-    total = 0
-    for item in items:
-        total += item.price
-    return total
+def calculate_total(items, tax_rate=0.08):
+    """Calculate total with tax."""
+    subtotal = sum(item.price for item in items)
+    tax = subtotal * tax_rate
+    return subtotal + tax

JavaScript Object Makeover

Before:

const config = {
  apiUrl: "https://api.example.com",
  timeout: 5000,
  retries: 3
};

After:

const config = {
  apiUrl: "https://api.example.com",
  timeout: 10000,
  retries: 3,
  headers: {
    "Authorization": "Bearer token"
  }
};

Diff output:

--- config.js
+++ config.js
@@ -1,5 +1,8 @@
 const config = {
   apiUrl: "https://api.example.com",
-  timeout: 5000,
+  timeout: 10000,
   retries: 3,
+  headers: {
+    "Authorization": "Bearer token"
+  }
 };

JSON Config Transformation

Before:

{
  "database": {
    "host": "localhost",
    "port": 5432
  },
  "features": ["auth", "logging"]
}

After:

{
  "database": {
    "host": "prod-db.example.com",
    "port": 5432,
    "ssl": true
  },
  "features": ["auth", "logging", "metrics"]
}

Diff output:

--- config.json
+++ config.json
@@ -1,6 +1,8 @@
 {
   "database": {
-    "host": "localhost",
+    "host": "prod-db.example.com",
     "port": 5432,
+    "ssl": true
   },
   "features": ["auth", "logging", "metrics"]
 }

How It Works

Linediff uses a two-phase approach:

  1. Syntax Parsing: Tree-sitter parsers convert source code into abstract syntax trees (ASTs)
  2. Structural Diffing: A graph-based algorithm finds optimal matches between AST nodes
  3. Fallback: For large files or unsupported languages, falls back to standard difflib

The diff engine implements Dijkstra's algorithm on a graph where nodes represent code elements and edges represent possible matches, insertions, or deletions.

Performance

  • Small files: Syntax-aware diffing provides more meaningful results
  • Large files: Automatically falls back to line-based diffing for performance
  • Memory efficient: Tree parsing is done on-demand with caching
  • Fast startup: Parser instances are cached for repeated operations

🛠️ Development

Get Started Contributing

Set up your dev environment:

git clone https://github.com/OthmaneBlial/linediff.git
cd linediff
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
pip install -e ".[dev]"

Run the Test Suite

pytest  # Fast, comprehensive testing

Comprehensive Testing Suite

For an interactive testing experience with various scenarios and display modes:

./test_linediff.sh

This provides a menu-driven interface to test different file types, languages, and display modes with real example files.

Building Documentation

# Generate documentation (if applicable)

Code Quality

# Run linting
flake8 src/

# Run type checking
mypy src/

# Format code
black src/

📚 Documentation

Comprehensive documentation is available in the docs/ directory:

For online documentation, visit https://github.com/OthmaneBlial/linediff/blob/main/docs/index.md.

🤝 Contributing

Join the revolution! We love contributions from developers like you.

Check out our Contributing Guide for the full details.

Quick Contribution Flow

  1. 🍴 Fork the repository
  2. 🌿 Create a feature branch: git checkout -b feature/amazing-idea
  3. 💻 Make your awesome changes
  4. ✅ Add comprehensive tests
  5. 🚀 Ensure all tests pass: pytest
  6. 📤 Submit a pull request

Add a New Language

Expand Linediff's universe:

  1. Add tree-sitter parser to pyproject.toml
  2. Configure language support in parser.py
  3. Write tests for the new language
  4. Update this README with examples

License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

Standing on the shoulders of giants:

  • Powered by Tree-sitter for syntax parsing
  • Built with Python and cutting-edge algorithms
  • Community-driven development with ❤️

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

linediff-0.1.3.tar.gz (25.9 kB view details)

Uploaded Source

Built Distribution

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

linediff-0.1.3-py3-none-any.whl (15.2 kB view details)

Uploaded Python 3

File details

Details for the file linediff-0.1.3.tar.gz.

File metadata

  • Download URL: linediff-0.1.3.tar.gz
  • Upload date:
  • Size: 25.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.7

File hashes

Hashes for linediff-0.1.3.tar.gz
Algorithm Hash digest
SHA256 c3f0ad2e6102f0a4c9d990f2db2cda3eecc3fe75b4a27e9ed5c7b774145dd687
MD5 c109b814509dc129f988f79266543566
BLAKE2b-256 fe5bdf60ac53026b17f5ef24e2df1ed06d9640bc0c5658e455b4802f8e527305

See more details on using hashes here.

File details

Details for the file linediff-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: linediff-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 15.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.7

File hashes

Hashes for linediff-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 b0583d83525328d1df24798a822b3d062ea0af054cb129c82e36963fc103f4b6
MD5 dc45a5fbb1b358c4c8fa82ffef4b19b5
BLAKE2b-256 5c059827c978ee5c2a6c84c8becd89aa693d48f3cbdb3a8972833fc17b4836c1

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