difflib

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 14, 2024 License: BSD-3-Clause Imports: 7 Imported by: 0

README

Difflib

Difflib is a fork of https://github.com/ianbruene/go-difflib, which itself is a fork of https://github.com/pmezard/go-difflib

Difflib is an as yet partial port of python 3's difflib package.

The following publicly visible classes and functions have been ported:

  • SequenceMatcher
  • Differ
  • unified_diff()
  • context_diff()

Installation

$ go get cogentcore.org/core/difflib
UnifiedDiff Quick Start

Diffs are configured with Unified (or ContextDiff) structures, and can be output to an io.Writer or returned as a string.

diff := difflib.LineDiffParams{
    A:        difflib.SplitLines("foo\nbar\n"),
    B:        difflib.SplitLines("foo\nbaz\n"),
    FromFile: "Original",
    ToFile:   "Current",
    Context:  3,
}
text, _ := difflib.GetUnifiedDiffString(diff)
fmt.Printf(text)

would output:

--- Original
+++ Current
@@ -1,3 +1,3 @@
 foo
-bar
+baz
Differ Quick Start

Differ has been implemented primarily for the Compare() function at this time.

diff := difflib.NewDiffer()
out, err := diff.Compare(
    []string{"foo\n", "bar\n", "baz\n"},
	[]string{"foo\n", "bar1\n", "asdf\n", "baz\n"})

would output:

  foo
- bar
+ bar1
?    +
+ asdf
  baz

Documentation

Overview

Package difflib is a partial port of Python difflib module.

It provides tools to compare sequences of strings and generate textual diffs.

The following class and functions have been ported:

- SequenceMatcher

- unified_diff

- context_diff

Getting unified diffs was the main goal of the port. Keep in mind this code is mostly suitable to output text differences in a human friendly way, there are no guarantees generated diffs are consumable by patch(1).

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetContextDiffString

func GetContextDiffString(diff LineDiffParams) (string, error)

GetContextDiffString is like WriteContextDiff but returns the diff as a string.

Example
a := `one
two
three
four`
b := `zero
one
tree
four`
diff := ContextDiff{
	A:        SplitLines(a),
	B:        SplitLines(b),
	FromFile: "Original",
	ToFile:   "Current",
	Context:  3,
	Eol:      "\n",
}
result, _ := GetContextDiffString(diff)
fmt.Print(strings.Replace(result, "\t", " ", -1))
Output:

*** Original
--- Current
***************
*** 1,4 ****
  one
! two
! three
  four
--- 1,4 ----
+ zero
  one
! tree
  four
Example (Fmt)
a := `one
two
three
four
fmt.Printf("%s,%T",a,b)`
b := `zero
one
tree
four`
diff := ContextDiff{
	A:        SplitLines(a),
	B:        SplitLines(b),
	FromFile: "Original",
	ToFile:   "Current",
	Context:  3,
	Eol:      "\n",
}
result, _ := GetContextDiffString(diff)
fmt.Print(strings.Replace(result, "\t", " ", -1))
Output:

*** Original
--- Current
***************
*** 1,5 ****
  one
! two
! three
  four
- fmt.Printf("%s,%T",a,b)
--- 1,4 ----
+ zero
  one
! tree
  four

func GetUnifiedDiffString

func GetUnifiedDiffString(diff LineDiffParams) (string, error)

GetUnifiedDiffString is like WriteUnifiedDiff but returns the diff a string.

Example
a := `one
two
three
four
fmt.Printf("%s,%T",a,b)`
b := `zero
one
three
four`
diff := LineDiffParams{
	A:        SplitLines(a),
	B:        SplitLines(b),
	FromFile: "Original",
	FromDate: "2005-01-26 23:30:50",
	ToFile:   "Current",
	ToDate:   "2010-04-02 10:20:52",
	Context:  3,
}
result, _ := GetUnifiedDiffString(diff)
fmt.Println(strings.Replace(result, "\t", " ", -1))
Output:

--- Original 2005-01-26 23:30:50
+++ Current 2010-04-02 10:20:52
@@ -1,5 +1,4 @@
+zero
 one
-two
 three
 four
-fmt.Printf("%s,%T",a,b)

func SplitLines

func SplitLines(s string) []string

SplitLines splits a string on "\n" while preserving them. The output can be used as input for LineDiffParams.

func WriteContextDiff

func WriteContextDiff(writer io.Writer, diff LineDiffParams) error

WriteContextDiff compares two sequences of lines and generates the delta as a context diff.

Context diffs are a compact way of showing line changes and a few lines of context. The number of context lines is set by diff.Context which defaults to three.

By default, the diff control lines (those with *** or ---) are created with a trailing newline.

For inputs that do not have trailing newlines, set the diff.Eol argument to "" so that the output will be uniformly newline free.

The context diff format normally has a header for filenames and modification times. Any or all of these may be specified using strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate. The modification times are normally expressed in the ISO 8601 format. If not specified, the strings default to blanks.

func WriteUnifiedDiff

func WriteUnifiedDiff(writer io.Writer, diff LineDiffParams) error

WriteUnifiedDiff compares two sequences of lines and generates the delta as a unified diff.

Unified diffs are a compact way of showing line changes and a few lines of context. The number of context lines is set by 'n' which defaults to three.

By default, the diff control lines (those with ---, +++, or @@) are created with a trailing newline. This is helpful so that inputs created from file.readlines() result in diffs that are suitable for file.writelines() since both the inputs and outputs have trailing newlines.

For inputs that do not have trailing newlines, set the lineterm argument to "" so that the output will be uniformly newline free.

The unidiff format normally has a header for filenames and modification times. Any or all of these may be specified using strings for 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'. The modification times are normally expressed in the ISO 8601 format.

Types

type ContextDiff

type ContextDiff = LineDiffParams

ContextDiff is for backward compatibility. Ugh.

type DiffLine

type DiffLine struct {
	Tag  byte
	Line string
}

func NewDiffLine

func NewDiffLine(tag byte, line string) (l DiffLine)

type Differ

type Differ struct {
	Linejunk func(string) bool
	Charjunk func(string) bool
}

func NewDiffer

func NewDiffer() *Differ

func (*Differ) Compare

func (d *Differ) Compare(a []string, b []string) (diffs []string, err error)

func (*Differ) Dump

func (d *Differ) Dump(tag string, x []string, low int, high int) (out []string)

func (*Differ) FancyReplace

func (d *Differ) FancyReplace(a []string, alo int, ahi int, b []string, blo int, bhi int) (out []string, err error)

func (*Differ) PlainReplace

func (d *Differ) PlainReplace(a []string, alo int, ahi int, b []string, blo int, bhi int) (out []string, err error)

func (*Differ) QFormat

func (d *Differ) QFormat(aline string, bline string, atags string, btags string) (out []string)

func (*Differ) StructuredDump

func (d *Differ) StructuredDump(tag byte, x []string, low int, high int) (out []DiffLine)

type LineDiffParams

type LineDiffParams struct {
	A          []string          // First sequence lines
	FromFile   string            // First file name
	FromDate   string            // First file time
	B          []string          // Second sequence lines
	ToFile     string            // Second file name
	ToDate     string            // Second file time
	Eol        string            // Headers end of line, defaults to LF
	Context    int               // Number of context lines
	AutoJunk   bool              // If true, use autojunking
	IsJunkLine func(string) bool // How to spot junk lines
}

LineDiffParams contains unified diff parameters

type Match

type Match struct {
	A    int
	B    int
	Size int
}

type OpCode

type OpCode struct {
	Tag byte
	I1  int
	I2  int
	J1  int
	J2  int
}

type SequenceMatcher

type SequenceMatcher struct {
	IsJunk func(string) bool
	// contains filtered or unexported fields
}

SequenceMatcher compares sequence of strings. The basic algorithm predates, and is a little fancier than, an algorithm published in the late 1980's by Ratcliff and Obershelp under the hyperbolic name "gestalt pattern matching". The basic idea is to find the longest contiguous matching subsequence that contains no "junk" elements (R-O doesn't address junk). The same idea is then applied recursively to the pieces of the sequences to the left and to the right of the matching subsequence. This does not yield minimal edit sequences, but does tend to yield matches that "look right" to people.

SequenceMatcher tries to compute a "human-friendly diff" between two sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the longest *contiguous* & junk-free matching subsequence. That's what catches peoples' eyes. The Windows(tm) windiff has another interesting notion, pairing up elements that appear uniquely in each sequence. That, and the method here, appear to yield more intuitive difference reports than does diff. This method appears to be the least vulnerable to synching up on blocks of "junk lines", though (like blank lines in ordinary text files, or maybe "<P>" lines in HTML files). That may be because this is the only method of the 3 that has a *concept* of "junk" <wink>.

Timing: Basic R-O is cubic time worst case and quadratic time expected case. SequenceMatcher is quadratic time for the worst case and has expected-case behavior dependent in a complicated way on how many elements the sequences have in common; best case time is linear.

func NewMatcher

func NewMatcher(a, b []string) *SequenceMatcher

func NewMatcherWithJunk

func NewMatcherWithJunk(a, b []string, autoJunk bool,
	isJunk func(string) bool) *SequenceMatcher

func (*SequenceMatcher) GetGroupedOpCodes

func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode

GetGroupedOpCodes isolates change clusters by eliminating ranges with no changes.

Return a generator of groups with up to n lines of context. Each group is in the same format as returned by GetOpCodes().

func (*SequenceMatcher) GetMatchingBlocks

func (m *SequenceMatcher) GetMatchingBlocks() []Match

GetMatchingBlocks returns a list of triples describing matching subsequences.

Each triple is of the form (i, j, n), and means that a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are adjacent triples in the list, and the second is not the last triple in the list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe adjacent equal blocks.

The last triple is a dummy, (len(a), len(b), 0), and is the only triple with n==0.

func (*SequenceMatcher) GetOpCodes

func (m *SequenceMatcher) GetOpCodes() []OpCode

GetOpCodes returns a list of 5-tuples describing how to turn a into b.

Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the tuple preceding it, and likewise for j1 == the previous j2.

The tags are characters, with these meanings:

'r' (replace): a[i1:i2] should be replaced by b[j1:j2]

'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case.

'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.

'e' (equal): a[i1:i2] == b[j1:j2]

func (*SequenceMatcher) QuickRatio

func (m *SequenceMatcher) QuickRatio() float64

QuickRatio returns an upper bound on ratio() relatively quickly.

This isn't defined beyond that it is an upper bound on .Ratio(), and is faster to compute.

func (*SequenceMatcher) Ratio

func (m *SequenceMatcher) Ratio() float64

Ratio returns a measure of the sequences' similarity (float in [0,1]).

Where T is the total number of elements in both sequences, and M is the number of matches, this is 2.0*M / T. Note that this is 1 if the sequences are identical, and 0 if they have nothing in common.

.Ratio() is expensive to compute if you haven't already computed .GetMatchingBlocks() or .GetOpCodes(), in which case you may want to try .QuickRatio() or .RealQuickRation() first to get an upper bound.

func (*SequenceMatcher) RealQuickRatio

func (m *SequenceMatcher) RealQuickRatio() float64

RealQuickRatio returns an upper bound on ratio() very quickly.

This isn't defined beyond that it is an upper bound on .Ratio(), and is faster to compute than either .Ratio() or .QuickRatio().

func (*SequenceMatcher) SetSeq1

func (m *SequenceMatcher) SetSeq1(a []string)

SetSeq1 sets the first sequence to be compared. The second sequence to be compared is not changed.

SequenceMatcher computes and caches detailed information about the second sequence, so if you want to compare one sequence S against many sequences, use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other sequences.

See also SetSeqs() and SetSeq2().

func (*SequenceMatcher) SetSeq2

func (m *SequenceMatcher) SetSeq2(b []string)

SetSeq2 sets the second sequence to be compared. The first sequence to be compared is not changed.

func (*SequenceMatcher) SetSeqs

func (m *SequenceMatcher) SetSeqs(a, b []string)

SetSeqs sets two sequences to be compared.

type UnifiedDiff

type UnifiedDiff = LineDiffParams

Directories

Path Synopsis
Package bytes is a partial port of Python difflib module for bytes.
Package bytes is a partial port of Python difflib module for bytes.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL