Skip to content

Commit 6401923

Browse files
authored
Warning dialog for converting changes to suggestions can be incorrect and vague (#6440)
Fixes #6263
1 parent ec5d189 commit 6401923

2 files changed

Lines changed: 75 additions & 16 deletions

File tree

src/common/diffHunk.ts

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -179,26 +179,83 @@ export function parsePatch(patch: string): DiffHunk[] {
179179
let diffHunkIter = diffHunkReader.next();
180180
const diffHunks: DiffHunk[] = [];
181181

182-
const right: string[] = [];
183182
while (!diffHunkIter.done) {
184183
const diffHunk = diffHunkIter.value;
185184
diffHunks.push(diffHunk);
185+
diffHunkIter = diffHunkReader.next();
186+
}
186187

187-
for (let j = 0; j < diffHunk.diffLines.length; j++) {
188-
const diffLine = diffHunk.diffLines[j];
189-
if (diffLine.type === DiffChangeType.Delete || diffLine.type === DiffChangeType.Control) {
190-
} else if (diffLine.type === DiffChangeType.Add) {
191-
right.push(diffLine.text);
192-
} else {
193-
const codeInFirstLine = diffLine.text;
194-
right.push(codeInFirstLine);
188+
return diffHunks;
189+
}
190+
191+
/**
192+
* Split a hunk into smaller hunks based on the context lines. Position in hunk and control lines are not preserved.
193+
*/
194+
export function splitIntoSmallerHunks(hunk: DiffHunk): DiffHunk[] {
195+
const splitHunks: DiffHunk[] = [];
196+
const newHunk = (fromLine: DiffLine) => {
197+
return {
198+
diffLines: [],
199+
newLength: 0,
200+
oldLength: 0,
201+
oldLineNumber: fromLine.oldLineNumber,
202+
newLineNumber: fromLine.newLineNumber,
203+
positionInHunk: 0
204+
};
205+
};
206+
207+
// Split hunk into smaller hunks on context lines.
208+
// Context lines will be duplicated across the new smaller hunks
209+
let currentHunk: DiffHunk | undefined;
210+
let nextHunk: DiffHunk | undefined;
211+
212+
const addLineToHunk = (hunk: DiffHunk, line: DiffLine) => {
213+
hunk.diffLines.push(line);
214+
if (line.type === DiffChangeType.Delete) {
215+
hunk.oldLength++;
216+
} else if (line.type === DiffChangeType.Add) {
217+
hunk.newLength++;
218+
} else if (line.type === DiffChangeType.Context) {
219+
hunk.oldLength++;
220+
hunk.newLength++;
221+
}
222+
};
223+
const hunkHasChanges = (hunk: DiffHunk) => {
224+
return hunk.diffLines.some(line => line.type !== DiffChangeType.Context);
225+
};
226+
const hunkHasSandwichedChanges = (hunk: DiffHunk) => {
227+
return hunkHasChanges(hunk) && hunk.diffLines[hunk.diffLines.length - 1].type === DiffChangeType.Context;
228+
};
229+
230+
for (const line of hunk.diffLines) {
231+
if (line.type === DiffChangeType.Context) {
232+
if (!currentHunk) {
233+
currentHunk = newHunk(line);
234+
}
235+
addLineToHunk(currentHunk, line);
236+
if (hunkHasSandwichedChanges(currentHunk)) {
237+
if (!nextHunk) {
238+
nextHunk = newHunk(line);
239+
}
240+
addLineToHunk(nextHunk, line);
241+
}
242+
} else if (currentHunk) {
243+
if (hunkHasSandwichedChanges(currentHunk)) {
244+
splitHunks.push(currentHunk);
245+
currentHunk = nextHunk!;
246+
nextHunk = undefined;
247+
}
248+
if ((line.type === DiffChangeType.Delete) || (line.type === DiffChangeType.Add)) {
249+
addLineToHunk(currentHunk, line);
195250
}
196251
}
252+
}
197253

198-
diffHunkIter = diffHunkReader.next();
254+
if (currentHunk) {
255+
splitHunks.push(currentHunk);
199256
}
200257

201-
return diffHunks;
258+
return splitHunks;
202259
}
203260

204261
export function getModifiedContentFromDiffHunk(originalContent: string, patch: string) {

src/view/reviewManager.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as vscode from 'vscode';
88
import type { Branch, Repository } from '../api/api';
99
import { GitApiImpl, GitErrorCodes, Status } from '../api/api1';
1010
import { openDescription } from '../commands';
11-
import { DiffChangeType, DiffHunk, parsePatch } from '../common/diffHunk';
11+
import { DiffChangeType, DiffHunk, parsePatch, splitIntoSmallerHunks } from '../common/diffHunk';
1212
import { commands } from '../common/executeCommands';
1313
import { GitChangeType, InMemFileChange, SlimFileChange } from '../common/file';
1414
import Logger from '../common/logger';
@@ -723,10 +723,12 @@ export class ReviewManager {
723723
let oldLineNumber = hunk.oldLineNumber;
724724
let oldLength = hunk.oldLength;
725725

726-
// start at 1 to skip the control line
727-
let i = 1;
726+
let i = 0;
728727
for (; i < hunk.diffLines.length; i++) {
729728
const line = hunk.diffLines[i];
729+
if (line.type === DiffChangeType.Control) {
730+
continue;
731+
}
730732
if (line.type === DiffChangeType.Context) {
731733
oldLineNumber++;
732734
oldLength--;
@@ -749,7 +751,7 @@ export class ReviewManager {
749751
// we have only inserted lines, so we need to include a context line so that
750752
// there's a line to anchor the suggestion to
751753
if (i > 1) {
752-
// include from the begginning of the hunk
754+
// include from the beginning of the hunk
753755
i--;
754756
oldLineNumber--;
755757
oldLength++;
@@ -789,7 +791,7 @@ export class ReviewManager {
789791
if (!resourceStrings.includes(changeFile.uri.toString()) || (changeFile.status !== Status.MODIFIED)) {
790792
return;
791793
}
792-
diff = parsePatch(await this._folderRepoManager.repository.diffWithHEAD(changeFile.uri.fsPath));
794+
diff = parsePatch(await this._folderRepoManager.repository.diffWithHEAD(changeFile.uri.fsPath)).map(hunk => splitIntoSmallerHunks(hunk)).flat();
793795
await Promise.allSettled(diff.map(async hunk => {
794796
try {
795797
await this._reviewCommentController?.createSuggestionsFromChanges(changeFile.uri, this.convertDiffHunkToSuggestion(hunk));

0 commit comments

Comments
 (0)