Skip to content

Commit 468a48f

Browse files
committed
feat: add udiff cmd package
Run uDiff as a standalone binary
1 parent 4ad777e commit 468a48f

File tree

3 files changed

+174
-0
lines changed

3 files changed

+174
-0
lines changed

cmd/go.mod

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module github.com/aymanbagabas/go-udiff/cmd
2+
3+
go 1.18
4+
5+
require (
6+
github.com/aymanbagabas/go-udiff v0.2.0
7+
github.com/mattn/go-isatty v0.0.20
8+
github.com/spf13/pflag v1.0.5
9+
)
10+
11+
require golang.org/x/sys v0.6.0 // indirect

cmd/go.sum

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
2+
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
3+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
4+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
5+
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
6+
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
7+
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
8+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

cmd/udiff/main.go

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"os"
7+
"strings"
8+
9+
"github.com/aymanbagabas/go-udiff"
10+
"github.com/mattn/go-isatty"
11+
"github.com/spf13/pflag"
12+
)
13+
14+
const (
15+
redSeq = "\033[31m"
16+
greenSeq = "\033[32m"
17+
resetSeq = "\033[0m"
18+
)
19+
20+
var (
21+
contextLines int
22+
color string
23+
)
24+
25+
func init() {
26+
pflag.Usage = usage
27+
pflag.IntVarP(&contextLines, "context", "C", udiff.DefaultContextLines, "number of context lines")
28+
pflag.StringVarP(&color, "color", "", "auto", "colorize the output; can be 'always', 'never', or 'auto'")
29+
}
30+
31+
func usage() {
32+
fmt.Fprintf(os.Stderr, "Usage: %s [options] file1 file2\n", os.Args[0])
33+
pflag.PrintDefaults()
34+
}
35+
36+
func main() {
37+
pflag.Parse()
38+
args := pflag.Args()
39+
if len(args) != 2 {
40+
pflag.Usage()
41+
os.Exit(1)
42+
}
43+
44+
var colorize bool
45+
switch strings.ToLower(color) {
46+
case "always":
47+
colorize = true
48+
case "auto":
49+
colorize = isatty.IsTerminal(os.Stdout.Fd())
50+
}
51+
52+
f1, err := os.Open(args[0])
53+
if err != nil {
54+
fmt.Fprintf(os.Stderr, "couldn't open file: %s\n", err)
55+
os.Exit(1)
56+
}
57+
58+
defer f1.Close()
59+
f2, err := os.Open(args[1])
60+
if err != nil {
61+
fmt.Fprintf(os.Stderr, "couldn't open file: %s\n", err)
62+
os.Exit(1)
63+
}
64+
65+
defer f2.Close()
66+
s1, err := io.ReadAll(f1)
67+
if err != nil {
68+
fmt.Fprintf(os.Stderr, "couldn't read file: %s\n", err)
69+
os.Exit(1)
70+
}
71+
72+
s2, err := io.ReadAll(f2)
73+
if err != nil {
74+
fmt.Fprintf(os.Stderr, "couldn't read file: %s\n", err)
75+
os.Exit(1)
76+
}
77+
78+
edits := udiff.Strings(string(s1), string(s2))
79+
u, err := udiff.ToUnifiedDiff(f1.Name(), f2.Name(), string(s1), edits, contextLines)
80+
if err != nil {
81+
fmt.Fprintf(os.Stderr, "couldn't generate diff: %s\n", err)
82+
os.Exit(1)
83+
}
84+
85+
fmt.Println(toString(u, colorize))
86+
}
87+
88+
// String converts a unified diff to the standard textual form for that diff.
89+
// The output of this function can be passed to tools like patch.
90+
func toString(u udiff.UnifiedDiff, colorize bool) string {
91+
if len(u.Hunks) == 0 {
92+
return ""
93+
}
94+
b := new(strings.Builder)
95+
fmt.Fprintf(b, "--- %s\n", u.From)
96+
fmt.Fprintf(b, "+++ %s\n", u.To)
97+
for _, hunk := range u.Hunks {
98+
fromCount, toCount := 0, 0
99+
for _, l := range hunk.Lines {
100+
switch l.Kind {
101+
case udiff.Delete:
102+
fromCount++
103+
case udiff.Insert:
104+
toCount++
105+
default:
106+
fromCount++
107+
toCount++
108+
}
109+
}
110+
fmt.Fprint(b, "@@")
111+
if fromCount > 1 {
112+
fmt.Fprintf(b, " -%d,%d", hunk.FromLine, fromCount)
113+
} else if hunk.FromLine == 1 && fromCount == 0 {
114+
// Match odd GNU diff -u behavior adding to empty file.
115+
fmt.Fprintf(b, " -0,0")
116+
} else {
117+
fmt.Fprintf(b, " -%d", hunk.FromLine)
118+
}
119+
if toCount > 1 {
120+
fmt.Fprintf(b, " +%d,%d", hunk.ToLine, toCount)
121+
} else if hunk.ToLine == 1 && toCount == 0 {
122+
// Match odd GNU diff -u behavior adding to empty file.
123+
fmt.Fprintf(b, " +0,0")
124+
} else {
125+
fmt.Fprintf(b, " +%d", hunk.ToLine)
126+
}
127+
fmt.Fprint(b, " @@\n")
128+
for _, l := range hunk.Lines {
129+
switch l.Kind {
130+
case udiff.Delete:
131+
if colorize {
132+
fmt.Fprint(b, redSeq)
133+
}
134+
fmt.Fprintf(b, "-%s", l.Content)
135+
if colorize {
136+
fmt.Fprint(b, resetSeq)
137+
}
138+
case udiff.Insert:
139+
if colorize {
140+
fmt.Fprint(b, greenSeq)
141+
}
142+
fmt.Fprintf(b, "+%s", l.Content)
143+
if colorize {
144+
fmt.Fprint(b, resetSeq)
145+
}
146+
default:
147+
fmt.Fprintf(b, " %s", l.Content)
148+
}
149+
if !strings.HasSuffix(l.Content, "\n") {
150+
fmt.Fprintf(b, "\n\\ No newline at end of file\n")
151+
}
152+
}
153+
}
154+
return b.String()
155+
}

0 commit comments

Comments
 (0)