Use buffered writer to avoid overwhelming arv-mount.
[lightning.git] / hgvs / diff.go
index e6e3b33647420c8ce90dad7354bbe35a7fb51db5..0f6365db267e3d4051e9066aeb7a0fcda0f4c84b 100644 (file)
@@ -11,10 +11,13 @@ type Variant struct {
        Position int
        Ref      string
        New      string
+       Left     string // base preceding an indel, if Ref or New is empty
 }
 
 func (v *Variant) String() string {
        switch {
+       case len(v.New) == 0 && len(v.Ref) == 0:
+               return fmt.Sprintf("%d=", v.Position)
        case len(v.New) == 0 && len(v.Ref) == 1:
                return fmt.Sprintf("%ddel", v.Position)
        case len(v.New) == 0:
@@ -30,37 +33,62 @@ func (v *Variant) String() string {
        }
 }
 
-func Diff(a, b string) []Variant {
+// PadLeft returns a Variant that is equivalent to v but (if possible)
+// uses the stashed preceding base (the Left field) to avoid having a
+// non-empty Ref or New part, even for an insertion or deletion.
+//
+// For example, if v is {Position: 45, Ref: "", New: "A"}, PadLeft
+// might return {Position: 44, Ref: "T", New: "TA"}.
+func (v *Variant) PadLeft() Variant {
+       if len(v.Ref) == 0 || len(v.New) == 0 {
+               return Variant{
+                       Position: v.Position - len(v.Left),
+                       Ref:      v.Left + v.Ref,
+                       New:      v.Left + v.New,
+               }
+       } else {
+               return *v
+       }
+}
+
+func Diff(a, b string, timeout time.Duration) ([]Variant, bool) {
        dmp := diffmatchpatch.New()
-       diffs := cleanup(dmp.DiffCleanupEfficiency(dmp.DiffBisect(a, b, time.Time{})))
+       var deadline time.Time
+       if timeout > 0 {
+               deadline = time.Now().Add(timeout)
+       }
+       diffs := dmp.DiffBisect(a, b, deadline)
+       timedOut := false
+       if timeout > 0 && time.Now().After(deadline) {
+               timedOut = true
+       }
+       diffs = cleanup(dmp.DiffCleanupEfficiency(diffs))
        pos := 1
        var variants []Variant
-       for i := 0; i < len(diffs); i++ {
-               switch diffs[i].Type {
-               case diffmatchpatch.DiffEqual:
+       for i := 0; i < len(diffs); {
+               left := "" // last char before an insertion or deletion
+               for ; i < len(diffs) && diffs[i].Type == diffmatchpatch.DiffEqual; i++ {
                        pos += len(diffs[i].Text)
-               case diffmatchpatch.DiffDelete:
-                       if i+1 < len(diffs) && diffs[i+1].Type == diffmatchpatch.DiffInsert {
-                               // deletion followed by insertion
-                               variants = append(variants, Variant{Position: pos, Ref: diffs[i].Text, New: diffs[i+1].Text})
-                               pos += len(diffs[i].Text)
-                               i++
-                       } else {
-                               variants = append(variants, Variant{Position: pos, Ref: diffs[i].Text})
-                               pos += len(diffs[i].Text)
+                       if tlen := len(diffs[i].Text); tlen > 0 {
+                               left = diffs[i].Text[tlen-1:]
                        }
-               case diffmatchpatch.DiffInsert:
-                       if i+1 < len(diffs) && diffs[i+1].Type == diffmatchpatch.DiffDelete {
-                               // insertion followed by deletion
-                               variants = append(variants, Variant{Position: pos, Ref: diffs[i+1].Text, New: diffs[i].Text})
-                               pos += len(diffs[i+1].Text)
-                               i++
+               }
+               if i >= len(diffs) {
+                       break
+               }
+               v := Variant{Position: pos, Left: left}
+               for ; i < len(diffs) && diffs[i].Type != diffmatchpatch.DiffEqual; i++ {
+                       if diffs[i].Type == diffmatchpatch.DiffDelete {
+                               v.Ref += diffs[i].Text
                        } else {
-                               variants = append(variants, Variant{Position: pos, New: diffs[i].Text})
+                               v.New += diffs[i].Text
                        }
                }
+               pos += len(v.Ref)
+               variants = append(variants, v)
+               left = ""
        }
-       return variants
+       return variants, timedOut
 }
 
 func cleanup(in []diffmatchpatch.Diff) (out []diffmatchpatch.Diff) {