8.8 KiB
8.8 KiB
Diff Viewer - Visualizzazione Side-by-Side delle Differenze
Descrizione
Il Diff Viewer è una nuova funzionalità che permette di visualizzare le differenze tra due versioni di un file in modo affiancato, con evidenziazione delle modifiche e una minimappa per navigare rapidamente tra le differenze.
Come Utilizzare
Accesso dalla GUI
- Esegui un'operazione di Differing dalla GUI principale
- Nella tabella dei risultati, doppio-click su qualsiasi riga per aprire il Diff Viewer
- La finestra mostrerà il file della baseline (a sinistra) e il file corrente (a destra) affiancati
Funzionalità
Visualizzazione Affiancata
- Colonna Sinistra: File dalla baseline (versione precedente)
- Colonna Centrale: Minimappa con rappresentazione visiva delle differenze
- Colonna Destra: File corrente (versione attuale)
Evidenziazione delle Differenze
- 🔴 Rosso: Linee cancellate (presenti solo nella baseline)
- 🟢 Verde: Linee aggiunte (presenti solo nella versione corrente)
- 🟡 Giallo: Linee modificate (diverse tra baseline e corrente)
- ⚪ Bianco: Linee uguali (nessuna modifica)
Numeri di Riga
- Ogni riga mostra il numero di linea originale
- Simboli speciali:
#### |= Linea uguale#### -= Linea cancellata#### += Linea aggiunta#### ~= Linea modificata~= Linea vuota (per allineamento)
Minimappa
- Visualizzazione compatta di tutte le differenze
- I colori corrispondono al tipo di modifica
- Click sulla minimappa per navigare rapidamente a una sezione specifica
- Il rettangolo blu indica la porzione attualmente visibile
Navigazione
- Scroll sincronizzato: entrambi i file scorrono insieme
- Mouse wheel: scorri verticalmente
- Frecce su/giù: navigazione linea per linea
- Scrollbar condivisa: controllo centralizzato dello scroll verticale
- Scrollbar orizzontali: separate per ogni file
Informazioni
- Barra info in basso:
- Numero di linee: baseline → corrente
- Conteggio linee: aggiunte, cancellate, modificate
Casi d'Uso
File Modificato
Baseline (test.py) Current (test.py)
------------------ -----------------
1 | def hello(): 1 | def hello():
2 | ~ print("Hi") 2 + ~ # nuovo
3 | ~ return 42 3 + ~ print("Hi")
4 | ~ return 42
File Cancellato
- Baseline: mostra il contenuto del file
- Current: mostra "N/A" (file non esiste più)
File Aggiunto
- Baseline: mostra "N/A" (file non esisteva)
- Current: mostra il contenuto del nuovo file
Architettura Tecnica
File: pyucc/gui/diff_viewer.py
Classe DiffViewer(tk.Toplevel)
Finestra modale per la visualizzazione delle differenze.
Parametri del costruttore:
parent: Widget parent (finestra principale)file_a_path: Path del file baseline (può essere None)file_b_path: Path del file corrente (può essere None)title_a: Titolo per il pannello sinistro (default: "Baseline")title_b: Titolo per il pannello destro (default: "Current")
Metodi principali:
_load_file(filepath): Carica il contenuto di un file_compute_diff_blocks(): Calcola i blocchi di differenze usandodifflib.SequenceMatcher_build_ui(): Costruisce l'interfaccia utente_populate_content(): Popola i text widget con evidenziazione_draw_minimap(): Disegna la minimappa delle differenze_setup_scroll_sync(): Configura la sincronizzazione dello scroll
Funzione Helper show_diff_viewer()
Funzione di convenienza per aprire il diff viewer.
from pyucc.gui.diff_viewer import show_diff_viewer
show_diff_viewer(parent, file_a_path, file_b_path,
title_a="Baseline", title_b="Current")
Integrazione con GUI Principale
File: pyucc/gui/gui.py
Modifiche apportate:
-
Tracking della modalità corrente (
_current_mode):'scan': Modalità scanning'countings': Modalità conteggi'metrics': Modalità metriche'differ': Modalità differ (abilita double-click)
-
Memorizzazione dei path root:
_differ_baseline_root: Path root della baseline_differ_current_root: Path root del progetto corrente
-
Event handler per double-click:
def _on_results_double_click(self, event): # Verifica modalità differ # Ottiene file dalla riga selezionata # Costruisce path completi # Apre DiffViewer -
Binding:
self.results_tree.bind('<Double-Button-1>', self._on_results_double_click)
Algoritmo di Diff
Il diff viewer utilizza difflib.SequenceMatcher di Python per calcolare le differenze:
- Caricamento file: Legge entrambi i file in liste di linee
- Calcolo differenze:
SequenceMatcher.get_opcodes()restituisce blocchi:'equal': Blocco di linee uguali'delete': Linee presenti solo in A (baseline)'insert': Linee presenti solo in B (corrente)'replace': Linee diverse tra A e B
- Rendering: Ogni blocco viene colorato appropriatamente
- Allineamento: Linee vuote (
~) mantengono l'allineamento visivo
Limitazioni e Note
Limitazioni Attuali
- ⚠️ La finestra è modale (blocca interazione con finestra principale)
- ⚠️ Non supporta diff a livello di carattere (solo linee intere)
- ⚠️ File molto grandi (>10k linee) potrebbero rallentare l'interfaccia
- ⚠️ Encoding: assume UTF-8 con fallback a ignore errors
Gestione Errori
- Se un file non esiste, viene mostrato come vuoto
- Se entrambi i file non esistono, viene mostrato un warning
- Errori di lettura file vengono gestiti silenziosamente
Performance
- Ottimizzato per file fino a ~5000 linee
- La minimappa usa rendering canvas nativo (veloce)
- Scroll sincronizzato con evento batching
Estensioni Future Possibili
- Diff inline: Evidenziare caratteri specifici modificati dentro una linea
- Navigazione tra differenze: Bottoni "Previous/Next Difference"
- Merge tool: Permettere di selezionare quale versione tenere
- Export diff: Salvare il diff in formato unified o patch
- Fold equal blocks: Nascondere blocchi di linee uguali lunghi
- Syntax highlighting: Colorazione sintassi del linguaggio
- Line numbers toggle: Nascondere/mostrare numeri di riga
- Search: Cercare testo all'interno del diff
Test
Per testare manualmente il diff viewer:
python tests/test_diff_viewer_manual.py
Questo script crea due file temporanei con differenze e apre il diff viewer.
Esempio di Utilizzo Programmatico
import tkinter as tk
from pyucc.gui.diff_viewer import show_diff_viewer
# Crea finestra principale
root = tk.Tk()
root.withdraw()
# Apri diff viewer
show_diff_viewer(
root,
file_a_path="/path/to/baseline/file.py",
file_b_path="/path/to/current/file.py",
title_a="Version 1.0",
title_b="Version 2.0"
)
root.mainloop()
Screenshot delle Funzionalità
Layout Principale
┌─────────────────────────────────────────────────────────────┐
│ Baseline: file.py Diff Map Current: file.py │
├───────────────────────────┬───────┬─────────────────────────┤
│ │ │ │
│ 1 | def hello(): │ ████ │ 1 | def hello(): │
│ 2 | ~ print("Hi") │ ████ │ 2 + ~ # comment │
│ 3 | ~ return 42 │ ████ │ 3 + ~ print("Hi") │
│ │ ████ │ 4 | ~ return 42 │
│ 4 | def goodbye(): │ ──── │ 5 | def goodbye(): │
│ 5 | ~ print("Bye") │ ──── │ 6 | ~ print("Bye") │
│ │ ████ │ 7 + def new(): │
│ │ ████ │ 8 + ~ pass │
│ │ │ │
├───────────────────────────┴───────┴─────────────────────────┤
│ Lines: 5 → 8 | Added: 3 Deleted: 0 Modified: 2 [Close]│
└─────────────────────────────────────────────────────────────┘
Legenda colori:
████Rosso = Linee cancellate████Verde = Linee aggiunte████Giallo = Linee modificate────Grigio = Linee uguali