Added a better commit diff.

This commit is contained in:
Loic d'Anterroches 2008-08-01 00:38:29 +02:00
parent cb15f036fe
commit 80ce87009e
5 changed files with 262 additions and 19 deletions

140
src/IDF/Diff.php Normal file
View File

@ -0,0 +1,140 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Diff parser.
*
*/
class IDF_Diff
{
public $repo = '';
public $diff = '';
protected $lines = array();
public $files = array();
public function __construct($diff, $repo='')
{
$this->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".'<table class="diff" summary="">'."\n";
$out .= '<tr id="diff-'.md5($filename).'"><th colspan="3">'.Pluf_esc($filename).'</th></tr>'."\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('<tr class="diff-line"><td class="diff-lc">%s</td><td class="diff-lc">%s</td><td class="%s mono">%s</td></tr>'."\n", $line[0], $line[1], $class, Pluf_esc($line[2]));
}
if (count($file['chunks']) > $cc)
$out .= '<tr class="diff-next"><td>...</td><td>...</td><td>&nbsp;</td></tr>'."\n";
$cc++;
}
$out .= '</table>';
}
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;
}
}

View File

@ -0,0 +1,57 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Test the diff parser.
*/
class IDF_Tests_TestDiff extends UnitTestCase
{
public function __construct()
{
parent::__construct('Test the diff parser.');
}
public function testGetFile()
{
$lines = array(
'diff --git a/src/IDF/Form/Register.php b/src/IDF/Form/Register.php',
'diff --git a/src/IDF/Form/RegisterConfirmation.php b/src/IDF/Form/RegisterConfirmation.php',
'diff --git a/src/IDF/Form/RegisterInputKey.php b/src/IDF/Form/RegisterInputKey.php',
'diff --git a/src/IDF/Views.php b/src/IDF/Views.php',
'diff --git a/src/IDF/conf/views.php b/src/IDF/conf/views.php',
);
$files = array(
'src/IDF/Form/Register.php',
'src/IDF/Form/RegisterConfirmation.php',
'src/IDF/Form/RegisterInputKey.php',
'src/IDF/Views.php',
'src/IDF/conf/views.php',
);
$i = 0;
foreach ($lines as $line) {
$this->assertEqual($files[$i], IDF_Diff::getFile($line));
$i++;
}
}
}

View File

@ -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,

View File

@ -17,9 +17,20 @@
<tr>
<th><strong>{trans 'Message:'}</strong></th><td>{issuetext $cobject.title, $project}{if isset($cobject.full_message)}<br /><br />{issuetext $cobject.full_message, $project}{/if}</td>
</tr>
<tr>
<th><strong>{trans 'Files:'}</strong></th>
<td>
{foreach $diff.files as $filename=>$diffdef}
{assign $ndiff = count($diffdef['chunks'])}
<a href="#diff-{$filename|md5}">{$filename}</a> (<a href="#diff-{$filename|md5}">{blocktrans $ndiff}{$ndiff} diff{plural}{$ndiff} diffs{/blocktrans}</a>)<br />
{/foreach}
</td>
</tr>
</table>
<h2>{trans 'Change Details'}</h2>
{$changes|safe}
{$diff.as_html()|safe}
{/block}
{block context}
<p><strong>{trans 'Branches:'}</strong><br />

View File

@ -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%;
}
span.hl-string {
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;
}
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;
}