github.com/daixiang0/gci@v0.13.0/pkg/analyzer/diff.go (about)

     1  package analyzer
     2  
     3  import (
     4  	"bytes"
     5  	"go/token"
     6  	"regexp"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/pmezard/go-difflib/difflib"
    11  	"golang.org/x/tools/go/analysis"
    12  )
    13  
    14  var hunkRE = regexp.MustCompile(`@@ -(\d+),(\d+) \+\d+,\d+ @@`)
    15  
    16  func GetSuggestedFix(file *token.File, a, b []byte) (*analysis.SuggestedFix, error) {
    17  	d := difflib.UnifiedDiff{
    18  		A:       difflib.SplitLines(string(a)),
    19  		B:       difflib.SplitLines(string(b)),
    20  		Context: 1,
    21  	}
    22  	diff, err := difflib.GetUnifiedDiffString(d)
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  	if diff == "" {
    27  		return nil, nil
    28  	}
    29  	var (
    30  		fix   analysis.SuggestedFix
    31  		found = false
    32  		edit  analysis.TextEdit
    33  		buf   bytes.Buffer
    34  	)
    35  	for _, line := range strings.Split(diff, "\n") {
    36  		if hunk := hunkRE.FindStringSubmatch(line); len(hunk) > 0 {
    37  			if found {
    38  				edit.NewText = buf.Bytes()
    39  				buf = bytes.Buffer{}
    40  				fix.TextEdits = append(fix.TextEdits, edit)
    41  				edit = analysis.TextEdit{}
    42  			}
    43  			found = true
    44  			start, err := strconv.Atoi(hunk[1])
    45  			if err != nil {
    46  				return nil, err
    47  			}
    48  			lines, err := strconv.Atoi(hunk[2])
    49  			if err != nil {
    50  				return nil, err
    51  			}
    52  			edit.Pos = file.LineStart(start)
    53  			end := start + lines
    54  			if end > file.LineCount() {
    55  				edit.End = token.Pos(file.Size())
    56  			} else {
    57  				edit.End = file.LineStart(end)
    58  			}
    59  			continue
    60  		}
    61  		// skip any lines until first hunk found
    62  		if !found {
    63  			continue
    64  		}
    65  		if line == "" {
    66  			continue
    67  		}
    68  		switch line[0] {
    69  		case '+':
    70  			buf.WriteString(line[1:])
    71  			buf.WriteRune('\n')
    72  		case '-':
    73  			// just skip
    74  		case ' ':
    75  			buf.WriteString(line[1:])
    76  			buf.WriteRune('\n')
    77  		}
    78  	}
    79  	edit.NewText = buf.Bytes()
    80  	fix.TextEdits = append(fix.TextEdits, edit)
    81  
    82  	return &fix, nil
    83  }