login/commits: syntax highlighting of diff lines
[minimedit.git] / login / commits / index.php
1 <?php
2 if (!$User->admin('edit')) {
3         require '403.inc.html';
4         return;
5 }
6
7 $hash = ltrim($Page->path, '/');
8 if (!$hash) {
9         return TRUE;
10 }
11
12 $this->title = "Wijzigingen in ".strtoupper($hash);
13 print "<h2>{$this->title}</h2>\n";
14
15 $gitcmd = "git show "
16         . "--word-diff=porcelain --no-prefix --pretty='%H%n%at\t%an\t%w(0,0,1)%B' "
17         . escapeshellarg($hash);
18 $log = popen($gitcmd, 'r');
19 if (!$log or strpos(fgets($log), $hash) !== 0) {
20         $Page->place['warn'] = "Kon inhoud niet ophalen met <code>$gitcmd</code>";
21         return;
22 }
23
24 # read metadata and commit message
25 list ($atime, $author, $msg) = explode("\t", fgets($log), 3);
26 while ( $line = fgets($log) ) {
27         if ($line == "\n") {
28                 fgets($log); // assume another empty line
29                 break;
30         }
31         $msg .= substr($line, 1);
32 }
33
34 # commit head
35 print '<p>';
36 printf('<small class="date">%s • %s</small>',
37         htmlspecialchars($author), strftime('%F %H:%M', $atime)
38 );
39 print "\n".nl2br(htmlspecialchars($msg));
40 print "</p>\n";
41
42 print '<style>
43 .diff {
44         white-space: pre-wrap;
45         white-space: break-spaces;
46         font-family: monospace;
47 }
48 td:first-of-type.change,
49 del {
50         background: #F002;
51         text-decoration: none;
52 }
53 td:last-of-type.change,
54 ins {
55         background: #0F02;
56         text-decoration: none;
57 }
58 .diff .head {
59         background: #00F2;
60 }
61 </style>';
62
63 # body
64 $row = $ln = NULL;
65 $col = ['', ''];
66 print '<table class="diff">';
67 while ( $line = fgets($log) ) {
68         preg_match(isset($ln) ? '/^(\W|\S+ )(.*)/' : '/^(\S+ )(.*)/', $line, $part);
69         $body = htmlspecialchars($part[2]);
70         switch ($part[1]) {
71         case 'diff ':
72                 # file start
73                 if (preg_match('/^--git (.+) (.*)/', $part[2], $diffhead)) {
74                         $row = "$diffhead[1]";
75                         if ($diffhead[1] !== $diffhead[2]) {
76                                 $row .= " → $diffhead[2]";
77                         }
78                 }
79                 else {
80                         $row = '?';
81                 }
82                 $ln = NULL;
83                 break;
84         case '@@ ':
85                 # chunk start
86                 if (preg_match('/^[-](\d+)(?:,\d+)? [+](\d+)(?:,\d+)? @@/', $part[2], $diffstart)) {
87                         array_shift($diffstart);
88                         $ln = $diffstart;
89                 }
90                 else {
91                         $ln = ['?', '?']; # unrecognised diff header
92                 }
93                 print '<tr class="head"><td colspan="4">'.$row.'</tr>'."\n";
94                 break;
95         case '-':
96                 $col[0] .= "<del>$body</del>";
97                 break;
98         case '+':
99                 $col[1] .= "<ins>$body</ins>";
100                 break;
101         case ' ':
102                 $col[0] .= $body;
103                 $col[1] .= $body;
104                 break;
105         case '~':
106                 # part end
107                 print '<tr>';
108                 foreach ($col as $i => $line) {
109                         if (empty($line)) {
110                                 print '<th><td>';
111                                 continue;
112                         }
113                         printf('<th>%s</th><td%s>%s</td>',
114                                 $ln[$i]++,
115                                 $col[0] == $col[1] ? '' : ' class="change"',
116                                 $line
117                         );
118                 }
119                 print "</tr>\n";
120                 $col = ['', ''];
121                 break;
122         }
123 }
124 print "</table>\n";
125 pclose($log);
126
127 return;