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