~reesmichael1/nim-simplediff

04e5e800853856dec7a6553de3fd03f8f8ac07d1 — Michael Rees 3 years ago 2ba8813
Add prettyDiff proc for creating formatted diff
3 files changed, 75 insertions(+), 2 deletions(-)

M README.md
M src/simplediff.nim
M tests/testDiff.nim
M README.md => README.md +3 -1
@@ 31,7 31,9 @@ for diff in stringDiff("the word is blue", "the word is red", seps={' '}):

Other convenience wrappers may be added in the future! Feel free to request one or submit a patch.

See the `when isMainModule` block within `simplediff.nim` for an example of using these functions to build a simple diff application.
`simplediff` also provides `prettyDiff`, which writes a formatted version of the diff to an output stream. It can be called directly or, if you need more flexibility than it offers, used as a starting point for building your own output.

See the `when isMainModule` block within `simplediff.nim` for an example of a simple diff application.

## Contributing


M src/simplediff.nim => src/simplediff.nim +39 -1
@@ 1,3 1,5 @@
import math
import streams
import strutils
import tables



@@ 39,7 41,7 @@ proc diff*[T](itemsOld, itemsNew: openArray[T]): seq[Diff[T]] =
      var newSuffixLen = 1
      if ixOld > 0 and overlap.getOrDefault(ixOld - 1, 0) > 0:
        newSuffixLen = overlap.getOrDefault(ixOld - 1, 0) + 1
      overlaptemp[ixOld] = newSuffixLen
      overlapTemp[ixOld] = newSuffixLen
      if overlapTemp[ixOld] > subLength:
        subLength = overlapTemp[ixOld]
        subStartOld = ixOld - subLength + 1


@@ 73,6 75,42 @@ proc stringDiff*(s1, s2: string, seps: set[char] = Newlines): seq[Diff[string]] 
  return diff(split(s1, seps = seps), split(s2, seps = seps))


proc prettyDiff*[T](itemsOld, itemsNew: openArray[T],
    outStream: Stream = newFileStream(stdout), insertionPrefix = "++++",
        deletionPrefix = "----") =
  ## Calculate the diff of `itemsOld` and `itemsNew`
  ## and write it to `outStream`.
  ## `insertionPrefix` is prepended to lines that were inserted,
  ## and `deletionPrefix` is prepended to lines that were deleted.
  let diffed = diff(itemsOld, itemsNew)
  var lineCounter = 1

  let linePadding = int(floor(log10(float(itemsOld.len)))) + 2
  proc formatLineCount(count: int): string =
    align($lineCounter, linePadding) & " "

  for ix, entry in diffed:
    case entry.kind
    of NoChange:
      for line in entry.tokens:
        lineCounter += 1
    of Insertion:
      if lineCounter != 1:
        lineCounter -= 1
      for line in entry.tokens:
        outStream.writeLine(formatLineCount(lineCounter) &
            insertionPrefix & " " & $line)
      # Don't increment the lines when inserting at the beginning
      # because we were adding at the zeroth line
      if lineCounter != 1:
        lineCounter += 1
    of Deletion:
      for line in entry.tokens:
        outStream.writeLine(formatLineCount(lineCounter) &
            deletionPrefix & " " & $line)
        lineCounter += 1


when isMainModule:
  import parseopt
  import terminal

M tests/testDiff.nim => tests/testDiff.nim +33 -0
@@ 1,3 1,4 @@
import streams
import strutils
import unittest



@@ 71,3 72,35 @@ suite "test stringDiff":
      Diff[string](kind: Deletion, tokens: @["def", "123"]),
      Diff[string](kind: Insertion, tokens: @["fed", "abc"])
      ]


suite "test prettyDiff":
  var output: Stream
  setup:
    output = newStringStream()

  test "correct output on unchanged strings":
    prettyDiff(["hello"], ["hello"], outStream = output)
    output.setPosition(0)
    check output.readAll() == ""

  test "correct output on deletion":
    prettyDiff(["hello", "world"], ["hello"], outStream = output)
    output.setPosition(0)
    check output.readAll() == " 2 ---- world\n"

  test "correct output on insertion":
    prettyDiff(["hello"], ["hello", "world"], outStream = output)
    output.setPosition(0)
    check output.readAll() == " 1 ++++ world\n"

  test "correct output on insertion, deletion, and unchanged":
    prettyDiff(["same", "deletion"], ["insertion", "same", ],
        outStream = output)
    output.setPosition(0)
    check output.readAll() == " 1 ++++ insertion\n 2 ---- deletion\n"

  test "correct output on non-string tokens":
    prettyDiff([1, 2], [3, 1], outStream = output)
    output.setPosition(0)
    check output.readAll() == " 1 ++++ 3\n 2 ---- 2\n"