github.com/d0sbit/gocode@v0.0.0-20211001144653-a968ce917518/srcedit/diff/diff.go (about) 1 package diff 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io/fs" 8 "io/ioutil" 9 10 "github.com/sergi/go-diff/diffmatchpatch" 11 ) 12 13 // Run walks the out fs and compares to in, rooted at rootDir ("." means root of fs), 14 // and report differences in a map of filenames to diff output. 15 // The outType indicates the output format, "html" will produce HTML output, any other 16 // value will product "term" output which is console/terminal output with highlighting. 17 func Run(in, out fs.FS, rootDir string, outType string) (map[string]string, error) { 18 19 ret := make(map[string]string) 20 21 trimPath := func(p string) string { 22 // return strings.TrimPrefix(strings.TrimPrefix(p, rootDir), "/") 23 return p 24 } 25 26 // walk the output directory and compare each file to the input 27 err := fs.WalkDir(out, rootDir, fs.WalkDirFunc(func(p string, d fs.DirEntry, err error) error { 28 if err != nil { 29 return err 30 } 31 32 if d.IsDir() { 33 return nil 34 } 35 36 outf, err := out.Open(p) 37 if err != nil { 38 return fmt.Errorf("error opening output file %q: %w", p, err) 39 } 40 defer outf.Close() 41 outb, err := ioutil.ReadAll(outf) 42 if err != nil { 43 return err 44 } 45 46 inf, err := in.Open(p) 47 if err != nil { 48 if !errors.Is(err, fs.ErrNotExist) { 49 return fmt.Errorf("error opening input file %q: %w", p, err) 50 } 51 // if no input file, then diff against empty string 52 df := diffContents("", string(outb), outType) 53 ret[trimPath(p)] = df 54 return nil 55 } 56 defer inf.Close() 57 inb, err := ioutil.ReadAll(inf) 58 if err != nil { 59 return err 60 } 61 62 // check if they are exactly the same 63 if bytes.Equal(inb, outb) { 64 return nil // no difference to report 65 } 66 67 // diff out against in file 68 df := diffContents(string(inb), string(outb), outType) 69 ret[trimPath(p)] = df 70 return nil 71 })) 72 if err != nil { 73 return nil, err 74 } 75 76 return ret, nil 77 } 78 79 // diffContents performs a diff on the from and to contents of a file. 80 // The format of the returned byte slice depends on outType. 81 func diffContents(from, to string, outType string) string { 82 83 // // fileAdmp, fileBdmp, dmpStrings := dmp.DiffLinesToChars(fileAtext, fileBtext) 84 // // _ = dmpStrings 85 // diffs := dmp.DiffMain(fileAtext, fileBtext, false) 86 // // diffs := dmp.DiffMain(fileAdmp, fileBdmp, false) 87 // // diffs = dmp.DiffCharsToLines(diffs, dmpStrings) 88 // // diffs = dmp.DiffCleanupSemantic(diffs) 89 // diffs = dmp.DiffCleanupSemanticLossless(diffs) 90 // // diffs = dmp.DiffCharsToLines(diffs, dmpStrings) 91 92 dmp := diffmatchpatch.New() 93 94 diffs := dmp.DiffMain(from, to, true) 95 96 diffs = dmp.DiffCleanupSemanticLossless(diffs) 97 98 if outType == "html" { 99 return dmp.DiffPrettyHtml(diffs) 100 } 101 102 return dmp.DiffPrettyText(diffs) 103 }