From f19f07ec59b307b2f89bd35251dd2837758faec1 Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Thu, 6 Oct 2011 02:06:51 +0200 Subject: [PATCH] Change the unidiff rendering by letting the actual content be rendered into a separate container that can overflow and side-scroll for long lines. This effectively removes the need for all kinds of line-breaking hacks that have been applied before and only worked when the browser was actually able to break a word group apart somewhere. Lines are now always rendered as-is; as a nice side effect the line numbers are always visible, independently how far one scrolled into one direction, so the context is always clear. If the rendering area is made smaller, the table rendering also degrades gracefully and provides horizontal scrolling for views that did not need them before. The size that is occupied by the number display is now also automatically determined by the size that is needed to render the biggest line number in a column. Empty columns are rendered with a zero size. Currently all this works nicely with a recent version of Chrome, Firefox still needs some fine tuning for the vertical positioning. Other browsers are untested as of now. --- src/IDF/Diff.php | 58 ++++++++++++++----- www/media/idf/css/style.css | 110 ++++++++++++++++++++++-------------- 2 files changed, 112 insertions(+), 56 deletions(-) diff --git a/src/IDF/Diff.php b/src/IDF/Diff.php index 501b2b1..8f10e7f 100644 --- a/src/IDF/Diff.php +++ b/src/IDF/Diff.php @@ -168,41 +168,73 @@ class IDF_Diff public function as_html() { $out = ''; - foreach ($this->files as $filename=>$file) { + foreach ($this->files as $filename => $file) { $pretty = ''; $fileinfo = IDF_FileUtil::getMimeType($filename); if (IDF_FileUtil::isSupportedExtension($fileinfo[2])) { $pretty = ' prettyprint'; } - $out .= "\n".''."\n"; - $out .= ''."\n"; + $cc = 1; + $offsets = array(); + $contents = array(); + $maxlinenum = 0; + foreach ($file['chunks'] as $chunk) { foreach ($chunk as $line) { - if ($line[0] and $line[1]) { + list($left, $right, $content) = $line; + if ($left and $right) { $class = 'diff diff-c'; - } elseif ($line[0]) { + } elseif ($left) { $class = 'diff diff-r'; } else { $class = 'diff diff-a'; } - $line_content = Pluf_esc($line[2]); - $line_content = preg_replace("/\t/", " ", $line_content); - $line_content = self::makeNonPrintableCharsVisible($line_content); - $out .= sprintf(''."\n", $line[0], $line[1], $class, $pretty, $line_content); + + $offsets[] = sprintf('', $left, $right); + $content = Pluf_esc($content); + $content = self::makeNonPrintableCharsVisible($content); + $contents[] = sprintf('', $class, $pretty, $content); + + $maxlinenum = max($maxlinenum, max($left, $right)); + } + if (count($file['chunks']) > $cc) { + $offsets[] = ''; + $contents[] = ''; } - if (count($file['chunks']) > $cc) - $out .= ''."\n"; $cc++; } - $out .= '
'.Pluf_esc($filename).'
%s%s%s
%s%s%s...... 
...... 
'; + + $inner = '' ."\n". + '' . + implode(''."\n".'', $contents) . + '' ."\n". + '
' ."\n"; + + $rows = count($offsets); + $colwidth = (ceil(log10($maxlinenum)) + 1) * 10; + $first = array_shift($offsets); + + $out .= '' ."\n". + '' ."\n". + ''. + ''. + '' ."\n". + '' . + $first . sprintf('', $rows, $inner) . + '' ."\n". + '' . + implode(''."\n".'', $offsets) . + '' ."\n". + '
'.Pluf_esc($filename).'
%s
' ."\n"; } + return Pluf_Template::markSafe($out); } private static function makeNonPrintableCharsVisible($line) { - return preg_replace('/([^[:print:]])/e', + return preg_replace('/([^[:print:]\t])/e', '"".bin2hex("\\1").""', $line); } diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css index 98f0b1f..08371e9 100644 --- a/www/media/idf/css/style.css +++ b/www/media/idf/css/style.css @@ -574,6 +574,7 @@ table.commit table.changes table.properties td.removed { table.diff { border-bottom: 1px solid #d3d7cf; width: 100%; + table-layout: fixed; } table.diff th { @@ -583,45 +584,63 @@ table.diff th { } -table.diff tr { - border-left: 1px solid #d3d7cf; - border-right: 1px solid #d3d7cf; - border-bottom: none; - border-top: none; +table.diff tr.line { + border: 1px solid #d3d7cf; +} + +table.diff tr.line td.diff-lc { + font-size: 90%; + padding: 1px 10px; + text-align: right; + width: 20px; +} + +table.diff tr.line div.diff-content { + overflow: auto; + display: block; } table.diff td { - font-size: 90%; vertical-align: top; - padding: 1px; border-color: inherit; + padding: 0; } -table.diff td.diff-lc { +table.diff td.next { + background-color: #e4e8E0; + vertical-align: top; text-align: right; - padding: 1px 5px; - border-color: inherit; - border-top: 1px solid #d3d7cf; - border-bottom: 1px solid #d3d7cf; - width: 3em; + border-color: #d3d7cf; + padding: 1px 10px; } -td.diff-a { +table.diff-content { + border: 0; + /* setting this to 100% sometimes triggers the overflow of the parent container, + when it is not actually needed. we try to prevent that by taking not the + complete available space... */ + width: 99.99%; + margin: 0; +} + +table.diff-content td.diff { + line-height: 12px; + padding: 2px; + font-size: 90%; + border: none; + white-space: pre; +} + +table.diff-content td.diff-a { background-color: #dfd; } -td.diff-r { +table.diff-content td.diff-r { background-color: #fdd; } -td.diff { - border-bottom: none; - border-top: none; - white-space: pre; -} - -td.diff > span.non-printable { - visibility: hidden; +table.diff-content td.diff > span.non-printable { + visibility: hidden; color: white; text-transform: uppercase; float: none; @@ -638,39 +657,44 @@ td.diff > span.non-printable { vertical-align: 10%; } -td.diff:hover > span.non-printable { - visibility: visible; +table.diff-content td.diff:hover > span.non-printable { + visibility: visible; } -td.diff-a > span.non-printable { - background: #0A0; +table.diff-content td.diff-a > span.non-printable { + background: #0A0; } -td.diff-r > span.non-printable { - background: #A00; +table.diff-content td.diff-r > span.non-printable { + background: #A00; } -td.diff-c > span.non-printable { - background: black; +table.diff-content td.diff-c > span.non-printable { + background: black; } /* override prettify css rule */ -td.diff > span.non-printable > * { - color: white; +table.diff-content td.diff > span.non-printable > * { + color: white; } -table.diff tr.diff-next { - background-color: #e4e8E0; - vertical-align: top; - text-align: right; - border-color: #d3d7cf; +/* + This is a special hack: the outer td.next has + top/bottom border and padding and comes to a total + height of 20px - BUT it shares the upper border + with the previous row, so the height is actually + only 19px. The inner table has no lines between rows, + so the upper border is counted and we have 20px + in the interior, which is one pixel too much for + every occurrence. + What we now do is to remove the 1px top padding and + therefor lower the total height of the inner one + again by one to match the outer. +*/ +table.diff-content td.next { + padding-top: 0; } -table.diff tr.diff-next td { - padding: 1px 5px; -} - - /** * view file content */