@@ -77,6 +77,19 @@ func (f Fixer) fixIssuesInFile(filePath string, issues []result.Issue) error {
77
77
return errors .Wrapf (err , "failed to make file %s" , tmpFileName )
78
78
}
79
79
80
+ // merge multiple issues per line into one issue
81
+ issuesPerLine := map [int ][]result.Issue {}
82
+ for _ , i := range issues {
83
+ issuesPerLine [i .Line ()] = append (issuesPerLine [i .Line ()], i )
84
+ }
85
+
86
+ issues = issues [:0 ] // reuse the same memory
87
+ for line , lineIssues := range issuesPerLine {
88
+ if mergedIssue := f .mergeLineIssues (line , lineIssues , origFileLines ); mergedIssue != nil {
89
+ issues = append (issues , * mergedIssue )
90
+ }
91
+ }
92
+
80
93
issues = f .findNotIntersectingIssues (issues )
81
94
82
95
if err = f .writeFixedFile (origFileLines , issues , tmpOutFile ); err != nil {
@@ -94,9 +107,76 @@ func (f Fixer) fixIssuesInFile(filePath string, issues []result.Issue) error {
94
107
return nil
95
108
}
96
109
110
+ //nolint:gocyclo
111
+ func (f Fixer ) mergeLineIssues (lineNum int , lineIssues []result.Issue , origFileLines [][]byte ) * result.Issue {
112
+ origLine := origFileLines [lineNum - 1 ] // lineNum is 1-based
113
+
114
+ if len (lineIssues ) == 1 && lineIssues [0 ].Replacement .Inline == nil {
115
+ return & lineIssues [0 ]
116
+ }
117
+
118
+ // check issues first
119
+ for _ , i := range lineIssues {
120
+ if i .LineRange != nil {
121
+ f .log .Infof ("Line %d has multiple issues but at least one of them is ranged: %#v" , lineNum , lineIssues )
122
+ return & lineIssues [0 ]
123
+ }
124
+
125
+ r := i .Replacement
126
+ if r .Inline == nil || len (r .NewLines ) != 0 || r .NeedOnlyDelete {
127
+ f .log .Infof ("Line %d has multiple issues but at least one of them isn't inline: %#v" , lineNum , lineIssues )
128
+ return & lineIssues [0 ]
129
+ }
130
+
131
+ if r .Inline .StartCol < 0 || r .Inline .Length <= 0 || r .Inline .StartCol + r .Inline .Length > len (origLine ) {
132
+ f .log .Warnf ("Line %d (%q) has invalid inline fix: %#v, %#v" , lineNum , origLine , i , r .Inline )
133
+ return nil
134
+ }
135
+ }
136
+
137
+ return f .applyInlineFixes (lineIssues , origLine , lineNum )
138
+ }
139
+
140
+ func (f Fixer ) applyInlineFixes (lineIssues []result.Issue , origLine []byte , lineNum int ) * result.Issue {
141
+ sort .Slice (lineIssues , func (i , j int ) bool {
142
+ return lineIssues [i ].Replacement .Inline .StartCol < lineIssues [j ].Replacement .Inline .StartCol
143
+ })
144
+
145
+ var newLineBuf bytes.Buffer
146
+ newLineBuf .Grow (len (origLine ))
147
+
148
+ //nolint:misspell
149
+ // example: origLine="it's becouse of them", StartCol=5, Length=7, NewString="because"
150
+
151
+ curOrigLinePos := 0
152
+ for _ , i := range lineIssues {
153
+ fix := i .Replacement .Inline
154
+ if fix .StartCol < curOrigLinePos {
155
+ f .log .Warnf ("Line %d has multiple intersecting issues: %#v" , lineNum , lineIssues )
156
+ return nil
157
+ }
158
+
159
+ if curOrigLinePos != fix .StartCol {
160
+ newLineBuf .Write (origLine [curOrigLinePos :fix .StartCol ])
161
+ }
162
+ newLineBuf .WriteString (fix .NewString )
163
+ curOrigLinePos = fix .StartCol + fix .Length
164
+ }
165
+ if curOrigLinePos != len (origLine ) {
166
+ newLineBuf .Write (origLine [curOrigLinePos :])
167
+ }
168
+
169
+ mergedIssue := lineIssues [0 ] // use text from the first issue (it's not really used)
170
+ mergedIssue .Replacement = & result.Replacement {
171
+ NewLines : []string {newLineBuf .String ()},
172
+ }
173
+ return & mergedIssue
174
+ }
175
+
97
176
func (f Fixer ) findNotIntersectingIssues (issues []result.Issue ) []result.Issue {
98
177
sort .SliceStable (issues , func (i , j int ) bool {
99
- return issues [i ].Line () < issues [j ].Line () //nolint:scopelint
178
+ a , b := issues [i ], issues [j ] //nolint:scopelint
179
+ return a .Line () < b .Line ()
100
180
})
101
181
102
182
var ret []result.Issue
0 commit comments