diff-loc: Map file locations across diffs

[ data, library, mit ] [ Propose Tags ]


Manual Flags


Enable test module.


Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info


Note: This package has metadata revisions in the cabal description newer than included in the tarball. To unpack the package including the revisions, use 'cabal get'.

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


  • No Candidates
Versions [RSS]
Change log CHANGELOG.md
Dependencies base (>=4.14 && <4.19), fingertree, show-combinators [details]
License MIT
Copyright 2022 Li-yao Xia
Author Li-yao Xia
Maintainer lysxia@gmail.com
Revised Revision 1 made by lyxia at 2023-01-19T10:46:39Z
Category Data
Home page https://gitlab.com/lysxia/diff-loc
Uploaded by lyxia at 2022-12-10T20:44:48Z
Distributions NixOS:, Stackage:
Downloads 46 total (9 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2022-12-10 [all 1 reports]

Readme for diff-loc-

[back to package description]

diff-loc: Map file locations across diffs Hackage


You have a diff between two versions of a file. Given a source span in one version, find the corresponding span in the other version.

For example, here is a diff d between a source string "abcdefgh" and a target string "appcfgzzh", with deletions and insertions in the middle:

  ab cdefg  h
-  b  de
+  pp     zz
  appc  fgzzh

Diffs are represented by the type Diff. Only locations and lengths are recorded, not the actual characters.

import DiffLoc
import DiffLoc.Unsafe (offset)

d :: Diff N
d = addDiff (Replace 1 (offset 1) (offset 2))  -- at location 1, replace "b" (length 1) with "pp" (length 2)
  $ addDiff (Replace 3 (offset 2) (offset 0))  -- at location 3, replace "de" with ""
  $ addDiff (Replace 7 (offset 0) (offset 2))  -- at location 7, replace "" with "zz"
  $ emptyDiff
-- N.B.: replacements should be inserted right to left, starting from 'emptyDiff'.

The span s of "fg" in the first string is an interval that starts at location 5 and has length 2.

s :: Interval N
s = 5 :.. offset 2

Illustration of the span:

 a b c d e f g h
0 1 2 3 4 5 6 7 8
          ^f+g+ length 2
          start 5

After applying the diff, the span has been shifted to location 4.

>>> mapDiff d (5 :.. offset 2)
Just (4 :.. offset 2)
 a p p c f g q q h
0 1 2 3 4 5 6 7 8 9
        ^f+g+ length 2
        start 4

Conversely, we can map spans from the target string to the source string of the diff:

>>> comapDiff d (4 :.. offset 2)
Just (5 :.. offset 2)

If part of the input span is modified by the diff, there is no corresponding output span.

>>> mapDiff d (1 :.. offset 2)  -- "bc" contains "b" which is edited by the diff

See the API documentation in DiffLoc.