A lightweight, syntax-aware diff tool for Python with tree-sitter integration
Project description
Linediff
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
- Installation
- Usage
- Git Integration
- Supported Languages
- Examples
- How It Works
- Performance
- Development
- Documentation
- Contributing
- License
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:
- Syntax Parsing: Tree-sitter parsers convert source code into abstract syntax trees (ASTs)
- Structural Diffing: A graph-based algorithm finds optimal matches between AST nodes
- 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
- 🍴 Fork the repository
- 🌿 Create a feature branch:
git checkout -b feature/amazing-idea - 💻 Make your awesome changes
- ✅ Add comprehensive tests
- 🚀 Ensure all tests pass:
pytest - 📤 Submit a pull request
Add a New Language
Expand Linediff's universe:
- Add tree-sitter parser to
pyproject.toml - Configure language support in
parser.py - Write tests for the new language
- 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c3f0ad2e6102f0a4c9d990f2db2cda3eecc3fe75b4a27e9ed5c7b774145dd687
|
|
| MD5 |
c109b814509dc129f988f79266543566
|
|
| BLAKE2b-256 |
fe5bdf60ac53026b17f5ef24e2df1ed06d9640bc0c5658e455b4802f8e527305
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b0583d83525328d1df24798a822b3d062ea0af054cb129c82e36963fc103f4b6
|
|
| MD5 |
dc45a5fbb1b358c4c8fa82ffef4b19b5
|
|
| BLAKE2b-256 |
5c059827c978ee5c2a6c84c8becd89aa693d48f3cbdb3a8972833fc17b4836c1
|