111 * |
111 * |
112 * @param string $line HTML-escape the value. |
112 * @param string $line HTML-escape the value. |
113 * @return string |
113 * @return string |
114 */ |
114 */ |
115 public function addedLine( $line ) { |
115 public function addedLine( $line ) { |
116 return "<td class='diff-addedline'>{$line}</td>"; |
116 return "<td class='diff-addedline'><span aria-hidden='true' class='dashicons dashicons-plus'></span><span class='screen-reader-text'>" . __( 'Added:' ) . " </span>{$line}</td>"; |
117 |
117 |
118 } |
118 } |
119 |
119 |
120 /** |
120 /** |
121 * @ignore |
121 * @ignore |
122 * |
122 * |
123 * @param string $line HTML-escape the value. |
123 * @param string $line HTML-escape the value. |
124 * @return string |
124 * @return string |
125 */ |
125 */ |
126 public function deletedLine( $line ) { |
126 public function deletedLine( $line ) { |
127 return "<td class='diff-deletedline'>{$line}</td>"; |
127 return "<td class='diff-deletedline'><span aria-hidden='true' class='dashicons dashicons-minus'></span><span class='screen-reader-text'>" . __( 'Deleted:' ) . " </span>{$line}</td>"; |
128 } |
128 } |
129 |
129 |
130 /** |
130 /** |
131 * @ignore |
131 * @ignore |
132 * |
132 * |
133 * @param string $line HTML-escape the value. |
133 * @param string $line HTML-escape the value. |
134 * @return string |
134 * @return string |
135 */ |
135 */ |
136 public function contextLine( $line ) { |
136 public function contextLine( $line ) { |
137 return "<td class='diff-context'>{$line}</td>"; |
137 return "<td class='diff-context'><span class='screen-reader-text'>" . __( 'Unchanged:' ) . " </span>{$line}</td>"; |
138 } |
138 } |
139 |
139 |
140 /** |
140 /** |
141 * @ignore |
141 * @ignore |
142 * |
142 * |
166 * htmlspecialchars. Use this filter to remove or change the processing. Passes a context |
166 * htmlspecialchars. Use this filter to remove or change the processing. Passes a context |
167 * indicating if the line is added, deleted or unchanged. |
167 * indicating if the line is added, deleted or unchanged. |
168 * |
168 * |
169 * @since 4.1.0 |
169 * @since 4.1.0 |
170 * |
170 * |
171 * @param String $processed_line The processed diffed line. |
171 * @param string $processed_line The processed diffed line. |
172 * @param String $line The unprocessed diffed line. |
172 * @param string $line The unprocessed diffed line. |
173 * @param string null The line context. Values are 'added', 'deleted' or 'unchanged'. |
173 * @param string $context The line context. Values are 'added', 'deleted' or 'unchanged'. |
174 */ |
174 */ |
175 $line = apply_filters( 'process_text_diff_html', $processed_line, $line, 'added' ); |
175 $line = apply_filters( 'process_text_diff_html', $processed_line, $line, 'added' ); |
176 } |
176 } |
177 |
177 |
178 if ( $this->_show_split_view ) { |
178 if ( $this->_show_split_view ) { |
247 * @return string |
247 * @return string |
248 */ |
248 */ |
249 public function _changed( $orig, $final ) { |
249 public function _changed( $orig, $final ) { |
250 $r = ''; |
250 $r = ''; |
251 |
251 |
252 // Does the aforementioned additional processing |
252 /* |
253 // *_matches tell what rows are "the same" in orig and final. Those pairs will be diffed to get word changes |
253 * Does the aforementioned additional processing: |
254 // match is numeric: an index in other column |
254 * *_matches tell what rows are "the same" in orig and final. Those pairs will be diffed to get word changes. |
255 // match is 'X': no match. It is a new row |
255 * - match is numeric: an index in other column. |
256 // *_rows are column vectors for the orig column and the final column. |
256 * - match is 'X': no match. It is a new row. |
257 // row >= 0: an indix of the $orig or $final array |
257 * *_rows are column vectors for the orig column and the final column. |
258 // row < 0: a blank row for that column |
258 * - row >= 0: an indix of the $orig or $final array. |
|
259 * - row < 0: a blank row for that column. |
|
260 */ |
259 list($orig_matches, $final_matches, $orig_rows, $final_rows) = $this->interleave_changed_lines( $orig, $final ); |
261 list($orig_matches, $final_matches, $orig_rows, $final_rows) = $this->interleave_changed_lines( $orig, $final ); |
260 |
262 |
261 // These will hold the word changes as determined by an inline diff |
263 // These will hold the word changes as determined by an inline diff. |
262 $orig_diffs = array(); |
264 $orig_diffs = array(); |
263 $final_diffs = array(); |
265 $final_diffs = array(); |
264 |
266 |
265 // Compute word diffs for each matched pair using the inline diff |
267 // Compute word diffs for each matched pair using the inline diff. |
266 foreach ( $orig_matches as $o => $f ) { |
268 foreach ( $orig_matches as $o => $f ) { |
267 if ( is_numeric( $o ) && is_numeric( $f ) ) { |
269 if ( is_numeric( $o ) && is_numeric( $f ) ) { |
268 $text_diff = new Text_Diff( 'auto', array( array( $orig[ $o ] ), array( $final[ $f ] ) ) ); |
270 $text_diff = new Text_Diff( 'auto', array( array( $orig[ $o ] ), array( $final[ $f ] ) ) ); |
269 $renderer = new $this->inline_diff_renderer; |
271 $renderer = new $this->inline_diff_renderer; |
270 $diff = $renderer->render( $text_diff ); |
272 $diff = $renderer->render( $text_diff ); |
271 |
273 |
272 // If they're too different, don't include any <ins> or <dels> |
274 // If they're too different, don't include any <ins> or <del>'s. |
273 if ( preg_match_all( '!(<ins>.*?</ins>|<del>.*?</del>)!', $diff, $diff_matches ) ) { |
275 if ( preg_match_all( '!(<ins>.*?</ins>|<del>.*?</del>)!', $diff, $diff_matches ) ) { |
274 // length of all text between <ins> or <del> |
276 // Length of all text between <ins> or <del>. |
275 $stripped_matches = strlen( strip_tags( join( ' ', $diff_matches[0] ) ) ); |
277 $stripped_matches = strlen( strip_tags( join( ' ', $diff_matches[0] ) ) ); |
276 // since we count lengith of text between <ins> or <del> (instead of picking just one), |
278 // Since we count length of text between <ins> or <del> (instead of picking just one), |
277 // we double the length of chars not in those tags. |
279 // we double the length of chars not in those tags. |
278 $stripped_diff = strlen( strip_tags( $diff ) ) * 2 - $stripped_matches; |
280 $stripped_diff = strlen( strip_tags( $diff ) ) * 2 - $stripped_matches; |
279 $diff_ratio = $stripped_matches / $stripped_diff; |
281 $diff_ratio = $stripped_matches / $stripped_diff; |
280 if ( $diff_ratio > $this->_diff_threshold ) { |
282 if ( $diff_ratio > $this->_diff_threshold ) { |
281 continue; // Too different. Don't save diffs. |
283 continue; // Too different. Don't save diffs. |
282 } |
284 } |
283 } |
285 } |
284 |
286 |
285 // Un-inline the diffs by removing del or ins |
287 // Un-inline the diffs by removing <del> or <ins>. |
286 $orig_diffs[ $o ] = preg_replace( '|<ins>.*?</ins>|', '', $diff ); |
288 $orig_diffs[ $o ] = preg_replace( '|<ins>.*?</ins>|', '', $diff ); |
287 $final_diffs[ $f ] = preg_replace( '|<del>.*?</del>|', '', $diff ); |
289 $final_diffs[ $f ] = preg_replace( '|<del>.*?</del>|', '', $diff ); |
288 } |
290 } |
289 } |
291 } |
290 |
292 |
333 * @since 2.6.0 |
335 * @since 2.6.0 |
334 * |
336 * |
335 * @param array $orig Lines of the original version of the text. |
337 * @param array $orig Lines of the original version of the text. |
336 * @param array $final Lines of the final version of the text. |
338 * @param array $final Lines of the final version of the text. |
337 * @return array { |
339 * @return array { |
338 * Array containing results of comparing the original text to the final text. |
340 * Array containing results of comparing the original text to the final text. |
339 * |
341 * |
340 * @type array $orig_matches Associative array of original matches. Index == row |
342 * @type array $orig_matches Associative array of original matches. Index == row |
341 * number of `$orig`, value == corresponding row number |
343 * number of `$orig`, value == corresponding row number |
342 * of that same line in `$final` or 'x' if there is no |
344 * of that same line in `$final` or 'x' if there is no |
343 * corresponding row (indicating it is a deleted line). |
345 * corresponding row (indicating it is a deleted line). |
344 * @type array $final_matches Associative array of final matches. Index == row |
346 * @type array $final_matches Associative array of final matches. Index == row |
345 * number of `$final`, value == corresponding row number |
347 * number of `$final`, value == corresponding row number |
346 * of that same line in `$orig` or 'x' if there is no |
348 * of that same line in `$orig` or 'x' if there is no |
347 * corresponding row (indicating it is a new line). |
349 * corresponding row (indicating it is a new line). |
348 * @type array $orig_rows Associative array of interleaved rows of `$orig` with |
350 * @type array $orig_rows Associative array of interleaved rows of `$orig` with |
349 * blanks to keep matches aligned with side-by-side diff |
351 * blanks to keep matches aligned with side-by-side diff |
350 * of `$final`. A value >= 0 corresponds to index of `$orig`. |
352 * of `$final`. A value >= 0 corresponds to index of `$orig`. |
351 * Value < 0 indicates a blank row. |
353 * Value < 0 indicates a blank row. |
352 * @type array $final_rows Associative array of interleaved rows of `$final` with |
354 * @type array $final_rows Associative array of interleaved rows of `$final` with |
353 * blanks to keep matches aligned with side-by-side diff |
355 * blanks to keep matches aligned with side-by-side diff |
354 * of `$orig`. A value >= 0 corresponds to index of `$final`. |
356 * of `$orig`. A value >= 0 corresponds to index of `$final`. |
355 * Value < 0 indicates a blank row. |
357 * Value < 0 indicates a blank row. |
356 * } |
358 * } |
357 */ |
359 */ |
358 public function interleave_changed_lines( $orig, $final ) { |
360 public function interleave_changed_lines( $orig, $final ) { |
359 |
361 |
360 // Contains all pairwise string comparisons. Keys are such that this need only be a one dimensional array. |
362 // Contains all pairwise string comparisons. Keys are such that this need only be a one dimensional array. |
372 foreach ( $matches as $keys => $difference ) { |
374 foreach ( $matches as $keys => $difference ) { |
373 list($o, $f) = explode( ',', $keys ); |
375 list($o, $f) = explode( ',', $keys ); |
374 $o = (int) $o; |
376 $o = (int) $o; |
375 $f = (int) $f; |
377 $f = (int) $f; |
376 |
378 |
377 // Already have better matches for these guys |
379 // Already have better matches for these guys. |
378 if ( isset( $orig_matches[ $o ] ) && isset( $final_matches[ $f ] ) ) { |
380 if ( isset( $orig_matches[ $o ] ) && isset( $final_matches[ $f ] ) ) { |
379 continue; |
381 continue; |
380 } |
382 } |
381 |
383 |
382 // First match for these guys. Must be best match |
384 // First match for these guys. Must be best match. |
383 if ( ! isset( $orig_matches[ $o ] ) && ! isset( $final_matches[ $f ] ) ) { |
385 if ( ! isset( $orig_matches[ $o ] ) && ! isset( $final_matches[ $f ] ) ) { |
384 $orig_matches[ $o ] = $f; |
386 $orig_matches[ $o ] = $f; |
385 $final_matches[ $f ] = $o; |
387 $final_matches[ $f ] = $o; |
386 continue; |
388 continue; |
387 } |
389 } |
388 |
390 |
389 // Best match of this final is already taken? Must mean this final is a new row. |
391 // Best match of this final is already taken? Must mean this final is a new row. |
390 if ( isset( $orig_matches[ $o ] ) ) { |
392 if ( isset( $orig_matches[ $o ] ) ) { |
391 $final_matches[ $f ] = 'x'; |
393 $final_matches[ $f ] = 'x'; |
392 } elseif ( isset( $final_matches[ $f ] ) ) { |
394 } elseif ( isset( $final_matches[ $f ] ) ) { |
393 // Best match of this orig is already taken? Must mean this orig is a deleted row. |
395 // Best match of this orig is already taken? Must mean this orig is a deleted row. |
394 $orig_matches[ $o ] = 'x'; |
396 $orig_matches[ $o ] = 'x'; |
395 } |
397 } |
396 } |
398 } |
397 |
399 |
398 // We read the text in this order |
400 // We read the text in this order. |
399 ksort( $orig_matches ); |
401 ksort( $orig_matches ); |
400 ksort( $final_matches ); |
402 ksort( $final_matches ); |
401 |
403 |
402 // Stores rows and blanks for each column. |
404 // Stores rows and blanks for each column. |
403 $orig_rows = $orig_rows_copy = array_keys( $orig_matches ); |
405 $orig_rows = array_keys( $orig_matches ); |
404 $final_rows = array_keys( $final_matches ); |
406 $orig_rows_copy = $orig_rows; |
|
407 $final_rows = array_keys( $final_matches ); |
405 |
408 |
406 // Interleaves rows with blanks to keep matches aligned. |
409 // Interleaves rows with blanks to keep matches aligned. |
407 // We may end up with some extraneous blank rows, but we'll just ignore them later. |
410 // We may end up with some extraneous blank rows, but we'll just ignore them later. |
408 foreach ( $orig_rows_copy as $orig_row ) { |
411 foreach ( $orig_rows_copy as $orig_row ) { |
409 $final_pos = array_search( $orig_matches[ $orig_row ], $final_rows, true ); |
412 $final_pos = array_search( $orig_matches[ $orig_row ], $final_rows, true ); |
418 $diff_array = range( -1, $orig_pos - $final_pos ); |
421 $diff_array = range( -1, $orig_pos - $final_pos ); |
419 array_splice( $orig_rows, $orig_pos, 0, $diff_array ); |
422 array_splice( $orig_rows, $orig_pos, 0, $diff_array ); |
420 } |
423 } |
421 } |
424 } |
422 |
425 |
423 // Pad the ends with blank rows if the columns aren't the same length |
426 // Pad the ends with blank rows if the columns aren't the same length. |
424 $diff_count = count( $orig_rows ) - count( $final_rows ); |
427 $diff_count = count( $orig_rows ) - count( $final_rows ); |
425 if ( $diff_count < 0 ) { |
428 if ( $diff_count < 0 ) { |
426 while ( $diff_count < 0 ) { |
429 while ( $diff_count < 0 ) { |
427 array_push( $orig_rows, $diff_count++ ); |
430 array_push( $orig_rows, $diff_count++ ); |
428 } |
431 } |