file comparison improved

This commit is contained in:
Radek Davidek 2026-02-10 18:31:51 +01:00
parent b1b10769e2
commit f4cb0c4b1f

View File

@ -20,6 +20,8 @@ public class FileComparisonDialog extends JFrame {
private final AppConfig config;
private JTextPane textPane1;
private JTextPane textPane2;
private JTextArea lineNumbers1;
private JTextArea lineNumbers2;
private JScrollPane scroll1;
private JScrollPane scroll2;
private List<String> lines1 = new ArrayList<>();
@ -34,6 +36,7 @@ public class FileComparisonDialog extends JFrame {
try {
this.lines1 = readLines(file1);
this.lines2 = readLines(file2);
performSmartAlignment();
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "Error reading files: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
@ -66,6 +69,14 @@ public class FileComparisonDialog extends JFrame {
TextPaneListener listener1 = new TextPaneListener();
textPane1.addMouseListener(listener1);
scroll1 = new JScrollPane(textPane1);
lineNumbers1 = new JTextArea("1");
lineNumbers1.setEditable(false);
lineNumbers1.setBackground(Color.LIGHT_GRAY);
lineNumbers1.setForeground(Color.DARK_GRAY);
lineNumbers1.setFont(new Font("Monospaced", Font.PLAIN, 12));
lineNumbers1.setMargin(new Insets(0, 5, 0, 5));
scroll1.setRowHeaderView(lineNumbers1);
textPane2 = new JTextPane();
textPane2.setEditable(false);
@ -74,6 +85,14 @@ public class FileComparisonDialog extends JFrame {
textPane2.addMouseListener(listener2);
scroll2 = new JScrollPane(textPane2);
lineNumbers2 = new JTextArea("1");
lineNumbers2.setEditable(false);
lineNumbers2.setBackground(Color.LIGHT_GRAY);
lineNumbers2.setForeground(Color.DARK_GRAY);
lineNumbers2.setFont(new Font("Monospaced", Font.PLAIN, 12));
lineNumbers2.setMargin(new Insets(0, 5, 0, 5));
scroll2.setRowHeaderView(lineNumbers2);
// Synchronize scrolling
scroll1.getVerticalScrollBar().setModel(scroll2.getVerticalScrollBar().getModel());
@ -153,12 +172,12 @@ public class FileComparisonDialog extends JFrame {
if (line1 < line2) {
int diff = line2 - line1;
for (int i = 0; i < diff; i++) {
lines1.add(line1, "");
lines1.add(line1, null);
}
} else if (line2 < line1) {
int diff = line1 - line2;
for (int i = 0; i < diff; i++) {
lines2.add(line2, "");
lines2.add(line2, null);
}
}
updateDisplay(false);
@ -198,6 +217,15 @@ public class FileComparisonDialog extends JFrame {
textPane2.setForeground(dark ? Color.WHITE : Color.BLACK);
textPane1.setCaretColor(dark ? Color.WHITE : Color.BLACK);
textPane2.setCaretColor(dark ? Color.WHITE : Color.BLACK);
if (lineNumbers1 != null) {
lineNumbers1.setBackground(dark ? bg.brighter() : bg.darker());
lineNumbers1.setForeground(dark ? Color.LIGHT_GRAY : Color.DARK_GRAY);
}
if (lineNumbers2 != null) {
lineNumbers2.setBackground(dark ? bg.brighter() : bg.darker());
lineNumbers2.setForeground(dark ? Color.LIGHT_GRAY : Color.DARK_GRAY);
}
}
Font f = config.getGlobalFont();
if (f != null) {
@ -205,6 +233,8 @@ public class FileComparisonDialog extends JFrame {
Font mono = new Font("Monospaced", Font.PLAIN, f.getSize());
textPane1.setFont(mono);
textPane2.setFont(mono);
if (lineNumbers1 != null) lineNumbers1.setFont(mono);
if (lineNumbers2 != null) lineNumbers2.setFont(mono);
}
}
@ -222,6 +252,8 @@ public class FileComparisonDialog extends JFrame {
textPane2.setText("");
StyledDocument doc1 = textPane1.getStyledDocument();
StyledDocument doc2 = textPane2.getStyledDocument();
StringBuilder ln1 = new StringBuilder();
StringBuilder ln2 = new StringBuilder();
Style diffStyle = textPane1.addStyle("diff", null);
Color bg = textPane1.getBackground();
@ -230,16 +262,35 @@ public class FileComparisonDialog extends JFrame {
StyleConstants.setForeground(diffStyle, dark ? Color.WHITE : Color.BLACK);
int maxLines = Math.max(lines1.size(), lines2.size());
for (int i = 0; i < maxLines; i++) {
String l1 = i < lines1.size() ? lines1.get(i) : "";
String l2 = i < lines2.size() ? lines2.get(i) : "";
int counter1 = 1;
int counter2 = 1;
boolean different = !l1.equals(l2);
for (int i = 0; i < maxLines; i++) {
String l1 = i < lines1.size() ? lines1.get(i) : null;
String l2 = i < lines2.size() ? lines2.get(i) : null;
boolean different = (l1 == null && l2 != null) || (l1 != null && l2 == null) || (l1 != null && l2 != null && !l1.equals(l2));
Style s = different ? diffStyle : null;
doc1.insertString(doc1.getLength(), l1 + "\n", s);
doc2.insertString(doc2.getLength(), l2 + "\n", s);
if (l1 != null) {
doc1.insertString(doc1.getLength(), l1 + "\n", s);
ln1.append(counter1++).append("\n");
} else {
doc1.insertString(doc1.getLength(), "\n", s);
ln1.append("\n");
}
if (l2 != null) {
doc2.insertString(doc2.getLength(), l2 + "\n", s);
ln2.append(counter2++).append("\n");
} else {
doc2.insertString(doc2.getLength(), "\n", s);
ln2.append("\n");
}
}
if (lineNumbers1 != null) lineNumbers1.setText(ln1.toString());
if (lineNumbers2 != null) lineNumbers2.setText(ln2.toString());
if (resetCaret) {
textPane1.setCaretPosition(0);
@ -250,6 +301,60 @@ public class FileComparisonDialog extends JFrame {
}
}
private void performSmartAlignment() {
if (lines1.isEmpty() || lines2.isEmpty()) return;
// Skip alignment for very large files to avoid O(N^2) memory/time issues
if (lines1.size() > 2000 || lines2.size() > 2000) return;
int n = lines1.size();
int m = lines2.size();
int[][] dp = new int[n + 1][m + 1];
// LCS computation with similarity check (exact or trimmed match)
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
String s1 = lines1.get(i - 1);
String s2 = lines2.get(j - 1);
if (s1.equals(s2) || (!s1.trim().isEmpty() && s1.trim().equals(s2.trim()))) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
List<String> aligned1 = new ArrayList<>();
List<String> aligned2 = new ArrayList<>();
int i = n, j = m;
while (i > 0 || j > 0) {
if (i > 0 && j > 0) {
String s1 = lines1.get(i - 1);
String s2 = lines2.get(j - 1);
if (s1.equals(s2) || (!s1.trim().isEmpty() && s1.trim().equals(s2.trim()))) {
aligned1.add(0, s1);
aligned2.add(0, s2);
i--;
j--;
continue;
}
}
if (j > 0 && (i == 0 || dp[i][j - 1] >= dp[i - 1][j])) {
aligned1.add(0, null); // Added gap marker
aligned2.add(0, lines2.get(j - 1));
j--;
} else if (i > 0) {
aligned1.add(0, lines1.get(i - 1));
aligned2.add(0, null); // Added gap marker
i--;
}
}
this.lines1 = aligned1;
this.lines2 = aligned2;
}
private List<String> readLines(File f) throws IOException {
List<String> lines = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(f))) {