diff --git a/src/IDF/Diff.php b/src/IDF/Diff.php new file mode 100644 index 0000000..ca5f25f --- /dev/null +++ b/src/IDF/Diff.php @@ -0,0 +1,140 @@ +repo = $repo; + $this->diff = $diff; + $this->lines = preg_split("/\015\012|\015|\012/", $diff); + } + + public function parse() + { + $current_file = ''; + $current_chunk = 0; + $lline = 0; + $rline = 0; + foreach ($this->lines as $line) { + if (0 === strpos($line, 'diff --git a')) { + $current_file = self::getFile($line); + $files[$current_file] = array(); + $files[$current_file]['chunks'] = array(); + $files[$current_file]['chunks_def'] = array(); + $current_chunk = 0; + continue; + } + if (0 === strpos($line, '@@ ')) { + $files[$current_file]['chunks_def'][] = self::getChunk($line); + $files[$current_file]['chunks'][] = array(); + $current_chunk++; + $lline = $files[$current_file]['chunks_def'][$current_chunk-1][0][0]; + $rline = $files[$current_file]['chunks_def'][$current_chunk-1][1][0]; + continue; + } + if (0 === strpos($line, '---') or 0 === strpos($line, '+++')) { + continue; + } + if (0 === strpos($line, '-')) { + $files[$current_file]['chunks'][$current_chunk-1][] = array($lline, '', substr($line, 1)); + $lline++; + continue; + } + if (0 === strpos($line, '+')) { + $files[$current_file]['chunks'][$current_chunk-1][] = array('', $rline, substr($line, 1)); + $rline++; + continue; + } + if (0 === strpos($line, ' ')) { + $files[$current_file]['chunks'][$current_chunk-1][] = array($lline, $rline, substr($line, 1)); + $rline++; + $lline++; + continue; + } + } + $this->files = $files; + return $files; + } + + public static function getFile($line) + { + $line = substr(trim($line), 10); + $n = (int) strlen($line)/2; + return trim(substr($line, 3, $n-3)); + } + + /** + * Return the html version of a parsed diff. + */ + public function as_html() + { + $out = ''; + foreach ($this->files as $filename=>$file) { + $out .= "\n".''."\n"; + $out .= ''."\n"; + $cc = 1; + foreach ($file['chunks'] as $chunk) { + foreach ($chunk as $line) { + if ($line[0] and $line[1]) { + $class = 'diff-c'; + } elseif ($line[0]) { + $class = 'diff-r'; + } else { + $class = 'diff-a'; + } + $out .= sprintf(''."\n", $line[0], $line[1], $class, Pluf_esc($line[2])); + } + if (count($file['chunks']) > $cc) + $out .= ''."\n"; + $cc++; + } + $out .= '
'.Pluf_esc($filename).'
%s%s%s
...... 
'; + } + return $out; + } + + /** + * @return array array(array(start, n), array(start, n)) + */ + public static function getChunk($line) + { + $elts = split(' ', $line); + $res = array(); + for ($i=1;$i<3;$i++) { + $res[] = split(',', trim(substr($elts[$i], 1))); + } + return $res; + } + +} \ No newline at end of file diff --git a/src/IDF/Tests/TestDiff.php b/src/IDF/Tests/TestDiff.php new file mode 100644 index 0000000..06f07d3 --- /dev/null +++ b/src/IDF/Tests/TestDiff.php @@ -0,0 +1,57 @@ +assertEqual($files[$i], IDF_Diff::getFile($line)); + $i++; + } + } +} \ No newline at end of file diff --git a/src/IDF/Views/Source.php b/src/IDF/Views/Source.php index 67628f4..ce7188d 100644 --- a/src/IDF/Views/Source.php +++ b/src/IDF/Views/Source.php @@ -162,15 +162,13 @@ class IDF_Views_Source $title = sprintf('%s Commit Details', (string) $request->project); $page_title = sprintf('%s Commit Details - %s', (string) $request->project, $commit); $cobject = $git->getCommit($commit); - require_once 'Text/Highlighter.php'; - $th = new Text_Highlighter(); - $h = $th->factory('DIFF'); - $changes = $h->highlight($cobject->changes); + $diff = new IDF_Diff($cobject->changes); + $diff->parse(); return Pluf_Shortcuts_RenderToResponse('source/commit.html', array( 'page_title' => $page_title, 'title' => $title, - 'changes' => $changes, + 'diff' => $diff, 'cobject' => $cobject, 'commit' => $commit, 'branches' => $branches, diff --git a/src/IDF/templates/source/commit.html b/src/IDF/templates/source/commit.html index d9be9e6..7cc9f48 100644 --- a/src/IDF/templates/source/commit.html +++ b/src/IDF/templates/source/commit.html @@ -17,9 +17,20 @@ {trans 'Message:'}{issuetext $cobject.title, $project}{if isset($cobject.full_message)}

{issuetext $cobject.full_message, $project}{/if} + +{trans 'Files:'} + +{foreach $diff.files as $filename=>$diffdef} +{assign $ndiff = count($diffdef['chunks'])} +{$filename} ({blocktrans $ndiff}{$ndiff} diff{plural}{$ndiff} diffs{/blocktrans})
+{/foreach} + +

{trans 'Change Details'}

-{$changes|safe} + +{$diff.as_html()|safe} + {/block} {block context}

{trans 'Branches:'}
diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css index e919868..f8c7563 100644 --- a/www/media/idf/css/style.css +++ b/www/media/idf/css/style.css @@ -386,24 +386,61 @@ table.commit td, table.commit th { /** * syntax highlighting of diffs */ -div.hl-main { - font-family: monospace; - white-space: pre; - border: 1px solid #d3d7cf; - padding: 1em; +table.diff { + border-bottom: 1px solid #d3d7cf; + width: 100%; +} + +table.diff th { + background-color: #e4e8E0; + vertical-align: top; + border-color: #d3d7cf; +} + + +table.diff tr { + border-left: 1px solid #d3d7cf; + border-right: 1px solid #d3d7cf; + border-bottom: none; + border-top: none; +} + +table.diff td { + font-size: 90%; + vertical-align: top; + padding: 1px; + border-color: inherit; +} + +table.diff td.diff-lc { + text-align: right; + padding: 1px 5px; + border-color: inherit; + border-top: 1px solid #d3d7cf; + border-bottom: 1px solid #d3d7cf; + width: 3em; } -span.hl-string { + +td.diff-a { background-color: #dfd; } -span.hl-quotes { + +td.diff-r { background-color: #fdd; } -span.hl-reserved, span.hl-var { - font-weight: bold; + +td.diff-a, td.diff-r, td.diff-c { + border-bottom: none; + border-top: none; } -span.hl-default { - color: #888a85; + +table.diff tr.diff-next { + background-color: #e4e8E0; + vertical-align: top; + text-align: right; + border-color: #d3d7cf; } -div.hl-main span { - line-height: 1.3; + +table.diff tr.diff-next td { + padding: 1px 5px; }