diff options
| author | Shulhan <ms@kilabit.info> | 2026-04-12 17:38:32 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2026-04-12 19:01:45 +0700 |
| commit | bfa79cf52d62418f622289cee7c1fe5807c73be6 (patch) | |
| tree | 240547182a2448ffa79feec94c9787f2c4466d3c | |
| parent | 8604b86d6e18bfdc9d2f8123a19bc8ed4dbfddb2 (diff) | |
| download | pakakeh.go-bfa79cf52d62418f622289cee7c1fe5807c73be6.tar.xz | |
text/diff: add example for Files, Lines, Text, and Unified
While at it,
- reorganize the order of functions alphabetically, following
the order from doc.
- changes the [Line.String] to print text in double quoted to help human
compare the changes for non-printable characters.
| -rw-r--r-- | lib/text/diff/diff.go | 95 | ||||
| -rw-r--r-- | lib/text/diff/diff_example_test.go | 125 | ||||
| -rw-r--r-- | lib/text/diff/line.go | 14 | ||||
| -rw-r--r-- | lib/text/diff/testdata/lao.txt | 11 | ||||
| -rw-r--r-- | lib/text/diff/testdata/tzu.txt | 13 | ||||
| -rw-r--r-- | lib/text/diff/unified_example_test.go | 65 |
6 files changed, 287 insertions, 36 deletions
diff --git a/lib/text/diff/diff.go b/lib/text/diff/diff.go index 7a2844c9..2e4102c0 100644 --- a/lib/text/diff/diff.go +++ b/lib/text/diff/diff.go @@ -52,16 +52,6 @@ func Files(oldfile, newfile string, level int) (diff *Data, err error) { return diff, nil } -// Text returns the difference between old and new text. -func Text(old, new []byte, level int) (diff *Data) { - oldlines := ParseLines(old) - newlines := ParseLines(new) - diff = Lines(oldlines, newlines, level) - diff.OldName = `old` - diff.NewName = `new` - return diff -} - // Lines returns the difference between old and new lines. func Lines(oldlines, newlines []Line, level int) (diff *Data) { diff = &Data{} @@ -209,6 +199,16 @@ func Lines(oldlines, newlines []Line, level int) (diff *Data) { return diff } +// Text returns the difference between old and new text. +func Text(old, new []byte, level int) (diff *Data) { + oldlines := ParseLines(old) + newlines := ParseLines(new) + diff = Lines(oldlines, newlines, level) + diff.OldName = `old` + diff.NewName = `new` + return diff +} + // checkIsMatched set the IsMatched to true if no changes found. func (diff *Data) checkIsMatched() { if len(diff.Adds) != 0 { @@ -223,19 +223,32 @@ func (diff *Data) checkIsMatched() { diff.IsMatched = true } -// PushAdd will add new line to diff set. +// GetAllAdds returns chunks of additions including the line changes. +func (diff Data) GetAllAdds() (chunks text.Chunks) { + for _, add := range diff.Adds { + chunks = append(chunks, text.Chunk{StartAt: 0, V: add.Val}) + } + chunks = append(chunks, diff.Changes.GetAllAdds()...) + return +} + +// GetAllDels returns chunks of deletions including the line changes. +func (diff Data) GetAllDels() (chunks text.Chunks) { + for _, del := range diff.Dels { + chunks = append(chunks, text.Chunk{StartAt: 0, V: del.Val}) + } + chunks = append(chunks, diff.Changes.GetAllDels()...) + return +} + +// PushAdd adds new line to slice of Adds. func (diff *Data) PushAdd(new Line) { new.Kind = LineKindAdd diff.Adds = append(diff.Adds, new) } -// PushDel will add deletion line to diff set. -func (diff *Data) PushDel(old Line) { - old.Kind = LineKindDel - diff.Dels = append(diff.Dels, old) -} - -// PushChange set to diff data. +// PushChange adds the old and new line to Dels, Adds, and [Data.Changes] +// respectively. func (diff *Data) PushChange(old, new Line) { if old.Num != new.Num { old.NumOther, new.NumOther = new.Num, old.Num @@ -246,27 +259,39 @@ func (diff *Data) PushChange(old, new Line) { diff.PushAdd(new) } -// GetAllAdds return chunks of additions including in line changes. -func (diff Data) GetAllAdds() (chunks text.Chunks) { - for _, add := range diff.Adds { - chunks = append(chunks, text.Chunk{StartAt: 0, V: add.Val}) - } - chunks = append(chunks, diff.Changes.GetAllAdds()...) - return -} - -// GetAllDels return chunks of deletions including in line changes. -func (diff Data) GetAllDels() (chunks text.Chunks) { - for _, del := range diff.Dels { - chunks = append(chunks, text.Chunk{StartAt: 0, V: del.Val}) - } - chunks = append(chunks, diff.Changes.GetAllDels()...) - return +// PushDel adds deletion line to slice of Dels. +func (diff *Data) PushDel(old Line) { + old.Kind = LineKindDel + diff.Dels = append(diff.Dels, old) } -// String return formatted data. +// String returns the additions and deletions with custom format. +// The custom format as follow, +// +// --- <OldName> +// +++ <NewName> +// ---- +// <NumOther>/ <Num>: <Kind><QuotedVal> +// ++++ +// <NumOther>/ <Num>: <Kind><Val> +// +// The lines after "----" is the deleted lines from the old text. +// The lines after "++++" is the added lines from the new text. +// +// The NumOther is the line number from the other text. +// The Num is the line number where line is deleted in old text, or added in +// new text. +// +// The Kind is '-' for deletion or '+' for addition. +// +// The QuotedVal value is the line text printed inside double quotes. func (diff Data) String() (s string) { + if diff.IsMatched { + return `` + } var sb strings.Builder + fmt.Fprintf(&sb, "--- %s\n", diff.OldName) + fmt.Fprintf(&sb, "+++ %s\n", diff.NewName) if len(diff.Dels) > 0 { sb.WriteString("----\n") for _, line := range diff.Dels { diff --git a/lib/text/diff/diff_example_test.go b/lib/text/diff/diff_example_test.go new file mode 100644 index 00000000..2b3709d3 --- /dev/null +++ b/lib/text/diff/diff_example_test.go @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2026 M. Shulhan <ms@kilabit.info> + +package diff_test + +import ( + "fmt" + "log" + + "git.sr.ht/~shulhan/pakakeh.go/lib/text/diff" +) + +func ExampleFiles() { + diff, err := diff.Files(`testdata/lao.txt`, `testdata/tzu.txt`, diff.LevelLines) + if err != nil { + log.Fatal(err) + } + fmt.Println(diff.String()) + + // Output: + // --- testdata/lao.txt + // +++ testdata/tzu.txt + // ---- + // 1/ 1: -"The Way that can be told of is not the eternal Way;" + // 2/ 2: -"The name that can be named is not the eternal name." + // 2/ 4: -"The Named is the mother of all things." + // ++++ + // 4/ 2: +"The named is the mother of all things." + // 3/ 3: +"" + // 11/ 11: +"They both may be called deep and profound." + // 12/ 12: +"Deeper and more profound," + // 13/ 13: +"The door of all subtleties!" +} + +func ExampleLines() { + lao := `The Way that can be told of is not the eternal Way; +The name that can be named is not the eternal name. +The Nameless is the origin of Heaven and Earth; +The Named is the mother of all things. +Therefore let there always be non-being, + so we may see their subtlety, +And let there always be being, + so we may see their outcome. +The two are the same, +But after they are produced, + they have different names.` + + tzu := `The Nameless is the origin of Heaven and Earth; +The named is the mother of all things. + +Therefore let there always be non-being, + so we may see their subtlety, +And let there always be being, + so we may see their outcome. +The two are the same, +But after they are produced, + they have different names. +They both may be called deep and profound. +Deeper and more profound, +The door of all subtleties! +` + oldlines := diff.ParseLines([]byte(lao)) + newlines := diff.ParseLines([]byte(tzu)) + diff := diff.Lines(oldlines, newlines, diff.LevelLines) + fmt.Println(diff.String()) + + // Output: + // --- oldlines + // +++ newlines + // ---- + // 1/ 1: -"The Way that can be told of is not the eternal Way;" + // 2/ 2: -"The name that can be named is not the eternal name." + // 2/ 4: -"The Named is the mother of all things." + // ++++ + // 4/ 2: +"The named is the mother of all things." + // 3/ 3: +"" + // 11/ 11: +"They both may be called deep and profound." + // 12/ 12: +"Deeper and more profound," + // 13/ 13: +"The door of all subtleties!" +} + +func ExampleText() { + lao := `The Way that can be told of is not the eternal Way; +The name that can be named is not the eternal name. +The Nameless is the origin of Heaven and Earth; +The Named is the mother of all things. +Therefore let there always be non-being, + so we may see their subtlety, +And let there always be being, + so we may see their outcome. +The two are the same, +But after they are produced, + they have different names.` + + tzu := `The Nameless is the origin of Heaven and Earth; +The named is the mother of all things. + +Therefore let there always be non-being, + so we may see their subtlety, +And let there always be being, + so we may see their outcome. +The two are the same, +But after they are produced, + they have different names. +They both may be called deep and profound. +Deeper and more profound, +The door of all subtleties! +` + diff := diff.Text([]byte(lao), []byte(tzu), diff.LevelLines) + fmt.Println(diff.String()) + + // Output: + // --- old + // +++ new + // ---- + // 1/ 1: -"The Way that can be told of is not the eternal Way;" + // 2/ 2: -"The name that can be named is not the eternal name." + // 2/ 4: -"The Named is the mother of all things." + // ++++ + // 4/ 2: +"The named is the mother of all things." + // 3/ 3: +"" + // 11/ 11: +"They both may be called deep and profound." + // 12/ 12: +"Deeper and more profound," + // 13/ 13: +"The door of all subtleties!" +} diff --git a/lib/text/diff/line.go b/lib/text/diff/line.go index e7a4825c..b7d428c7 100644 --- a/lib/text/diff/line.go +++ b/lib/text/diff/line.go @@ -54,12 +54,24 @@ func ReadLines(file string) (lines []Line, err error) { return lines, nil } +// WriteLines writes each line in lines into w using the [Line.String] return +// value. func WriteLines(w io.Writer, lines []Line) { for _, line := range lines { fmt.Fprintln(w, line.String()) } } +// Strings returns the formatted line data. +// The format is as follow, +// +// <NumOther>/<Num>: <Kind><QuotedVal> +// +// The NumOther is line number from other text being compared. +// The Num is line number. +// The Kind is '-' for deletion or '+' for addition. +// The QuotedVal is double quoted line text, to help human compare and see the +// changes for non-printable characters. func (line Line) String() string { - return fmt.Sprintf("%4d/%4d: %c%s", line.NumOther, line.Num, line.Kind, line.Val) + return fmt.Sprintf("%4d/%4d: %c%q", line.NumOther, line.Num, line.Kind, line.Val) } diff --git a/lib/text/diff/testdata/lao.txt b/lib/text/diff/testdata/lao.txt new file mode 100644 index 00000000..635ef2c4 --- /dev/null +++ b/lib/text/diff/testdata/lao.txt @@ -0,0 +1,11 @@ +The Way that can be told of is not the eternal Way; +The name that can be named is not the eternal name. +The Nameless is the origin of Heaven and Earth; +The Named is the mother of all things. +Therefore let there always be non-being, + so we may see their subtlety, +And let there always be being, + so we may see their outcome. +The two are the same, +But after they are produced, + they have different names. diff --git a/lib/text/diff/testdata/tzu.txt b/lib/text/diff/testdata/tzu.txt new file mode 100644 index 00000000..5af88a8f --- /dev/null +++ b/lib/text/diff/testdata/tzu.txt @@ -0,0 +1,13 @@ +The Nameless is the origin of Heaven and Earth; +The named is the mother of all things. + +Therefore let there always be non-being, + so we may see their subtlety, +And let there always be being, + so we may see their outcome. +The two are the same, +But after they are produced, + they have different names. +They both may be called deep and profound. +Deeper and more profound, +The door of all subtleties! diff --git a/lib/text/diff/unified_example_test.go b/lib/text/diff/unified_example_test.go new file mode 100644 index 00000000..20cb971a --- /dev/null +++ b/lib/text/diff/unified_example_test.go @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2026 M. Shulhan <ms@kilabit.info> + +package diff_test + +import ( + "fmt" + "strings" + + "git.sr.ht/~shulhan/pakakeh.go/lib/text/diff" +) + +func ExampleUnified() { + lao := `The Way that can be told of is not the eternal Way; +The name that can be named is not the eternal name. +The Nameless is the origin of Heaven and Earth; +The Named is the mother of all things. +Therefore let there always be non-being, + so we may see their subtlety, +And let there always be being, + so we may see their outcome. +The two are the same, +But after they are produced, + they have different names.` + + tzu := `The Nameless is the origin of Heaven and Earth; +The named is the mother of all things. + +Therefore let there always be non-being, + so we may see their subtlety, +And let there always be being, + so we may see their outcome. +The two are the same, +But after they are produced, + they have different names. +They both may be called deep and profound. +Deeper and more profound, +The door of all subtleties! +` + dif := diff.Unified([]byte(lao), []byte(tzu)) + var sb strings.Builder + dif.WriteUnified(&sb, 3) + fmt.Println(sb.String()) + + // Output: + // --- old + // +++ new + // @@ -1,7 +1,6 @@ + // -The Way that can be told of is not the eternal Way; + // -The name that can be named is not the eternal name. + // The Nameless is the origin of Heaven and Earth; + // -The Named is the mother of all things. + // +The named is the mother of all things. + // + + // Therefore let there always be non-being, + // so we may see their subtlety, + // And let there always be being, + // @@ -9,3 +8,6 @@ + // The two are the same, + // But after they are produced, + // they have different names. + // +They both may be called deep and profound. + // +Deeper and more profound, + // +The door of all subtleties! +} |
