Current oav website
This commit is contained in:
346
dotclear._no/inc/libs/clearbricks/diff/lib.diff.php
Normal file
346
dotclear._no/inc/libs/clearbricks/diff/lib.diff.php
Normal file
@ -0,0 +1,346 @@
|
||||
<?php
|
||||
/**
|
||||
* @class diff
|
||||
* @brief Unified diff
|
||||
*
|
||||
* Diff utilities
|
||||
*
|
||||
* @package Clearbricks
|
||||
* @subpackage Diff
|
||||
*
|
||||
* @copyright Olivier Meunier & Association Dotclear
|
||||
* @copyright GPL-2.0-only
|
||||
*/
|
||||
|
||||
class diff
|
||||
{
|
||||
private static $us_range = "@@ -%s,%s +%s,%s @@\n";
|
||||
private static $us_ctx = " %s\n";
|
||||
private static $us_ins = "+%s\n";
|
||||
private static $us_del = "-%s\n";
|
||||
|
||||
private static $up_range = '/^@@ -([\d]+),([\d]+) \+([\d]+),([\d]+) @@/';
|
||||
private static $up_ctx = '/^ (.*)$/';
|
||||
private static $up_ins = '/^\+(.*)$/';
|
||||
private static $up_del = '/^-(.*)$/';
|
||||
|
||||
/**
|
||||
* Finds the shortest edit script using a fast algorithm taken from paper
|
||||
* "An O(ND) Difference Algorithm and Its Variations" by Eugene W.Myers,
|
||||
* 1986.
|
||||
*
|
||||
* @param array $src Original data
|
||||
* @param array $dst New data
|
||||
* @return array
|
||||
*/
|
||||
public static function SES($src, $dst)
|
||||
{
|
||||
$cx = count($src);
|
||||
$cy = count($dst);
|
||||
|
||||
$stack = [];
|
||||
$V = [1 => 0];
|
||||
$end_reached = false;
|
||||
|
||||
# Find LCS length
|
||||
for ($D = 0; $D < $cx + $cy + 1 && !$end_reached; $D++) {
|
||||
for ($k = -$D; $k <= $D; $k += 2) {
|
||||
$x = ($k == -$D || $k != $D && $V[$k - 1] < $V[$k + 1])
|
||||
? $V[$k + 1] : $V[$k - 1] + 1;
|
||||
$y = $x - $k;
|
||||
|
||||
while ($x < $cx && $y < $cy && $src[$x] == $dst[$y]) {
|
||||
$x++;
|
||||
$y++;
|
||||
}
|
||||
|
||||
$V[$k] = $x;
|
||||
|
||||
if ($x == $cx && $y == $cy) {
|
||||
$end_reached = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$stack[] = $V;
|
||||
}
|
||||
$D--;
|
||||
|
||||
# Recover edit path
|
||||
$res = [];
|
||||
for ($D = $D; $D > 0; $D--) {
|
||||
$V = array_pop($stack);
|
||||
$cx = $x;
|
||||
$cy = $y;
|
||||
|
||||
# Try right diagonal
|
||||
$k++;
|
||||
$x = array_key_exists($k, $V) ? $V[$k] : 0;
|
||||
$y = $x - $k;
|
||||
$y++;
|
||||
|
||||
while ($x < $cx && $y < $cy
|
||||
&& isset($src[$x]) && isset($dst[$y]) && $src[$x] == $dst[$y]) {
|
||||
$x++;
|
||||
$y++;
|
||||
}
|
||||
|
||||
if ($x == $cx && $y == $cy) {
|
||||
$x = $V[$k];
|
||||
$y = $x - $k;
|
||||
|
||||
$res[] = ['i', $x, $y];
|
||||
continue;
|
||||
}
|
||||
|
||||
# Right diagonal wasn't the solution, use left diagonal
|
||||
$k -= 2;
|
||||
$x = $V[$k];
|
||||
$y = $x - $k;
|
||||
$res[] = ['d', $x, $y];
|
||||
}
|
||||
|
||||
return array_reverse($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unified diff from source $src to destination $dst.
|
||||
*
|
||||
* @param string $src Original data
|
||||
* @param string $dst New data
|
||||
* @param string $ctx Context length
|
||||
* @return string
|
||||
*/
|
||||
public static function uniDiff($src, $dst, $ctx = 2)
|
||||
{
|
||||
list($src, $dst) = [explode("\n", $src), explode("\n", $dst)];
|
||||
$cx = count($src);
|
||||
$cy = count($dst);
|
||||
|
||||
$ses = diff::SES($src, $dst);
|
||||
$res = '';
|
||||
|
||||
$pos_x = 0;
|
||||
$pos_y = 0;
|
||||
$old_lines = 0;
|
||||
$new_lines = 0;
|
||||
$buffer = '';
|
||||
|
||||
foreach ($ses as $cmd) {
|
||||
list($cmd, $x, $y) = [$cmd[0], $cmd[1], $cmd[2]];
|
||||
|
||||
# New chunk
|
||||
if ($x - $pos_x > 2 * $ctx || $pos_x == 0 && $x > $ctx) {
|
||||
# Footer for current chunk
|
||||
for ($i = 0; $buffer && $i < $ctx; $i++) {
|
||||
$buffer .= sprintf(self::$us_ctx, $src[$pos_x + $i]);
|
||||
}
|
||||
|
||||
# Header for current chunk
|
||||
$res .= sprintf(self::$us_range,
|
||||
$pos_x + 1 - $old_lines, $old_lines + $i,
|
||||
$pos_y + 1 - $new_lines, $new_lines + $i
|
||||
) . $buffer;
|
||||
|
||||
$pos_x = $x;
|
||||
$pos_y = $y;
|
||||
$old_lines = 0;
|
||||
$new_lines = 0;
|
||||
$buffer = '';
|
||||
|
||||
# Header for next chunk
|
||||
for ($i = $ctx; $i > 0; $i--) {
|
||||
$buffer .= sprintf(self::$us_ctx, $src[$pos_x - $i]);
|
||||
$old_lines++;
|
||||
$new_lines++;
|
||||
}
|
||||
}
|
||||
|
||||
# Context
|
||||
while ($x > $pos_x) {
|
||||
$old_lines++;
|
||||
$new_lines++;
|
||||
$buffer .= sprintf(self::$us_ctx, $src[$pos_x]);
|
||||
$pos_x++;
|
||||
$pos_y++;
|
||||
}
|
||||
# Deletion
|
||||
if ($cmd == 'd') {
|
||||
$old_lines++;
|
||||
$buffer .= sprintf(self::$us_del, $src[$x]);
|
||||
$pos_x++;
|
||||
}
|
||||
# Insertion
|
||||
elseif ($cmd == 'i') {
|
||||
$new_lines++;
|
||||
$buffer .= sprintf(self::$us_ins, $dst[$y]);
|
||||
$pos_y++;
|
||||
}
|
||||
}
|
||||
|
||||
# Remaining chunk
|
||||
if ($buffer) {
|
||||
# Footer
|
||||
for ($i = 0; $i < $ctx; $i++) {
|
||||
if (!isset($src[$pos_x + $i])) {
|
||||
break;
|
||||
}
|
||||
$buffer .= sprintf(self::$us_ctx, $src[$pos_x + $i]);
|
||||
}
|
||||
|
||||
# Header for current chunk
|
||||
$res .= sprintf(self::$us_range,
|
||||
$pos_x + 1 - $old_lines, $old_lines + $i,
|
||||
$pos_y + 1 - $new_lines, $new_lines + $i
|
||||
) . $buffer;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a unified patch to a piece of text.
|
||||
* Throws an exception on invalid or not applicable diff.
|
||||
*
|
||||
* @param string $src Source text
|
||||
* @param string $diff Patch to apply
|
||||
* @return string
|
||||
*/
|
||||
public static function uniPatch($src, $diff)
|
||||
{
|
||||
$dst = [];
|
||||
$src = explode("\n", $src);
|
||||
$diff = explode("\n", $diff);
|
||||
|
||||
$t = count($src);
|
||||
$old_length = $new_length = 0;
|
||||
|
||||
foreach ($diff as $line) {
|
||||
# New chunk
|
||||
if (preg_match(self::$up_range, $line, $m)) {
|
||||
$m[1]--;
|
||||
$m[3]--;
|
||||
|
||||
if ($m[1] > $t) {
|
||||
throw new Exception(__('Bad range'));
|
||||
}
|
||||
|
||||
if ($t - count($src) > $m[1]) {
|
||||
throw new Exception(__('Invalid range'));
|
||||
}
|
||||
|
||||
while ($t - count($src) < $m[1]) {
|
||||
$dst[] = array_shift($src);
|
||||
}
|
||||
|
||||
if (count($dst) !== $m[3]) {
|
||||
throw new Exception(__('Invalid line number'));
|
||||
}
|
||||
|
||||
if ($old_length || $new_length) {
|
||||
throw new Exception(__('Chunk is out of range'));
|
||||
}
|
||||
|
||||
$old_length = (integer) $m[2];
|
||||
$new_length = (integer) $m[4];
|
||||
}
|
||||
# Context
|
||||
elseif (preg_match(self::$up_ctx, $line, $m)) {
|
||||
if (array_shift($src) !== $m[1]) {
|
||||
throw new Exception(__('Bad context'));
|
||||
}
|
||||
$dst[] = $m[1];
|
||||
$old_length--;
|
||||
$new_length--;
|
||||
}
|
||||
# Addition
|
||||
elseif (preg_match(self::$up_ins, $line, $m)) {
|
||||
$dst[] = $m[1];
|
||||
$new_length--;
|
||||
}
|
||||
# Deletion
|
||||
elseif (preg_match(self::$up_del, $line, $m)) {
|
||||
if (array_shift($src) !== $m[1]) {
|
||||
throw new Exception(__('Bad context (in deletion)'));
|
||||
}
|
||||
$old_length--;
|
||||
} elseif ($line == '') {
|
||||
continue;
|
||||
} else {
|
||||
throw new Exception(__('Invalid diff format'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($old_length || $new_length) {
|
||||
throw new Exception(__('Chunk is out of range'));
|
||||
}
|
||||
|
||||
return implode("\n", array_merge($dst, $src));
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception on invalid unified diff.
|
||||
*
|
||||
* @param string $diff Diff text to check
|
||||
*/
|
||||
public static function uniCheck($diff)
|
||||
{
|
||||
$diff = explode("\n", $diff);
|
||||
|
||||
$cur_line = 1;
|
||||
$ins_lines = 0;
|
||||
|
||||
# Chunk length
|
||||
$old_length = $new_length = 0;
|
||||
|
||||
foreach ($diff as $line) {
|
||||
# New chunk
|
||||
if (preg_match(self::$up_range, $line, $m)) {
|
||||
if ($cur_line > $m[1]) {
|
||||
throw new Exception(__('Invalid range'));
|
||||
}
|
||||
while ($cur_line < $m[1]) {
|
||||
$ins_lines++;
|
||||
$cur_line++;
|
||||
}
|
||||
if ($ins_lines + 1 != $m[3]) {
|
||||
throw new Exception(__('Invalid line number'));
|
||||
}
|
||||
|
||||
if ($old_length || $new_length) {
|
||||
throw new Exception(__('Chunk is out of range'));
|
||||
}
|
||||
|
||||
$old_length = $m[2];
|
||||
$new_length = $m[4];
|
||||
}
|
||||
# Context
|
||||
elseif (preg_match(self::$up_ctx, $line, $m)) {
|
||||
$ins_lines++;
|
||||
$cur_line++;
|
||||
$old_length--;
|
||||
$new_length--;
|
||||
}
|
||||
# Addition
|
||||
elseif (preg_match(self::$up_ins, $line, $m)) {
|
||||
$ins_lines++;
|
||||
$new_length--;
|
||||
}
|
||||
# Deletion
|
||||
elseif (preg_match(self::$up_del, $line, $m)) {
|
||||
$cur_line++;
|
||||
$old_length--;
|
||||
}
|
||||
# Skip empty lines
|
||||
elseif ($line == '') {
|
||||
continue;
|
||||
}
|
||||
# Unrecognized diff format
|
||||
else {
|
||||
throw new Exception(__('Invalid diff format'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($old_length || $new_length) {
|
||||
throw new Exception(__('Chunk is out of range'));
|
||||
}
|
||||
}
|
||||
}
|
||||
336
dotclear._no/inc/libs/clearbricks/diff/lib.tidy.diff.php
Normal file
336
dotclear._no/inc/libs/clearbricks/diff/lib.tidy.diff.php
Normal file
@ -0,0 +1,336 @@
|
||||
<?php
|
||||
/**
|
||||
* @class tidyDiff
|
||||
* @brief TIDY diff
|
||||
*
|
||||
* A TIDY diff representation
|
||||
*
|
||||
* @package Clearbricks
|
||||
* @subpackage Diff
|
||||
*
|
||||
* @copyright Olivier Meunier & Association Dotclear
|
||||
* @copyright GPL-2.0-only
|
||||
*/
|
||||
|
||||
class tidyDiff
|
||||
{
|
||||
protected $__data = []; ///< array: Chunks array
|
||||
|
||||
private $up_range = '/^@@ -([\d]+),([\d]+) \+([\d]+),([\d]+) @@/m';
|
||||
private $up_ctx = '/^ (.*)$/';
|
||||
private $up_ins = '/^\+(.*)$/';
|
||||
private $up_del = '/^-(.*)$/';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Creates a diff representation from unified diff.
|
||||
*
|
||||
* @param string $udiff Unified diff
|
||||
* @param array $inline_changes Find inline changes
|
||||
*/
|
||||
public function __construct($udiff, $inline_changes = false)
|
||||
{
|
||||
diff::uniCheck($udiff);
|
||||
|
||||
preg_match_all($this->up_range, $udiff, $context);
|
||||
|
||||
$chunks = preg_split($this->up_range, $udiff, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
foreach ($chunks as $k => $chunk) {
|
||||
$tidy_chunk = new tidyDiffChunk();
|
||||
$tidy_chunk->setRange(
|
||||
(int) $context[1][$k],
|
||||
(int) $context[2][$k],
|
||||
(int) $context[3][$k],
|
||||
(int) $context[4][$k]
|
||||
);
|
||||
|
||||
$old_line = (int) $context[1][$k];
|
||||
$new_line = (int) $context[3][$k];
|
||||
|
||||
foreach (explode("\n", $chunk) as $line) {
|
||||
# context
|
||||
if (preg_match($this->up_ctx, $line, $m)) {
|
||||
$tidy_chunk->addLine('context', [$old_line, $new_line], $m[1]);
|
||||
$old_line++;
|
||||
$new_line++;
|
||||
}
|
||||
# insertion
|
||||
if (preg_match($this->up_ins, $line, $m)) {
|
||||
$tidy_chunk->addLine('insert', [$old_line, $new_line], $m[1]);
|
||||
$new_line++;
|
||||
}
|
||||
# deletion
|
||||
if (preg_match($this->up_del, $line, $m)) {
|
||||
$tidy_chunk->addLine('delete', [$old_line, $new_line], $m[1]);
|
||||
$old_line++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($inline_changes) {
|
||||
$tidy_chunk->findInsideChanges();
|
||||
}
|
||||
|
||||
array_push($this->__data, $tidy_chunk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All chunks
|
||||
*
|
||||
* Returns all chunks defined.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getChunks()
|
||||
{
|
||||
return $this->__data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class tidyDiffChunk
|
||||
* @brief TIDY diff chunk
|
||||
*
|
||||
* A diff chunk representation. Used by a TIDY diff.
|
||||
*
|
||||
* @package Clearbricks
|
||||
* @subpackage Diff
|
||||
*/
|
||||
class tidyDiffChunk
|
||||
{
|
||||
protected $__info; ///< array: Chunk information array
|
||||
protected $__data; ///< array: Chunk data array
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Creates and initializes a chunk representation for a TIDY diff.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->__info = [
|
||||
'context' => 0,
|
||||
'delete' => 0,
|
||||
'insert' => 0,
|
||||
'range' => [
|
||||
'start' => [],
|
||||
'end' => []
|
||||
]
|
||||
];
|
||||
$this->__data = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set chunk range
|
||||
*
|
||||
* Sets chunk range in TIDY chunk object.
|
||||
*
|
||||
* @param integer $line_start Old start line number
|
||||
* @param integer $offest_start Old offset number
|
||||
* @param integer $line_end new start line number
|
||||
* @param integer $offset_end New offset number
|
||||
*/
|
||||
public function setRange($line_start, $offest_start, $line_end, $offset_end)
|
||||
{
|
||||
if (is_int($line_start) && is_int($offest_start) && is_int($line_end) && is_int($offset_end)) {
|
||||
$this->__info['range']['start'] = [$line_start, $offest_start];
|
||||
$this->__info['range']['end'] = [$line_end, $offset_end];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add line
|
||||
*
|
||||
* Adds TIDY line object for TIDY chunk object.
|
||||
*
|
||||
* @param string $type Tine type
|
||||
* @param array $lines Line number for old and new context
|
||||
* @param string $content Line content
|
||||
*/
|
||||
public function addLine($type, $lines, $content)
|
||||
{
|
||||
$tidy_line = new tidyDiffLine($type, $lines, $content);
|
||||
|
||||
if (!is_null($tidy_line)) {
|
||||
array_push($this->__data, $tidy_line);
|
||||
$this->__info[$type]++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All lines
|
||||
*
|
||||
* Returns all lines defined.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLines()
|
||||
{
|
||||
return $this->__data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chunk information
|
||||
*
|
||||
* Returns chunk information according to the given name, null otherwise.
|
||||
*
|
||||
* @param string $n Info name
|
||||
* @return string
|
||||
*/
|
||||
public function getInfo($n)
|
||||
{
|
||||
return array_key_exists($n, $this->__info) ? $this->__info[$n] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find changes
|
||||
*
|
||||
* Finds changes inside lines for each groups of diff lines. Wraps changes
|
||||
* by string \0 and \1
|
||||
*/
|
||||
public function findInsideChanges()
|
||||
{
|
||||
$groups = $this->getGroups();
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$middle = count($group) / 2;
|
||||
for ($i = 0; $i < $middle; $i++) {
|
||||
$from = $group[$i];
|
||||
$to = $group[$i + $middle];
|
||||
$threshold = $this->getChangeExtent($from->content, $to->content);
|
||||
|
||||
if ($threshold['start'] != 0 || $threshold['end'] != 0) {
|
||||
$start = $threshold['start'];
|
||||
$end = $threshold['end'] + strlen($from->content);
|
||||
$offset = $end - $start;
|
||||
$from->overwrite(
|
||||
substr($from->content, 0, $start) . '\0' .
|
||||
substr($from->content, $start, $offset) . '\1' .
|
||||
substr($from->content, $end, strlen($from->content))
|
||||
);
|
||||
$end = $threshold['end'] + strlen($to->content);
|
||||
$offset = $end - $start;
|
||||
$to->overwrite(
|
||||
substr($to->content, 0, $start) . '\0' .
|
||||
substr($to->content, $start, $offset) . '\1' .
|
||||
substr($to->content, $end, strlen($to->content))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getGroups()
|
||||
{
|
||||
$res = $group = [];
|
||||
$allowed_types = ['delete', 'insert'];
|
||||
$delete = $insert = 0;
|
||||
|
||||
foreach ($this->__data as $k => $line) {
|
||||
if (in_array($line->type, $allowed_types)) {
|
||||
array_push($group, $line);
|
||||
${$line->type}++;
|
||||
} else {
|
||||
if ($delete === $insert && count($group) > 0) {
|
||||
array_push($res, $group);
|
||||
}
|
||||
$delete = $insert = 0;
|
||||
$group = [];
|
||||
}
|
||||
}
|
||||
if ($delete === $insert && count($group) > 0) {
|
||||
array_push($res, $group);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function getChangeExtent($str1, $str2)
|
||||
{
|
||||
$start = 0;
|
||||
$limit = min(strlen($str1), strlen($str2));
|
||||
while ($start < $limit and $str1[$start] === $str2[$start]) {
|
||||
$start++;
|
||||
}
|
||||
|
||||
$end = -1;
|
||||
$limit = $limit - $start;
|
||||
|
||||
while (-$end <= $limit && $str1[strlen($str1) + $end] === $str2[strlen($str2) + $end]) {
|
||||
$end--;
|
||||
}
|
||||
|
||||
return ['start' => $start, 'end' => $end + 1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class tidyDiffLine
|
||||
* @brief TIDY diff line
|
||||
*
|
||||
* A diff line representation. Used by a TIDY chunk.
|
||||
*
|
||||
* @package Clearbricks
|
||||
* @subpackage Diff
|
||||
*/
|
||||
class tidyDiffLine
|
||||
{
|
||||
protected $type; ///< string: Line type
|
||||
protected $lines; ///< array: Line number for old and new context
|
||||
protected $content; ///< string: Line content
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Creates a line representation for a tidy chunk. Returns a new object if
|
||||
* all parameters are fine, null otherwise.
|
||||
*
|
||||
* @param string $type Tine type
|
||||
* @param array $lines Line number for old and new context
|
||||
* @param string $content Line content
|
||||
* @return object
|
||||
*/
|
||||
public function __construct($type, $lines, $content)
|
||||
{
|
||||
$allowed_type = ['context', 'delete', 'insert'];
|
||||
|
||||
if (in_array($type, $allowed_type) && is_array($lines) && is_string($content)) {
|
||||
$this->type = $type;
|
||||
$this->lines = $lines;
|
||||
$this->content = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic get
|
||||
*
|
||||
* Returns field content according to the given name, null otherwise.
|
||||
*
|
||||
* @param string $n Field name
|
||||
* @return string
|
||||
*/
|
||||
public function __get($n)
|
||||
{
|
||||
return isset($n, $this) ? $this->{$n} : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite
|
||||
*
|
||||
* Overwrites content for the current line.
|
||||
*
|
||||
* @param string $content Line content
|
||||
*/
|
||||
public function overwrite($content)
|
||||
{
|
||||
if (is_string($content)) {
|
||||
$this->content = $content;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user