SXXXXXXX_PyUCC/doc/DIFF_VIEWER.md

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

  1. Esegui un'operazione di Differing dalla GUI principale
  2. Nella tabella dei risultati, doppio-click su qualsiasi riga per aprire il Diff Viewer
  3. 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 usando difflib.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:

  1. Tracking della modalità corrente (_current_mode):

    • 'scan': Modalità scanning
    • 'countings': Modalità conteggi
    • 'metrics': Modalità metriche
    • 'differ': Modalità differ (abilita double-click)
  2. Memorizzazione dei path root:

    • _differ_baseline_root: Path root della baseline
    • _differ_current_root: Path root del progetto corrente
  3. 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
    
  4. 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:

  1. Caricamento file: Legge entrambi i file in liste di linee
  2. 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
  3. Rendering: Ogni blocco viene colorato appropriatamente
  4. 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

  1. Diff inline: Evidenziare caratteri specifici modificati dentro una linea
  2. Navigazione tra differenze: Bottoni "Previous/Next Difference"
  3. Merge tool: Permettere di selezionare quale versione tenere
  4. Export diff: Salvare il diff in formato unified o patch
  5. Fold equal blocks: Nascondere blocchi di linee uguali lunghi
  6. Syntax highlighting: Colorazione sintassi del linguaggio
  7. Line numbers toggle: Nascondere/mostrare numeri di riga
  8. 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