Skip to content

Commit 8efad91

Browse files
Stores hunks and safety state on webview host, uses for ai operations and safety checks
1 parent b221e58 commit 8efad91

File tree

6 files changed

+81
-120
lines changed

6 files changed

+81
-120
lines changed

src/webviews/apps/plus/composer/components/app.ts

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
OpenOnboardingCommand,
2828
ReloadComposerCommand,
2929
} from '../../../../plus/composer/protocol';
30-
import { createCombinedDiffForCommit, updateHunkAssignments } from '../../../../plus/composer/utils';
30+
import { updateHunkAssignments } from '../../../../plus/composer/utils';
3131
import type { RepoButtonGroupClickEvent } from '../../../shared/components/repo-button-group';
3232
import { focusableBaseStyles } from '../../../shared/components/styles/lit/a11y.css';
3333
import { boxSizingBase } from '../../../shared/components/styles/lit/base.css';
@@ -1271,9 +1271,7 @@ export class ComposerApp extends LitElement {
12711271
private finishAndCommit() {
12721272
this._ipc.sendCommand(FinishAndCommitCommand, {
12731273
commits: this.state.commits,
1274-
hunks: this.hunksWithAssignments,
12751274
baseCommit: this.state.baseCommit,
1276-
safetyState: this.state.safetyState,
12771275
});
12781276
}
12791277

@@ -1288,7 +1286,6 @@ export class ComposerApp extends LitElement {
12881286
private handleReloadComposer() {
12891287
this.resetHistory();
12901288
this._ipc.sendCommand(ReloadComposerCommand, {
1291-
repoPath: this.state.safetyState.repoPath,
12921289
mode: this.state.mode,
12931290
});
12941291
}
@@ -1480,11 +1477,8 @@ export class ComposerApp extends LitElement {
14801477
this.saveToHistory();
14811478

14821479
this._ipc.sendCommand(GenerateCommitsCommand, {
1483-
hunks: hunksToGenerate,
1484-
// In preview mode, send empty commits array to overwrite existing commits
1485-
// In interactive mode, send existing commits to preserve them
1480+
hunkIndices: hunksToGenerate.map(hunk => hunk.index),
14861481
commits: this.isPreviewMode ? [] : this.state.commits,
1487-
hunkMap: this.state.hunkMap,
14881482
baseCommit: this.state.baseCommit,
14891483
customInstructions: customInstructions || undefined,
14901484
});
@@ -1499,15 +1493,9 @@ export class ComposerApp extends LitElement {
14991493
return;
15001494
}
15011495

1502-
// Create combined diff for the commit
1503-
const { patch } = createCombinedDiffForCommit(commit, this.hunksWithAssignments);
1504-
if (!patch) {
1505-
return;
1506-
}
1507-
15081496
this._ipc.sendCommand(GenerateCommitMessageCommand, {
15091497
commitId: commitId,
1510-
diff: patch,
1498+
commitHunkIndices: commit.hunkIndices,
15111499
overwriteExistingMessage: commit.message.trim() !== '',
15121500
});
15131501
}

src/webviews/apps/plus/composer/stateProvider.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,7 @@ export class ComposerStateProvider implements StateProvider<State> {
136136
...this._state,
137137
hunks: msg.params.hunks,
138138
commits: msg.params.commits,
139-
hunkMap: msg.params.hunkMap,
140139
baseCommit: msg.params.baseCommit,
141-
safetyState: msg.params.safetyState,
142140
loadingError: msg.params.loadingError,
143141
hasChanges: msg.params.hasChanges,
144142
safetyError: null, // Clear any existing safety errors

src/webviews/plus/composer/composerWebview.ts

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ import type {
2525
ComposerContext,
2626
ComposerGenerateCommitMessageEventData,
2727
ComposerGenerateCommitsEventData,
28+
ComposerHunk,
2829
ComposerLoadedErrorData,
30+
ComposerSafetyState,
2931
ComposerTelemetryEvent,
3032
FinishAndCommitParams,
3133
GenerateCommitMessageParams,
@@ -78,6 +80,7 @@ import {
7880
import type { ComposerWebviewShowingArgs } from './registration';
7981
import {
8082
convertToComposerDiffInfo,
83+
createCombinedDiffForCommit,
8184
createHunksFromDiffs,
8285
createSafetyState,
8386
getWorkingTreeDiffs,
@@ -98,6 +101,10 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
98101
private _repositorySubscription?: Disposable;
99102
private _currentRepository?: Repository;
100103

104+
// Hunk map and safety state
105+
private _hunks: ComposerHunk[] = [];
106+
private _safetyState: ComposerSafetyState;
107+
101108
// Telemetry context - tracks composer-specific data for getTelemetryContext
102109
private _context: ComposerContext;
103110

@@ -111,6 +118,15 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
111118
this.container.ai.onDidChangeModel(this.onAIModelChanged, this),
112119
);
113120
this._context = { ...baseContext };
121+
this._safetyState = {
122+
repoPath: '',
123+
headSha: '',
124+
hashes: {
125+
staged: null,
126+
unstaged: null,
127+
unified: null,
128+
},
129+
};
114130
}
115131

116132
dispose(): void {
@@ -308,7 +324,8 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
308324
// Allow composer to open with no changes - we'll handle this in the UI
309325
const hasChanges = Boolean(staged?.contents || unstaged?.contents);
310326

311-
const { hunkMap, hunks } = createHunksFromDiffs(staged?.contents, unstaged?.contents);
327+
const hunks = createHunksFromDiffs(staged?.contents, unstaged?.contents);
328+
this._hunks = hunks;
312329

313330
const baseCommit = getSettledValue(commitResult);
314331
if (baseCommit == null) {
@@ -359,6 +376,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
359376

360377
// Create safety state snapshot for validation
361378
const safetyState = await createSafetyState(repo, diffs, baseCommit.sha);
379+
this._safetyState = safetyState;
362380

363381
const aiEnabled = this.getAiEnabled();
364382
const aiModel = await this.container.ai.getModel(
@@ -395,15 +413,13 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
395413
return {
396414
...this.initialState,
397415
hunks: hunks,
398-
hunkMap: hunkMap,
399416
baseCommit: {
400417
sha: baseCommit.sha,
401418
message: baseCommit.message ?? '',
402419
repoName: repo.name,
403420
branchName: currentBranch.name,
404421
},
405422
commits: commits,
406-
safetyState: safetyState,
407423
aiEnabled: aiEnabled,
408424
ai: {
409425
model: aiModel,
@@ -533,9 +549,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
533549
await this.host.notify(DidReloadComposerNotification, {
534550
hunks: composerData.hunks,
535551
commits: composerData.commits,
536-
hunkMap: composerData.hunkMap,
537552
baseCommit: composerData.baseCommit,
538-
safetyState: composerData.safetyState,
539553
loadingError: composerData.loadingError,
540554
hasChanges: composerData.hasChanges,
541555
repositoryState: composerData.repositoryState,
@@ -788,14 +802,10 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
788802
await this.host.notify(DidStartGeneratingNotification, undefined);
789803

790804
// Transform the data for the AI service
791-
const hunks = params.hunks.map(hunk => ({
792-
index: hunk.index,
793-
fileName: hunk.fileName,
794-
diffHeader: hunk.diffHeader || `diff --git a/${hunk.fileName} b/${hunk.fileName}`,
795-
hunkHeader: hunk.hunkHeader,
796-
content: hunk.content,
797-
source: hunk.source,
798-
}));
805+
const hunks = [];
806+
for (const index of params.hunkIndices) {
807+
hunks.push({ ...this._hunks.find(m => m.index === index)!, assigned: true });
808+
}
799809

800810
const existingCommits = params.commits.map(commit => ({
801811
id: commit.id,
@@ -808,7 +818,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
808818
const result = await this.container.ai.generateCommits(
809819
hunks,
810820
existingCommits,
811-
params.hunkMap,
821+
this._hunks.map(m => ({ index: m.index, hunkHeader: m.hunkHeader })),
812822
{ source: 'composer', correlationId: this.host.instanceId },
813823
{
814824
cancellation: this._generateCommitsCancellation.token,
@@ -942,9 +952,28 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
942952
// Notify webview that commit message generation is starting
943953
await this.host.notify(DidStartGeneratingCommitMessageNotification, { commitId: params.commitId });
944954

955+
// Create combined diff for the commit
956+
const { patch } = createCombinedDiffForCommit(
957+
this._hunks.filter(h => params.commitHunkIndices.includes(h.index)),
958+
);
959+
if (!patch) {
960+
this._context.operations.generateCommitMessage.errorCount++;
961+
this._context.errors.operation.count++;
962+
// Send error notification for failure (not cancellation)
963+
this.sendTelemetryEvent('composer/action/generateCommitMessage/failed', {
964+
...eventData,
965+
'failure.reason': 'error',
966+
'failure.error.message': 'Failed to create diff for commit',
967+
});
968+
await this.host.notify(DidErrorAIOperationNotification, {
969+
operation: 'generate commit message',
970+
error: 'Failed to create diff for commit',
971+
});
972+
}
973+
945974
// Call the AI service to generate commit message
946975
const result = await this.container.ai.generateCommitMessage(
947-
params.diff,
976+
patch,
948977
{ source: 'composer', correlationId: this.host.instanceId },
949978
{
950979
cancellation: this._generateCommitMessageCancellation.token,
@@ -1032,7 +1061,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
10321061
await this.host.notify(DidStartCommittingNotification, undefined);
10331062

10341063
// Get the specific repository from the safety state
1035-
const repo = this.container.git.getRepository(params.safetyState.repoPath);
1064+
const repo = this.container.git.getRepository(this._safetyState.repoPath);
10361065
if (!repo) {
10371066
// Clear loading state and show safety error
10381067
await this.host.notify(DidFinishCommittingNotification, undefined);
@@ -1049,13 +1078,18 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
10491078
return;
10501079
}
10511080

1052-
// Extract hunk sources for smart validation
1053-
const hunksBeingCommitted = params.hunks.filter(hunk =>
1081+
const commitHunkIndices = params.commits.flatMap(c => c.hunkIndices);
1082+
const hunks: ComposerHunk[] = [];
1083+
for (const hunk of commitHunkIndices) {
1084+
hunks.push({ ...this._hunks.find(m => m.index === hunk)!, assigned: true });
1085+
}
1086+
1087+
const hunksBeingCommitted = hunks.filter(hunk =>
10541088
params.commits.some(c => c.hunkIndices.includes(hunk.index)),
10551089
);
10561090

10571091
// Validate repository safety state before proceeding
1058-
const validation = await validateSafetyState(repo, params.safetyState, hunksBeingCommitted);
1092+
const validation = await validateSafetyState(repo, this._safetyState, hunksBeingCommitted);
10591093
if (!validation.isValid) {
10601094
// Clear loading state and show safety error
10611095
await this.host.notify(DidFinishCommittingNotification, undefined);
@@ -1073,8 +1107,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
10731107
return;
10741108
}
10751109

1076-
// Convert composer data to ComposerDiffInfo format
1077-
const diffInfo = convertToComposerDiffInfo(params.commits, params.hunks);
1110+
const diffInfo = convertToComposerDiffInfo(params.commits, hunks);
10781111
const svc = this.container.git.getRepositoryService(repo.path);
10791112
if (!svc) {
10801113
this._context.errors.operation.count++;
@@ -1120,7 +1153,7 @@ export class ComposerWebviewProvider implements WebviewProvider<State, State, Co
11201153

11211154
if (
11221155
!validateResultingDiff(
1123-
params.safetyState,
1156+
this._safetyState,
11241157
await sha256(resultingDiff),
11251158
this._context.diff.unstagedIncluded,
11261159
)

src/webviews/plus/composer/mockData.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Mock data for the composer following the proper data model
22
// This represents the structure that would come from AI rebase results
33

4-
import type { ComposerCommit, ComposerHunk, ComposerHunkMap } from './protocol';
4+
import type { ComposerCommit, ComposerHunk } from './protocol';
55

66
// Mock hunks following the AI rebase result structure
77
export const mockHunks: ComposerHunk[] = [
@@ -350,20 +350,6 @@ export const mockCommits: ComposerCommit[] = [
350350

351351
// Callbacks are no longer used - replaced with IPC commands
352352

353-
// Mock hunk map (maps hunk indices to hunk headers for combined diff)
354-
export const mockHunkMap: ComposerHunkMap[] = [
355-
{ index: 1, hunkHeader: '@@ -0,0 +1,15 @@' },
356-
{ index: 2, hunkHeader: '@@ -0,0 +1,12 @@' },
357-
{ index: 3, hunkHeader: '@@ -0,0 +1,18 @@' },
358-
{ index: 4, hunkHeader: '@@ -0,0 +1,20 @@' },
359-
{ index: 5, hunkHeader: '@@ -0,0 +1,10 @@' },
360-
{ index: 6, hunkHeader: '@@ -0,0 +1,25 @@' },
361-
{ index: 7, hunkHeader: '@@ -0,0 +1,30 @@' },
362-
{ index: 8, hunkHeader: '@@ -0,0 +1,8 @@' },
363-
{ index: 9, hunkHeader: '@@ -0,0 +1,15 @@' },
364-
{ index: 10, hunkHeader: '@@ -0,0 +1,12 @@' },
365-
];
366-
367353
// Mock base commit
368354
export const mockBaseCommit = {
369355
sha: 'abc123def456789',

0 commit comments

Comments
 (0)