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  }