1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* "Inline" diff renderer. |
5
|
|
|
* |
6
|
|
|
* This class renders diffs in the Wiki-style "inline" format. |
7
|
|
|
* |
8
|
|
|
* $Horde: framework/Text_Diff/Diff/Renderer/inline.php,v 1.14 2005/07/22 19:45:15 chuck Exp $ |
9
|
|
|
* |
10
|
|
|
* @author Ciprian Popovici |
11
|
|
|
* @package Text_Diff |
12
|
|
|
*/ |
13
|
|
|
class Text_Diff_Renderer_inline extends Text_Diff_Renderer |
14
|
|
|
{ |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* Number of leading context "lines" to preserve. |
18
|
|
|
*/ |
19
|
|
|
public $_leading_context_lines = 10000; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* Number of trailing context "lines" to preserve. |
23
|
|
|
*/ |
24
|
|
|
public $_trailing_context_lines = 10000; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* Prefix for inserted text. |
28
|
|
|
*/ |
29
|
|
|
public $_ins_prefix = '<ins>'; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Suffix for inserted text. |
33
|
|
|
*/ |
34
|
|
|
public $_ins_suffix = '</ins>'; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Prefix for deleted text. |
38
|
|
|
*/ |
39
|
|
|
public $_del_prefix = '<del>'; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Suffix for deleted text. |
43
|
|
|
*/ |
44
|
|
|
public $_del_suffix = '</del>'; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Header for each change block. |
48
|
|
|
*/ |
49
|
|
|
public $_block_header = ''; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* What are we currently splitting on? Used to recurse to show word-level |
53
|
|
|
* changes. |
54
|
|
|
*/ |
55
|
|
|
public $_split_level = 'lines'; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @param $xbeg |
59
|
|
|
* @param $xlen |
60
|
|
|
* @param $ybeg |
61
|
|
|
* @param $ylen |
62
|
|
|
* @return string |
63
|
|
|
*/ |
64
|
|
|
public function _blockHeader($xbeg, $xlen, $ybeg, $ylen) |
65
|
|
|
{ |
66
|
|
|
return $this->_block_header; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @param $header |
71
|
|
|
* @return mixed |
72
|
|
|
*/ |
73
|
|
|
public function _startBlock($header) |
74
|
|
|
{ |
75
|
|
|
return $header; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @param $lines |
80
|
|
|
* @param string $prefix |
81
|
|
|
* @param bool $encode |
82
|
|
|
* @return string |
83
|
|
|
*/ |
84
|
|
|
public function _lines($lines, $prefix = ' ', $encode = true) |
85
|
|
|
{ |
86
|
|
|
if ($encode) { |
87
|
|
|
array_walk($lines, array(&$this, '_encode')); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
if ($this->_split_level == 'words') { |
91
|
|
|
return implode('', $lines); |
92
|
|
|
} else { |
93
|
|
|
return implode("\n", $lines) . "\n"; |
94
|
|
|
} |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* @param $lines |
99
|
|
|
* @return string |
100
|
|
|
*/ |
101
|
|
View Code Duplication |
public function _added($lines) |
|
|
|
|
102
|
|
|
{ |
103
|
|
|
array_walk($lines, array(&$this, '_encode')); |
104
|
|
|
$lines[0] = $this->_ins_prefix . $lines[0]; |
105
|
|
|
$lines[count($lines) - 1] .= $this->_ins_suffix; |
106
|
|
|
|
107
|
|
|
return $this->_lines($lines, ' ', false); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* @param $lines |
112
|
|
|
* @param bool $words |
113
|
|
|
* @return string |
114
|
|
|
*/ |
115
|
|
View Code Duplication |
public function _deleted($lines, $words = false) |
|
|
|
|
116
|
|
|
{ |
117
|
|
|
array_walk($lines, array(&$this, '_encode')); |
118
|
|
|
$lines[0] = $this->_del_prefix . $lines[0]; |
119
|
|
|
$lines[count($lines) - 1] .= $this->_del_suffix; |
120
|
|
|
|
121
|
|
|
return $this->_lines($lines, ' ', false); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* @param $orig |
126
|
|
|
* @param $final |
127
|
|
|
* @return string |
128
|
|
|
*/ |
129
|
|
|
public function _changed($orig, $final) |
130
|
|
|
{ |
131
|
|
|
/* If we've already split on words, don't try to do so again - just |
132
|
|
|
* display. */ |
133
|
|
|
if ($this->_split_level == 'words') { |
134
|
|
|
$prefix = ''; |
135
|
|
|
while ($orig[0] !== false && $final[0] !== false && substr($orig[0], 0, 1) == ' ' |
136
|
|
|
&& substr($final[0], 0, 1) == ' ') { |
137
|
|
|
$prefix .= substr($orig[0], 0, 1); |
138
|
|
|
$orig[0] = substr($orig[0], 1); |
139
|
|
|
$final[0] = substr($final[0], 1); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
return $prefix . $this->_deleted($orig) . $this->_added($final); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
$text1 = implode("\n", $orig); |
146
|
|
|
$text2 = implode("\n", $final); |
147
|
|
|
|
148
|
|
|
/* Non-printing newline marker. */ |
149
|
|
|
$nl = "\0"; |
150
|
|
|
|
151
|
|
|
/* We want to split on word boundaries, but we need to |
152
|
|
|
* preserve whitespace as well. Therefore we split on words, |
153
|
|
|
* but include all blocks of whitespace in the wordlist. */ |
154
|
|
|
$diff = new Text_Diff($this->_splitOnWords($text1, $nl), $this->_splitOnWords($text2, $nl)); |
155
|
|
|
|
156
|
|
|
/* Get the diff in inline format. */ |
157
|
|
|
$renderer = new Text_Diff_Renderer_inline(array_merge($this->getParams(), array('split_level' => 'words'))); |
158
|
|
|
|
159
|
|
|
/* Run the diff and get the output. */ |
160
|
|
|
return str_replace($nl, "\n", $renderer->render($diff)) . "\n"; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* @param $string |
165
|
|
|
* @param string $newlineEscape |
166
|
|
|
* @return array |
167
|
|
|
*/ |
168
|
|
|
public function _splitOnWords($string, $newlineEscape = "\n") |
169
|
|
|
{ |
170
|
|
|
$words = array(); |
171
|
|
|
$length = strlen($string); |
172
|
|
|
$pos = 0; |
173
|
|
|
|
174
|
|
|
while ($pos < $length) { |
175
|
|
|
// Eat a word with any preceding whitespace. |
176
|
|
|
$spaces = strspn(substr($string, $pos), " \n"); |
177
|
|
|
$nextpos = strcspn(substr($string, $pos + $spaces), " \n"); |
178
|
|
|
$words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos)); |
179
|
|
|
$pos += $spaces + $nextpos; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
return $words; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* @param $string |
187
|
|
|
*/ |
188
|
|
|
public function _encode(&$string) |
189
|
|
|
{ |
190
|
|
|
$string = htmlspecialchars($string); |
191
|
|
|
} |
192
|
|
|
} |
193
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.