Сделать вывод diff-lcs удобочитаемым для человека

Я использую гем diff-lcs для вывода разницы между двумя телами html-контента. Вот образец содержания.

Версия 1:

<p>Paragraph one. Sentence one.</p>

<p>Paragraph two. Another sentence.</p>

<p>Paragraph three. I dare you to change me!</p>

Версия вторая:

<p>Paragraph one. Sentence two.</p>

<p>Paragraph two. Another sentence.</p>

<p>Paragraph three. I dare you to update me!</p>

Используя это:

seq1 = @versionOne.body
seq2 = @versionTwo.body

seq = Diff::LCS.diff(seq1, seq2)

Вы получите этого монстра:

seq => [[#<Diff::LCS::Change:0x0000000be539f8 @action="-", @position=27, @element="t">, #<Diff::LCS::Change:0x0000000be538b8 @action="-", @position=28, @element="w">], [#<Diff::LCS::Change:0x0000000be53520 @action="+", @position=28, @element="n">, #<Diff::LCS::Change:0x0000000be53408 @action="+", @position=29, @element="e">], [#<Diff::LCS::Change:0x0000000be3aa70 @action="-", @position=110, @element="u">, #<Diff::LCS::Change:0x0000000be3a840 @action="-", @position=111, @element="p">, #<Diff::LCS::Change:0x0000000be34ee0 @action="-", @position=112, @element="d">, #<Diff::LCS::Change:0x0000000be349e0 @action="+", @position=110, @element="c">, #<Diff::LCS::Change:0x0000000be348a0 @action="+", @position=111, @element="h">], [#<Diff::LCS::Change:0x0000000be34580 @action="-", @position=114, @element="t">, #<Diff::LCS::Change:0x0000000be34210 @action="+", @position=113, @element="n">, #<Diff::LCS::Change:0x0000000be33f40 @action="+", @position=114, @element="g">], [#<Diff::LCS::Change:0x0000000be331d0 @action="-", @position=124, @element="">]]

Результаты sdiff и других методов, найденные в документации, также ужасны. Я понимаю структуру массива (массивов), но должен быть простой способ показать различия в удобочитаемой и стильной манере.

PS - Если кто-то хочет создать тег diff-lcs, это будет оценено.


person Archonic    schedule 26.03.2013    source источник


Ответы (2)


То, что я скармливал diff-lcs, было обычной строкой — массивом символов. Если то, что я хотел, было сравнением символов, я получил то, что хотел, но я хотел что-то более читабельное — сравнение слов, строк или предложений. Я выбрал предложения.

seq1 = @versionOne.body.split('.')
seq2 = @versionTwo.body.split('.')
compareDiff = Diff::LCS.sdiff(seq1, seq2)

Это дало гораздо более читаемый и разборчивый контент. На самом деле, я также хочу разделить на ! и ?. Однако структура не является обычным массивом массивов или хешей. Вывод в браузере меня сбил с толку, но это массив объектов, и вы можете анализировать его, как и все остальное. Это форматированный вывод YAML, который я получил в консоли rails (не знаю, почему он не показал это в браузере):

---
- !ruby/object:Diff::LCS::ContextChange
  action: "="
  new_element: <p>Paragraph one
  new_position: 0
  old_element: <p>Paragraph one
  old_position: 0
- !ruby/object:Diff::LCS::ContextChange
  action: "!"
  new_element: " Sentence two"
  new_position: 1
  old_element: " Sentence one"
  old_position: 1
- !ruby/object:Diff::LCS::ContextChange
  action: "="
  new_element: |-
    </p>
    <p>Paragraph two
  new_position: 2
  old_element: |-
    </p>
    <p>Paragraph two
  old_position: 2
- !ruby/object:Diff::LCS::ContextChange
  action: "="
  new_element: " Another sentence"
  new_position: 3
  old_element: " Another sentence"
  old_position: 3
- !ruby/object:Diff::LCS::ContextChange
  action: "="
  new_element: |-
    </p>
    <p>Paragraph three
  new_position: 4
  old_element: |-
    </p>
    <p>Paragraph three
  old_position: 4
- !ruby/object:Diff::LCS::ContextChange
  action: "!"
  new_element: " I dare you to update me!</p>"
  new_position: 5
  old_element: " I dare you to change me!</p>"
  old_position: 5
 => nil

Супер полезно! Это выведет вики-подобный diff:

sdiff = Diff::LCS.sdiff(seq2, seq1)

diffHTML = ''

sdiff.each do |diff|
  case diff.action
  when '='
    diffHTML << diff.new_element + "."
  when '!'
    # strip_tags only needed on old_element. removes pre-mature end tags.
    diffHTML << "<del>#{diff.old_element.strip_tags}</del> <add>#{diff.new_element}</add>. "
  end
end

@compareBody = diffHTML.html

...[format do block]

Затем просто настройте <del> и <add> по своему усмотрению. Если вы ищете что-то более простое, возможно, это diffy, но это очень гибко, как только вы это поймете из.

person Archonic    schedule 26.03.2013
comment
Возвращаясь спустя 8 лет, чтобы сказать, что есть готовое решение получше. Это решение имеет проблемы с созданием действительного HTML, когда изменения касаются начала или конца элемента. Чтобы разобраться в этом, вы должны использовать алгоритм древовидного сравнения вместо сравнения с плоской строкой. Есть пакет Python под названием html-tree-diff. Я написал для него рубиновую оболочку здесь: github.com/archonic/ruby_html_tree_differ - person Archonic; 07.06.2021

Существует также блочная версия, которую вы можете использовать, которую вы можете комбинировать с деструктуризацией массива:

seq1.sdiff(seq2) { |action, (old_pos, old_element), (new_pos, new_element)|
  ...
  puts [action, old_pos, old_element, new_pos, new_element].inspect
  ...
}

seq1.diff(seq2) { |action, position, element| 
  puts [action, position, element].inspect 
}
person Christopher Oezbek    schedule 06.06.2021