From f2d29ad24c7c1c3ef2eb4a2ce47bba3a45823674 Mon Sep 17 00:00:00 2001 From: Ayman Bagabas Date: Wed, 13 Dec 2023 16:45:50 -0500 Subject: [PATCH] feat: add udiff cmd package Run uDiff as a standalone binary --- examples/go.mod | 13 ++++ examples/go.sum | 6 ++ examples/udiff/main.go | 155 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 174 insertions(+) create mode 100644 examples/go.mod create mode 100644 examples/go.sum create mode 100644 examples/udiff/main.go diff --git a/examples/go.mod b/examples/go.mod new file mode 100644 index 0000000..e88e749 --- /dev/null +++ b/examples/go.mod @@ -0,0 +1,13 @@ +module github.com/aymanbagabas/go-udiff/cmd + +go 1.23.0 + +replace github.com/aymanbagabas/go-udiff => ./.. + +require ( + github.com/aymanbagabas/go-udiff v0.3.1 + github.com/mattn/go-isatty v0.0.20 + github.com/spf13/pflag v1.0.5 +) + +require golang.org/x/sys v0.6.0 // indirect diff --git a/examples/go.sum b/examples/go.sum new file mode 100644 index 0000000..df9f88d --- /dev/null +++ b/examples/go.sum @@ -0,0 +1,6 @@ +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/examples/udiff/main.go b/examples/udiff/main.go new file mode 100644 index 0000000..3b6a344 --- /dev/null +++ b/examples/udiff/main.go @@ -0,0 +1,155 @@ +package main + +import ( + "fmt" + "io" + "os" + "strings" + + "github.com/aymanbagabas/go-udiff" + "github.com/mattn/go-isatty" + "github.com/spf13/pflag" +) + +const ( + redSeq = "\033[31m" + greenSeq = "\033[32m" + resetSeq = "\033[0m" +) + +var ( + contextLines int + color string +) + +func init() { + pflag.Usage = usage + pflag.IntVarP(&contextLines, "context", "C", udiff.DefaultContextLines, "number of context lines") + pflag.StringVarP(&color, "color", "", "auto", "colorize the output; can be 'always', 'never', or 'auto'") +} + +func usage() { + fmt.Fprintf(os.Stderr, "Usage: %s [options] file1 file2\n", os.Args[0]) + pflag.PrintDefaults() +} + +func main() { + pflag.Parse() + args := pflag.Args() + if len(args) != 2 { + pflag.Usage() + os.Exit(1) + } + + var colorize bool + switch strings.ToLower(color) { + case "always": + colorize = true + case "auto": + colorize = isatty.IsTerminal(os.Stdout.Fd()) + } + + f1, err := os.Open(args[0]) + if err != nil { + fmt.Fprintf(os.Stderr, "couldn't open file: %s\n", err) + os.Exit(1) + } + + defer f1.Close() + f2, err := os.Open(args[1]) + if err != nil { + fmt.Fprintf(os.Stderr, "couldn't open file: %s\n", err) + os.Exit(1) + } + + defer f2.Close() + s1, err := io.ReadAll(f1) + if err != nil { + fmt.Fprintf(os.Stderr, "couldn't read file: %s\n", err) + os.Exit(1) + } + + s2, err := io.ReadAll(f2) + if err != nil { + fmt.Fprintf(os.Stderr, "couldn't read file: %s\n", err) + os.Exit(1) + } + + edits := udiff.Strings(string(s1), string(s2)) + u, err := udiff.ToUnifiedDiff(f1.Name(), f2.Name(), string(s1), edits, contextLines) + if err != nil { + fmt.Fprintf(os.Stderr, "couldn't generate diff: %s\n", err) + os.Exit(1) + } + + fmt.Println(toString(u, colorize)) +} + +// String converts a unified diff to the standard textual form for that diff. +// The output of this function can be passed to tools like patch. +func toString(u udiff.UnifiedDiff, colorize bool) string { + if len(u.Hunks) == 0 { + return "" + } + b := new(strings.Builder) + fmt.Fprintf(b, "--- %s\n", u.From) + fmt.Fprintf(b, "+++ %s\n", u.To) + for _, hunk := range u.Hunks { + fromCount, toCount := 0, 0 + for _, l := range hunk.Lines { + switch l.Kind { + case udiff.Delete: + fromCount++ + case udiff.Insert: + toCount++ + default: + fromCount++ + toCount++ + } + } + fmt.Fprint(b, "@@") + if fromCount > 1 { + fmt.Fprintf(b, " -%d,%d", hunk.FromLine, fromCount) + } else if hunk.FromLine == 1 && fromCount == 0 { + // Match odd GNU diff -u behavior adding to empty file. + fmt.Fprintf(b, " -0,0") + } else { + fmt.Fprintf(b, " -%d", hunk.FromLine) + } + if toCount > 1 { + fmt.Fprintf(b, " +%d,%d", hunk.ToLine, toCount) + } else if hunk.ToLine == 1 && toCount == 0 { + // Match odd GNU diff -u behavior adding to empty file. + fmt.Fprintf(b, " +0,0") + } else { + fmt.Fprintf(b, " +%d", hunk.ToLine) + } + fmt.Fprint(b, " @@\n") + for _, l := range hunk.Lines { + switch l.Kind { + case udiff.Delete: + if colorize { + fmt.Fprint(b, redSeq) + } + fmt.Fprintf(b, "-%s", l.Content) + if colorize { + fmt.Fprint(b, resetSeq) + } + case udiff.Insert: + if colorize { + fmt.Fprint(b, greenSeq) + } + fmt.Fprintf(b, "+%s", l.Content) + if colorize { + fmt.Fprint(b, resetSeq) + } + default: + fmt.Fprintf(b, " %s", l.Content) + } + if !strings.HasSuffix(l.Content, "\n") { + fmt.Fprintf(b, "\n\\ No newline at end of file\n") + } + } + } + return b.String() +}