e6e3b33647420c8ce90dad7354bbe35a7fb51db5
[lightning.git] / hgvs / diff.go
1 package hgvs
2
3 import (
4         "fmt"
5         "time"
6
7         "github.com/sergi/go-diff/diffmatchpatch"
8 )
9
10 type Variant struct {
11         Position int
12         Ref      string
13         New      string
14 }
15
16 func (v *Variant) String() string {
17         switch {
18         case len(v.New) == 0 && len(v.Ref) == 1:
19                 return fmt.Sprintf("%ddel", v.Position)
20         case len(v.New) == 0:
21                 return fmt.Sprintf("%d_%ddel", v.Position, v.Position+len(v.Ref)-1)
22         case len(v.Ref) == 1 && len(v.New) == 1:
23                 return fmt.Sprintf("%d%s>%s", v.Position, v.Ref, v.New)
24         case len(v.Ref) == 0:
25                 return fmt.Sprintf("%d_%dins%s", v.Position-1, v.Position, v.New)
26         case len(v.Ref) == 1 && len(v.New) > 0:
27                 return fmt.Sprintf("%ddelins%s", v.Position, v.New)
28         default:
29                 return fmt.Sprintf("%d_%ddelins%s", v.Position, v.Position+len(v.Ref)-1, v.New)
30         }
31 }
32
33 func Diff(a, b string) []Variant {
34         dmp := diffmatchpatch.New()
35         diffs := cleanup(dmp.DiffCleanupEfficiency(dmp.DiffBisect(a, b, time.Time{})))
36         pos := 1
37         var variants []Variant
38         for i := 0; i < len(diffs); i++ {
39                 switch diffs[i].Type {
40                 case diffmatchpatch.DiffEqual:
41                         pos += len(diffs[i].Text)
42                 case diffmatchpatch.DiffDelete:
43                         if i+1 < len(diffs) && diffs[i+1].Type == diffmatchpatch.DiffInsert {
44                                 // deletion followed by insertion
45                                 variants = append(variants, Variant{Position: pos, Ref: diffs[i].Text, New: diffs[i+1].Text})
46                                 pos += len(diffs[i].Text)
47                                 i++
48                         } else {
49                                 variants = append(variants, Variant{Position: pos, Ref: diffs[i].Text})
50                                 pos += len(diffs[i].Text)
51                         }
52                 case diffmatchpatch.DiffInsert:
53                         if i+1 < len(diffs) && diffs[i+1].Type == diffmatchpatch.DiffDelete {
54                                 // insertion followed by deletion
55                                 variants = append(variants, Variant{Position: pos, Ref: diffs[i+1].Text, New: diffs[i].Text})
56                                 pos += len(diffs[i+1].Text)
57                                 i++
58                         } else {
59                                 variants = append(variants, Variant{Position: pos, New: diffs[i].Text})
60                         }
61                 }
62         }
63         return variants
64 }
65
66 func cleanup(in []diffmatchpatch.Diff) (out []diffmatchpatch.Diff) {
67         for i := 0; i < len(in); i++ {
68                 d := in[i]
69                 for i < len(in)-1 && in[i].Type == in[i+1].Type {
70                         d.Text += in[i+1].Text
71                         i++
72                 }
73                 out = append(out, d)
74         }
75         return
76 }