diff --git a/example/dark-theme.css b/example/dark-theme.css index 77669d2..175b075 100644 --- a/example/dark-theme.css +++ b/example/dark-theme.css @@ -6,8 +6,8 @@ body { } pre { - width: 100%; overflow: auto; + width: 100%; } a, a:visited { @@ -19,35 +19,35 @@ a, a:visited { */ .Differences { - width: 100%; border-collapse: collapse; border-spacing: 0; empty-cells: show; + width: 100%; } .Differences thead th { - text-align: left; - border-bottom: 1px solid #000000; background: #AAAAAA; + border-bottom: 1px solid #000000; color: #000000; padding: 4px; + text-align: left; } .Differences tbody th { - text-align: right; background: #AAAAAA; + border-right: 1px solid #000000; color: #272822; - width: 4em; + font-size: 13px; padding: 1px 2px; - border-right: 1px solid #000000; + text-align: right; vertical-align: top; - font-size: 13px; + width: 4em; } .Differences td { - padding: 1px 2px; font-family: Consolas, monospace; font-size: 13px; + padding: 1px 2px; } .Differences .Skipped { @@ -65,7 +65,7 @@ a, a:visited { * HTML Side by Side Diff */ .DifferencesSideBySide .ChangeInsert td.Left { - background: #008000; + background: #DDFFDD; } .DifferencesSideBySide .ChangeInsert td.Right { @@ -83,7 +83,7 @@ a, a:visited { } .DifferencesSideBySide .ChangeReplace .Left { - background: #FFEE99; + background: #FFDD88; color: #272822; } @@ -114,10 +114,12 @@ a, a:visited { .DifferencesUnified .ChangeReplace .Right, .DifferencesUnified .ChangeInsert .Right { background: #DDFFDD; + color: #272822; } .DifferencesUnified .ChangeReplace ins { background: #008000; + color: #272822; } .DifferencesUnified .ChangeReplace del { @@ -128,13 +130,16 @@ a, a:visited { /* * HTML Merged Diff */ -.DifferencesMerged .ChangeReplace .Left, +.DifferencesMerged td.ChangeReplace { + background: #FFDD88; + color: #272822; +} + .DifferencesMerged .ChangeDelete { background: #FFDDDD; color: #272822; } -.DifferencesMerged .ChangeReplace .Right, .DifferencesMerged .ChangeInsert { background: #DDFFDD; color: #272822; @@ -153,3 +158,7 @@ a, a:visited { .DifferencesMerged th.ChangeDelete { background-image: linear-gradient(-45deg, #AAAAAA 0%, #EE9999 100%); } + +.DifferencesMerged th.ChangeReplace { + background-image: linear-gradient(-45deg, #CCCCCC 0%, #FFDD88 100%); +} diff --git a/example/styles.css b/example/styles.css index bbf2ba7..5dd6abe 100644 --- a/example/styles.css +++ b/example/styles.css @@ -6,8 +6,8 @@ body { } pre { - width: 100%; overflow: auto; + width: 100%; } /* @@ -15,34 +15,35 @@ pre { */ .Differences { - width: 100%; border-collapse: collapse; border-spacing: 0; empty-cells: show; + width: 100%; } .Differences thead th { - text-align: left; - border-bottom: 1px solid #000000; background: #AAAAAA; + border-bottom: 1px solid #000000; color: #000000; padding: 4px; + text-align: left; } .Differences tbody th { - text-align: right; background: #CCCCCC; - width: 4em; - padding: 1px 2px; border-right: 1px solid #000000; - vertical-align: top; font-size: 13px; + padding: 1px 2px; + text-align: right; + vertical-align: top; + width: 4em; } .Differences td { - padding: 1px 2px; - font-family: Consolas, monospace; - font-size: 13px; + font-family: Consolas, monospace; + font-size: 13px; + padding: 1px 2px; + vertical-align: top; } .Differences .Skipped { @@ -111,12 +112,14 @@ pre { /* * HTML Merged Diff */ -.DifferencesMerged .ChangeReplace .Left, +.DifferencesMerged td.ChangeReplace { + background: #FFDD88; +} + .DifferencesMerged .ChangeDelete { background: #FFDDDD; } -.DifferencesMerged .ChangeReplace .Right, .DifferencesMerged .ChangeInsert { background: #DDFFDD; } @@ -132,3 +135,7 @@ pre { .DifferencesMerged th.ChangeDelete { background-image: linear-gradient(-45deg, #CCCCCC 0%, #EE9999 100%); } + +.DifferencesMerged th.ChangeReplace { + background-image: linear-gradient(-45deg, #CCCCCC 0%, #FFDD88 100%); +} diff --git a/lib/jblond/Diff/Renderer/Html/Merged.php b/lib/jblond/Diff/Renderer/Html/Merged.php index ab75577..3ccfd04 100644 --- a/lib/jblond/Diff/Renderer/Html/Merged.php +++ b/lib/jblond/Diff/Renderer/Html/Merged.php @@ -112,7 +112,7 @@ public function generateSkippedLines(): string return << - $marker + $marker … HTML; @@ -125,18 +125,19 @@ public function generateSkippedLines(): string */ public function generateLinesEqual(array $changes): string { - $html = ''; - $headerClass = ''; + $html = ''; foreach ($changes['base']['lines'] as $lineNo => $line) { - $fromLine = $changes['base']['offset'] + $lineNo + 1 + $this->lineOffset; + $fromLine = $changes['base']['offset'] + $lineNo + 1 + $this->lineOffset; + $headerClass = ''; + if (!$lineNo && $this->lastDeleted !== null) { $headerClass = 'ChangeDelete'; } $html .= << - $fromLine + $fromLine $line HTML; @@ -153,19 +154,19 @@ public function generateLinesEqual(array $changes): string */ public function generateLinesInsert(array $changes): string { - $html = ''; - $headerClass = ''; + $html = ''; foreach ($changes['changed']['lines'] as $lineNo => $line) { $this->lineOffset++; - $toLine = $changes['base']['offset'] + $this->lineOffset; + $toLine = $changes['base']['offset'] + $this->lineOffset; + $headerClass = ''; if (!$lineNo && $this->lastDeleted !== null) { $headerClass = 'ChangeDelete'; } $html .= << - $toLine + $toLine $line HTML; @@ -185,7 +186,7 @@ public function generateLinesDelete(array $changes): string { $this->lineOffset -= count($changes['base']['lines']); - $title = "Lines deleted at {$this->options['title2']}:\n"; + $title = "Lines of {$this->options['title1']} deleted at {$this->options['title2']}:\n"; foreach ($changes['base']['lines'] as $lineNo => $line) { $fromLine = $changes['base']['offset'] + $lineNo + 1; @@ -196,7 +197,7 @@ public function generateLinesDelete(array $changes): string TEXT; } - $this->lastDeleted = $title; + $this->lastDeleted = htmlentities($title); return ''; } @@ -208,41 +209,68 @@ public function generateLinesDelete(array $changes): string */ public function generateLinesReplace(array $changes): string { - $html = ''; - $headerClass = ''; - - foreach ($changes['base']['lines'] as $lineNo => $line) { - $fromLine = $changes['base']['offset'] + $lineNo + 1 + $this->lineOffset; - if (!$lineNo && $this->lastDeleted !== null) { - $headerClass = 'ChangeDelete'; + $html = ''; + $baseLineCount = count($changes['base']['lines']); + $changedLineCount = count($changes['changed']['lines']); + + if (count($changes['base']['lines']) == $changedLineCount) { + // Lines of Version 1 are modified at version 2. + foreach ($changes['base']['lines'] as $lineNo => $line) { + $fromLine = $changes['base']['offset'] + $lineNo + 1 + $this->lineOffset; + + // Capture line-parts which are added to the same line at version 2. + $addedParts = []; + preg_match_all('/\x0.*?\x1/', $changes['changed']['lines'][$lineNo], $addedParts, PREG_PATTERN_ORDER); + array_unshift($addedParts[0], ''); + + // Inline Replacement: + // Concatenate line-parts which are removed at version2 with line-parts which are added at version 2. + $line = preg_replace_callback( + '/\x0.*?\x1/', + function ($removedParts) use ($addedParts) { + $addedPart = str_replace(["\0", "\1"], $this->options['insertMarkers'], next($addedParts[0])); + $removedPart = str_replace(["\0", "\1"], $this->options['deleteMarkers'], $removedParts[0]); + + return "$removedPart$addedPart"; + }, + $line + ); + + $html .= << + $fromLine + $line + +HTML; } - // Capture added parts. - $addedParts = []; - preg_match_all('/\x0.*?\x1/', $changes['changed']['lines'][$lineNo], $addedParts, PREG_PATTERN_ORDER); - array_unshift($addedParts[0], ''); + return $html; + } - // Concatenate removed parts with added parts. - $line = preg_replace_callback( - '/\x0.*?\x1/', - function ($removedParts) use ($addedParts) { - $addedPart = str_replace(["\0", "\1"], $this->options['insertMarkers'], next($addedParts[0])); - $removedPart = str_replace(["\0", "\1"], $this->options['deleteMarkers'], $removedParts[0]); + // More or less lines at version 2. Block of version 1 is replaced by block of version 2. + $title = ''; - return "$removedPart$addedPart"; - }, - $line - ); + foreach ($changes['changed']['lines'] as $lineNo => $line) { + $toLine = $changes['changed']['offset'] + $lineNo + 1; - $html .= << $baseLine) { + $title .= $changes['base']['offset'] + $baseLineNo + 1 . ": $baseLine\n"; + } + } + + $title = htmlentities($title); + $html .= << - $fromLine - $line + $toLine + $line HTML; - $this->lastDeleted = null; } + $this->lineOffset = $this->lineOffset + $changedLineCount - $baseLineCount; + return $html; } diff --git a/tests/resources/htmlMerged.txt b/tests/resources/htmlMerged.txt index 0e261b7..666263d 100644 --- a/tests/resources/htmlMerged.txt +++ b/tests/resources/htmlMerged.txt @@ -13,7 +13,7 @@ 3         <meta http-equiv="Content-type" content="text/html; charset=utf-8"/> - 4 + 4         <title>Hello WorldYou!</title> 5 @@ -25,18 +25,18 @@ 7         <h1>This is demo content to show features of the php-diff package.</h1> - 8         <h2>This line is the same for both versions.</h2> - 9 + 9         <h2>this line has inlineThis line has differences between both versions.</h2> 10         <h2>This line is the same for both versions.</h2> - 11 + 11         <h2>This line also has inlInLine differences between both versions.</h2> 12 @@ -54,13 +54,13 @@ 16             It's also compatible with multibyte characters (like Chinese and emoji) as shown below: - 17 + 17             另外我覺得那個評的白色櫃子有點沒有必要欸。外觀我就不說了 ,怎麼連空間都那麼狹隘。不過倒是從這個地方看出所謂的“改革” - 18 + 18             Do you know what "金槍魚罐頭魚の缶詰" means in Chinese? - 19 + 19             🍏🍎🍎🍏🙂 20 @@ -84,13 +84,13 @@ 27         <p>Just some lines to demonstrate the collapsing of a block of lines which are the same in both versions.</p> - 28 + 28                 <h2>This line also has inline differences between both versions. It's the whitespace in front.</h2> 29         <h2>This line is the same for both versions.</h2> - 30 + 30         <h2>This line also has inline differences between both versions.!</h2> 31