Skip to content

Commit f45d62f

Browse files
feat: Add progress bar to CLI scan command and update QUICKSTART.md
## CLI Progress Bar Enhancement ### rcompare_core/src/comparison.rs - Add new `compare_with_vfs_and_progress()` method with progress callback support - Accept optional `Fn(usize, usize)` closure for progress reporting - Report (current, total) progress as files are compared - Update existing `compare_with_vfs_and_cancel()` to use new method ### rcompare_cli/src/main.rs - Replace spinner with progress bar during comparison phase - Calculate total items before comparison for accurate progress tracking - Display progress as: `[####>-----] 42/100 files (42%) [00:05] Comparing files...` - Update progress bar in real-time as each file is processed - Retain progress bar for scanning phase (already implemented) ## QUICKSTART.md Documentation Updates ### Added PySide6 GUI Section - Installation instructions for Python dependencies - Launch commands and usage guide - Feature list: specialized diff viewers (text, image, CSV, Excel, JSON, binary) - File operations and export capabilities ### Added C/C++ Integration (FFI) Section - Static library build instructions for Linux/macOS/Windows - Basic C example for parsing diffs - CMake integration guide - FFI features overview (parse, apply, blend, serialize) - Documentation references ### Updated Feature Status - Moved implemented features from "What's Next?" to "Current Features" - Added comprehensive feature checklist with completion status - Updated version to 0.3.0-dev - Added roadmap reference for future enhancements - Last updated: 2026-01-30 ## User Experience Improvements **Before (spinner)**: ``` ⠋ Comparing files... [00:05] ``` **After (progress bar)**: ``` ⠋ [###########>-----------] 42/100 files (42%) [00:05] Comparing files... ``` Benefits: - Users can see exact progress (42/100 files) - Percentage completion visible - Better perception of remaining work - More professional appearance ## Testing - ✅ Builds successfully without warnings - ✅ Core library compiles with new progress callback - ✅ CLI compiles with progress bar implementation - ✅ Progress bar displays during file comparison - ✅ Backwards compatible (progress callback is optional) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 92dfd6c commit f45d62f

File tree

3 files changed

+263
-26
lines changed

3 files changed

+263
-26
lines changed

QUICKSTART.md

Lines changed: 181 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,19 @@ rcompare_cli scan /left /right -L
8383

8484
## Using the GUI
8585

86-
### Launch the GUI
86+
RCompare provides two graphical interfaces:
87+
- **Slint GUI** (rcompare_gui): Native, lightweight, cross-platform
88+
- **PySide6 GUI** (Python frontend): Feature-rich with advanced diff viewers
89+
90+
### Slint GUI (Native)
91+
92+
#### Launch the GUI
8793

8894
```bash
8995
rcompare_gui
9096
```
9197

92-
### GUI Features
98+
#### GUI Features
9399

94100
1. **Select Directories**: Click "Select Left..." and "Select Right..." to choose directories
95101
2. **Compare**: Click the "Compare" button to run the comparison
@@ -101,6 +107,126 @@ rcompare_gui
101107
4. **Status Bar**: Shows summary statistics at the bottom
102108
5. **Refresh**: Click "Refresh" to re-run the comparison
103109

110+
### PySide6 GUI (Python Frontend)
111+
112+
The PySide6 frontend provides advanced features including specialized diff viewers for text, images, CSV, Excel, JSON, and binary files.
113+
114+
#### Installation
115+
116+
```bash
117+
# Install Python dependencies
118+
pip install PySide6 Pillow openpyxl
119+
120+
# Or use requirements file (if available)
121+
pip install -r frontend/requirements.txt
122+
```
123+
124+
#### Launch the PySide6 GUI
125+
126+
```bash
127+
# From the repository root
128+
python frontend/main.py
129+
130+
# Or if installed as a package
131+
rcompare-pyside6
132+
```
133+
134+
#### PySide6 Features
135+
136+
1. **Directory Comparison**: Full directory tree comparison with expand/collapse
137+
2. **Specialized Diff Viewers**:
138+
- **Text Diff**: Line-by-line comparison with syntax highlighting
139+
- **Image Diff**: Side-by-side image comparison with pixel differences
140+
- **CSV Diff**: Row/column comparison for CSV files
141+
- **Excel Diff**: Sheet and cell-level comparison
142+
- **JSON/YAML Diff**: Structural comparison with formatting
143+
- **Binary Diff**: Hex viewer for binary files
144+
3. **File Operations**: Copy left/right with progress tracking
145+
4. **Export**: JSON output for automation and scripting
146+
5. **Settings**: Configurable comparison options and display preferences
147+
148+
## C/C++ Integration (FFI)
149+
150+
RCompare provides a C-compatible Foreign Function Interface (FFI) for integrating patch parsing and manipulation into C/C++ applications. This is particularly useful for KDE applications and other C++ projects that need libkomparediff2-compatible functionality.
151+
152+
### Building the FFI Library
153+
154+
```bash
155+
# Build the static library
156+
cargo build --package rcompare_ffi --release
157+
158+
# Library location:
159+
# Linux/macOS: target/release/librcompare_ffi.a
160+
# Windows: target/release/rcompare_ffi.lib
161+
162+
# Header file:
163+
# rcompare_ffi/include/rcompare.h
164+
```
165+
166+
### Basic C Example
167+
168+
```c
169+
#include "rcompare.h"
170+
#include <stdio.h>
171+
#include <string.h>
172+
173+
int main(void) {
174+
const char* diff_text =
175+
"--- a/file.txt\n"
176+
"+++ b/file.txt\n"
177+
"@@ -1 +1 @@\n"
178+
"-old\n"
179+
"+new\n";
180+
181+
// Parse diff
182+
PatchSetHandle* handle = NULL;
183+
int result = rcompare_parse_diff(
184+
(const uint8_t*)diff_text,
185+
strlen(diff_text),
186+
&handle
187+
);
188+
189+
if (result != 0) {
190+
fprintf(stderr, "Parse failed\n");
191+
return 1;
192+
}
193+
194+
// Access metadata
195+
size_t file_count = rcompare_patchset_file_count(handle);
196+
printf("Files: %zu\n", file_count);
197+
198+
// Cleanup
199+
rcompare_free_patchset(handle);
200+
return 0;
201+
}
202+
```
203+
204+
### CMake Integration
205+
206+
```cmake
207+
# Add RCompare FFI to your project
208+
add_subdirectory(path/to/rcompare/rcompare_ffi)
209+
210+
# Link to your target
211+
target_link_libraries(your_target PRIVATE rcompare)
212+
```
213+
214+
### FFI Features
215+
216+
- **Parse multiple diff formats**: Unified, context, normal, RCS, ed
217+
- **Auto-detect generators**: CVS, Perforce, Subversion, plain diff
218+
- **Patch operations**: Apply/unapply individual or all differences
219+
- **File blending**: Merge original file content with patch hunks
220+
- **Serialization**: Convert patch model back to unified diff format
221+
- **Memory safe**: Opaque handle pattern with proper lifetime management
222+
223+
### Documentation
224+
225+
For complete API reference and examples, see:
226+
- [rcompare_ffi/README.md](rcompare_ffi/README.md)
227+
- [rcompare_ffi/include/rcompare.h](rcompare_ffi/include/rcompare.h)
228+
- C examples: `rcompare_ffi/examples/`
229+
104230
## Understanding the Output
105231

106232
### Status Symbols (CLI)
@@ -328,20 +454,63 @@ rcompare_cli scan \
328454
- **Issues**: Report bugs on GitHub
329455
- **CLI Help**: `rcompare_cli --help`
330456

457+
## Current Features
458+
459+
RCompare currently provides:
460+
461+
### Core Comparison Engine
462+
- ✅ BLAKE3 hashing with persistent cache
463+
- ✅ Parallel directory traversal
464+
- ✅ Gitignore pattern support
465+
- ✅ Cross-platform support (Linux, Windows, macOS)
466+
467+
### Specialized Format Support
468+
- ✅ Text diff viewer with syntax highlighting
469+
- ✅ Archive comparison (ZIP, TAR, 7Z)
470+
- ✅ Binary hex viewer
471+
- ✅ Image comparison with pixel diff
472+
- ✅ CSV and Excel comparison
473+
- ✅ JSON and YAML structural diff
474+
- ✅ Parquet DataFrame comparison
475+
476+
### Patch System
477+
- ✅ Multi-format diff parser (unified, context, normal, RCS, ed)
478+
- ✅ Patch apply/unapply operations
479+
- ✅ File blending with original content
480+
- ✅ C/C++ FFI layer (libkomparediff2-compatible)
481+
482+
### User Interfaces
483+
- ✅ CLI with progress indicators and JSON output
484+
- ✅ Native Slint GUI
485+
- ✅ PySide6 GUI with specialized viewers
486+
- ✅ Copy operations (left/right)
487+
331488
## What's Next?
332489

333-
Current version (0.1.0) provides basic comparison. Future enhancements:
490+
Future enhancements planned:
491+
492+
### Phase 4: Performance & GUI (In Progress)
493+
- 🚧 Parallel hash computing (2-3x speedup)
494+
- 📋 Three-way merge
495+
- 📋 Tabs for multiple comparisons
496+
- 📋 Synced scrolling with diff map
497+
498+
### Phase 5: Reporting & Workflow
499+
- 📋 HTML/Markdown/CSV report export
500+
- 📋 JUnit XML for CI integration
501+
- 📋 Diff statistics dashboard
502+
- 📋 Comparison presets (save/load)
503+
504+
### Phase 6-7: Cloud & Advanced
505+
- 📋 Additional cloud providers (GCS, Azure, Dropbox)
506+
- 📋 Watch mode for continuous monitoring
507+
- 📋 Semantic diff (AST-based)
508+
- 📋 Plugin/extension system
334509

335-
- Text diff viewer with syntax highlighting
336-
- Archive comparison (ZIP, TAR)
337-
- File operations (copy, move, delete)
338-
- Three-way merge
339-
- Binary hex comparison
340-
- Image diff visualization
341-
- Batch scripting support
510+
See [ROADMAP.md](ROADMAP.md) for detailed development timeline.
342511

343512
---
344513

345-
**Version**: 0.1.0
346-
**Last Updated**: 2026-01-24
514+
**Version**: 0.3.0-dev
515+
**Last Updated**: 2026-01-30
347516
**License**: MIT OR Apache-2.0

rcompare_cli/src/main.rs

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -463,34 +463,64 @@ fn run_scan(
463463
}
464464

465465
// Compare directories
466+
// Calculate total items to compare
467+
let total_items = {
468+
let mut paths: std::collections::HashSet<PathBuf> = std::collections::HashSet::new();
469+
for entry in &left_entries {
470+
paths.insert(entry.path.clone());
471+
}
472+
for entry in &right_entries {
473+
paths.insert(entry.path.clone());
474+
}
475+
paths.len() as u64
476+
};
477+
466478
let pb_compare = if show_progress {
467-
let pb = ProgressBar::new_spinner();
479+
let pb = ProgressBar::new(total_items);
468480
pb.set_style(
469-
ProgressStyle::default_spinner()
470-
.template("{spinner:.green} {msg} [{elapsed_precise}]")
471-
.unwrap(),
481+
ProgressStyle::default_bar()
482+
.template("{spinner:.green} [{bar:40.cyan/blue}] {pos}/{len} files ({percent}%) [{elapsed_precise}] {msg}")
483+
.unwrap()
484+
.progress_chars("#>-"),
472485
);
473486
if verify_hashes {
474487
pb.set_message("Comparing and hashing files...");
475488
} else {
476489
pb.set_message("Comparing files...");
477490
}
478-
pb.enable_steady_tick(std::time::Duration::from_millis(100));
479491
Some(pb)
480492
} else {
481493
info!("Comparing directories...");
482494
None
483495
};
484496

485497
let comparison_engine = ComparisonEngine::new(hash_cache).with_hash_verification(verify_hashes);
486-
let diff_nodes = comparison_engine.compare_with_vfs(
487-
left_source.root(),
488-
right_source.root(),
489-
left_entries,
490-
right_entries,
491-
left_source.vfs(),
492-
right_source.vfs(),
493-
)?;
498+
499+
// Use progress callback if progress bar is enabled
500+
let diff_nodes = if let Some(ref pb) = pb_compare {
501+
let pb_clone = pb.clone();
502+
comparison_engine.compare_with_vfs_and_progress(
503+
left_source.root(),
504+
right_source.root(),
505+
left_entries,
506+
right_entries,
507+
left_source.vfs(),
508+
right_source.vfs(),
509+
None,
510+
Some(move |current, _total| {
511+
pb_clone.set_position(current as u64);
512+
}),
513+
)?
514+
} else {
515+
comparison_engine.compare_with_vfs(
516+
left_source.root(),
517+
right_source.root(),
518+
left_entries,
519+
right_entries,
520+
left_source.vfs(),
521+
right_source.vfs(),
522+
)?
523+
};
494524

495525
if let Some(pb) = &pb_compare {
496526
pb.finish_with_message(format!(

rcompare_core/src/comparison.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,37 @@ impl ComparisonEngine {
187187
right_vfs: Option<&dyn Vfs>,
188188
cancel: Option<&AtomicBool>,
189189
) -> Result<Vec<DiffNode>, RCompareError> {
190+
self.compare_with_vfs_and_progress::<fn(usize, usize)>(
191+
left_root,
192+
right_root,
193+
left_entries,
194+
right_entries,
195+
left_vfs,
196+
right_vfs,
197+
cancel,
198+
None,
199+
)
200+
}
201+
202+
/// Compare directory entries with VFS support, cancellation, and progress callback
203+
///
204+
/// # Arguments
205+
///
206+
/// * `progress_fn` - Optional callback function that receives (current, total) progress updates
207+
pub fn compare_with_vfs_and_progress<F>(
208+
&self,
209+
left_root: &Path,
210+
right_root: &Path,
211+
left_entries: Vec<FileEntry>,
212+
right_entries: Vec<FileEntry>,
213+
left_vfs: Option<&dyn Vfs>,
214+
right_vfs: Option<&dyn Vfs>,
215+
cancel: Option<&AtomicBool>,
216+
progress_fn: Option<F>,
217+
) -> Result<Vec<DiffNode>, RCompareError>
218+
where
219+
F: Fn(usize, usize),
220+
{
190221
info!(
191222
"Comparing {} left entries with {} right entries",
192223
left_entries.len(),
@@ -211,13 +242,20 @@ impl ComparisonEngine {
211242
all_paths.sort();
212243
all_paths.dedup();
213244

214-
for path in all_paths {
245+
let total = all_paths.len();
246+
247+
for (idx, path) in all_paths.into_iter().enumerate() {
215248
if cancel.is_some_and(|flag| flag.load(Ordering::Relaxed)) {
216249
return Err(RCompareError::Comparison(
217250
"Comparison cancelled".to_string(),
218251
));
219252
}
220253

254+
// Report progress
255+
if let Some(ref progress) = progress_fn {
256+
progress(idx + 1, total);
257+
}
258+
221259
let left = left_map.remove(&path);
222260
let right = right_map.remove(&path);
223261

0 commit comments

Comments
 (0)