1 | <?php
|
---|
2 | /**
|
---|
3 | * "Inline" diff renderer.
|
---|
4 | *
|
---|
5 | * $Horde: framework/Text_Diff/Diff/Renderer/inline.php,v 1.21 2008/01/04 10:07:51 jan Exp $
|
---|
6 | *
|
---|
7 | * Copyright 2004-2008 The Horde Project (http://www.horde.org/)
|
---|
8 | *
|
---|
9 | * See the enclosed file COPYING for license information (LGPL). If you did
|
---|
10 | * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
|
---|
11 | *
|
---|
12 | * @author Ciprian Popovici
|
---|
13 | * @package Text_Diff
|
---|
14 | */
|
---|
15 |
|
---|
16 | /** Text_Diff_Renderer */
|
---|
17 |
|
---|
18 | // WP #7391
|
---|
19 | require_once dirname(dirname(__FILE__)) . '/Renderer.php';
|
---|
20 |
|
---|
21 | /**
|
---|
22 | * "Inline" diff renderer.
|
---|
23 | *
|
---|
24 | * This class renders diffs in the Wiki-style "inline" format.
|
---|
25 | *
|
---|
26 | * @author Ciprian Popovici
|
---|
27 | * @package Text_Diff
|
---|
28 | */
|
---|
29 | class Text_Diff_Renderer_inline extends Text_Diff_Renderer {
|
---|
30 |
|
---|
31 | /**
|
---|
32 | * Number of leading context "lines" to preserve.
|
---|
33 | */
|
---|
34 | var $_leading_context_lines = 10000;
|
---|
35 |
|
---|
36 | /**
|
---|
37 | * Number of trailing context "lines" to preserve.
|
---|
38 | */
|
---|
39 | var $_trailing_context_lines = 10000;
|
---|
40 |
|
---|
41 | /**
|
---|
42 | * Prefix for inserted text.
|
---|
43 | */
|
---|
44 | var $_ins_prefix = '<ins>';
|
---|
45 |
|
---|
46 | /**
|
---|
47 | * Suffix for inserted text.
|
---|
48 | */
|
---|
49 | var $_ins_suffix = '</ins>';
|
---|
50 |
|
---|
51 | /**
|
---|
52 | * Prefix for deleted text.
|
---|
53 | */
|
---|
54 | var $_del_prefix = '<del>';
|
---|
55 |
|
---|
56 | /**
|
---|
57 | * Suffix for deleted text.
|
---|
58 | */
|
---|
59 | var $_del_suffix = '</del>';
|
---|
60 |
|
---|
61 | /**
|
---|
62 | * Header for each change block.
|
---|
63 | */
|
---|
64 | var $_block_header = '';
|
---|
65 |
|
---|
66 | /**
|
---|
67 | * What are we currently splitting on? Used to recurse to show word-level
|
---|
68 | * changes.
|
---|
69 | */
|
---|
70 | var $_split_level = 'lines';
|
---|
71 |
|
---|
72 | function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
|
---|
73 | {
|
---|
74 | return $this->_block_header;
|
---|
75 | }
|
---|
76 |
|
---|
77 | function _startBlock($header)
|
---|
78 | {
|
---|
79 | return $header;
|
---|
80 | }
|
---|
81 |
|
---|
82 | function _lines($lines, $prefix = ' ', $encode = true)
|
---|
83 | {
|
---|
84 | if ($encode) {
|
---|
85 | array_walk($lines, array(&$this, '_encode'));
|
---|
86 | }
|
---|
87 |
|
---|
88 | if ($this->_split_level == 'words') {
|
---|
89 | return implode('', $lines);
|
---|
90 | } else {
|
---|
91 | return implode("\n", $lines) . "\n";
|
---|
92 | }
|
---|
93 | }
|
---|
94 |
|
---|
95 | function _added($lines)
|
---|
96 | {
|
---|
97 | array_walk($lines, array(&$this, '_encode'));
|
---|
98 | $lines[0] = $this->_ins_prefix . $lines[0];
|
---|
99 | $lines[count($lines) - 1] .= $this->_ins_suffix;
|
---|
100 | return $this->_lines($lines, ' ', false);
|
---|
101 | }
|
---|
102 |
|
---|
103 | function _deleted($lines, $words = false)
|
---|
104 | {
|
---|
105 | array_walk($lines, array(&$this, '_encode'));
|
---|
106 | $lines[0] = $this->_del_prefix . $lines[0];
|
---|
107 | $lines[count($lines) - 1] .= $this->_del_suffix;
|
---|
108 | return $this->_lines($lines, ' ', false);
|
---|
109 | }
|
---|
110 |
|
---|
111 | function _changed($orig, $final)
|
---|
112 | {
|
---|
113 | /* If we've already split on words, don't try to do so again - just
|
---|
114 | * display. */
|
---|
115 | if ($this->_split_level == 'words') {
|
---|
116 | $prefix = '';
|
---|
117 | while ($orig[0] !== false && $final[0] !== false &&
|
---|
118 | substr($orig[0], 0, 1) == ' ' &&
|
---|
119 | substr($final[0], 0, 1) == ' ') {
|
---|
120 | $prefix .= substr($orig[0], 0, 1);
|
---|
121 | $orig[0] = substr($orig[0], 1);
|
---|
122 | $final[0] = substr($final[0], 1);
|
---|
123 | }
|
---|
124 | return $prefix . $this->_deleted($orig) . $this->_added($final);
|
---|
125 | }
|
---|
126 |
|
---|
127 | $text1 = implode("\n", $orig);
|
---|
128 | $text2 = implode("\n", $final);
|
---|
129 |
|
---|
130 | /* Non-printing newline marker. */
|
---|
131 | $nl = "\0";
|
---|
132 |
|
---|
133 | /* We want to split on word boundaries, but we need to
|
---|
134 | * preserve whitespace as well. Therefore we split on words,
|
---|
135 | * but include all blocks of whitespace in the wordlist. */
|
---|
136 | $diff = new Text_Diff($this->_splitOnWords($text1, $nl),
|
---|
137 | $this->_splitOnWords($text2, $nl));
|
---|
138 |
|
---|
139 | /* Get the diff in inline format. */
|
---|
140 | $renderer = new Text_Diff_Renderer_inline(array_merge($this->getParams(),
|
---|
141 | array('split_level' => 'words')));
|
---|
142 |
|
---|
143 | /* Run the diff and get the output. */
|
---|
144 | return str_replace($nl, "\n", $renderer->render($diff)) . "\n";
|
---|
145 | }
|
---|
146 |
|
---|
147 | function _splitOnWords($string, $newlineEscape = "\n")
|
---|
148 | {
|
---|
149 | // Ignore \0; otherwise the while loop will never finish.
|
---|
150 | $string = str_replace("\0", '', $string);
|
---|
151 |
|
---|
152 | $words = array();
|
---|
153 | $length = strlen($string);
|
---|
154 | $pos = 0;
|
---|
155 |
|
---|
156 | while ($pos < $length) {
|
---|
157 | // Eat a word with any preceding whitespace.
|
---|
158 | $spaces = strspn(substr($string, $pos), " \n");
|
---|
159 | $nextpos = strcspn(substr($string, $pos + $spaces), " \n");
|
---|
160 | $words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos));
|
---|
161 | $pos += $spaces + $nextpos;
|
---|
162 | }
|
---|
163 |
|
---|
164 | return $words;
|
---|
165 | }
|
---|
166 |
|
---|
167 | function _encode(&$string)
|
---|
168 | {
|
---|
169 | $string = htmlspecialchars($string);
|
---|
170 | }
|
---|
171 |
|
---|
172 | }
|
---|