Skip to content

Conversation

ibolton336
Copy link
Member

@ibolton336 ibolton336 commented Aug 18, 2025

Resolves:
konveyor/kai#844
#689
#588

Summary by CodeRabbit

  • New Features

    • Streaming vertical inline diffs with per-file and per-block Accept/Reject actions, CodeLens controls, and a FileEditor utility; auto-apply-on-save option.
  • UI

    • "View with Decorations" replaces modal diff; Modified File actions now show a diff-status banner, refreshed primary actions, tooltips, and dark‑mode styling; diff status bar shows per-file green/red counts.
  • UX

    • Unified review flow driven by inline decorations and explicit Continue/Accept/Reject actions.
  • Removals

    • Legacy diff view, navigation, modal/hunk UIs, and related clipboard/path copy commands removed.

@ibolton336 ibolton336 requested a review from a team as a code owner August 18, 2025 20:32
@ibolton336 ibolton336 marked this pull request as draft August 18, 2025 20:32
Copy link

coderabbitai bot commented Aug 18, 2025

Walkthrough

Replaces the legacy diff-view and virtual-storage flows with a streaming "vertical diff" subsystem. Adds diff models and converters (vscode/src/diff/types.ts, diff/myers.ts), static-diff streaming adapter (staticDiffAdapter.ts), file/IDE helpers (ideUtils.ts, pathUtils.ts), decorations and handler/manager for vertical diffs (diff/vertical/*), a CodeLens provider, new VS Code commands for show/accept/reject/clear diffs and block-level actions, auto-accept-on-save config and status bar, and webview actions to drive the decorator-based workflow. Removes diffView, virtualStorage, many diff-related commands, and multiple legacy ModifiedFile UI components and exports.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~150 minutes

Possibly related PRs

Suggested reviewers

  • pranavgaikwad
  • djzager

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@ibolton336 ibolton336 force-pushed the decorators-3 branch 2 times, most recently from 8166a64 to d743288 Compare August 18, 2025 21:41
@ibolton336 ibolton336 marked this pull request as ready for review August 18, 2025 21:41
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Nitpick comments (35)
webview-ui/src/index.css (1)

155-156: New PF global warning token added — looks good; consider using it consistently

The token definition is fine. To maximize value, consider replacing hard-coded warning/orange usages in the UI (e.g., “pending” states) with this token for consistency and easy theming.

vscode/src/utilities/ideUtils.ts (2)

8-17: Prefer TextDecoder over Buffer for decoding; better portability and BOM handling

Using TextDecoder avoids Node-specific Buffer coupling and handles UTF-8 cleanly in modern VS Code hosts.

Apply this diff:

-    const content = await vscode.workspace.fs.readFile(uri);
-    return Buffer.from(content).toString("utf8");
+    const content = await vscode.workspace.fs.readFile(uri);
+    const decoder = new TextDecoder("utf-8");
+    return decoder.decode(content);

30-39: Open file UX options

Consider exposing optional arguments to control viewColumn and preview behavior to avoid hijacking the current editor tab unintentionally.

Possible extension:

-export async function openFile(filepath: string): Promise<void> {
+export async function openFile(
+  filepath: string,
+  options?: { viewColumn?: vscode.ViewColumn; preview?: boolean }
+): Promise<void> {
   try {
     const uri = vscode.Uri.file(filepath);
     const document = await vscode.workspace.openTextDocument(uri);
-    await vscode.window.showTextDocument(document);
+    await vscode.window.showTextDocument(document, {
+      viewColumn: options?.viewColumn,
+      preview: options?.preview ?? false,
+    });
webview-ui/src/components/ResolutionsPage/ModifiedFile/modifiedFileMessage.css (5)

130-153: Primary “view with decorations” button — consistent with PF tokens

Good use of PF primary tokens and explicit min-width for emphasis. Consider adding focus-visible styles to improve keyboard accessibility parity.

Example:

 .view-with-decorations-button:focus {
   box-shadow: 0 0 0 2px var(--pf-global--primary-color--100);
 }
+.view-with-decorations-button:focus-visible {
+  outline: 2px solid var(--pf-global--primary-color--200);
+  outline-offset: 2px;
+}

212-217: Duplicate “force icon visibility” rules — consolidate to reduce override noise

Two separate blocks force opacity/visibility (and then later add display). Merge them into one to avoid stacking specificity and future maintenance issues.

Apply this diff to keep a single consolidated block:

-.modified-file-actions .pf-v6-c-button svg,
-.modified-file-actions .pf-v6-c-button .pf-v6-c-button__icon {
-  opacity: 1 !important;
-  visibility: visible !important;
-}
-
-/* Ensure all SVG icons are visible in PatternFly v6 buttons */
-.modified-file-actions .pf-v6-c-button svg {
-  opacity: 1 !important;
-  visibility: visible !important;
-  display: inline-block !important;
-}
+/* Ensure all SVG icons are visible in PatternFly v6 buttons */
+.modified-file-actions .pf-v6-c-button svg,
+.modified-file-actions .pf-v6-c-button .pf-v6-c-button__icon {
+  opacity: 1 !important;
+  visibility: visible !important;
+  display: inline-block !important;
+}

Also applies to: 285-289


219-238: Icon color overrides for button variants — consider using currentColor

Instead of hard-coding to editor foreground, you could set svg { fill: currentColor } and drive color via the button’s color, reducing special-casing across variants. PF v6 typically respects currentColor for icons.

Example:

-.modified-file-actions .pf-v6-c-button.pf-m-primary svg {
-  color: var(--vscode-editor-foreground) !important;
-  fill: var(--vscode-editor-foreground) !important;
-}
+.modified-file-actions .pf-v6-c-button.pf-m-primary svg {
+  fill: currentColor !important;
+}

Then set the button’s color as appropriate for each variant and theme, letting icons inherit automatically.


291-325: Diff status banner — align warning/pending with new PF warning token

Banner styling looks good. If you plan to represent “pending” states here, consider leveraging the newly added --pf-global--warning-color--100 for borders and accents.


130-153: Adopt the new PF warning token for “pending” states across this component

To reduce hex scatter and improve theming, replace hard-coded #ffa500 with var(--pf-global--warning-color--100) (defined in index.css). This affects several pending-state rules not in the changed hunk; proposed patch below:

Additional patch (outside the selected lines):

-.hunk-item.hunk-pending {
-  border: 2px solid #ffa500;
-  border-left: 2px solid #ffa500 !important;
-  background-color: rgba(255, 165, 0, 0.05);
-  box-shadow: 0 2px 8px rgba(255, 165, 0, 0.2);
+.hunk-item.hunk-pending {
+  border: 2px solid var(--pf-global--warning-color--100);
+  border-left: 2px solid var(--pf-global--warning-color--100) !important;
+  background-color: color-mix(in srgb, var(--pf-global--warning-color--100) 12%, transparent);
+  box-shadow: 0 2px 8px color-mix(in srgb, var(--pf-global--warning-color--100) 20%, transparent);
 }
 
-.hunk-item.hunk-pending::before {
+.hunk-item.hunk-pending::before {
   content: "?";
   position: absolute;
   top: -8px;
   right: -8px;
   width: 20px;
   height: 20px;
-  background-color: #ffa500;
+  background-color: var(--pf-global--warning-color--100);
   color: white;
   border-radius: 50%;
   display: flex;
   align-items: center;
   justify-content: center;
   font-size: 12px;
   font-weight: bold;
   z-index: 10;
   animation: hunkPending 0.5s ease-in-out;
 }
 
-.hunk-item.hunk-pending .hunk-item-header {
-  border-bottom-color: #ffa500;
-}
+.hunk-item.hunk-pending .hunk-item-header {
+  border-bottom-color: var(--pf-global--warning-color--100);
+}
 
-.hunk-status-badge.hunk-status-pending {
-  background-color: #ffa500;
+.hunk-status-badge.hunk-status-pending {
+  background-color: var(--pf-global--warning-color--100);
   color: white;
-  border: 1px solid #ffa500;
+  border: 1px solid var(--pf-global--warning-color--100);
 }

Note: color-mix is broadly supported in modern Chromium, but if you need to avoid it for compatibility, keep rgba() variants derived from the token.

vscode/src/diff/types.ts (1)

5-38: Type surface is clear and cohesive

The DiffType/Line/Char and IDE interfaces look solid and straightforward. As a future enhancement, consider a discriminated union for ApplyState to avoid many optional fields and enforce state-specific shape at compile time.

Example (no immediate change required):

export type ApplyState =
  | { status: "streaming"; numDiffs?: number; filepath?: string; streamId: string; toolCallId?: string }
  | { status: "done"; numDiffs?: number; fileContent?: string; filepath?: string }
  | { status: "closed"; filepath?: string; streamId?: string; toolCallId?: string };
vscode/src/diff/myers.ts (2)

31-41: Whitespace-only changes are being collapsed — verify intent or make configurable

You’re merging consecutive old/new pairs when their lines are equal after trimming. This hides whitespace-only edits from the diff and prevents them from being accepted/applied downstream. If that’s not strictly intended, gate it behind an option so callers can opt in.

Proposed change (preserves current behavior by default):

-export function myersDiff(oldContent: string, newContent: string): DiffLine[] {
+export function myersDiff(
+  oldContent: string,
+  newContent: string,
+  options: { ignoreWhitespaceOnly?: boolean } = { ignoreWhitespaceOnly: true },
+): DiffLine[] {
@@
-  // Combine consecutive old/new pairs that are identical after trimming
-  for (let i = 0; i < ourFormat.length - 1; i++) {
-    if (
-      ourFormat[i]?.type === "old" &&
-      ourFormat[i + 1]?.type === "new" &&
-      ourFormat[i].line.trim() === ourFormat[i + 1].line.trim()
-    ) {
-      ourFormat[i] = { type: "same", line: ourFormat[i].line };
-      ourFormat.splice(i + 1, 1);
-    }
-  }
+  if (options.ignoreWhitespaceOnly) {
+    // Combine consecutive old/new pairs that are identical after trimming
+    for (let i = 0; i < ourFormat.length - 1; i++) {
+      if (
+        ourFormat[i]?.type === "old" &&
+        ourFormat[i + 1]?.type === "new" &&
+        ourFormat[i].line.trim() === ourFormat[i + 1].line.trim()
+      ) {
+        // Prefer the "new" line when collapsing to preserve latest whitespace
+        ourFormat[i] = { type: "same", line: ourFormat[i + 1].line };
+        ourFormat.splice(i + 1, 1);
+        i--; // Re-check current index after splice to catch chains
+      }
+    }
+  }

Note: When collapsing, consider preferring the “new” line content so accepted diffs preserve the new whitespace.


55-206: Char indices use UTF-16 code units — verify consumer expectations

diffChars and string.length operate in UTF-16 code units. If any consumer maps indices to editor columns or grapheme clusters (e.g., emojis, combined glyphs), this can misalign. If you need grapheme awareness, iterate with Intl.Segmenter or a grapheme splitter. Otherwise, document that indices are UTF-16-based.

webview-ui/src/components/ResolutionsPage/ModifiedFile/modifiedFileActions.css (4)

57-60: Duplicate selector for .pf-v6-c-tooltip__content; consolidate to a single block

You define .pf-v6-c-tooltip__content twice. Combine them to reduce CSS duplication and potential drift.

Apply this consolidation:

 .pf-v6-c-tooltip__content {
-  background-color: var(--vscode-editor-background) !important;
-  color: var(--vscode-editor-foreground) !important;
-}
-
-/* Fix tooltip text alignment */
-.pf-v6-c-tooltip__content {
-  text-align: left !important;
+  background-color: var(--vscode-editor-background) !important;
+  color: var(--vscode-editor-foreground) !important;
+  text-align: left !important;
 }

Also applies to: 81-83


21-24: Potential specificity/override confusion for .continue-button margin-left

Two rules set margin-left on .continue-button, with different values. The more specific .diff-status-banner .continue-button will win inside the banner, but the global .continue-button may surprise in other contexts. Consider scoping the border and spacing consistently, or drop margin-left from the global rule if not needed elsewhere.

Also applies to: 45-48


31-40: Minimum widths are helpful; consider responsive adjustments

Fixed min-widths ensure stable layouts, but can squeeze in narrow panels. If users resize the webview, consider responsive breakpoints or allowing buttons to wrap.


31-40: Prefer VS Code theme vars over raw rgba where possible

Hard-coded rgba(255, 255, 255, …) might not contrast well in some themes. Where feasible, use VS Code theme variables for backgrounds and borders to maintain accessibility across themes.

Also applies to: 50-78

vscode/src/diff/vertical/decorations.ts (6)

31-40: Theme hard-coding: prefer theme colors over literal RGBA

indexDecorationType and belowIndexDecorationType use hard-coded rgba whites. Consider theme keys (e.g., editor.lineHighlightBackground, editor.selectionBackground) to ensure readability across themes.


58-74: Range merging logic is good; minor iteration guard

The contiguous range merge in addLines is solid. Consider early-returning after setDecorations to keep the method side-effect surface minimal, but functionally this is fine.


142-150: Avoid calling setDecorations in a loop; batch apply for performance

applyDecorations sets decorations per range/type in a loop, which can be expensive. If you refactor to a single decoration type with DecorationOptions[], you can call setDecorations once per apply cycle.


80-92: Verbose console logging — gate behind a debug flag/logger

The repeated console.log calls will spam the extension host logs during streaming. Consider using your existing logger with a debug level and/or a feature flag.

Also applies to: 116-136, 160-167


169-184: deleteRangesStartingAt sequential scan — OK, but consider binary search if ranges grow

Current linear scan is fine for small/medium lists. If removed ranges can be large and sorted, a binary search would reduce time complexity. Optional at this stage.


3-20: Use a single shared DecorationType with per-range renderOptions; drop unsupported CSS hacks

  • Per-line calls to vscode.window.createTextEditorDecorationType are expensive and increase GC pressure. Define one shared removedLineDecorationType in vscode/src/diff/vertical/decorations.ts:
    const removedLineDecorationType = vscode.window.createTextEditorDecorationType({
      isWholeLine: true,
      backgroundColor: { id: "diffEditor.removedLineBackground" },
      outlineWidth: "1px",
      outlineStyle: "solid",
      outlineColor: { id: "diffEditor.removedTextBorder" },
      rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
    });
  • Collect each removed line into a DecorationOptions array and set per-range ghost text via:
    const options: DecorationOptions[] = removedLines.map(line => ({
      range: /* your range */,
      renderOptions: {
        after: { contentText: line, color: "#808080" }
      }
    }));
    editor.setDecorations(removedLineDecorationType, options);
  • VS Code’s textDecoration only maps to the CSS text-decoration property; arbitrary rules like display: none or white-space: pre are unsupported hacks and may break. To preserve whitespace or hide text reliably, use:
    • Non-breaking spaces (\u00A0) for spacing
    • Supported props: margin, opacity, fontStyle, etc.
    • The InlineCompletion / injected text API for ghosted text
webview-ui/src/components/ResolutionsPage/ModifiedFile/useModifiedFileData.ts (1)

46-106: Type guards are permissive — consider tightening if inputs vary

isModifiedFileMessageValue only checks for a string path, which is fine for leniency. If payloads start varying more, consider validating messageToken/content/diff presence to avoid constructing partial states. Optional.

webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (3)

68-71: Avoid treating an intentionally empty selection as "no selection"

selectedContent || content will ignore an empty string selection. Use nullish coalescing.

-    const contentToApply = selectedContent || content;
+    const contentToApply = selectedContent ?? content;

258-268: Clean up props passed to ModifiedFileActions

  • You pass both onView and onViewWithDecorations; the former triggers an obsolete message path. Prefer removing onView to avoid confusion.
  • isFileApplied being wired to isViewingDiff is semantically odd; if the intent is “show Continue once viewing started,” consider renaming the prop in ModifiedFileActions to reflect that. Otherwise, pass a true "applied" state.
             <ModifiedFileActions
               actionTaken={actionTaken}
               mode={mode}
               normalizedData={normalizedData}
               onApply={() => applyFile()}
               onReject={rejectFile}
-              onView={viewFileInVSCode}
               onViewWithDecorations={viewFileWithDecorations}
               onQuickResponse={handleQuickResponse}
-              isFileApplied={isViewingDiff}
+              isFileApplied={actionTaken === "applied"}
               onContinue={handleContinue}
             />

173-176: Address lint warnings: remove stray blank lines

Static analysis flagged extra blank lines.

- 
-
-
-
 

And:

-
-
 

Also applies to: 271-274

vscode/src/diff/verticalDiffCodeLens.ts (2)

24-37: Compute totals once and reuse in top-level CodeLenses

Minor readability/perf nit: avoid repeating reduce.

-    // Add accept/reject all buttons at the top of the file
-    const topRange = new vscode.Range(0, 0, 0, 0);
+    // Add accept/reject all buttons at the top of the file
+    const topRange = new vscode.Range(0, 0, 0, 0);
+    const totalGreen = blocks.reduce((sum, b) => sum + b.numGreen, 0);
+    const totalRed = blocks.reduce((sum, b) => sum + b.numRed, 0);
     codeLenses.push(
       new vscode.CodeLens(topRange, {
-        title: `✓ Accept All Changes (${blocks.reduce((sum, b) => sum + b.numGreen, 0)}+, ${blocks.reduce((sum, b) => sum + b.numRed, 0)}-)`,
+        title: `✓ Accept All Changes (${totalGreen}+, ${totalRed}-)`,
         command: "konveyor.acceptDiff",
         arguments: [document.uri.fsPath],
       }),
       new vscode.CodeLens(topRange, {
-        title: "✗ Reject All Changes",
+        title: `✗ Reject All Changes`,
         command: "konveyor.rejectDiff",
         arguments: [document.uri.fsPath],
       }),
     );

24-37: Unify identifier type (URI vs fsPath) across commands for consistency

Top-level actions pass fsPath; per-block actions pass fileUri. The commands handle both, but normalizing to one form improves consistency and reduces conversions.

Also applies to: 43-52

vscode/src/extension.ts (1)

687-690: Stale comment mentions "merge editor"

The disposal comment still references "merge editor". Update to reflect the vertical diff system to avoid confusion.

-  // Removed decorator disposal since we're using merge editor now
+  // Vertical diff system handles its own cleanup; no decorator disposal needed here
vscode/src/commands.ts (3)

788-820: content parameter is unused in showDiffWithDecorations

You accept content from the webview but don't use it. Either remove it from the command signature and upstream message, or annotate its planned use to avoid confusion.

-    "konveyor.showDiffWithDecorations": async (
-      filePath: string,
-      diff: string,
-      content: string,
-      messageToken: string,
-    ) => {
+    "konveyor.showDiffWithDecorations": async (
+      filePath: string,
+      diff: string,
+      _content: string, // unused
+      messageToken: string,
+    ) => {

Optionally follow up by removing content from the message payload if not required.


990-1009: Scope CodeLens provider to file scheme to reduce overhead

Registering with "*" applies to everything (including untitled, virtual docs). Restrict to scheme: "file" to avoid unnecessary provider calls.

-      state.extensionContext.subscriptions.push(
-        vscode.languages.registerCodeLensProvider("*", verticalCodeLensProvider),
-      );
+      state.extensionContext.subscriptions.push(
+        vscode.languages.registerCodeLensProvider({ scheme: "file" }, verticalCodeLensProvider),
+      );

130-133: Minor nit: update message label for restart solution server errors

This path logs correctly; consider aligning user-facing messages with logger phrasing for consistency in future edits. No changes required.

vscode/src/diff/staticDiffAdapter.ts (2)

20-25: Parsing multi-file unified diffs without filtering to filePath may merge unrelated hunks.

parsePatch can return multiple “files” (hunks) from a unified diff. This adapter ignores filenames and processes all hunks, which will corrupt the mapping if unifiedDiff contains more than one file. Consider filtering by old/new filename that corresponds to filePath, or require/validate single-file diffs.

Would you like me to refactor parseUnifiedDiffToDiffLines to accept filePath and filter hunks accordingly?


166-177: Delegate accept/reject-all to the manager’s public API (ensures consistent behavior).

Calling clearForfileUri directly here bypasses the higher-level logic in VerticalDiffManager (and clearForfileUri isn’t async-aware). Prefer using the manager’s acceptAll/rejectAll which operate per file and keep state in sync.

Apply these diffs:

   async acceptAll(filePath: string): Promise<void> {
     const fileUri = vscode.Uri.file(filePath).toString();
-    this.verticalDiffManager.clearForfileUri(fileUri, true);
+    await this.verticalDiffManager.acceptAll(fileUri);
   }
   async rejectAll(filePath: string): Promise<void> {
     const fileUri = vscode.Uri.file(filePath).toString();
-    this.verticalDiffManager.clearForfileUri(fileUri, false);
+    await this.verticalDiffManager.rejectAll(fileUri);
   }
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileActions.tsx (2)

51-53: Remove unsupported Icon prop; use styling instead.

PatternFly’s Icon doesn’t support a status prop. This prop is likely ignored and may trigger TS complaints depending on types. Style the icon via className or inline styles instead.

Apply this diff:

-        <Icon status="warning">
+        <Icon className="pf-v5-u-warning-color-100">
           <ExclamationTriangleIcon color="#b98412" />
         </Icon>

Or drop the wrapper and style the ExclamationTriangleIcon directly with a className.


178-230: Prop type drift: remove unused onApply/onReject from QuickResponseButtons.

The QuickResponseButtons props include onApply/onReject but they are not used. Trim to avoid confusion.

Apply this diff:

 const QuickResponseButtons: React.FC<{
   quickResponses: Array<{ id: string; content: string }>;
   isNew: boolean;
   mode: "agent" | "non-agent";
   actionTaken: "applied" | "rejected" | null;
   onView: () => void;
   onQuickResponse: (responseId: string) => void;
-  onApply: () => void;
-  onReject: () => void;
-}> = ({ quickResponses, isNew, mode, actionTaken, onView, onQuickResponse }) => (
+}> = ({ quickResponses, isNew, mode, actionTaken, onView, onQuickResponse }) => (

And remove the now-nonexistent props from the callsite (lines 274-276).

-        onApply={onApply}
-        onReject={onReject}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e210422 and d743288.

📒 Files selected for processing (20)
  • vscode/src/commands.ts (5 hunks)
  • vscode/src/diff/myers.ts (1 hunks)
  • vscode/src/diff/staticDiffAdapter.ts (1 hunks)
  • vscode/src/diff/types.ts (1 hunks)
  • vscode/src/diff/vertical/decorations.ts (1 hunks)
  • vscode/src/diff/vertical/handler.ts (1 hunks)
  • vscode/src/diff/vertical/manager.ts (1 hunks)
  • vscode/src/diff/verticalDiffCodeLens.ts (1 hunks)
  • vscode/src/extension.ts (6 hunks)
  • vscode/src/extensionState.ts (3 hunks)
  • vscode/src/utilities/configuration.ts (1 hunks)
  • vscode/src/utilities/ideUtils.ts (1 hunks)
  • vscode/src/webviewMessageHandler.ts (2 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileActions.tsx (6 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (5 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/index.ts (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/modifiedFileActions.css (1 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/modifiedFileMessage.css (3 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/useModifiedFileData.ts (1 hunks)
  • webview-ui/src/index.css (1 hunks)
💤 Files with no reviewable changes (1)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/index.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-01T13:53:20.774Z
Learnt from: djzager
PR: konveyor/editor-extensions#645
File: vscode/src/webviewMessageHandler.ts:575-577
Timestamp: 2025-08-01T13:53:20.774Z
Learning: The TOGGLE_AGENT_MODE action handler in vscode/src/webviewMessageHandler.ts correctly calls toggleAgentMode() without parameters, as the toggleAgentMode function in vscode/src/utilities/configuration.ts has been updated to internally handle reading and toggling the agent mode configuration value.

Applied to files:

  • vscode/src/webviewMessageHandler.ts
🧬 Code Graph Analysis (12)
vscode/src/utilities/ideUtils.ts (1)
webview-ui/src/utils/vscode.ts (1)
  • vscode (18-18)
vscode/src/diff/myers.ts (1)
vscode/src/diff/types.ts (2)
  • DiffLine (7-10)
  • DiffChar (12-21)
vscode/src/diff/staticDiffAdapter.ts (3)
vscode/src/diff/vertical/manager.ts (1)
  • VerticalDiffManager (12-298)
vscode/src/diff/types.ts (1)
  • DiffLine (7-10)
vscode/src/diff/vertical/handler.ts (1)
  • fileUri (84-86)
vscode/src/diff/vertical/decorations.ts (1)
vscode/src/diff/vertical/handler.ts (1)
  • range (74-78)
vscode/src/diff/verticalDiffCodeLens.ts (2)
vscode/src/diff/vertical/manager.ts (1)
  • VerticalDiffManager (12-298)
vscode/src/diff/vertical/handler.ts (2)
  • fileUri (84-86)
  • range (74-78)
vscode/src/extensionState.ts (2)
vscode/src/diff/vertical/manager.ts (1)
  • VerticalDiffManager (12-298)
vscode/src/diff/staticDiffAdapter.ts (1)
  • StaticDiffAdapter (10-186)
vscode/src/commands.ts (4)
vscode/src/client/tracer.ts (1)
  • logger (60-65)
webview-ui/src/utils/vscode.ts (1)
  • vscode (18-18)
vscode/src/diff/vertical/handler.ts (1)
  • fileUri (84-86)
vscode/src/diff/verticalDiffCodeLens.ts (1)
  • VerticalDiffCodeLensProvider (4-58)
vscode/src/extension.ts (6)
vscode/src/extensionState.ts (1)
  • ExtensionState (21-57)
vscode/src/utilities/configuration.ts (1)
  • getConfigAutoAcceptOnSave (70-71)
vscode/src/diff/vertical/handler.ts (1)
  • fileUri (84-86)
vscode/src/utilities/ideUtils.ts (1)
  • SimpleIDE (52-57)
vscode/src/diff/vertical/manager.ts (1)
  • VerticalDiffManager (12-298)
vscode/src/diff/staticDiffAdapter.ts (1)
  • StaticDiffAdapter (10-186)
vscode/src/diff/vertical/manager.ts (2)
vscode/src/diff/vertical/handler.ts (3)
  • fileUri (84-86)
  • VerticalDiffHandler (27-582)
  • VerticalDiffHandlerOptions (15-25)
vscode/src/diff/types.ts (2)
  • DiffLine (7-10)
  • IDE (33-38)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (4)
vscode/webpack.config.js (1)
  • mode (8-8)
webview-ui/src/components/ResolutionsPage/ModifiedFile/useModifiedFileData.ts (1)
  • isLocalChange (110-110)
shared/src/types/types.ts (1)
  • LocalChange (99-108)
vscode/src/utilities/ModifiedFiles/handleQuickResponse.ts (1)
  • handleQuickResponse (8-140)
vscode/src/webviewMessageHandler.ts (2)
vscode/src/client/tracer.ts (1)
  • logger (60-65)
webview-ui/src/utils/vscode.ts (1)
  • vscode (18-18)
vscode/src/diff/vertical/handler.ts (4)
vscode/src/diff/types.ts (2)
  • ApplyState (23-30)
  • DiffLine (7-10)
vscode/src/diff/vertical/decorations.ts (4)
  • RemovedLineDecorationManager (107-185)
  • AddedLineDecorationManager (47-102)
  • indexDecorationType (31-35)
  • belowIndexDecorationType (36-40)
vscode/src/diff/vertical/manager.ts (1)
  • VerticalDiffCodeLens (6-10)
vscode/src/diff/myers.ts (1)
  • myersDiff (25-53)
🪛 GitHub Check: Lint
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx

[warning] 173-173:
Delete ⏎⏎⏎⏎


[warning] 271-271:
Delete ⏎⏎

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build (linux)
  • GitHub Check: Build (windows)
🔇 Additional comments (28)
vscode/src/utilities/ideUtils.ts (2)

52-57: SimpleIDE wiring looks good

The thin wrapper class cleanly exposes the utilities with no surprising state or this-binding issues.


41-47: No downstream consumers of getCurrentFile()—OS path return is safe

A repository-wide search found only:

  • vscode/src/utilities/ideUtils.ts (implementation)
  • vscode/src/diff/types.ts (interface declaration)

No call sites normalize or parse URIs (e.g. via Uri.file(), Uri.parse(), or literal file://), and there are no other usages of getCurrentFile(). Returning the platform’s native file path will have no impact on downstream code.

webview-ui/src/components/ResolutionsPage/ModifiedFile/modifiedFileMessage.css (3)

87-87: Button width reductions are sensible

Shrinking main Accept/Reject min-width from 140px to 120px improves layout density without harming readability.

Also applies to: 111-111


175-195: Dark theme: global PF v6 icon recolor — verify intended scope

These selectors target all PF v6 buttons across the entire webview in dark mode, not just ModifiedFile. That’s a broad override and may unintentionally recolor icons in unrelated components.

If the intent is to scope to the ModifiedFile component, consider tightening selectors:

-.vscode-dark .pf-v6-c-button svg,
-.vscode-dark .pf-v6-c-button .pf-v6-c-button__icon svg {
+.vscode-dark .modified-file-actions .pf-v6-c-button svg,
+.vscode-dark .modified-file-actions .pf-v6-c-button .pf-v6-c-button__icon svg {
   color: var(--vscode-editor-foreground) !important;
   fill: var(--vscode-editor-foreground) !important;
 }
 
-.vscode-dark .pf-v6-c-button:hover svg,
-.vscode-dark .pf-v6-c-button:hover .pf-v6-c-button__icon svg {
+.vscode-dark .modified-file-actions .pf-v6-c-button:hover svg,
+.vscode-dark .modified-file-actions .pf-v6-c-button:hover .pf-v6-c-button__icon svg {
   color: var(--vscode-editor-foreground) !important;
   fill: var(--vscode-editor-foreground) !important;
 }

240-260: Accept/Reject icon colors — good specificity and theme alignment

Clear intent and specificity ensure these rules win over the broader icon overrides; using VS Code gutter colors keeps parity with the editor.

vscode/src/diff/myers.ts (2)

5-15: Good utility: clean conversion from jsdiff Change to DiffLine

The trailing-EOF handling and type derivation are sound. Splitting by newline and trimming a terminal empty element aligns with your ignoreNewlineAtEof decision.


43-50: Dropping trailing empty ‘old’ lines — confirm there’s no downstream reliance

This normalization removes trailing empty deletions. If a consumer expects to visualize or apply removal of trailing blank lines, this will hide them. Please confirm with the vertical diff handler/apply flow.

webview-ui/src/components/ResolutionsPage/ModifiedFile/useModifiedFileData.ts (1)

61-66: Robust guard for quickResponses — nice

The array and length checks prevent passing empty or malformed quick responses to the UI. Good defensive coding.

webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (3)

130-155: LGTM: new SHOW_DIFF_WITH_DECORATORS flow with re-entry guard

The isViewingDiff guard prevents duplicate posts, and the payload matches the host handler. Looks good.


157-171: LGTM: handleContinue aligns with new flow

This mirrors apply behavior and properly resets UI state.


103-128: Replace legacy SHOW_MAXIMIZED_DIFF with SHOW_DIFF_WITH_DECORATORS

We confirmed via rg that the only occurrence of "SHOW_MAXIMIZED_DIFF" is in:

  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (lines ~117–121)

Apply this diff:

-      // Agent mode: Use SHOW_MAXIMIZED_DIFF message
-      interface ShowMaximizedDiffPayload {
-        path: string;
-        content: string;
-        diff: string;
-        messageToken: string;
-      }
-      const payload: ShowMaximizedDiffPayload = {
-        path: filePath,
-        content: content,
-        diff: fileDiff,
-        messageToken: messageToken,
-      };
-      window.vscode.postMessage({
-        type: "SHOW_MAXIMIZED_DIFF",
-        payload,
-      });
+      // Agent mode: Use SHOW_DIFF_WITH_DECORATORS message
+      interface ShowDiffWithDecoratorsPayload {
+        path: string;
+        content: string;
+        diff: string;
+        messageToken: string;
+      }
+      const payload: ShowDiffWithDecoratorsPayload = {
+        path: filePath,
+        content,
+        diff: fileDiff,
+        messageToken,
+      };
+      window.vscode.postMessage({
+        type: "SHOW_DIFF_WITH_DECORATORS",
+        payload,
+      });

• Verify that the host application supports the new SHOW_DIFF_WITH_DECORATORS event and remove any leftover legacy handling of SHOW_MAXIMIZED_DIFF.

vscode/src/diff/verticalDiffCodeLens.ts (1)

15-21: LGTM: empty state handling

Returning [] when no blocks exist is correct and avoids unnecessary lenses.

vscode/src/webviewMessageHandler.ts (2)

234-250: LGTM: New SHOW_DIFF_WITH_DECORATORS action and error handling

The command invocation, logging, and user-facing error message are all appropriate. The payload matches the webview sender.


490-493: LGTM: Normalization aligns with diff processing

Replacing CRLF and trimming a single trailing newline is a sensible normalization for content comparison.

vscode/src/extension.ts (4)

218-220: LGTM: Vertical diff initialization early in startup

Initializing the vertical diff system up-front is appropriate given downstream dependencies.


327-345: Auto-accept-on-save behavior: confirm intended default and UX

Auto-accept-on-save is gated behind getConfigAutoAcceptOnSave() (default appears to be true). Confirm this default is intentional; auto-accepting all blocks on any save could surprise users.

Would you like to flip the default to false and prompt users to enable it, or surface a one-time notification when auto-accept first triggers?


436-451: LGTM: Diff status bar setup

Good use of command binding and event listeners to maintain status.


453-473: LGTM: Status bar updates computed from active file blocks

Accurately derives totals and visibility per active editor.

vscode/src/commands.ts (4)

822-837: LGTM: Accept all diffs command

Guards against uninitialized adapter and handles active-editor fallback.


838-852: LGTM: Reject all diffs command

Same as accept; good validation and messaging.


854-880: LGTM: Per-block accept/reject commands

URI-to-fsPath conversion and adapter invocation are correct. Error handling is appropriate.


882-899: LGTM: Clear diff decorations command

Cleans a single file or all active diffs; safe-guards around manager presence look good.

vscode/src/diff/staticDiffAdapter.ts (1)

140-155: Good: clean integration point and diagnostics around streaming.

Using messageToken as a streamId and logging the active editor and counts will help trace decorated-diff sessions.

webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileActions.tsx (2)

84-176: Solid action model and disabling logic.

  • “Review Changes” primary CTA for agent mode with isViewingDiff/actionTaken guards is clear.
  • Accept/Reject All hidden during diff viewing reduces confusion.
  • Proper ARIA labels.

232-296: Nice UX: contextual banner and “Continue” flow in agent mode.

The banner succinctly instructs users on how to proceed in-editor and exposes a Continue action for completing the flow.

vscode/src/diff/vertical/manager.ts (1)

183-251: Overall: good orchestration of streaming lifecycle and UI context.

  • Properly sets konveyor.diffVisible and konveyor.streamingDiff.
  • Re-enables user change listener after streaming; disables it on errors.
  • Captures logDiffs for diagnostics.
vscode/src/diff/vertical/handler.ts (2)

295-385: Thoughtful reapplication with Myers diff and decoration rebuild.

The approach to reject pending blocks, replace the target range deterministically, and then reconstruct CodeLens blocks is sound. Handling trailing newline semantics avoids off-by-one issues.


208-254: Block accept/reject shifting looks correct; keep skipStatusUpdate in acceptAll/rejectAll callers.

Offset calculation uses -(accept ? numRed : numGreen), which matches line count change. Remember to pass skipStatusUpdate when bulk-applying to avoid noisy intermediate status.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
webview-ui/src/components/ResolutionsPage/resolutionsPage.css (4)

65-70: Prevent truncation/overflow: avoid forcing no-wrap and use gap for spacing

flex-wrap: nowrap !important; on the title risks overflow on narrow viewports (especially with long titles). Using gap on the container preserves layout without relying on a margin on the indicator and allows the title to wrap when needed.

Apply this minimal change (paired with the previous comment’s selector scoping if you accept it):

 .pf-v6-c-page__main-section .pf-v6-c-title {
   display: flex !important;
   align-items: center !important;
-  flex-wrap: nowrap !important;
+  gap: var(--pf-v6-global--spacer--sm);
   min-width: 0 !important;
 }

Optional (if you need single-line with truncation): add ellipsis handling to the title text node/wrapper.

/* Example if title text is wrapped in .title-text */
.resolutions-page .pf-v6-c-title .title-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1 1 auto;
}

72-76: Prefer container gap over ad-hoc margins; remove fixed 10px spacing

The margin on .loading-indicator becomes unnecessary once the container uses gap. It also hard-codes LTR spacing. Removing it prevents RTL issues and keeps spacing consistent with PF tokens.

Apply:

 .pf-v6-c-page__main-section .pf-v6-c-title .loading-indicator {
   display: inline-flex !important;
-  margin-left: 10px !important;
   flex-shrink: 0 !important;
 }

If you keep margin instead of container gap, at least use logical properties and PF spacers:

-  margin-left: 10px !important;
+  margin-inline-start: var(--pf-v6-global--spacer--sm) !important;

79-91: Reduce heavy-handed overrides in the responsive block; also scope to page

Forcing visibility: visible and opacity: 1 globally on PF titles can override legitimate hidden states and utility classes. If the goal is to counter a specific PF utility that hides the header on small screens, scope to the page and only assert display.

Apply:

 @media (max-width: 768px) {
-  .pf-v6-c-page__main-section .pf-v6-c-title {
-    display: flex !important;
-    visibility: visible !important;
-    opacity: 1 !important;
-  }
+  .resolutions-page .pf-v6-c-title {
+    display: flex !important;
+  }
 
-  .pf-v6-c-page__main-section .pf-v6-c-title .loading-indicator {
-    display: inline-flex !important;
-    visibility: visible !important;
-    opacity: 1 !important;
-  }
+  .resolutions-page .pf-v6-c-title .loading-indicator {
+    display: inline-flex !important;
+  }
 }

If elements are hidden via PF responsive utility classes, consider removing those utilities from the markup on this page rather than overriding them in CSS.


65-76: Alignment nit: baseline often reads better for text + spinner

align-items: center is fine, but aligning to baseline can visually integrate the spinner with the text on various font sizes.

Consider:

-.resolutions-page .pf-v6-c-title {
-  display: flex !important;
-  align-items: center !important;
+.resolutions-page .pf-v6-c-title {
+  display: flex !important;
+  align-items: baseline !important;
   gap: var(--pf-v6-global--spacer--sm);
   min-width: 0 !important;
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d743288 and 835efa4.

📒 Files selected for processing (2)
  • vscode/src/diff/vertical/manager.ts (1 hunks)
  • webview-ui/src/components/ResolutionsPage/resolutionsPage.css (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • vscode/src/diff/vertical/manager.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build (linux)
  • GitHub Check: Build (windows)
  • GitHub Check: Build (macos)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (6)
vscode/src/diff/vertical/manager.ts (6)

27-36: Make createVerticalDiffHandler async and await the prior handler’s clear to avoid races

The previous handler’s async clear is not awaited. This can interleave document edits/decoration teardown with new handler setup.

Apply:

-  createVerticalDiffHandler(
+  async createVerticalDiffHandler(
     fileUri: string,
     startLine: number,
     endLine: number,
     options: VerticalDiffHandlerOptions,
-  ): VerticalDiffHandler | undefined {
-    if (this.fileUriToHandler.has(fileUri)) {
-      this.fileUriToHandler.get(fileUri)?.clear(false);
-      this.fileUriToHandler.delete(fileUri);
-    }
+  ): Promise<VerticalDiffHandler | undefined> {
+    const existing = this.fileUriToHandler.get(fileUri);
+    if (existing) {
+      await existing.clear(false);
+      this.fileUriToHandler.delete(fileUri);
+    }

205-211: Remove redundant pre-clear; avoid double-clear and race with new handler setup

You clear here and again inside createVerticalDiffHandler. Keep one clear and await it in a single place (the factory).

-    // Clear any existing handler
-    const existingHandler = this.getHandlerForFile(fileUri);
-    if (existingHandler) {
-      console.log("[Manager] Clearing existing handler");
-      await existingHandler.clear(false);
-    }

156-178: Make clearForFileUri async and await handler.clear before cleanup; fix casing

Without awaiting, you can delete state and toggle UI context before the handler finishes tearing down decorations/edits.

-  clearForfileUri(fileUri: string | undefined, accept: boolean = false) {
+  async clearForFileUri(fileUri: string | undefined, accept = false): Promise<void> {
     if (!fileUri) {
       return;
     }

     const handler = this.fileUriToHandler.get(fileUri);
     if (handler) {
-      handler.clear(accept);
+      await handler.clear(accept);
       this.fileUriToHandler.delete(fileUri);
     }

     this.disableDocumentChangeListener();

     this.fileUriToCodeLens.delete(fileUri);
     this.refreshCodeLens();

     void vscode.commands.executeCommand("setContext", "konveyor.diffVisible", false);

     // Notify status change
     if (this.onDiffStatusChange) {
       this.onDiffStatusChange(fileUri);
     }
   }

Run to locate remaining references to the old name and update call sites to await this.clearForFileUri(...):

#!/bin/bash
rg -n -C2 --type=ts 'clearForfileUri|createVerticalDiffHandler\s*\('

253-274: Accept-all should consume fresh state until empty; then await clear

Iterating a snapshot while state mutates can miss or misaddress blocks. Loop until empty and await the async clear.

-    const handler = this.fileUriToHandler.get(fileUri);
-    if (handler) {
-      // Accept all blocks
-      const blocks = this.fileUriToCodeLens.get(fileUri);
-      if (blocks) {
-        for (const block of blocks) {
-          await handler.acceptRejectBlock(true, block.start, block.numGreen, block.numRed);
-        }
-      }
-      this.clearForfileUri(fileUri, true);
-    }
+    const handler = this.fileUriToHandler.get(fileUri);
+    if (!handler) return;
+    // Accept blocks until none remain (state mutates per operation)
+    let blocks = this.fileUriToCodeLens.get(fileUri);
+    while (blocks && blocks.length > 0) {
+      const { start, numGreen, numRed } = blocks[0];
+      await handler.acceptRejectBlock(true, start, numGreen, numRed);
+      blocks = this.fileUriToCodeLens.get(fileUri);
+    }
+    await this.clearForFileUri(fileUri, true);

276-297: Mirror the accept-all fix for reject-all; loop until empty and await clear

Avoid stale indices and ensure proper teardown.

-    const handler = this.fileUriToHandler.get(fileUri);
-    if (handler) {
-      // Reject all blocks
-      const blocks = this.fileUriToCodeLens.get(fileUri);
-      if (blocks) {
-        for (const block of blocks) {
-          await handler.acceptRejectBlock(false, block.start, block.numGreen, block.numRed);
-        }
-      }
-      this.clearForfileUri(fileUri, false);
-    }
+    const handler = this.fileUriToHandler.get(fileUri);
+    if (!handler) return;
+    // Reject blocks until none remain (state mutates per operation)
+    let blocks = this.fileUriToCodeLens.get(fileUri);
+    while (blocks && blocks.length > 0) {
+      const { start, numGreen, numRed } = blocks[0];
+      await handler.acceptRejectBlock(false, start, numGreen, numRed);
+      blocks = this.fileUriToCodeLens.get(fileUri);
+    }
+    await this.clearForFileUri(fileUri, false);

215-229: Await the now-async createVerticalDiffHandler

Update the call site to ensure prior clear completes before constructing the new handler.

-    const diffHandler = this.createVerticalDiffHandler(fileUri, startLine, endLine, {
+    const diffHandler = await this.createVerticalDiffHandler(fileUri, startLine, endLine, {
🧹 Nitpick comments (3)
vscode/src/diff/vertical/manager.ts (3)

63-80: Signature nit: this listener creator doesn’t return a Disposable

The method doesn’t return the created disposable. Make the signature void to match behavior.

-  private enableDocumentChangeListener(): vscode.Disposable | undefined {
+  private enableDocumentChangeListener(): void {
     if (this.userChangeListener) {
       //Only create one listener per file
       return;
     }

37-39: Simplify equality check; string compare is sufficient here

URI.equal is unnecessary for toString() equality.

-    if (editor && URI.equal(editor.document.uri.toString(), fileUri)) {
+    if (editor && editor.document.uri.toString() === fileUri) {

212-214: Drop the arbitrary 150ms sleep; rely on awaited clear for UI stability

Once you await handler.clear, the extra delay adds latency without guarantees. Prefer event-driven gating or no delay.

-    // Small delay to ensure UI updates
-    await new Promise((resolve) => setTimeout(resolve, 150));
+    // No artificial delay needed once clear is awaited
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 835efa4 and a68623d.

📒 Files selected for processing (2)
  • vscode/src/diff/vertical/manager.ts (1 hunks)
  • webview-ui/src/components/ResolutionsPage/resolutionsPage.css (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • webview-ui/src/components/ResolutionsPage/resolutionsPage.css
🧰 Additional context used
🧬 Code Graph Analysis (1)
vscode/src/diff/vertical/manager.ts (2)
vscode/src/diff/vertical/handler.ts (3)
  • fileUri (84-86)
  • VerticalDiffHandler (27-582)
  • VerticalDiffHandlerOptions (15-25)
vscode/src/diff/types.ts (2)
  • DiffLine (7-10)
  • IDE (33-38)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build (windows)
  • GitHub Check: Build (linux)

@ibolton336 ibolton336 force-pushed the decorators-3 branch 2 times, most recently from d6e90fe to 4c0b212 Compare August 19, 2025 13:31
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
vscode/src/extension.ts (1)

72-76: Status bar item should be disposed

The StatusBarItem is created but never added to the extension subscriptions, which means it won't be automatically disposed when the extension deactivates.

    this.diffStatusBarItem = vscode.window.createStatusBarItem(
      vscode.StatusBarAlignment.Right,
      100,
    );
+   this.context.subscriptions.push(this.diffStatusBarItem);
vscode/src/diff/vertical/manager.ts (2)

145-146: Await clearForFileUri to ensure proper cleanup

The clearForFileUri call should be awaited to ensure the handler cleanup completes before proceeding.

     if (blocks.length === 1) {
-      this.clearForFileUri(fileUri, true);
+      await this.clearForFileUri(fileUri, true);

31-57: Clear handler asynchronously to prevent race conditions

The handler clear operation at line 38 is not awaited, which could lead to race conditions where the old handler's cleanup operations interleave with the new handler creation.

-  createVerticalDiffHandler(
+  async createVerticalDiffHandler(
     fileUri: string,
     startLine: number,
     endLine: number,
     options: VerticalDiffHandlerOptions,
-  ): VerticalDiffHandler | undefined {
+  ): Promise<VerticalDiffHandler | undefined> {
     if (this.fileUriToHandler.has(fileUri)) {
-      this.fileUriToHandler.get(fileUri)?.clear(false);
+      const existingHandler = this.fileUriToHandler.get(fileUri);
+      if (existingHandler) {
+        await existingHandler.clear(false);
+      }
       this.fileUriToHandler.delete(fileUri);
     }

Then update the call site at line 221:

-    const diffHandler = this.createVerticalDiffHandler(fileUri, startLine, endLine, {
+    const diffHandler = await this.createVerticalDiffHandler(fileUri, startLine, endLine, {
🧹 Nitpick comments (7)
vscode/src/commands.ts (4)

550-555: Fix the indentation consistency in the filter object

The "Executable Files" and "All Files" properties have inconsistent indentation compared to the rest of the object structure.

       filters: isWindows
         ? {
-              "Executable Files": ["exe"],
-              "All Files": ["*"],
-            }
+             "Executable Files": ["exe"],
+             "All Files": ["*"],
+           }
         : {
-              "All Files": ["*"],
-            },
+             "All Files": ["*"],
+           },

788-819: Ensure proper error handling throughout the diff application flow

The error handling provides user feedback, but there are a few considerations:

  1. The error message could be more specific - consider distinguishing between initialization errors and diff application errors
  2. The function swallows errors after displaying them, which could mask issues in the calling code

Consider propagating the error after user notification to allow calling code to handle it appropriately:

     } catch (error) {
       logger.error("Error in vertical diff:", error);
-      vscode.window.showErrorMessage(`Failed to show diff: ${error}`);
+      const errorMessage = error instanceof Error ? error.message : String(error);
+      if (!state.staticDiffAdapter) {
+        vscode.window.showErrorMessage(`Diff system not initialized: ${errorMessage}`);
+      } else {
+        vscode.window.showErrorMessage(`Failed to apply diff: ${errorMessage}`);
+      }
+      throw error; // Re-throw to allow proper error propagation
     }

822-836: Consider adding try-catch error handling for consistency

While the undefined checks are good, consider wrapping the acceptAll call in try-catch for consistency with other command handlers.

   "konveyor.acceptDiff": async (filePath?: string) => {
     if (!filePath) {
       const editor = vscode.window.activeTextEditor;
       if (!editor) {
         return;
       }
       filePath = editor.document.fileName;
     }
     if (!state.staticDiffAdapter) {
       vscode.window.showErrorMessage("Vertical diff system not initialized");
       return;
     }
-    await state.staticDiffAdapter.acceptAll(filePath);
-    vscode.window.showInformationMessage("Changes accepted");
+    try {
+      await state.staticDiffAdapter.acceptAll(filePath);
+      vscode.window.showInformationMessage("Changes accepted");
+    } catch (error) {
+      logger.error("Error accepting diff changes:", error);
+      vscode.window.showErrorMessage(`Failed to accept changes: ${error}`);
+    }
   },

838-852: Apply consistent error handling pattern

Similar to the acceptDiff command, add error handling for consistency.

   "konveyor.rejectDiff": async (filePath?: string) => {
     if (!filePath) {
       const editor = vscode.window.activeTextEditor;
       if (!editor) {
         return;
       }
       filePath = editor.document.fileName;
     }
     if (!state.staticDiffAdapter) {
       vscode.window.showErrorMessage("Vertical diff system not initialized");
       return;
     }
-    await state.staticDiffAdapter.rejectAll(filePath);
-    vscode.window.showInformationMessage("Changes rejected");
+    try {
+      await state.staticDiffAdapter.rejectAll(filePath);
+      vscode.window.showInformationMessage("Changes rejected");
+    } catch (error) {
+      logger.error("Error rejecting diff changes:", error);
+      vscode.window.showErrorMessage(`Failed to reject changes: ${error}`);
+    }
   },
vscode/src/extension.ts (1)

447-462: Consider adding the status bar item to subscriptions in setupDiffStatusBar

While the status bar item will be disposed if added to subscriptions in the constructor, it would be cleaner to handle its lifecycle in the same method where it's configured.

 private setupDiffStatusBar(): void {
   this.diffStatusBarItem.name = "Konveyor Diff Status";
   this.diffStatusBarItem.tooltip = "Click to accept/reject all diff changes";
   this.diffStatusBarItem.command = "konveyor.showDiffActions";
   this.diffStatusBarItem.hide();

   // Update status bar when active editor changes
   this.listeners.push(
     vscode.window.onDidChangeActiveTextEditor((editor) => {
       this.updateDiffStatusBar(editor);
     }),
   );

   // Initial update
   this.updateDiffStatusBar(vscode.window.activeTextEditor);
 }
vscode/src/diff/vertical/manager.ts (2)

69-72: Return type mismatch in enableDocumentChangeListener

The method signature indicates it should return vscode.Disposable | undefined, but when a listener already exists, it returns void (implicit undefined from the empty return statement).

   private enableDocumentChangeListener(): vscode.Disposable | undefined {
     if (this.userChangeListener) {
       //Only create one listener per file
-      return;
+      return undefined;
     }

209-214: Remove redundant handler clearing

The existing handler is already cleared here, but createVerticalDiffHandler will also clear it (once made async per earlier comment), resulting in redundant operations.

-    // Clear any existing handler
-    const existingHandler = this.getHandlerForFile(fileUri);
-    if (existingHandler) {
-      console.log("[Manager] Clearing existing handler");
-      await existingHandler.clear(false);
-    }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a68623d and bd9bdcf.

📒 Files selected for processing (6)
  • vscode/src/commands.ts (5 hunks)
  • vscode/src/diff/staticDiffAdapter.ts (1 hunks)
  • vscode/src/diff/vertical/manager.ts (1 hunks)
  • vscode/src/extension.ts (7 hunks)
  • webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx (1 hunks)
  • webview-ui/src/components/ResolutionsPage/resolutionsPage.css (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • webview-ui/src/components/ResolutionsPage/resolutionsPage.css
  • vscode/src/diff/staticDiffAdapter.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-23T12:10:46.399Z
Learnt from: pranavgaikwad
PR: konveyor/editor-extensions#581
File: vscode/src/extension.ts:243-251
Timestamp: 2025-07-23T12:10:46.399Z
Learning: In vscode/src/extension.ts, clearing all configErrors in the onDidSaveTextDocument listener for settings YAML is intentional behavior. The setupModelProvider and updateConfigErrors functions are designed to work together to rebuild the entire configuration error state.

Applied to files:

  • vscode/src/extension.ts
🧬 Code Graph Analysis (3)
vscode/src/commands.ts (3)
vscode/src/client/tracer.ts (1)
  • logger (60-65)
vscode/src/diff/vertical/handler.ts (1)
  • fileUri (84-86)
vscode/src/diff/verticalDiffCodeLens.ts (1)
  • VerticalDiffCodeLensProvider (4-58)
vscode/src/extension.ts (5)
vscode/src/extensionState.ts (1)
  • ExtensionState (21-57)
vscode/src/utilities/configuration.ts (1)
  • getConfigAutoAcceptOnSave (70-71)
vscode/src/utilities/ideUtils.ts (1)
  • SimpleIDE (52-57)
vscode/src/diff/vertical/manager.ts (1)
  • VerticalDiffManager (13-315)
vscode/src/diff/staticDiffAdapter.ts (1)
  • StaticDiffAdapter (10-167)
vscode/src/diff/vertical/manager.ts (3)
vscode/src/diff/vertical/handler.ts (3)
  • fileUri (84-86)
  • VerticalDiffHandler (27-582)
  • VerticalDiffHandlerOptions (15-25)
vscode/src/diff/types.ts (2)
  • DiffLine (7-10)
  • IDE (33-38)
agentic/src/cache.ts (1)
  • InMemoryCacheWithRevisions (129-186)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build (windows)
  • GitHub Check: Build (macos)
🔇 Additional comments (7)
webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx (1)

403-409: Verification complete – CSS hooked correctly, no code/test dependencies found

  • CSS selectors in webview-ui/src/components/ResolutionsPage/resolutionsPage.css reference .resolutions-page, confirming the style hook is applied.
  • No references to .resolutions-page were found in TS/JS source or in unit/e2e tests—no further updates required.

✅ Approved for merge.

vscode/src/commands.ts (1)

990-1010: LGTM! Well-structured CodeLens provider registration

The CodeLens provider registration is properly implemented with:

  • Appropriate null checks for the vertical diff manager
  • Registration to extension subscriptions for cleanup
  • Proper refresh callback connection
  • Clear logging for debugging
vscode/src/extension.ts (2)

330-355: Robust implementation of auto-accept on save feature

The implementation correctly:

  • Checks the configuration setting before processing
  • Verifies that a handler exists for the file
  • Provides user feedback via information messages
  • Handles errors with proper logging and user notification
  • Executes BEFORE the save operation to ensure the document is saved in its final state

430-445: Well-designed vertical diff initialization

The initialization properly:

  • Creates the SimpleIDE abstraction for file operations
  • Initializes the VerticalDiffManager with required dependencies
  • Sets up the diff status change callback for UI updates
  • Creates the StaticDiffAdapter for handling unified diffs
  • Includes appropriate logging
vscode/src/diff/vertical/manager.ts (3)

160-182: LGTM! Proper async implementation of clearForFileUri

The method correctly awaits the handler's clear operation before proceeding with cleanup, preventing race conditions between document edits and state cleanup.


283-291: LGTM! Proper handling of concurrent mutations

Good use of slice() to create a shallow copy of the blocks array before iteration. This prevents issues when acceptRejectBlock mutates the original array during processing.


305-313: LGTM! Consistent handling pattern with acceptAll

The rejectAll method correctly mirrors the acceptAll implementation with proper array copying to avoid mutation issues.

@ibolton336 ibolton336 force-pushed the decorators-3 branch 2 times, most recently from e86d633 to 55aa122 Compare August 19, 2025 14:20
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (1)

32-40: Fix initial actionTaken mapping: map "discarded" to "rejected" (not "rejected").

LocalChange.state is "pending" | "applied" | "discarded". Initialize actionTaken by mapping "discarded" → "rejected" and keep "applied" as-is. Optionally also tolerate a legacy "rejected" string if present.

-  const [actionTaken, setActionTaken] = useState<"applied" | "rejected" | null>(() => {
-    if (status === "applied") {
-      return "applied";
-    }
-    if (status === "rejected") {
-      return "rejected";
-    }
-    return null; // for "pending" or undefined
-  });
+  const [actionTaken, setActionTaken] = useState<"applied" | "rejected" | null>(() => {
+    if (status === "applied") return "applied";
+    if (status === "discarded" || status === "rejected") return "rejected";
+    return null; // for "pending" or undefined
+  });
vscode/src/diff/vertical/handler.ts (1)

483-494: Fix potential out-of-bounds error when deleting ranges at end of document.

The method constructs an end position at range.end.line + 1, which can exceed document bounds when the range ends at the last line.

Apply this diff to fix the bounds issue:

 private async deleteRangeLines(ranges: vscode.Range[]) {
   await this.editor.edit(
     (editBuilder) => {
       for (const range of ranges) {
+        const docLineCount = this.editor.document.lineCount;
+        const safeEndLine = Math.min(range.end.line + 1, docLineCount);
         editBuilder.delete(
-          new vscode.Range(range.start, new vscode.Position(range.end.line + 1, 0)),
+          new vscode.Range(range.start, new vscode.Position(safeEndLine, 0)),
         );
       }
     },
     { undoStopAfter: false, undoStopBefore: false },
   );
 }
🧹 Nitpick comments (3)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (3)

138-164: Harden decorated-view handler: guard non-agent mode and align payload types.

  • Add an early return for non-agent mode to avoid posting a message the host may not handle, and delegate to onView when available.
  • The comment says “Prevent multiple applications”; it should say “Prevent multiple view requests”.
  • If messageToken and content can be undefined (common for LocalChange), mark them optional in the payload type or assert their presence before posting.
-  const viewFileWithDecorations = (filePath: string, fileDiff: string) => {
-    // Prevent multiple applications
+  const viewFileWithDecorations = (filePath: string, fileDiff: string) => {
+    // In non-agent mode, fall back to standard view behavior
+    if (mode !== "agent") {
+      if (onView && isLocalChange(data)) onView(data);
+      return;
+    }
+
+    // Prevent multiple concurrent view requests
     if (isViewingDiff) {
       return;
     }
 
     setIsViewingDiff(true);
 
     // Use the reliable vertical diff system
-    interface ShowDiffWithDecoratorsPayload {
-      path: string;
-      content: string;
-      diff: string;
-      messageToken: string;
-    }
+    interface ShowDiffWithDecoratorsPayload {
+      path: string;
+      diff: string;
+      content?: string;
+      messageToken?: string;
+    }
     const payload: ShowDiffWithDecoratorsPayload = {
       path: filePath,
       content: content,
       diff: fileDiff,
       messageToken: messageToken,
     };
     window.vscode.postMessage({
       type: "SHOW_DIFF_WITH_DECORATORS",
       payload,
     });
   };

If the host strictly requires messageToken/content, alternatively add runtime guards to ensure they exist before posting and log/return otherwise.


165-179: DRY: reuse applyFile in handleContinue.

handleContinue duplicates applyFile logic. Delegate to applyFile() to keep behavior consistent and reduce maintenance.

-  const handleContinue = () => {
-    // Mark as applied to continue the conversation flow
-    if (mode === "agent") {
-      postFileResponse("apply", messageToken, path, content);
-      // Trigger scroll after action in agent mode
-      onUserAction?.();
-    } else {
-      if (onApply && isLocalChange(data)) {
-        const modifiedChange: LocalChange = { ...data, content };
-        onApply(modifiedChange);
-      }
-    }
-    setActionTaken("applied");
-    setIsViewingDiff(false);
-  };
+  const handleContinue = () => applyFile();

272-276: Verify prop semantics: isFileApplied receives isViewingDiff.

Passing isViewingDiff into isFileApplied is potentially misleading. If the child uses this to gate a “Continue” action while a decorated view is active, consider renaming the prop to isViewingDiff (or isDiffActive) for clarity, or pass a correctly named boolean.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bd9bdcf and 55aa122.

📒 Files selected for processing (10)
  • vscode/package.json (2 hunks)
  • vscode/src/commands.ts (5 hunks)
  • vscode/src/diff/staticDiffAdapter.ts (1 hunks)
  • vscode/src/diff/vertical/handler.ts (1 hunks)
  • vscode/src/diff/vertical/manager.ts (1 hunks)
  • vscode/src/extension.ts (9 hunks)
  • vscode/src/extensionState.ts (3 hunks)
  • vscode/src/utilities/configuration.ts (1 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (5 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/useModifiedFileData.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • vscode/src/utilities/configuration.ts
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/useModifiedFileData.ts
  • vscode/src/commands.ts
  • vscode/src/diff/staticDiffAdapter.ts
  • vscode/src/extensionState.ts
  • vscode/src/diff/vertical/manager.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-23T12:10:46.399Z
Learnt from: pranavgaikwad
PR: konveyor/editor-extensions#581
File: vscode/src/extension.ts:243-251
Timestamp: 2025-07-23T12:10:46.399Z
Learning: In vscode/src/extension.ts, clearing all configErrors in the onDidSaveTextDocument listener for settings YAML is intentional behavior. The setupModelProvider and updateConfigErrors functions are designed to work together to rebuild the entire configuration error state.

Applied to files:

  • vscode/src/extension.ts
🧬 Code Graph Analysis (3)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (2)
webview-ui/src/components/ResolutionsPage/ModifiedFile/useModifiedFileData.ts (1)
  • isLocalChange (110-110)
shared/src/types/types.ts (1)
  • LocalChange (99-108)
vscode/src/diff/vertical/handler.ts (4)
vscode/src/diff/types.ts (2)
  • ApplyState (23-30)
  • DiffLine (7-10)
vscode/src/diff/vertical/decorations.ts (4)
  • RemovedLineDecorationManager (107-185)
  • AddedLineDecorationManager (47-102)
  • belowIndexDecorationType (36-40)
  • indexDecorationType (31-35)
vscode/src/diff/vertical/manager.ts (2)
  • VerticalDiffCodeLens (7-11)
  • clearForFileUri (160-182)
vscode/src/diff/myers.ts (1)
  • myersDiff (25-53)
vscode/src/extension.ts (5)
vscode/src/extensionState.ts (1)
  • ExtensionState (21-57)
vscode/src/utilities/configuration.ts (1)
  • getConfigAutoAcceptOnSave (70-71)
vscode/src/utilities/ideUtils.ts (1)
  • SimpleIDE (52-57)
vscode/src/diff/vertical/manager.ts (1)
  • VerticalDiffManager (13-328)
vscode/src/diff/staticDiffAdapter.ts (1)
  • StaticDiffAdapter (10-162)
🔇 Additional comments (16)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (4)

41-41: LGTM: separate diff-view state is appropriate.

This state cleanly gates the decorated view workflow and prevents re-entrancy.


74-74: LGTM: reset diff view after apply.

Closing the decorated view on apply avoids stale UI.


96-96: LGTM: reset diff view after reject.

Consistent with apply; prevents lingering decorated view.


189-189: LGTM: reset diff view on quick responses.

Keeps UI coherent when using quick actions.

vscode/src/diff/vertical/handler.ts (6)

87-120: LGTM! Well-implemented clear method with proper cleanup and notifications.

The method correctly handles both accept and reject cases, properly clears decorations, updates status, and handles disposal. The user feedback through status updates and diff status change notifications is comprehensive.


126-160: Robust queue processing implementation with proper locking mechanism.

The queue-based approach with lock protection prevents race conditions when processing diff lines. The method correctly handles cases where the editor is not active, queuing items for later processing.


162-205: LGTM! Comprehensive run method with proper error handling.

The async generator consumption pattern is well-implemented. The method includes proper cancellation checks, handles the deletion buffer, applies Myers diff for final reconciliation, and includes appropriate error recovery with rollback.


294-384: Well-structured Myers diff reapplication with correct line preservation logic.

The method properly resets the initial diff state, reconstructs old/new content, handles trailing newlines correctly to prevent line count discrepancies, and rebuilds code lens blocks. The 100ms delay comment explains the necessity for UI synchronization.


386-440: Insertion buffer mechanism correctly handles deferred deletions.

The method properly manages the deletion buffer for visual diff blocks, correctly shifts decorations, and updates code lens state. The logic for handling empty buffers and inserting placeholder lines is sound.


506-527: Clean and straightforward diff line processing logic.

The switch statement correctly handles each diff type with appropriate buffer management and line index updates.

vscode/package.json (1)

543-549: LGTM! Well-configured auto-accept-on-save setting.

The new configuration option is properly defined with a sensible default (true), clear description, and appropriate ordering.

vscode/src/extension.ts (5)

72-75: LGTM! Status bar item properly registered for disposal.

The status bar item is correctly created and added to context.subscriptions (line 280), ensuring proper cleanup on extension deactivation.


315-358: Well-implemented auto-accept-on-save feature with proper error handling.

The implementation correctly:

  • Checks if auto-accept is enabled before processing
  • Verifies if the file has active diffs before attempting to accept
  • Provides user feedback for both success and failure cases
  • Handles errors gracefully with appropriate logging

432-447: LGTM! Clean initialization of vertical diff system.

The initialization properly creates the IDE wrapper, sets up the managers with appropriate dependencies, configures the status change callback, and provides clear logging.


449-485: Well-structured diff status bar setup with comprehensive updates.

The status bar implementation correctly:

  • Sets up meaningful tooltip and command associations
  • Updates on active editor changes
  • Shows diff counts (green/red) when diffs are present
  • Hides when no diffs exist

701-719: Robust cleanup in deactivation with proper error handling.

The deactivation properly disposes of the vertical diff manager, clears the static diff adapter reference, and includes comprehensive error handling with try-catch-finally to ensure cleanup completes even if errors occur.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
vscode/src/diff/vertical/handler.ts (1)

449-461: Fix empty-file append: avoid document.lineAt(-1) when lineCount is 0

insertTextAboveLine() dereferences lineAt(lineCount - 1) even when the document is empty, which will throw. Handle lineCount === 0 by inserting at Position(0, 0) and omitting the leading newline.

   private async insertTextAboveLine(index: number, text: string) {
     await this.editor.edit(
       (editBuilder) => {
         const lineCount = this.editor.document.lineCount;
-        if (index >= lineCount) {
-          // Append to end of file
-          editBuilder.insert(
-            new vscode.Position(lineCount, this.editor.document.lineAt(lineCount - 1).text.length),
-            `\n${text}`,
-          );
-        } else {
-          editBuilder.insert(new vscode.Position(index, 0), `${text}\n`);
-        }
+        if (index >= lineCount) {
+          // Append to end of file
+          if (lineCount === 0) {
+            editBuilder.insert(new vscode.Position(0, 0), text);
+          } else {
+            const lastLineLen = this.editor.document.lineAt(lineCount - 1).text.length;
+            editBuilder.insert(new vscode.Position(lineCount, lastLineLen), `\n${text}`);
+          }
+        } else {
+          editBuilder.insert(new vscode.Position(index, 0), `${text}\n`);
+        }
       },
       { undoStopAfter: false, undoStopBefore: false },
     );
   }
🧹 Nitpick comments (6)
vscode/src/diff/vertical/handler.ts (6)

473-481: Clamp delete end position in deleteLinesAt to mirror deleteRangeLines safety

deleteLinesAt() computes a Range to line index + numLines without clamping. If callers ever pass a value that reaches beyond the buffer (e.g., at EOF), this may throw. Align with deleteRangeLines() by clamping to document.lineCount.

   private async deleteLinesAt(index: number, numLines = 1) {
-    const startLine = new vscode.Position(index, 0);
+    const startLine = new vscode.Position(index, 0);
+    const safeEndLine = Math.min(index + numLines, this.editor.document.lineCount);
     await this.editor.edit(
       (editBuilder) => {
-        editBuilder.delete(new vscode.Range(startLine, startLine.translate(numLines)));
+        editBuilder.delete(new vscode.Range(startLine, new vscode.Position(safeEndLine, 0)));
       },
       { undoStopAfter: false, undoStopBefore: false },
     );
   }

330-334: Avoid double trailing newline when Myers output ends with an 'old' sentinel

replaceContent appends a trailing newline when the original content ends with one. Because old lines are materialized as empty strings to reserve vertical space, body can already end with a newline (e.g., when the last diff item is old). Guard against double-EOL to prevent an extra blank line at the block boundary.

Please verify with a case where the last hunk ends with deletions-only.

-    const replaceContent =
-      myersDiffs.map((diff) => (diff.type === "old" ? "" : diff.line)).join("\n") +
-      (originalContentEndsWithNewline ? "\n" : "");
+    const body = myersDiffs.map((diff) => (diff.type === "old" ? "" : diff.line)).join("\n");
+    // If body already ends with a newline (e.g., last item was 'old'), don't add another.
+    const replaceContent = body.endsWith("\n")
+      ? body
+      : body + (originalContentEndsWithNewline ? "\n" : "");

386-441: Off-by-one in placeholder insertion for deletion-only end-of-stream

insertDeletionBuffer inserts "\n".repeat(this.deletionBuffer.length - 1). This intentionally holds back one line to keep the cursor aligned during streaming. However, if the stream ends with only deletions (no following "same" or "new"), the final flush will still insert only N-1 placeholders for N deletions, temporarily undercounting placeholders until Myers reapply. If that's intentional, add a short comment; otherwise, consider inserting all N at end-of-stream.

Consider adding a clarifying comment:

-    // Insert the block of deleted lines as empty new lines
+    // Insert the block of deleted lines as empty new lines.
+    // We intentionally insert (N - 1) to keep the current line aligned while streaming.
+    // Myers reapply will normalize the final shape at the end of the stream.

505-526: Queue-time edits are robust; minor logging noise could be gated

Queueing and handling of diff lines is solid. The console logs here are verbose and will flood the devtools console during large diffs. Consider gating logs behind a debug flag or VS Code setting.


495-504: Index progress decorations are effectively disabled

updateIndexLineDecorations() immediately returns. Since it's called from multiple places as a visual progress cue, either wire it to a real decoration (as per decorations.ts) or remove the calls to avoid confusion.


324-343: Replace range might exclude trailing EOL; consider replacing through end-of-line to simplify EOL handling

Currently, replace() targets this.range which ends at (endLine, MAX_SAFE_INTEGER). This excludes the newline after endLine. You’re compensating with originalContentEndsWithNewline checks. An alternative is to replace through the start of endLine+1 (clamped), then omit special-casing EOLs. This can reduce edge cases around double EOLs.

Example:

const doc = this.editor.document;
const safeEndLine = Math.min(this.endLine + 1, doc.lineCount);
const replaceRange = new vscode.Range(this.startLine, 0, safeEndLine, 0);
// build `replaceContent` without appending a trailing newline
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 55aa122 and a9da677.

📒 Files selected for processing (2)
  • vscode/src/diff/vertical/handler.ts (1 hunks)
  • vscode/src/diff/vertical/manager.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • vscode/src/diff/vertical/manager.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
vscode/src/diff/vertical/handler.ts (4)
vscode/src/diff/types.ts (2)
  • ApplyState (23-30)
  • DiffLine (7-10)
vscode/src/diff/vertical/decorations.ts (4)
  • RemovedLineDecorationManager (107-185)
  • AddedLineDecorationManager (47-102)
  • belowIndexDecorationType (36-40)
  • indexDecorationType (31-35)
vscode/src/diff/vertical/manager.ts (2)
  • VerticalDiffCodeLens (7-11)
  • clearForFileUri (160-182)
vscode/src/diff/myers.ts (1)
  • myersDiff (25-53)
🔇 Additional comments (2)
vscode/src/diff/vertical/handler.ts (2)

528-541: CodeLens shift skips the active block by design; ensure removal of the active block always happens

shiftCodeLensObjects() filters out x.start === startLine (the block being accepted/rejected). Make sure acceptRejectBlock() always removes the corresponding block before any other shifting operations to avoid orphaned entries if start values drift in edge cases. Looks correct as written; leaving a note to double-check manager->provider expectations.


122-160: Queue drain on editor activation looks good

The lock-and-poke pattern using queueDiffLine(undefined) upon editor activation is a good approach to resume processing without race conditions. No changes needed.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
vscode/src/utilities/pathUtils.ts (2)

1-1: Optional: Prefer node:url import for built-in URL utilities

Using "node:url" makes it explicit that this is a Node built-in and helps bundlers avoid pulling browser polyfills.

-import { fileURLToPath } from "url";
+import { fileURLToPath } from "node:url";

3-8: Make file URL detection more robust and browser-safe; improve Windows “/C:/” handling

  • Accept any “file:” scheme variant (e.g., file:/, file://, mixed-case) via URL parsing instead of a fragile string prefix check.
  • Gracefully handle malformed URLs (avoid throwing; fall back to plain path).
  • Guard against process being undefined in non-Node/browser contexts.
  • Handle “/C:” with or without a trailing slash and both separators ([\/]).
-export function fileUriToPath(path: string): string {
-  const cleanPath = path.startsWith("file://") ? fileURLToPath(path) : path;
-  return process.platform === "win32" && cleanPath.match(/^\/[A-Za-z]:\//)
-    ? cleanPath.substring(1)
-    : cleanPath;
-}
+export function fileUriToPath(input: string): string {
+  // Try to interpret as a file URL first; fall back to plain path on failure.
+  let cleanPath = input;
+  if (typeof input === "string" && input.length >= 5 && input.slice(0, 5).toLowerCase() === "file:") {
+    try {
+      const url = new URL(input);
+      if (url.protocol === "file:") {
+        cleanPath = fileURLToPath(url);
+      }
+    } catch {
+      // Not a valid URL; treat as a plain path.
+    }
+  }
+  const isWin = typeof process !== "undefined" && process.platform === "win32";
+  if (isWin && /^\/[A-Za-z]:(?:[\\/]|$)/.test(cleanPath)) {
+    return cleanPath.substring(1);
+  }
+  return cleanPath;
+}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a9da677 and 754000f.

📒 Files selected for processing (4)
  • vscode/src/commands.ts (5 hunks)
  • vscode/src/diff/vertical/manager.ts (1 hunks)
  • vscode/src/diff/verticalDiffCodeLens.ts (1 hunks)
  • vscode/src/utilities/pathUtils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • vscode/src/commands.ts
  • vscode/src/diff/verticalDiffCodeLens.ts
  • vscode/src/diff/vertical/manager.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
vscode/src/extension.ts (2)

315-358: Auto-accept on save must use event.waitUntil; settings.yaml should be handled onDidSave (reads stale file otherwise)

  • The onWillSave listener currently awaits asynchronous work directly, which does not delay the save. VS Code only guarantees pre-save edits if you call event.waitUntil with a Thenable.
  • The comment claims acceptance happens “BEFORE saving,” but without waitUntil this is not guaranteed.
  • Running setupModelProvider onWillSave reads settings.yaml from disk before the editor flushes the updated content; this regresses the previously intentional onDidSave behavior and can validate stale config.

Apply this refactor to make the save path correct and deterministic:

-      vscode.workspace.onWillSaveTextDocument(async (event) => {
-          const doc = event.document;
-
-          // Handle settings.yaml configuration changes
-          if (doc.uri.fsPath === paths().settingsYaml.fsPath) {
-            const configError = await this.setupModelProvider(paths().settingsYaml);
-            this.state.mutateData((draft) => {
-              draft.configErrors = [];
-              if (configError) {
-                draft.configErrors.push(configError);
-              }
-              updateConfigErrors(draft, paths().settingsYaml.fsPath);
-            });
-          }
-
-          // Auto-accept all diff decorations BEFORE saving (if enabled)
-          // This ensures the document is saved in its final state
-          if (getConfigAutoAcceptOnSave() && this.state.verticalDiffManager) {
-            const fileUri = doc.uri.toString();
-            const handler = this.state.verticalDiffManager.getHandlerForFile(fileUri);
-            if (handler && handler.hasDiffForCurrentFile()) {
-              try {
-                // Accept all diffs BEFORE the save operation
-                // This ensures the document is saved in its final state
-                await this.state.staticDiffAdapter?.acceptAll(doc.uri.fsPath);
-                this.state.logger.info(
-                  `Auto-accepted all diff decorations for ${doc.fileName} before save`,
-                );
-                // Show user feedback that diffs were auto-accepted
-                vscode.window.showInformationMessage(
-                  `Auto-accepted all diff changes for ${doc.fileName} - saving final state`,
-                );
-              } catch (error) {
-                this.state.logger.error(
-                  `Failed to auto-accept diff decorations before save for ${doc.fileName}:`,
-                  error,
-                );
-                vscode.window.showErrorMessage(
-                  `Failed to auto-accept diff changes for ${doc.fileName}: ${error}`,
-                );
-              }
-            }
-          }
-        }),
+      vscode.workspace.onWillSaveTextDocument((event) => {
+        event.waitUntil(
+          (async () => {
+            const doc = event.document;
+
+            // Auto-accept all diff decorations BEFORE saving (if enabled)
+            if (getConfigAutoAcceptOnSave() && this.state.verticalDiffManager) {
+              const fileUri = doc.uri.toString();
+              const handler = this.state.verticalDiffManager.getHandlerForFile(fileUri);
+              if (handler && handler.hasDiffForCurrentFile()) {
+                try {
+                  // Ensure acceptance completes before save
+                  await this.state.verticalDiffManager.acceptAll(fileUri);
+                  this.state.logger.info(
+                    `Auto-accepted all diff changes for ${doc.fileName} before save`,
+                  );
+                  vscode.window.showInformationMessage(
+                    `Auto-accepted all diff changes for ${doc.fileName} - saving final state`,
+                  );
+                } catch (error) {
+                  this.state.logger.error(
+                    `Failed to auto-accept diff changes before save for ${doc.fileName}:`,
+                    error,
+                  );
+                  vscode.window.showErrorMessage(
+                    `Failed to auto-accept diff changes for ${doc.fileName}: ${error}`,
+                  );
+                }
+              }
+            }
+          })(),
+        );
+      }),

Then, add a separate onDidSave handler for settings.yaml so the new contents are on disk before parsing:

// Place near the other listeners (e.g., after onWillSave)
this.listeners.push(
  vscode.workspace.onDidSaveTextDocument(async (doc) => {
    if (doc.uri.fsPath === paths().settingsYaml.fsPath) {
      const configError = await this.setupModelProvider(paths().settingsYaml);
      this.state.mutateData((draft) => {
        draft.configErrors = [];
        if (configError) {
          draft.configErrors.push(configError);
        }
        updateConfigErrors(draft, paths().settingsYaml.fsPath);
      });
    }
  }),
);

Optional but recommended: keep adapter behavior consistent by making StaticDiffAdapter.acceptAll/rejectAll call the manager’s acceptAll/rejectAll (see snippet in the next comment).


315-358: Replace StaticDiffAdapter.acceptAll with VerticalDiffManager.acceptAll to ensure diffs are actually applied

StaticDiffAdapter.acceptAll currently delegates to clearForFileUri(..., true), which only disposes decorations without applying any diff blocks. By contrast, VerticalDiffManager.acceptAll first iterates and applies each block before clearing. To guarantee “accept all before save” actually applies every change, please make these updates:

• In vscode/src/extension.ts, inside the onWillSaveTextDocument handler:
Replace

await this.state.staticDiffAdapter?.acceptAll(doc.uri.fsPath);

with

await this.state.verticalDiffManager.acceptAll(fileUri);

• In vscode/src/diff/staticDiffAdapter.ts, update the adapter to delegate correctly:

export class StaticDiffAdapter {
  // …
- async acceptAll(filePath: string): Promise<void> {
-   const fileUri = vscode.Uri.file(filePath).toString();
-   await this.verticalDiffManager.clearForFileUri(fileUri, true);
- }
+ async acceptAll(filePath: string): Promise<void> {
+   const fileUri = vscode.Uri.file(filePath).toString();
+   await this.verticalDiffManager.acceptAll(fileUri);
+ }

- async rejectAll(filePath: string): Promise<void> {
-   const fileUri = vscode.Uri.file(filePath).toString();
-   await this.verticalDiffManager.clearForFileUri(fileUri, false);
- }
+ async rejectAll(filePath: string): Promise<void> {
+   const fileUri = vscode.Uri.file(filePath).toString();
+   await this.verticalDiffManager.rejectAll(fileUri);
+ }
}

These changes restore the intended behavior—applying all diff blocks, not silently dropping them.

🧹 Nitpick comments (3)
vscode/src/extension.ts (3)

72-76: Ensure StatusBarItem is always disposed even if initialize fails

You create the StatusBarItem in the constructor but only push it to context.subscriptions later (Line 280). If initialize throws before Line 280, extension.dispose won’t dispose the StatusBarItem, leaking a resource (even if hidden).

  • Push the item into subscriptions immediately after creation.
  • Remove the later push to avoid duplication.
@@
     this.diffStatusBarItem = vscode.window.createStatusBarItem(
       vscode.StatusBarAlignment.Right,
       100,
     );
+    this.context.subscriptions.push(this.diffStatusBarItem);
@@
-      this.context.subscriptions.push(this.diffStatusBarItem);

Also applies to: 280-281


60-60: Publicly exposing state increases coupling

Exposing the entire ExtensionState as public can make future refactors harder. Consider exposing a narrower surface (e.g., dedicated methods to update diff status bar, access modelProvider, etc.) and keep state private.


449-454: Nit: optional accessibility label for status bar item

Consider setting accessibilityInformation for better screen reader support, e.g., “Konveyor Diff Status: X additions, Y deletions. Click for actions.”

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 754000f and 8dd399b.

📒 Files selected for processing (2)
  • vscode/src/diff/vertical/manager.ts (1 hunks)
  • vscode/src/extension.ts (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • vscode/src/diff/vertical/manager.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-23T12:10:46.399Z
Learnt from: pranavgaikwad
PR: konveyor/editor-extensions#581
File: vscode/src/extension.ts:243-251
Timestamp: 2025-07-23T12:10:46.399Z
Learning: In vscode/src/extension.ts, clearing all configErrors in the onDidSaveTextDocument listener for settings YAML is intentional behavior. The setupModelProvider and updateConfigErrors functions are designed to work together to rebuild the entire configuration error state.

Applied to files:

  • vscode/src/extension.ts
🧬 Code Graph Analysis (1)
vscode/src/extension.ts (6)
vscode/src/extensionState.ts (1)
  • ExtensionState (21-57)
vscode/src/paths.ts (2)
  • paths (100-105)
  • ExtensionPaths (8-29)
vscode/src/utilities/configuration.ts (1)
  • getConfigAutoAcceptOnSave (70-71)
vscode/src/utilities/ideUtils.ts (1)
  • SimpleIDE (52-57)
vscode/src/diff/vertical/manager.ts (1)
  • VerticalDiffManager (14-329)
vscode/src/diff/staticDiffAdapter.ts (1)
  • StaticDiffAdapter (10-162)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build (windows)
  • GitHub Check: Build (macos)
  • GitHub Check: Build (linux)
🔇 Additional comments (4)
vscode/src/extension.ts (4)

432-447: Vertical diff system initialization looks solid

Instantiates SimpleIDE, wires VerticalDiffManager and StaticDiffAdapter, and propagates onDiffStatusChange to the status bar updater. This is cohesive and keeps concerns localized.


424-426: Good UX: defers status bar wiring until after initialization

Deferring setupDiffStatusBar until after initialization helps avoid UI flicker or stale counts on startup.


466-485: Status bar update logic is correct and efficient

  • Hides when no diffs present.
  • Sums added/removed across blocks without expensive operations.
  • Uses the fileUri-based handler lookup to scope to the active editor.

700-720: Deactivation cleans up vertical diff managers and extension reliably

  • Disposes VerticalDiffManager explicitly.
  • Clears adapter reference.
  • Calls extension.dispose and guards with try/finally.

Looks good.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
vscode/src/diff/vertical/handler.ts (2)

487-497: Good fix: end-of-document clamp prevents out-of-bounds deletes.

Clamping the end position to document.lineCount removes the original edge-case when deleting the last decorated line. This aligns with VS Code’s edit API expectations.


126-139: Queue processing preserves order on errors.

Good use of a simple lock plus unshift() to re-queue the current item on edit failure (e.g., editor switch), maintaining correct processing order.

Also applies to: 144-158

🧹 Nitpick comments (5)
vscode/src/diff/vertical/handler.ts (5)

2-2: Avoid pulling in uri-js; compare URIs with strings (or vscode.Uri) directly.

Using uri-js just for equality adds an unnecessary dependency in the extension host. String equality on toString() is sufficient here.

-import * as URI from "uri-js";
-      if (URI.equal(editor.document.uri.toString(), this.fileUri)) {
+      if (editor.document.uri.toString() === this.fileUri) {
         this.editor = editor;
         this.removedLineDecorations.applyToNewEditor(editor);
         this.addedLineDecorations.applyToNewEditor(editor);
         this.updateIndexLineDecorations();
         this.refreshCodeLens();
 
         // Handle any lines received while editor was closed
         this.queueDiffLine(undefined);
       }

If uri-js is used elsewhere in the PR, we can keep it. Otherwise, consider removing it from dependencies.

Also applies to: 58-68


453-466: Empty-file insertion path yields different trailing-newline semantics.

When lineCount === 0, you insert text without appending a newline, while other paths append a newline around the inserted text. This can lead to subtle off-by-one line count differences when inserting deletion buffers vs. single lines into an empty file.

Consider normalizing insertion behavior. One option is to compute the exact insertion string once:

  • For index >= lineCount: insertion = "\n" + text
  • Else: insertion = text + "\n"
  • For empty file: insertion = text + (text.endsWith("\n") ? "" : "\n")

I can draft a precise diff if you want to standardize all branches.


499-507: Index progress decorations disabled – consider removing imports if permanent.

updateIndexLineDecorations() is a no-op, but you still import indexDecorationType/belowIndexDecorationType and clear them. If progress will remain disabled, consider removing the unused progress decorations and related imports to reduce noise.


32-32: Remove unused state newLinesAdded.

newLinesAdded is incremented but never read. Dead state can confuse future maintainers.

-  private newLinesAdded = 0;
-    this.newLinesAdded++;

Also applies to: 471-475


300-304: Reverse mutates the shared blocks array; safe here but brittle.

blocks.reverse() mutates the array retrieved from the map. It works because you immediately iterate and then clear decorations, but it does mutate shared state while acceptRejectBlock also updates the map. Safer to iterate a copy in reverse order.

-    for (const block of blocks.reverse()) {
+    for (const block of [...blocks].reverse()) {
       await this.acceptRejectBlock(false, block.start, block.numGreen, block.numRed, true);
     }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 14f0c57 and b67c369.

📒 Files selected for processing (1)
  • vscode/src/diff/vertical/handler.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
vscode/src/diff/vertical/handler.ts (4)
vscode/src/diff/types.ts (2)
  • ApplyState (23-30)
  • DiffLine (7-10)
vscode/src/diff/vertical/decorations.ts (4)
  • RemovedLineDecorationManager (107-185)
  • AddedLineDecorationManager (47-102)
  • belowIndexDecorationType (36-40)
  • indexDecorationType (31-35)
vscode/src/diff/vertical/manager.ts (2)
  • VerticalDiffCodeLens (8-12)
  • clearForFileUri (161-183)
vscode/src/diff/myers.ts (1)
  • myersDiff (25-53)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build (windows)
  • GitHub Check: Build (linux)
🔇 Additional comments (2)
vscode/src/diff/vertical/handler.ts (2)

312-335: Confirm trailing newline strategy in Myers reapply.

Mapping old → "" and appending a trailing newline only when the original range had one maintains vertical alignment (blank placeholders for deletions). This is intentional for vertical diff. Just confirm that the original range (this.range) corresponds to the whole file when intended; otherwise, the trailing newline condition could mismatch the actual file’s final newline.

If this.range is sometimes a subsection, you may want to derive the trailing newline from the target subsection semantics rather than the entire document’s EOL policy.


532-545: Potential duplicate-start removal in shiftCodeLensObjects.

Filtering by x.start !== startLine assumes block starts are unique. If blocks can share a start line (edge cases after multiple shifts), this may remove more than intended.

Confirm uniqueness of block starts. If not guaranteed, consider using object identity or an ID to remove only the targeted block.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
vscode/src/extension.ts (1)

456-471: Register the missing konveyor.showDiffActions command

The status bar item references konveyor.showDiffActions command which doesn't appear to be registered anywhere.

Add the command registration:

   private setupDiffStatusBar(): void {
     this.diffStatusBarItem.name = "Konveyor Diff Status";
     this.diffStatusBarItem.tooltip = "Click to accept/reject all diff changes";
-    this.diffStatusBarItem.command = "konveyor.showDiffActions";
+    this.diffStatusBarItem.command = {
+      command: "konveyor.showDiffActions",
+      title: "Show Diff Actions"
+    };
     this.diffStatusBarItem.hide();

+    // Register the command
+    this.context.subscriptions.push(
+      vscode.commands.registerCommand("konveyor.showDiffActions", async () => {
+        const editor = vscode.window.activeTextEditor;
+        if (!editor || !this.state.verticalDiffManager) {
+          return;
+        }
+        
+        const fileUri = editor.document.uri.toString();
+        const handler = this.state.verticalDiffManager.getHandlerForFile(fileUri);
+        if (!handler || !handler.hasDiffForCurrentFile()) {
+          return;
+        }
+
+        const action = await vscode.window.showQuickPick(
+          ["Accept All Changes", "Reject All Changes", "Cancel"],
+          { placeHolder: "Choose an action for diff changes" }
+        );
+
+        if (action === "Accept All Changes") {
+          await this.state.staticDiffAdapter?.acceptAll(editor.document.uri.fsPath);
+        } else if (action === "Reject All Changes") {
+          await this.state.staticDiffAdapter?.rejectAll(editor.document.uri.fsPath);
+        }
+      })
+    );
+
     // Update status bar when active editor changes
vscode/src/diff/vertical/manager.ts (1)

96-117: Update handler decorations instead of just logging

The handleDocumentChange method calculates line deltas but doesn't actually update the handler's decorations, which will cause misaligned diff decorations when users edit the document.

Call the handler's updateLineDelta method to properly update decorations:

   private handleDocumentChange(
     event: vscode.TextDocumentChangeEvent,
-    _handler: VerticalDiffHandler,
+    handler: VerticalDiffHandler,
   ) {
     // Loop through each change in the event
     event.contentChanges.forEach((change) => {
       // Calculate the number of lines added or removed
       const linesAdded = change.text.split("\n").length - 1;
       const linesDeleted = change.range.end.line - change.range.start.line;
 
       // Calculate the net change in lines
       const lineDelta = linesAdded - linesDeleted;
 
       // Get the line number where the change occurred
       const lineNumber = change.range.start.line;
 
-      // Update decorations based on the change
-      // Note: updateDecorations method would need to be implemented in handler
-      // For now, we'll just log the change
-      this.logger.debug(`Document change at line ${lineNumber}, delta: ${lineDelta}`);
+      // Update decorations based on the change
+      const fileUri = event.document.uri.toString();
+      handler.updateLineDelta(fileUri, lineNumber, lineDelta);
+      this.logger.debug(`Updated decorations for document change at line ${lineNumber}, delta: ${lineDelta}`);
     });
   }
vscode/src/diff/vertical/handler.ts (1)

90-101: Process ranges bottom-up to prevent index drift during rejection

When rejecting all changes, the sequential processing of ranges can cause index misalignment as earlier deletions shift subsequent line positions.

Process ranges in reverse order to maintain correct line positions:

     const removedRanges = this.removedLineDecorations.ranges;
     if (accept) {
       // Accept all: delete all the red ranges and clear green decorations
       await this.deleteRangeLines(removedRanges.map((r) => r.range));
     } else {
       // Reject all: Re-insert red lines, delete green ones
-      for (const r of removedRanges) {
+      // Process bottom-up to prevent index drift
+      const sortedRanges = [...removedRanges].sort((a, b) => b.range.start.line - a.range.start.line);
+      for (const r of sortedRanges) {
         await this.deleteRangeLines([r.range]);
         await this.insertTextAboveLine(r.range.start.line, r.line);
       }
       await this.deleteRangeLines(this.addedLineDecorations.ranges);
     }
🧹 Nitpick comments (2)
vscode/src/diff/staticDiffAdapter.ts (1)

20-85: Handle edge cases in diff parsing

The parsing logic doesn't handle some edge cases that could lead to incorrect diff application:

  1. Empty hunks or missing hunks could cause misalignment
  2. Multi-file diffs might not be handled correctly

Add validation and better error handling:

   private parseUnifiedDiffToDiffLines(unifiedDiff: string, originalContent: string): DiffLine[] {
     const diffLines: DiffLine[] = [];
     const originalLines = originalContent.split("\n");
 
     // Parse the unified diff
     const parsedDiff = parsePatch(unifiedDiff);
     if (!parsedDiff || parsedDiff.length === 0) {
       throw new Error("Failed to parse diff");
     }
 
+    // Validate single file diff
+    if (parsedDiff.length > 1) {
+      this.logger.warn(`Multiple file diffs detected (${parsedDiff.length}), using first file only`);
+    }
+
+    const fileDiff = parsedDiff[0];
+    if (!fileDiff.hunks || fileDiff.hunks.length === 0) {
+      this.logger.debug("No hunks in diff, returning unchanged content");
+      return originalLines.map(line => ({ type: "same", line }));
+    }
+
     // Track which original lines have been processed
     let originalLineIndex = 0;
 
-    for (const fileDiff of parsedDiff) {
-      if (!fileDiff.hunks) {
-        continue;
-      }
-
-      for (const hunk of fileDiff.hunks) {
+    for (const hunk of fileDiff.hunks) {
         // Add unchanged lines before this hunk
         const hunkStartLine = hunk.oldStart - 1; // Convert to 0-based
+
+        // Validate hunk boundaries
+        if (hunkStartLine < originalLineIndex) {
+          this.logger.warn(`Overlapping hunks detected at line ${hunkStartLine}`);
+          continue;
+        }
vscode/src/diff/vertical/handler.ts (1)

478-485: Add bounds checking for line deletion

While unlikely in normal operation, deleteLinesAt could attempt to delete beyond document bounds if called with large numLines values.

Add defensive bounds checking:

   private async deleteLinesAt(index: number, numLines = 1) {
     const startLine = new vscode.Position(index, 0);
+    const docLineCount = this.editor.document.lineCount;
+    const safeNumLines = Math.min(numLines, Math.max(0, docLineCount - index));
+    
     await this.editor.edit(
       (editBuilder) => {
-        editBuilder.delete(new vscode.Range(startLine, startLine.translate(numLines)));
+        editBuilder.delete(new vscode.Range(startLine, startLine.translate(safeNumLines)));
       },
       { undoStopAfter: false, undoStopBefore: false },
     );
   }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b67c369 and bd6796e.

📒 Files selected for processing (8)
  • vscode/src/commands.ts (5 hunks)
  • vscode/src/diff/staticDiffAdapter.ts (1 hunks)
  • vscode/src/diff/types.ts (1 hunks)
  • vscode/src/diff/vertical/decorations.ts (1 hunks)
  • vscode/src/diff/vertical/handler.ts (1 hunks)
  • vscode/src/diff/vertical/manager.ts (1 hunks)
  • vscode/src/extension.ts (9 hunks)
  • vscode/src/utilities/ideUtils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • vscode/src/utilities/ideUtils.ts
  • vscode/src/diff/vertical/decorations.ts
  • vscode/src/commands.ts
  • vscode/src/diff/types.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-23T12:10:46.399Z
Learnt from: pranavgaikwad
PR: konveyor/editor-extensions#581
File: vscode/src/extension.ts:243-251
Timestamp: 2025-07-23T12:10:46.399Z
Learning: In vscode/src/extension.ts, clearing all configErrors in the onDidSaveTextDocument listener for settings YAML is intentional behavior. The setupModelProvider and updateConfigErrors functions are designed to work together to rebuild the entire configuration error state.

Applied to files:

  • vscode/src/extension.ts
🧬 Code Graph Analysis (4)
vscode/src/diff/vertical/handler.ts (4)
vscode/src/diff/types.ts (2)
  • ApplyState (23-30)
  • DiffLine (7-10)
vscode/src/diff/vertical/decorations.ts (4)
  • RemovedLineDecorationManager (127-227)
  • AddedLineDecorationManager (47-122)
  • belowIndexDecorationType (36-40)
  • indexDecorationType (31-35)
vscode/src/diff/vertical/manager.ts (2)
  • VerticalDiffCodeLens (9-13)
  • clearForFileUri (163-185)
vscode/src/diff/myers.ts (1)
  • myersDiff (25-53)
vscode/src/diff/staticDiffAdapter.ts (4)
vscode/src/diff/vertical/manager.ts (1)
  • VerticalDiffManager (15-331)
vscode/src/client/tracer.ts (1)
  • logger (60-65)
vscode/src/diff/types.ts (1)
  • DiffLine (7-10)
vscode/src/diff/vertical/handler.ts (1)
  • fileUri (83-85)
vscode/src/extension.ts (6)
vscode/src/extensionState.ts (1)
  • ExtensionState (21-57)
vscode/src/utilities/configuration.ts (1)
  • getConfigAutoAcceptOnSave (70-71)
vscode/src/diff/types.ts (1)
  • FileEditor (33-38)
vscode/src/utilities/ideUtils.ts (1)
  • FileEditor (52-57)
vscode/src/diff/vertical/manager.ts (1)
  • VerticalDiffManager (15-331)
vscode/src/diff/staticDiffAdapter.ts (1)
  • StaticDiffAdapter (11-166)
vscode/src/diff/vertical/manager.ts (3)
vscode/src/diff/types.ts (2)
  • DiffLine (7-10)
  • FileEditor (33-38)
agentic/src/cache.ts (1)
  • InMemoryCacheWithRevisions (129-186)
vscode/src/utilities/pathUtils.ts (1)
  • fileUriToPath (3-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build (windows)
  • GitHub Check: Build (macos)
  • GitHub Check: Build (linux)
🔇 Additional comments (4)
vscode/src/diff/staticDiffAdapter.ts (1)

100-141: No changes needed—diff placement doesn’t depend on editor.selection

I inspected VerticalDiffManager.streamDiffLines in vscode/src/diff/vertical/manager.ts and found that it always applies edits using explicit ranges derived from the parsed DiffLine data. It never reads or relies on editor.selection. Therefore, setting a full-document selection before streaming diffs isn’t necessary.

vscode/src/diff/vertical/manager.ts (1)

216-228: Cache key format consistency verified

I searched for all kaiFsCache.set/.get calls and any direct URI-based cache keys—only the .set(filePath, …) in vscode/src/diff/vertical/manager.ts was found. There are no other cache operations or URI keys, so the current absolute-path key usage is consistent.

vscode/src/diff/vertical/handler.ts (2)

454-456: LGTM! Empty file handling fixed

Good fix for the empty file edge case by explicitly checking and inserting at Position(0, 0).


491-492: LGTM! Out-of-bounds protection added

Good defensive programming - clamping endLine to document.lineCount prevents potential out-of-bounds errors.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (1)

32-40: Fix type mismatch when initializing actionTaken (discarded → rejected)

status can be "pending" | "applied" | "discarded". The initializer checks for "rejected", which is never present, so "discarded" won’t be reflected. Map discarded to rejected.

Apply this diff:

-  const [actionTaken, setActionTaken] = useState<"applied" | "rejected" | null>(() => {
-    if (status === "applied") {
-      return "applied";
-    }
-    if (status === "rejected") {
-      return "rejected";
-    }
-    return null; // for "pending" or undefined
-  });
+  const [actionTaken, setActionTaken] = useState<"applied" | "rejected" | null>(() => {
+    if (status === "applied") return "applied";
+    if (status === "discarded") return "rejected";
+    return null; // for "pending" or undefined
+  });
vscode/src/extension.ts (1)

316-359: Move auto-accept logic to onDidSave to avoid save races

Async operations in onWillSaveTextDocument can race with the save. Accepting diff decorations there may yield partial writes and re-entrancy hazards.

Apply this diff to remove the auto-accept from onWillSave (keep the settings.yaml handling):

-          // Auto-accept all diff decorations BEFORE saving (if enabled)
-          // This ensures the document is saved in its final state
-          if (getConfigAutoAcceptOnSave() && this.state.verticalDiffManager) {
-            const fileUri = doc.uri.toString();
-            const handler = this.state.verticalDiffManager.getHandlerForFile(fileUri);
-            if (handler && handler.hasDiffForCurrentFile()) {
-              try {
-                // Accept all diffs BEFORE the save operation
-                // This ensures the document is saved in its final state
-                await this.state.staticDiffAdapter?.acceptAll(doc.uri.fsPath);
-                this.state.logger.info(
-                  `Auto-accepted all diff decorations for ${doc.fileName} before save`,
-                );
-                // Show user feedback that diffs were auto-accepted
-                vscode.window.showInformationMessage(
-                  `Auto-accepted all diff changes for ${doc.fileName} - saving final state`,
-                );
-              } catch (error) {
-                this.state.logger.error(
-                  `Failed to auto-accept diff decorations before save for ${doc.fileName}:`,
-                  error,
-                );
-                vscode.window.showErrorMessage(
-                  `Failed to auto-accept diff changes for ${doc.fileName}: ${error}`,
-                );
-              }
-            }
-          }

And add a separate onDidSaveTextDocument handler with a per-file guard to avoid loops:

// Place near other listeners
const savingAfterAccept = new Set<string>();
this.listeners.push(
  vscode.workspace.onDidSaveTextDocument(async (doc) => {
    if (!getConfigAutoAcceptOnSave() || !this.state.verticalDiffManager) return;

    const fileUri = doc.uri.toString();
    const filePath = doc.uri.fsPath;

    // Avoid infinite save loops
    if (savingAfterAccept.has(fileUri)) {
      savingAfterAccept.delete(fileUri);
      return;
    }

    const handler = this.state.verticalDiffManager.getHandlerForFile(fileUri);
    if (handler && handler.hasDiffForCurrentFile()) {
      try {
        await this.state.staticDiffAdapter?.acceptAll(filePath);
        this.state.logger.info(`Auto-accepted all diff decorations for ${doc.fileName} after save`);
        savingAfterAccept.add(fileUri);
        await doc.save();
        vscode.window.showInformationMessage(
          `Auto-accepted all diff changes for ${doc.fileName}`,
        );
      } catch (error) {
        this.state.logger.error(
          `Failed to auto-accept diff decorations after save for ${doc.fileName}:`,
          error,
        );
        vscode.window.showErrorMessage(
          `Failed to auto-accept diff changes for ${doc.fileName}: ${error}`,
        );
      }
    }
  }),
);
🧹 Nitpick comments (6)
vscode/package.json (2)

475-481: New setting looks good; consider scope semantics

konveyor.diff.autoAcceptOnSave default and description are sensible. Consider whether "resource" scope would better support per-workspace overrides; "window" applies globally to the window.


610-610: Add missing contributed commands in package.json

The following programmatically‐registered commands aren’t declared under contributes.commands in agentic/package.json. Declaring them will surface them in the Command Palette (and satisfy Marketplace/lint rules). You can optionally hide them from the palette with a "when": "false" or "when": "never" clause.

• File: agentic/package.json
Insert under "contributes": { "commands": [ … ] }:

{
  "command": "konveyor.showDiffWithDecorations",
  "title": "Show Diff with Decorations",
  "category": "Konveyor"
},
{
  "command": "konveyor.acceptDiff",
  "title": "Accept All Diff Changes",
  "category": "Konveyor"
},
{
  "command": "konveyor.rejectDiff",
  "title": "Reject All Diff Changes",
  "category": "Konveyor"
},
{
  "command": "konveyor.acceptVerticalDiffBlock",
  "title": "Accept Diff Block",
  "category": "Konveyor"
},
{
  "command": "konveyor.rejectVerticalDiffBlock",
  "title": "Reject Diff Block",
  "category": "Konveyor"
},
{
  "command": "konveyor.clearDiffDecorations",
  "title": "Clear Diff Decorations",
  "category": "Konveyor"
},
{
  "command": "konveyor.showDiffActions",
  "title": "Show Diff Actions",
  "category": "Konveyor"
}
vscode/src/utilities/ModifiedFiles/handleModifiedFile.ts (2)

137-141: Deletion detection may misclassify whitespace-only edits

Using modifiedContent.trim() === "" (Line 140) treats files that end up with only whitespace as deletions. If that’s not desired, consider a stricter check: compare length, or only treat as deleted when modifiedContent === "".


166-185: Pending interaction callback ignores response payload

The resolver added to pendingInteractions (Line 169) discards the response argument, which may be needed to determine apply/reject or partial-apply semantics in some flows. If higher layers rely on this value, this will silently drop it.

If the intent is only to unblock the queue, add a comment clarifying that response is handled elsewhere (e.g., in webviewMessageHandler). Otherwise, forward response to the appropriate handler here.

Also prefer using state.logger over console.error for consistency:

-          } catch (error) {
-            console.error(`Error in ModifiedFile resolver for messageId: ${msg.id}:`, error);
+          } catch (error) {
+            state.logger.error(`Error in ModifiedFile resolver for messageId: ${msg.id}:`, error);
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (1)

265-269: Prop naming mismatch: isFileApplied receives isViewingDiff

You pass isViewingDiff to a prop named isFileApplied. This is misleading and can cause UI logic errors. Either rename the prop in ModifiedFileActions to isViewingDiff, or pass a true "file applied" boolean.

vscode/src/extension.ts (1)

72-75: Dispose StatusBarItem reliably even if initialize fails

The StatusBarItem is created in the constructor but only added to subscriptions later (Line 281). If initialize throws before that, the item won’t be disposed.

Apply this diff to push it into subscriptions right after creation:

     this.diffStatusBarItem = vscode.window.createStatusBarItem(
       vscode.StatusBarAlignment.Right,
       100,
     );
+    this.context.subscriptions.push(this.diffStatusBarItem);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bd6796e and 0cb1c58.

📒 Files selected for processing (6)
  • vscode/package.json (3 hunks)
  • vscode/src/commands.ts (8 hunks)
  • vscode/src/extension.ts (11 hunks)
  • vscode/src/utilities/ModifiedFiles/handleModifiedFile.ts (1 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (6 hunks)
  • webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-12T02:13:55.897Z
Learnt from: djzager
PR: konveyor/editor-extensions#678
File: vscode/src/commands.ts:135-139
Timestamp: 2025-08-12T02:13:55.897Z
Learning: In vscode/src/commands.ts, the solutionState is conditionally reset to "none" after successful getSolution completion, but only when !state.isWaitingForUserInteraction. This prevents permanent blocking of future getSolution calls while still maintaining proper concurrency control during active workflows.

Applied to files:

  • vscode/src/commands.ts
📚 Learning: 2025-07-23T12:10:46.399Z
Learnt from: pranavgaikwad
PR: konveyor/editor-extensions#581
File: vscode/src/extension.ts:243-251
Timestamp: 2025-07-23T12:10:46.399Z
Learning: In vscode/src/extension.ts, clearing all configErrors in the onDidSaveTextDocument listener for settings YAML is intentional behavior. The setupModelProvider and updateConfigErrors functions are designed to work together to rebuild the entire configuration error state.

Applied to files:

  • vscode/src/extension.ts
🧬 Code Graph Analysis (4)
vscode/src/utilities/ModifiedFiles/handleModifiedFile.ts (1)
vscode/src/utilities/ModifiedFiles/queueManager.ts (1)
  • handleUserInteractionComplete (170-187)
vscode/src/commands.ts (2)
vscode/src/diff/vertical/handler.ts (1)
  • fileUri (83-85)
vscode/src/diff/verticalDiffCodeLens.ts (1)
  • VerticalDiffCodeLensProvider (4-62)
vscode/src/extension.ts (6)
vscode/src/extensionState.ts (1)
  • ExtensionState (21-57)
vscode/src/utilities/configuration.ts (1)
  • getConfigAutoAcceptOnSave (70-71)
vscode/src/diff/types.ts (1)
  • FileEditor (33-38)
vscode/src/utilities/ideUtils.ts (1)
  • FileEditor (52-57)
vscode/src/diff/vertical/manager.ts (1)
  • VerticalDiffManager (15-331)
vscode/src/diff/staticDiffAdapter.ts (1)
  • StaticDiffAdapter (11-166)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (2)
webview-ui/src/components/ResolutionsPage/ModifiedFile/useModifiedFileData.ts (1)
  • isLocalChange (110-110)
shared/src/types/types.ts (1)
  • LocalChange (99-108)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build (linux)
  • GitHub Check: Build (windows)
🔇 Additional comments (1)
vscode/src/commands.ts (1)

791-805: Handle new-file case when opening original content fails

showDiffWithDecorations opens the current file to get originalContent. For new files, openTextDocument will fail; fall back to empty original content. Also, content param is currently unused—fine to leave but be aware.

[ suggest_essential_refactor ]
Apply this diff:

-        // Get original content
-        const uri = Uri.file(filePath);
-        const doc = await workspace.openTextDocument(uri);
-        const originalContent = doc.getText();
+        // Get original content; fall back to empty for new files
+        let originalContent = "";
+        try {
+          const doc = await workspace.openTextDocument(Uri.file(filePath));
+          originalContent = doc.getText();
+        } catch {
+          originalContent = "";
+        }

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
shared/src/types/types.ts (1)

183-183: Prefer unknown over any for diffResolvedStates

We’ve verified that all producers (extension.ts, commands.ts) and consumers (ExtensionStateContext.tsx) default to {} and will remain compatible when typed as Record<string, unknown>. Please apply this minimal change:

• shared/src/types/types.ts (line 183)

- diffResolvedStates: Record<string, any>;
+ diffResolvedStates: Record<string, unknown>;

Optional long-term refactor for stronger typing:

• Create shared/src/types/diff.ts

export type DiffResolvedStates = Record<string, unknown>; // refine shape when stable

• Update shared/src/types/types.ts

- diffResolvedStates: Record<string, unknown>;
+ diffResolvedStates: DiffResolvedStates;

This will catch invalid uses at compile time without affecting runtime behavior.

webview-ui/src/context/ExtensionStateContext.tsx (1)

48-49: Use nullish coalescing (??) instead of || when defaulting objects.

|| can inadvertently treat other falsey values as "absent". While unlikely here, ?? is the safer, idiomatic choice for defaulting possibly undefined/null inputs.

Apply this diff:

-        diffResolvedStates: windowData.diffResolvedStates || {},
+        diffResolvedStates: windowData.diffResolvedStates ?? {},
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0cb1c58 and b033b41.

📒 Files selected for processing (2)
  • shared/src/types/types.ts (1 hunks)
  • webview-ui/src/context/ExtensionStateContext.tsx (3 hunks)
🔇 Additional comments (2)
webview-ui/src/context/ExtensionStateContext.tsx (2)

27-28: Good: default diffResolvedStates initialized.

Initializing to an empty object ensures downstream code never deals with undefined.


84-85: Guard against unintended resets on partial postMessage payloads.

If an incoming message omits diffResolvedStates, this line forces it back to {}, potentially discarding previously known state. If the extension always posts the full state, this is fine; otherwise, prefer preserving the last known value and only overriding when provided.

Apply this minimal improvement:

-        diffResolvedStates: event.data.diffResolvedStates || {},
+        diffResolvedStates: event.data.diffResolvedStates ?? {},

If you expect partial updates, consider preserving prior state:

useEffect(() => {
  const handleMessage = (event: MessageEvent<Partial<ExtensionData>>) => {
    setState((prev) => {
      const next: ExtensionData = {
        ...defaultState,
        ...prev,
        ...event.data,
        localChanges: Array.isArray(event.data.localChanges) ? event.data.localChanges : prev.localChanges,
        ruleSets: Array.isArray(event.data.ruleSets) ? event.data.ruleSets : prev.ruleSets,
        enhancedIncidents: Array.isArray(event.data.enhancedIncidents) ? event.data.enhancedIncidents : prev.enhancedIncidents,
        chatMessages: Array.isArray(event.data.chatMessages) ? event.data.chatMessages : prev.chatMessages,
        configErrors: Array.isArray(event.data.configErrors) ? event.data.configErrors : prev.configErrors,
        profiles: Array.isArray(event.data.profiles) ? event.data.profiles : prev.profiles,
        diffResolvedStates: event.data.diffResolvedStates ?? prev.diffResolvedStates,
      };
      return next;
    });
  };
  // ...
}, []);

To verify whether the extension always posts complete ExtensionData, search for the posting sites:

- Add decorator support for code lens accept/reject functionality
- Implement visual layer for code lens interactions
- Add diff manager for handling code changes
- Stream diff functionality with proper error handling
- Accept/Reject all operations with clean save functionality
- Style improvements and cleanup

Signed-off-by: Ian Bolton <[email protected]>
Signed-off-by: Ian Bolton <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
tests/e2e/pages/vscode.page.ts (2)

161-165: Reduce selector brittleness; wait explicitly before dblclick

The exact heading text “Konveyor: Konveyor Issues” can drift. Match via regex and add an explicit visibility wait for stability before double-click.

-    await this.window
-      .getByRole('heading', { name: 'Konveyor: Konveyor Issues' })
-      .first()
-      .dblclick();
+    const issuesHeading = this.window
+      .getByRole('heading', { name: /Konveyor Issues/ })
+      .first();
+    await expect(issuesHeading).toBeVisible({ timeout: 15000 });
+    await issuesHeading.dblclick();

166-170: Target role=button and add explicit wait for the “Open Analysis View” action

Scoping by label is fine, but targeting role=button and asserting visibility first improves reliability and debuggability. A small click delay helps on slower CI (consistent with other waits in this suite).

-    await this.window
-      .getByLabel('Konveyor Issues Section')
-      .getByLabel('Open Konveyor Analysis View')
-      .first()
-      .click();
+    const openAnalysisBtn = this.window
+      .getByLabel('Konveyor Issues Section')
+      .getByRole('button', { name: 'Open Konveyor Analysis View' })
+      .first();
+    await expect(openAnalysisBtn).toBeVisible({ timeout: 15000 });
+    await openAnalysisBtn.click({ delay: 500 });
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between aa4e28b and 8357e77.

📒 Files selected for processing (1)
  • tests/e2e/pages/vscode.page.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-05T16:16:56.005Z
Learnt from: abrugaro
PR: konveyor/editor-extensions#657
File: tests/global.setup.ts:22-27
Timestamp: 2025-08-05T16:16:56.005Z
Learning: In the Playwright test framework for konveyor/editor-extensions, the global setup in tests/global.setup.ts is designed to handle only essential initialization like extension installation verification. Individual tests are responsible for configuring GenAI when needed, rather than doing it globally. The openAnalysisView() method is used as a fast verification mechanism to ensure the extension was successfully installed, which is more efficient than configuring GenAI globally for all tests.

Applied to files:

  • tests/e2e/pages/vscode.page.ts
📚 Learning: 2025-07-21T15:15:59.771Z
Learnt from: abrugaro
PR: konveyor/editor-extensions#591
File: tests/e2e/pages/vscode.pages.ts:169-174
Timestamp: 2025-07-21T15:15:59.771Z
Learning: In tests/e2e/pages/vscode.pages.ts, the hardcoded 15-second wait in the runAnalysis() method and the 600000ms (10-minute) timeout for the "Run Analysis" button are intentionally set to accommodate performance differences between Linux and Windows environments where tests run slower. These timeouts should not be reduced or replaced with element-based waits without understanding the cross-platform testing requirements.

Applied to files:

  • tests/e2e/pages/vscode.page.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build (windows)
  • GitHub Check: Build (linux)
  • GitHub Check: Build (macos)
🔇 Additional comments (2)
tests/e2e/pages/vscode.page.ts (2)

158-158: Comment-only change — looks good

No functional impact.


150-157: Harden palette path: wait for Analysis View to open and verify command definition

Before returning immediately after the quick-command, add a short wait to ensure the Analysis View tab actually appears—otherwise flakiness can creep in when the command is accepted but the view loads slowly. Also, double-check that your package.json defines the command title and category exactly as used below.

• File: tests/e2e/pages/vscode.page.ts (around lines 150–157)
• Verify in your extension’s package.json under contributes.commands that you have:

  • category: "Konveyor"
  • title: "Open Konveyor Analysis View"
    so that the palette entry is exactly Konveyor: Open Konveyor Analysis View.

Suggested diff:

     try {
       await this.executeQuickCommand('Konveyor: Open Konveyor Analysis View');
+      // Ensure the Analysis View actually appeared before returning
+      await this.window
+        .locator(`div.tab.active[aria-label="${KAIViews.analysisView}"]`)
+        .waitFor({ timeout: 15000 });
       return;
     } catch (error) {
       // Fallback to activity bar approach for when extension is visible
     }

Please confirm the command definition in package.json and that the ARIA label locator (KAIViews.analysisView) matches the UI.

djzager
djzager previously approved these changes Aug 21, 2025
Copy link
Member

@djzager djzager left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

The things that I was playing with that I believe absolutely must work worked. I don't see anything that stands out as wrong in the code but admittedly I struggled to apply a critical/focused eye to all 44 pages of changes. I'm confident that accepting this now moves us forward and gives us time to address more in follow-ups.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
vscode/src/utilities/ModifiedFiles/handleFileResponse.ts (1)

17-27: Use path.dirname instead of string slicing for cross‑platform directory creation

String slicing with "/" will fail on Windows paths. Use Node’s path API to derive the directory and guard against ".".

Apply this diff:

+import * as path from "path";
@@
-    // Ensure the directory structure exists
-    const directoryPath = filePath.substring(0, filePath.lastIndexOf("/"));
-    if (directoryPath) {
-      const directoryUri = vscode.Uri.file(directoryPath);
+    // Ensure the directory structure exists (cross-platform)
+    const directoryPath = path.dirname(filePath);
+    if (directoryPath && directoryPath !== "." && directoryPath !== filePath) {
+      const directoryUri = vscode.Uri.file(directoryPath);
       try {
         await vscode.workspace.fs.createDirectory(directoryUri);
       } catch (dirError) {
         state.logger
           .child({ component: "handleFileResponse.createNewFile" })
           .error(`Failed to create directory at ${directoryPath}:`, dirError);
       }
     }
♻️ Duplicate comments (3)
vscode/src/utilities/ModifiedFiles/handleModifiedFile.ts (2)

66-71: Guard against undefined modifiedContent in isDeleted computation

Calling trim() on undefined will throw. Align deletion detection with a safe check used across the codebase.

Apply this diff:

-  const isNew = fileState.originalContent === undefined;
-  const isDeleted = !isNew && fileState.modifiedContent.trim() === "";
+  const isNew = fileState.originalContent === undefined;
+  const isDeleted =
+    !isNew &&
+    (fileState.modifiedContent === undefined ||
+      fileState.modifiedContent.trim() === "");

137-141: Same unsafe trim() in handler; unify with createFileDiff logic

Repeat of the undefined trim() issue; fix here too to avoid runtime TypeErrors during message handling.

Apply this diff:

-      const isNew = fileState.originalContent === undefined;
-      const isDeleted = !isNew && fileState.modifiedContent.trim() === "";
+      const isNew = fileState.originalContent === undefined;
+      const isDeleted =
+        !isNew &&
+        (fileState.modifiedContent === undefined ||
+          fileState.modifiedContent.trim() === "");
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (1)

28-33: Guard access to activeDecorators and drop debug logs

Avoid indexing with an undefined messageToken and remove noisy console logs.

Apply this diff:

-  const hasActiveDecorators = !!(state.activeDecorators && state.activeDecorators[messageToken]);
-  console.log(
-    `[ModifiedFileMessage] messageToken: ${messageToken}, hasActiveDecorators: ${hasActiveDecorators}, activeDecorators:`,
-    state.activeDecorators,
-  );
+  const hasActiveDecorators = Boolean(
+    messageToken && state.activeDecorators?.[messageToken],
+  );
🧹 Nitpick comments (12)
vscode/src/utilities/ModifiedFiles/handleFileResponse.ts (1)

169-182: Avoid reusing the name messageIndex inside mutateData for clarity

The inner messageIndex shadows the outer one; prefer a distinct name to reduce cognitive load.

Apply this diff:

-      state.mutateData((draft) => {
-        const messageIndex = draft.chatMessages.findIndex(
+      state.mutateData((draft) => {
+        const idx = draft.chatMessages.findIndex(
           (msg) => msg.messageToken === messageToken,
         );
-        if (
-          messageIndex >= 0 &&
-          draft.chatMessages[messageIndex].kind === ChatMessageType.ModifiedFile
+        if (
+          idx >= 0 &&
+          draft.chatMessages[idx].kind === ChatMessageType.ModifiedFile
         ) {
-          const modifiedFileMessage = draft.chatMessages[messageIndex].value as any;
+          const modifiedFileMessage = draft.chatMessages[idx].value as any;
           modifiedFileMessage.status = "applied";
         }
       });
vscode/src/utilities/ModifiedFiles/handleModifiedFile.ts (3)

48-53: Don’t clear all pending promises on error; it can break other files’ processing

Nuking the shared array risks hiding work that other handlers are awaiting. Prefer targeted cleanup or leave the array intact.

Apply this diff to stop clearing globally:

-  // Clear any promises that might be stuck in the array
-  // Since we can't easily identify which promises are related to this specific file,
-  // we'll clear all promises that haven't been resolved yet
-  // This is a conservative approach to prevent stuck promises
-  modifiedFilesPromises.length = 0;
+  // Avoid clearing shared promises array here; let callers manage their own awaits.

And, after successful await, reset the list to prevent unbounded growth (see next comment).


130-133: Reset modifiedFilesPromises after successful await to avoid growth

Promises accumulate across messages; after they’ve all resolved, clear the array to release references.

Apply this diff:

-    await Promise.all(modifiedFilesPromises);
+    await Promise.all(modifiedFilesPromises);
+    // All processed; clear the list to prevent growth across messages
+    modifiedFilesPromises.length = 0;

142-157: Initialize a pending status on the chat message to aid UI logic

Explicitly set status: "pending" so the webview can display consistent state before user action.

Apply this diff:

         draft.chatMessages.push({
           kind: ChatMessageType.ModifiedFile,
           messageToken: msg.id,
           timestamp: new Date().toISOString(),
           value: {
             path: filePath,
             content: fileState.modifiedContent,
             originalContent: fileState.originalContent, // Use from ModifiedFileState
             isNew: isNew,
             isDeleted: isDeleted,
             diff: diff,
             messageToken: msg.id, // Add message token to value for reference
+            status: "pending",
           },
         });
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (3)

35-47: Resolve lint warning and normalize status mapping ("discarded" → "rejected")

Use status as a fallback only when global state is absent, and map "discarded" to "rejected".

Apply this diff:

-  // Get status from global state as the single source of truth
+  // Get status from global state as the single source of truth
   const currentMessage = state.chatMessages.find((msg) => msg.messageToken === messageToken);
   const globalStatus =
     currentMessage?.kind === ChatMessageType.ModifiedFile
       ? (currentMessage.value as any)?.status
       : null;
-  const actionTaken =
-    globalStatus === "applied" ? "applied" : globalStatus === "rejected" ? "rejected" : null;
-
-  console.log(
-    `[ModifiedFileMessage] Global state check - messageToken: ${messageToken}, globalStatus: ${globalStatus}, actionTaken: ${actionTaken}`,
-  );
+  const mappedNormalizedStatus =
+    status === "applied" ? "applied" : status === "discarded" ? "rejected" : null;
+  const actionTaken =
+    globalStatus === "applied"
+      ? "applied"
+      : globalStatus === "rejected"
+        ? "rejected"
+        : mappedNormalizedStatus;

125-148: Also guard SHOW_DIFF_WITH_DECORATORS on missing messageToken

Make the decorator view robust in edge cases; otherwise the host may ignore the message.

Apply this diff:

-    setIsViewingDiff(true);
+    setIsViewingDiff(true);
@@
-    const payload: ShowDiffWithDecoratorsPayload = {
+    const payload: ShowDiffWithDecoratorsPayload = {
       path: filePath,
       content: content,
       diff: fileDiff,
-      messageToken: messageToken,
+      messageToken: messageToken ?? "",
     };
-    window.vscode.postMessage({
-      type: "SHOW_DIFF_WITH_DECORATORS",
-      payload,
-    });
+    if (messageToken) {
+      window.vscode.postMessage({
+        type: "SHOW_DIFF_WITH_DECORATORS",
+        payload,
+      });
+    } else {
+      console.warn("[ModifiedFileMessage] Missing messageToken for SHOW_DIFF_WITH_DECORATORS.");
+    }

220-230: Pass through an isProcessingAction flag (optional)

If you later surface host round‑trip latency, consider passing an isProcessingAction state into ModifiedFileActions to disable buttons while awaiting host responses.

Would you like me to wire a lightweight isProcessingAction state that toggles around postMessage calls?

webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileActions.tsx (1)

13-24: Remove unused onView prop to reduce API surface (and update caller)

onView is required in props but ignored in the component (renamed _onView). Drop it to avoid confusion and update the caller to stop passing it.

Apply this diff here:

 interface ModifiedFileActionsProps {
   actionTaken: "applied" | "rejected" | null;
   normalizedData: NormalizedFileData;
   onApply: () => void;
   onReject: () => void;
-  onView: (path: string, diff: string) => void;
   onViewWithDecorations?: (path: string, diff: string) => void;
   isViewingDiff?: boolean;
   onContinue?: () => void;
   hasActiveDecorators?: boolean;
   isProcessingAction?: boolean;
 }
@@
-const ModifiedFileActions: React.FC<ModifiedFileActionsProps> = ({
+const ModifiedFileActions: React.FC<ModifiedFileActionsProps> = ({
   actionTaken,
   normalizedData,
   onApply,
   onReject,
-  onView: _onView,
   onViewWithDecorations,
   isViewingDiff,
   onContinue,
   hasActiveDecorators,
   isProcessingAction,
 }) => {

And in the caller (ModifiedFileMessage.tsx), remove the prop:

-            <ModifiedFileActions
+            <ModifiedFileActions
               actionTaken={actionTaken}
               normalizedData={normalizedData}
               onApply={() => applyFile()}
               onReject={rejectFile}
-              onView={viewFileInVSCode}
               onViewWithDecorations={viewFileWithDecorations}
               isViewingDiff={isViewingDiff}
               onContinue={handleContinue}
               hasActiveDecorators={hasActiveDecorators}
             />

Also applies to: 175-187

vscode/src/webviewMessageHandler.ts (4)

315-318: Normalization trims only one trailing newline; consider trimming all trailing newlines and centralize helper

If you want to avoid false mismatches when there are multiple trailing newlines, strip all of them. Also, this helper is duplicated in CONTINUE_WITH_FILE_STATE; pull it into a shared util to keep parity with myers.ts and prevent drift.

-      const normalize = (text: string) => {
-        // Use the same normalization approach as myers.ts
-        return text.replace(/\r\n/g, "\n").replace(/\n$/, ""); // Remove trailing newline for comparison
-      };
+      const normalize = (text: string) =>
+        text.replace(/\r\n/g, "\n").replace(/\n+$/, ""); // Remove trailing newlines for comparison

If myers.ts deliberately removes only a single newline, import and reuse that exact function instead to keep behavior identical.


420-423: Deduplicate normalization helper

Same normalization logic as in CHECK_FILE_STATE; extract to a shared utility (e.g., utilities/text/normalize.ts) and use it in both places.

-      const normalize = (text: string) => text.replace(/\r\n/g, "\n").replace(/\n$/, "");
+      // reuse a shared normalize(text) helper here

430-431: Remove stray console.log noise; use logger.debug or drop it

Console logs in VS Code extensions are noisy and inconsistent with winston.

-      console.log("Continue decision: ", { responseId, hasChanges });
+      logger.debug("Continue decision", { responseId, hasChanges });

189-205: Refine SHOW_DIFF_WITH_DECORATORS handler signature and add a fail-fast guard

To prevent accidental payload drift and eliminate any, update the handler in vscode/src/webviewMessageHandler.ts so it:
• Accepts a strongly-typed payload object.
• Early-exits if required fields (path or diff) are missing.
• Delegates to the existing command registration in vscode/src/commands.ts (“konveyor.showDiffWithDecorations” takes (filePath, diff, content, messageToken) in that order).

Suggested diff:

--- a/vscode/src/webviewMessageHandler.ts
+++ b/vscode/src/webviewMessageHandler.ts
@@ -184,16 +184,28 @@ export const webviewMessageHandler: MessageHandler = {
   SHOW_DIFF_WITH_DECORATORS: async ({ path, diff, content, messageToken }, state, logger) => {
     try {
       logger.info("SHOW_DIFF_WITH_DECORATORS called", { path, messageToken });
-      // Execute the command to show diff with decorations using streaming approach
-      await vscode.commands.executeCommand(
-        "konveyor.showDiffWithDecorations",
-        path,
-        diff,
-        content,
-        messageToken,
-      );
+      // Sanity-check required fields before dispatch
+      if (!path || !diff) {
+        logger.error("SHOW_DIFF_WITH_DECORATORS missing required fields", { path, diff, payload: { content, messageToken } });
+        return;
+      }
+
+      // Forward to the registered extension command
+      await vscode.commands.executeCommand(
+        "konveyor.showDiffWithDecorations",
+        path,         // maps to filePath in commands.ts
+        diff,
+        content,
+        messageToken,
+      );
     } catch (error) {
       logger.error("Error handling SHOW_DIFF_WITH_DECORATORS:", error);
       vscode.window.showErrorMessage(`Failed to show diff with decorations: ${error}`);
     }
   },

And update the handler signature to declare the payload’s shape:

type ShowDiffWithDecoratorsPayload = {
  path: string;
  diff: string;
  content: string;
  messageToken: string;
};
  
SHOW_DIFF_WITH_DECORATORS: async (
  payload: ShowDiffWithDecoratorsPayload,
  state,
  logger
) => {
  const { path, diff, content, messageToken } = payload;
  
}

This ensures strong typing at the message boundary and fails fast when essential data is missing.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled
  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8357e77 and 1693775.

📒 Files selected for processing (8)
  • tests/e2e/pages/vscode.page.ts (1 hunks)
  • vscode/src/utilities/ModifiedFiles/handleFileResponse.ts (1 hunks)
  • vscode/src/utilities/ModifiedFiles/handleModifiedFile.ts (1 hunks)
  • vscode/src/utilities/ModifiedFiles/handleQuickResponse.ts (0 hunks)
  • vscode/src/webviewMessageHandler.ts (5 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileActions.tsx (2 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (6 hunks)
  • webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx (4 hunks)
💤 Files with no reviewable changes (1)
  • vscode/src/utilities/ModifiedFiles/handleQuickResponse.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/e2e/pages/vscode.page.ts
  • webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-01T13:53:20.774Z
Learnt from: djzager
PR: konveyor/editor-extensions#645
File: vscode/src/webviewMessageHandler.ts:575-577
Timestamp: 2025-08-01T13:53:20.774Z
Learning: The TOGGLE_AGENT_MODE action handler in vscode/src/webviewMessageHandler.ts correctly calls toggleAgentMode() without parameters, as the toggleAgentMode function in vscode/src/utilities/configuration.ts has been updated to internally handle reading and toggling the agent mode configuration value.

Applied to files:

  • vscode/src/webviewMessageHandler.ts
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx
🪛 GitHub Check: Lint
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx

[warning] 24-24:
'status' is assigned a value but never used

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build (windows)
🔇 Additional comments (7)
vscode/src/utilities/ModifiedFiles/handleFileResponse.ts (2)

112-168: Apply flow reads clean and robust; analyzer trigger is well-scoped

Good separation of create/update/delete, agent-mode analysis trigger is wrapped to not fail the apply path, and solution-server notification covers both delete and update cases.


184-197: Confirm whether the solution server requires a discard notification on reject

Currently, reject only updates local state. If the solution server expects a discard signal (symmetry with changeApplied), wire it here to keep external state consistent.

Option if needed:

} else {
  // For reject, also update the global state
  state.mutateData((draft) => {
    ...
  });
+  // Notify solution server that the proposed change was rejected
+  try {
+    await vscode.commands.executeCommand("konveyor.changeDiscarded", path);
+  } catch (error) {
+    logger.error("Error notifying solution server on reject:", error);
+  }
}
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileActions.tsx (2)

45-94: “Continue” disabled while decorators are active — confirm host clears the flag

The banner disables Continue when hasActiveDecorators is true. Ensure host reliably clears activeDecorators after accept/reject/save, otherwise users could get stuck.

If needed, emit a “decoratorsClosed” or similar signal from host to flip hasActiveDecorators to false once the decorated editor is torn down.


120-136: Good UX: single “Review Changes” button funnels users into the decorated flow

The primary action and spinner handling read clean and match the new vertical diff experience.

vscode/src/webviewMessageHandler.ts (3)

43-44: LGTM: toggleAgentMode import and usage are correct

This aligns with the prior change where toggleAgentMode() reads/toggles config internally; no parameters needed.


257-284: diff.reversePatch usage is valid in diff v5.x

Verified that the diff v5.x API publicly exports reversePatch(patch) and that it accepts either a single StructuredPatch or an array of them, returning the corresponding reversed patch (StructuredPatch | StructuredPatch[]) (npmjs.com, jsdocs.io). The output of reversePatch can be passed directly to applyPatch (or applyPatches), as those methods accept either a unified‐diff string or structured/parsed-patch object(s) (npmjs.com).

No changes required.


175-188: Confirm whether REJECT_FILE should handle a backend “reject” or can be removed

  • I didn’t find any client-side emitters posting a type: "REJECT_FILE" message in the webview code, so this handler is never invoked and only shows a toast.
  • If you intend a dedicated “reject changes” action:
    • Update the handler signature to accept messageToken (and content)
    • Call await handleFileResponse(messageToken, "reject", path, content, state) after showing the toast
    • Change the error log label from "NEW_REJECT_FILE" to "REJECT_FILE" for consistency
  • If this flow isn’t needed, consider removing the REJECT_FILE entry (or renaming it to REJECT_FILE_LOCALLY) to avoid dead code and update the surrounding comments accordingly

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (5)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (3)

28-30: Guard against undefined/empty messageToken when checking activeDecorators

Indexing with an empty string can create a misleading “truthy” lookup under the "" key.

-  const hasActiveDecorators = !!(state.activeDecorators && state.activeDecorators[messageToken]);
+  const hasActiveDecorators = Boolean(
+    messageToken && state.activeDecorators?.[messageToken],
+  );

105-109: Don’t post FILE_RESPONSE without a valid messageToken

Posting with undefined/empty token can break the host handler. Keep UI behavior consistent but avoid sending invalid messages.

-    // Use FILE_RESPONSE flow for standalone apply button
-    postFileResponse("apply", messageToken, path, contentToApply);
-    // Trigger scroll after action
-    onUserAction?.();
+    // Use FILE_RESPONSE flow for standalone apply button (only with a valid token)
+    if (messageToken) {
+      postFileResponse("apply", messageToken, path, contentToApply);
+      // Trigger scroll after action
+      onUserAction?.();
+    } else {
+      console.warn("[ModifiedFileMessage] Missing messageToken on apply; ignoring action.");
+    }

115-119: Guard reject flow on messageToken as well

Same rationale as apply.

-    // Use FILE_RESPONSE flow for standalone reject button
-    postFileResponse("reject", messageToken, path);
-    // Trigger scroll after action
-    onUserAction?.();
+    // Use FILE_RESPONSE flow for standalone reject button (only with a valid token)
+    if (messageToken) {
+      postFileResponse("reject", messageToken, path);
+      // Trigger scroll after action
+      onUserAction?.();
+    } else {
+      console.warn("[ModifiedFileMessage] Missing messageToken on reject; ignoring action.");
+    }
vscode/src/webviewMessageHandler.ts (2)

258-260: Bug: Uint8Array.toString() doesn’t decode UTF‑8; decode bytes properly.

Using .toString() on Uint8Array returns a non-text representation and will break comparisons and downstream logic.

Apply this diff:

-      const currentContent = await vscode.workspace.fs.readFile(uri);
-      const currentText = currentContent.toString();
+      const currentBytes = await vscode.workspace.fs.readFile(uri);
+      const currentText = new TextDecoder("utf-8").decode(currentBytes);

261-271: Guard missing originalContent; otherwise you’ll incorrectly “apply” against an empty baseline.

If modifiedFileState is missing or originalContent is undefined, defaulting to "" will mark any non-empty file as “changed”, causing an unintended “apply”.

Apply this diff:

-      const modifiedFileState = state.modifiedFiles.get(path);
-      const originalContent = modifiedFileState?.originalContent || "";
+      const modifiedFileState = state.modifiedFiles.get(path);
+      if (!modifiedFileState?.originalContent) {
+        logger.warn("CONTINUE_WITH_FILE_STATE: missing originalContent; defaulting to reject", {
+          path,
+          messageToken,
+        });
+        await handleFileResponse(messageToken, "reject", path, content, state);
+        return;
+      }
+      const originalContent = modifiedFileState.originalContent;

Nit: For the newline normalization, consider handling multiple trailing newlines:

-      const normalize = (text: string) => text.replace(/\r\n/g, "\n").replace(/\n$/, "");
+      const normalize = (text: string) => text.replace(/\r\n/g, "\n").replace(/\n+$/, "");
🧹 Nitpick comments (14)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileActions.tsx (4)

26-46: Prefer PatternFly tokens over hard-coded colors in StatusDisplay

Hard-coding "green"/"red" makes themes inconsistent (dark mode, downstream themes). Use PF CSS vars for semantic colors.

-            <CheckCircleIcon color="green" /> Changes applied
+            <CheckCircleIcon color="var(--pf-v5-global--success-color--100)" /> Changes applied
...
-            <TimesCircleIcon color="red" /> Changes rejected
+            <TimesCircleIcon color="var(--pf-v5-global--danger-color--100)" /> Changes rejected

88-96: Re-evaluate disabling “Continue” while decorators are active

Setting isDisabled={hasActiveDecorators} can strand users: they cannot proceed while a diff is open even if they’ve already saved/accepted. If “active decorators” simply means the diff is visible, this block may be overzealous.

Consider enabling Continue and letting the backend return the authoritative state via CONTINUE_WITH_FILE_STATE. If you must gate, perhaps only disable when there are pending, unapplied hunks (a stronger signal than “decorators visible”).

-            isDisabled={hasActiveDecorators}
+            isDisabled={false}

If we keep the guard, please document why decorators imply “cannot continue.”


205-209: Guard and memoize the onViewWithDecorations callback

Minor: wrap the callback in useCallback and guard against missing diff/path to avoid needless re-renders and accidental undefined payloads.

-      onViewWithDecorations={
-        onViewWithDecorations
-          ? () => onViewWithDecorations(normalizedData.path, normalizedData.diff)
-          : undefined
-      }
+      onViewWithDecorations={
+        onViewWithDecorations && normalizedData.path && normalizedData.diff
+          ? () => onViewWithDecorations(normalizedData.path, normalizedData.diff)
+          : undefined
+      }

63-86: Use PatternFly Icon status prop for semantic colors

PatternFly v5’s React <Icon> component supports a status prop ('custom' | 'info' | 'success' | 'warning' | 'danger') which applies the appropriate semantic color via PF CSS modifiers (e.g. pf-m-info, pf-m-warning) automatically—no manual hex colors needed. (react-staging.patternfly.org, pf5.patternfly.org)

Please update the two icon usages in webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileActions.tsx (around lines 63–86) as follows:

-          <Icon status="warning">
-            <ExclamationTriangleIcon color="#b98412" />
-          </Icon>
+          <Icon status="warning">
+            <ExclamationTriangleIcon />
+          </Icon>-            <Icon>
-              <InfoCircleIcon color="#4394e5" />
-            </Icon>
+            <Icon status="info">
+              <InfoCircleIcon />
+            </Icon>

This will ensure your icons inherit PatternFly’s semantic color tokens without hard-coding hex values.

webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (4)

33-37: Avoid any-cast; narrow ModifiedFile message value safely

Remove the any and leverage the shared ModifiedFileMessageValue type.

-  const globalStatus =
-    currentMessage?.kind === ChatMessageType.ModifiedFile
-      ? (currentMessage.value as any)?.status
-      : null;
+  let globalStatus: "applied" | "rejected" | null = null;
+  if (currentMessage?.kind === ChatMessageType.ModifiedFile) {
+    const v = currentMessage.value as ModifiedFileMessageValue;
+    globalStatus = (v.status as "applied" | "rejected" | undefined) ?? null;
+  }

146-161: Validate messageToken before CONTINUE_WITH_FILE_STATE

Mirror the safety check used in other host calls. Optionally set local “processing” here if the action can originate outside the banner.

-  const handleContinue = () => {
-    // Send CONTINUE_WITH_FILE_STATE message to check current file state
-    window.vscode.postMessage({
-      type: "CONTINUE_WITH_FILE_STATE",
-      payload: {
-        messageToken,
-        path,
-        content,
-      },
-    });
+  const handleContinue = () => {
+    if (!messageToken) {
+      console.warn("[ModifiedFileMessage] Missing messageToken on continue; ignoring.");
+      return;
+    }
+    // Send CONTINUE_WITH_FILE_STATE message to check current file state
+    window.vscode.postMessage({
+      type: "CONTINUE_WITH_FILE_STATE",
+      payload: {
+        messageToken,
+        path,
+        content,
+      },
+    });

186-189: Satisfy linter by breaking long ternary across lines

This addresses the warnings surfaced by the Lint check.

-                <span className={`status-badge status-${effectiveActionTaken}`}>
-                  {effectiveActionTaken === "applied" ? "✓ Applied" 
-                   : effectiveActionTaken === "rejected" ? "✗ Rejected"
-                   : "⏳ Processing..."}
-                </span>
+                <span className={`status-badge status-${effectiveActionTaken}`}>
+                  {effectiveActionTaken === "applied"
+                    ? "✓ Applied"
+                    : effectiveActionTaken === "rejected"
+                    ? "✗ Rejected"
+                    : "⏳ Processing..."}
+                </span>

62-68: Optionally derive isViewingDiff from activeDecorators to avoid stale banner

If the editor closes the diff without changing status, the banner may remain. Syncing to decorator presence avoids confusion.

   useEffect(() => {
     if (effectiveActionTaken !== null && effectiveActionTaken !== "processing") {
       setIsViewingDiff(false);
     }
   }, [effectiveActionTaken]);
+
+  // Optional: hide banner if decorators are no longer active
+  useEffect(() => {
+    if (!hasActiveDecorators && isViewingDiff) {
+      setIsViewingDiff(false);
+    }
+  }, [hasActiveDecorators, isViewingDiff]);
webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx (2)

26-74: Type the state parameter instead of any in useResolutionData

Using any obscures contract changes and hurts refactors. Prefer a typed shape (e.g., ExtensionState) from context/types.

Example (outline):

const useResolutionData = (state: ExtensionState) => { /* ... */ };

If ExtensionState isn’t exported, consider a local interface that captures only the consumed fields.


138-199: Prune unused dependency from renderChatMessages

isFetchingSolution isn’t referenced inside the callback; removing it avoids unnecessary memo invalidations.

-  }, [chatMessages, isFetchingSolution, isAnalyzing, triggerScrollOnUserAction]);
+  }, [chatMessages, isAnalyzing, triggerScrollOnUserAction]);
vscode/src/webviewMessageHandler.ts (4)

175-188: REJECT_FILE doesn’t update state/solution server; fix logging label and route through the unified handler when possible.

Right now this only shows a toast and logs. It neither updates chat message status nor notifies the solution server (e.g., konveyor.changeDiscarded). Also, the error label says "NEW_REJECT_FILE", which looks stale.

Consider routing through handleFileResponse when a messageToken is available; otherwise, fall back to notifying the solution server only. Also correct the log label.

Apply this diff:

-  REJECT_FILE: async ({ path }, _state, logger) => {
+  REJECT_FILE: async (
+    { path, messageToken }: { path: string; messageToken?: string },
+    state,
+    logger,
+  ) => {
     try {
-      // For rejecting changes, we don't need to do anything since we're not
-      // directly modifying the real file until the user applies changes
-      vscode.window.showInformationMessage(
-        `Changes rejected for ${vscode.workspace.asRelativePath(vscode.Uri.file(path))}`,
-      );
+      if (messageToken) {
+        await handleFileResponse(messageToken, "reject", path, undefined, state);
+      } else {
+        // Fallback for callers that don't use chat messages
+        await vscode.commands.executeCommand("konveyor.changeDiscarded", path);
+      }
+      vscode.window.showInformationMessage(`Changes rejected for ${vscode.workspace.asRelativePath(vscode.Uri.file(path))}`);
     } catch (error) {
-      logger.error("Error handling NEW_REJECT_FILE:", error);
+      logger.error("Error handling REJECT_FILE:", error);
       vscode.window.showErrorMessage(`Failed to reject changes: ${error}`);
     }
   },

272-276: Remove console.log and use logger consistently.

Avoid mixing console.log with the structured logger; keep logs in the output channel.

Apply this diff:

       logger.debug(
         `Continue decision: ${responseId.toUpperCase()} - ${hasChanges ? "file has changes" : "file unchanged"}`,
       );
-      console.log("Continue decision: ", { responseId, hasChanges });

284-296: Redundant status mutation; handleFileResponse already updates chat message status.

handleFileResponse mutates the message state (applied/rejected) and resolves pending interactions. The extra mutateData here can cause duplicate updates and unnecessary re-renders.

Apply this diff to remove both blocks:

-      // Update the chat message status
-      state.mutateData((draft) => {
-        const messageIndex = draft.chatMessages.findIndex(
-          (msg) => msg.messageToken === messageToken,
-        );
-        if (
-          messageIndex >= 0 &&
-          draft.chatMessages[messageIndex].kind === ChatMessageType.ModifiedFile
-        ) {
-          const modifiedFileMessage = draft.chatMessages[messageIndex].value as any;
-          modifiedFileMessage.status = hasChanges ? "applied" : "rejected";
-        }
-      });
@@
-      state.mutateData((draft) => {
-        const messageIndex = draft.chatMessages.findIndex(
-          (msg) => msg.messageToken === messageToken,
-        );
-        if (
-          messageIndex >= 0 &&
-          draft.chatMessages[messageIndex].kind === ChatMessageType.ModifiedFile
-        ) {
-          const modifiedFileMessage = draft.chatMessages[messageIndex].value as any;
-          modifiedFileMessage.status = "rejected";
-        }
-      });

Also applies to: 302-313


255-271: Optional: prefer unsaved editor content over filesystem when comparing.

If the user hasn’t saved yet, fs.readFile won’t reflect the in-editor changes. Prefer an open editor’s text when available.

Apply this diff (builds on the decoding fix):

-      // Get the current file content
-      const currentBytes = await vscode.workspace.fs.readFile(uri);
-      const currentText = new TextDecoder("utf-8").decode(currentBytes);
+      // Get the current file content: prefer unsaved editor text, fallback to filesystem
+      const openDoc = vscode.workspace.textDocuments.find((d) => d.uri.fsPath === path);
+      const currentText = openDoc
+        ? openDoc.getText()
+        : new TextDecoder("utf-8").decode(await vscode.workspace.fs.readFile(uri));
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled
  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1693775 and d0b6c41.

📒 Files selected for processing (16)
  • vscode/src/utilities/ModifiedFiles/handleFileResponse.ts (1 hunks)
  • vscode/src/utilities/ModifiedFiles/handleQuickResponse.ts (0 hunks)
  • vscode/src/webviewMessageHandler.ts (4 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/DiffLegend.tsx (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/DiffLinesRenderer.tsx (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/EnhancedDiffRenderer.tsx (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/HunkSelectionInterface.tsx (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileActions.tsx (1 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (5 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileModal.tsx (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileModalHeader.css (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileModalHeader.tsx (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/SingleHunkDisplay.tsx (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/enhancedSyntaxHighlighting.css (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/index.ts (1 hunks)
  • webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx (4 hunks)
💤 Files with no reviewable changes (10)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/enhancedSyntaxHighlighting.css
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileModalHeader.css
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileModalHeader.tsx
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/HunkSelectionInterface.tsx
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/SingleHunkDisplay.tsx
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/DiffLegend.tsx
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/DiffLinesRenderer.tsx
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/EnhancedDiffRenderer.tsx
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileModal.tsx
  • vscode/src/utilities/ModifiedFiles/handleQuickResponse.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • vscode/src/utilities/ModifiedFiles/handleFileResponse.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-12T02:13:55.897Z
Learnt from: djzager
PR: konveyor/editor-extensions#678
File: vscode/src/commands.ts:135-139
Timestamp: 2025-08-12T02:13:55.897Z
Learning: In vscode/src/commands.ts, the solutionState is conditionally reset to "none" after successful getSolution completion, but only when !state.isWaitingForUserInteraction. This prevents permanent blocking of future getSolution calls while still maintaining proper concurrency control during active workflows.

Applied to files:

  • webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx
📚 Learning: 2025-08-01T13:53:20.774Z
Learnt from: djzager
PR: konveyor/editor-extensions#645
File: vscode/src/webviewMessageHandler.ts:575-577
Timestamp: 2025-08-01T13:53:20.774Z
Learning: The TOGGLE_AGENT_MODE action handler in vscode/src/webviewMessageHandler.ts correctly calls toggleAgentMode() without parameters, as the toggleAgentMode function in vscode/src/utilities/configuration.ts has been updated to internally handle reading and toggling the agent mode configuration value.

Applied to files:

  • vscode/src/webviewMessageHandler.ts
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx
🧬 Code graph analysis (4)
webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx (7)
webview-ui/src/hooks/useScrollManagement.ts (1)
  • useScrollManagement (5-259)
shared/src/types/types.ts (1)
  • ModifiedFileMessageValue (294-304)
webview-ui/src/hooks/actions.ts (1)
  • openFile (52-58)
webview-ui/src/components/ResolutionsPage/MessageWrapper.tsx (1)
  • MessageWrapper (9-11)
webview-ui/src/components/ResolutionsPage/ToolMessage.tsx (1)
  • ToolMessage (116-181)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (1)
  • ModifiedFileMessage (17-236)
webview-ui/src/components/ResolutionsPage/ReceivedMessage.tsx (1)
  • ReceivedMessage (22-83)
vscode/src/webviewMessageHandler.ts (2)
vscode/src/client/tracer.ts (1)
  • logger (60-65)
vscode/src/utilities/ModifiedFiles/handleFileResponse.ts (1)
  • handleFileResponse (92-225)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileActions.tsx (1)
webview-ui/src/components/ResolutionsPage/ModifiedFile/useModifiedFileData.ts (1)
  • NormalizedFileData (33-44)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (3)
shared/src/types/types.ts (2)
  • ModifiedFileMessageValue (294-304)
  • LocalChange (99-108)
webview-ui/src/components/ResolutionsPage/ModifiedFile/useModifiedFileData.ts (1)
  • useModifiedFileData (46-107)
webview-ui/src/context/ExtensionStateContext.tsx (1)
  • useExtensionStateContext (102-108)
🪛 GitHub Check: Lint
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx

[warning] 188-188:
Insert ···


[warning] 187-187:
Replace ···················:·effectiveActionTaken·===·"rejected" with ····················:·effectiveActionTaken·===·"rejected"⏎·····················


[warning] 186-186:
Replace ·?·"✓·Applied"· with ⏎····················?·"✓·Applied"

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build (windows)
  • GitHub Check: Build (macos)
🔇 Additional comments (6)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (1)

163-171: Confirm message type alignment: OPEN_FILE_IN_EDITOR vs OPEN_FILE

Elsewhere we dispatch openFile(…) which emits OPEN_FILE. Ensure the extension’s webview handler implements OPEN_FILE_IN_EDITOR to prevent a no-op.

Would you like me to scan vscode/src/webviewMessageHandler.ts for the handler and open a quick follow-up PR if the message type needs alignment?

webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx (1)

126-128: Good consolidation of data selection in one hook

The simplified “hasNothingToView/hasContent” logic reads clean and matches the new chat-centric flow.

webview-ui/src/components/ResolutionsPage/ModifiedFile/index.ts (1)

6-11: Re-exports look correct and non-breaking

Default-export alias for ModifiedFileActions and forwarding detectLanguage/isLanguageSupported from shared are appropriate. Make sure the shared package indeed re-exports both helpers.

If you want, I can add a tiny type-only import check to CI to ensure these symbols resolve.

vscode/src/webviewMessageHandler.ts (3)

43-43: Good: toggleAgentMode imported and used without params (matches current config API).

This aligns with the updated configuration helper which internally reads and toggles the value. Noted in prior learnings; no action needed.


189-205: Manual verification needed

Our automated search did not find any occurrences of .toString() being used for byte‐array→string decoding nor any other direct assignments to status on ModifiedFile messages outside of handleFileResponse. Please manually confirm that:

  • No parts of the codebase still decode text via Uint8Array.toString(), Buffer.toString(), or similar.
  • All updates to ModifiedFile message statuses flow through the single canonical handler (handleFileResponse).

189-205: SHOW_DIFF_WITH_DECORATORS signature verified

I’ve confirmed that the konveyor.showDiffWithDecorations command handler in vscode/src/commands.ts is declared with the signature

(filePath: string, diff: string, content: string, messageToken: string)

and that the invocation in vscode/src/webviewMessageHandler.ts passes the arguments in exactly that same order:

await vscode.commands.executeCommand(
  "konveyor.showDiffWithDecorations",
  path,
  diff,
  content,
  messageToken,
);

Since the parameter order and types align perfectly, no wiring changes are needed here.

…ns loaded

When too many extensions are installed, the Konveyor extension may be hidden
in the Additional Views overflow menu, making it inaccessible via activity bar
selectors. This fix prioritizes using the command palette to open the analysis
view, which works reliably regardless of extension visibility.

Changes:
- Modified openAnalysisView() to try command palette first
- Falls back to existing activity bar approach if command palette fails
- Fixed logic flow to only run fallback when needed

Signed-off-by: Ian Bolton <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (12)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (6)

25-30: Guard hasActiveDecorators against empty/undefined messageToken

Indexing with an empty string can create confusing lookups; also keep console noise down elsewhere (see below).

Apply this diff:

-  const hasActiveDecorators = !!(state.activeDecorators && state.activeDecorators[messageToken]);
+  const hasActiveDecorators = Boolean(
+    messageToken && state.activeDecorators?.[messageToken],
+  );

62-65: Remove verbose console logging in production paths

This log will spam the console on each render and includes potentially large fields.

Apply this diff:

-  console.log(
-    `[ModifiedFileMessage] Status check - messageToken: ${messageToken}, path: ${path}, data.status: ${status}, globalStatus: ${globalStatus}, actionTaken: ${actionTaken}, effectiveActionTaken: ${effectiveActionTaken}, foundMessage: ${!!currentMessage}`,
-  );
+  // Consider adding debug logs behind a dev flag if needed.

113-124: Preserve empty-string selections and don’t emit FILE_RESPONSE without a token

  • Using || treats "" as falsy and falls back to full content.
  • Sending a FILE_RESPONSE with an empty/undefined messageToken breaks host correlation.

Apply this diff:

   const applyFileChanges = (selectedContent?: string) => {
     setIsViewingDiff(false);
-    setActionTaken("applied");
-
-    // Use provided selected content or fall back to full content
-    const contentToApply = selectedContent || content;
-
-    // Use FILE_RESPONSE flow for standalone apply button
-    postFileResponse("apply", messageToken, path, contentToApply);
-    // Trigger scroll after action
-    onUserAction?.();
+    // Optional: set to "processing" until backend confirms status.
+    // setActionTaken("processing");
+    setActionTaken("applied");
+
+    // Preserve intentionally empty selections
+    const contentToApply = selectedContent !== undefined ? selectedContent : content;
+
+    if (messageToken) {
+      postFileResponse("apply", messageToken, path, contentToApply);
+      onUserAction?.();
+    } else {
+      console.warn("[ModifiedFileMessage] Missing messageToken on apply; ignoring action.");
+    }
   };

126-134: Guard reject flow on messageToken

Avoid emitting an uncorrelated reject; optionally use “processing” until confirmation.

Apply this diff:

   const rejectFileChanges = () => {
     setIsViewingDiff(false);
-    setActionTaken("rejected");
+    // setActionTaken("processing");
+    setActionTaken("rejected");
 
-    // Use FILE_RESPONSE flow for standalone reject button
-    postFileResponse("reject", messageToken, path);
-    // Trigger scroll after action
-    onUserAction?.();
+    if (messageToken) {
+      postFileResponse("reject", messageToken, path);
+      onUserAction?.();
+    } else {
+      console.warn("[ModifiedFileMessage] Missing messageToken on reject; ignoring action.");
+    }
   };

136-160: Validate messageToken before SHOW_DIFF_WITH_DECORATORS and set view state after posting

Prevent uncorrelated diff requests and avoid getting stuck in “viewing” state if validation fails.

Apply this diff:

   const viewFileWithDecorations = (filePath: string, fileDiff: string) => {
-    if (isViewingDiff) {
-      return;
-    }
-
-    setIsViewingDiff(true);
+    if (isViewingDiff) return;
+    if (!messageToken) {
+      console.warn("[ModifiedFileMessage] Missing messageToken on view; aborting SHOW_DIFF_WITH_DECORATORS.");
+      return;
+    }
 
     interface ShowDiffWithDecoratorsPayload {
       path: string;
       content: string;
       diff: string;
       messageToken: string;
     }
     const payload: ShowDiffWithDecoratorsPayload = {
       path: filePath,
       content: content,
       diff: fileDiff,
       messageToken: messageToken,
     };
     window.vscode.postMessage({
       type: "SHOW_DIFF_WITH_DECORATORS",
       payload,
     });
+    setIsViewingDiff(true);
   };

161-176: Guard CONTINUE_WITH_FILE_STATE on messageToken

Avoid sending a message the host can’t correlate.

Apply this diff:

   const handleContinue = () => {
-    // Send CONTINUE_WITH_FILE_STATE message to check current file state
-    window.vscode.postMessage({
-      type: "CONTINUE_WITH_FILE_STATE",
-      payload: {
-        messageToken,
-        path,
-        content,
-      },
-    });
-    // Trigger scroll after action
-    onUserAction?.();
+    if (messageToken) {
+      window.vscode.postMessage({
+        type: "CONTINUE_WITH_FILE_STATE",
+        payload: { messageToken, path, content },
+      });
+      onUserAction?.();
+    } else {
+      console.warn("[ModifiedFileMessage] Missing messageToken on continue; ignoring action.");
+    }
 
     // Keep viewing diff state until backend responds with final status
     // This prevents the UI from reverting to action buttons prematurely
   };
vscode/src/webviewMessageHandler.ts (6)

266-267: Normalization: trim multiple trailing newlines

Trimming a single newline may produce false positives when files end with multiple line breaks.

-      const normalize = (text: string) => text.replace(/\r\n/g, "\n").replace(/\n$/, "");
+      const normalize = (text: string) => text.replace(/\r\n/g, "\n").replace(/\n+$/, "");

275-276: Use logger instead of console.log

Stick to the centralized logger for consistency and channel control.

-      console.log("Continue decision: ", { responseId, hasChanges });
+      // (removed) use logger.debug/info above

284-296: Remove redundant chat status mutation; handleFileResponse already sets status

handleFileResponse updates ModifiedFile message status for both apply and reject (see vscode/src/utilities/ModifiedFiles/handleFileResponse.ts). Double mutation risks divergence.

-      // Update the chat message status
-      state.mutateData((draft) => {
-        const messageIndex = draft.chatMessages.findIndex(
-          (msg) => msg.messageToken === messageToken,
-        );
-        if (
-          messageIndex >= 0 &&
-          draft.chatMessages[messageIndex].kind === ChatMessageType.ModifiedFile
-        ) {
-          const modifiedFileMessage = draft.chatMessages[messageIndex].value as any;
-          modifiedFileMessage.status = hasChanges ? "applied" : "rejected";
-        }
-      });

302-313: Remove redundant status mutation in error path

Same rationale as above; handleFileResponse("reject", …) already updates status.

-      state.mutateData((draft) => {
-        const messageIndex = draft.chatMessages.findIndex(
-          (msg) => msg.messageToken === messageToken,
-        );
-        if (
-          messageIndex >= 0 &&
-          draft.chatMessages[messageIndex].kind === ChatMessageType.ModifiedFile
-        ) {
-          const modifiedFileMessage = draft.chatMessages[messageIndex].value as any;
-          modifiedFileMessage.status = "rejected";
-        }
-      });

255-260: Bug: Decoding file bytes via Uint8Array.toString() is incorrect; prefer unsaved editor text then UTF-8 decode

Uint8Array.toString() doesn’t decode text; it returns a non-text representation, leading to wrong comparisons and content. Also, prefer unsaved editor content over disk to respect in-memory edits.

Apply this focused patch:

-      // Get the current file content
-      const currentContent = await vscode.workspace.fs.readFile(uri);
-      const currentText = currentContent.toString();
+      // Get the current file content: prefer unsaved editor text, fallback to filesystem
+      const openDoc = vscode.workspace.textDocuments.find((d) => d.uri.fsPath === path);
+      const currentText = openDoc
+        ? openDoc.getText()
+        : new TextDecoder("utf-8").decode(await vscode.workspace.fs.readFile(uri));

Optionally scan the repo for similar patterns:

#!/bin/bash
# All fs.readFile call sites
rg -nP --type=ts 'workspace\.fs\.readFile\(' -C2
# Places already using safe decoding
rg -nP --type=ts 'TextDecoder|Buffer\.from\(.+\)\.toString\(..utf-8|utf8..\)'

261-264: Guard missing originalContent to avoid false "apply" decisions

If originalContent is absent, defaulting it to "" makes any non-empty file look changed. Fail fast and reject to avoid unintended applies.

-      // Get the original content to compare against
-      const modifiedFileState = state.modifiedFiles.get(path);
-      const originalContent = modifiedFileState?.originalContent || "";
+      // Get the original content to compare against
+      const modifiedFileState = state.modifiedFiles.get(path);
+      if (!modifiedFileState?.originalContent) {
+        logger.warn("CONTINUE_WITH_FILE_STATE: missing originalContent; defaulting to reject", {
+          path,
+          messageToken,
+        });
+        await handleFileResponse(messageToken, "reject", path, content, state);
+        return;
+      }
+      const originalContent = modifiedFileState.originalContent;
🧹 Nitpick comments (5)
webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx (2)

26-35: Type safety: avoid any for state in useResolutionData

Using any here discards compile-time guarantees. Prefer the concrete extension state type (e.g., ExtensionState) so downstream selectors remain safe and refactors are less error-prone.

Would you like me to wire the proper type from ExtensionStateContext or extensionState.ts?


138-199: Trim unused dependency and guard against unknown kinds

  • isFetchingSolution isn’t used inside renderChatMessages; remove it from deps to avoid unnecessary re-creations.
  • Optional: add a default branch to log/ignore unknown ChatMessageType for future-proofing.

Apply this diff to the dependency list:

-  }, [chatMessages, isFetchingSolution, isAnalyzing, triggerScrollOnUserAction]);
+  }, [chatMessages, isAnalyzing, triggerScrollOnUserAction]);
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (2)

31-42: Make message lookup robust: rely on messageToken + kind only

Matching on content is brittle (content may change or be large). messageToken should uniquely identify the message.

Apply this diff:

-  const currentMessage = state.chatMessages.find(
-    (msg) =>
-      msg.messageToken === messageToken &&
-      msg.kind === ChatMessageType.ModifiedFile &&
-      (msg.value as any)?.path === path &&
-      (msg.value as any)?.content === content,
-  );
+  const currentMessage = state.chatMessages.find(
+    (msg) => msg.messageToken === messageToken && msg.kind === ChatMessageType.ModifiedFile,
+  );

67-76: Stabilize effect deps to avoid unnecessary updates

Depending on currentMessage (an object) and state.chatMessages (an array) may retrigger frequently. Key off messageToken and globalStatus (and optionally path) to reduce churn.

Apply this diff:

-  }, [globalStatus, currentMessage, messageToken, path, state.chatMessages]);
+  }, [globalStatus, messageToken, path]);
vscode/src/webviewMessageHandler.ts (1)

177-188: Fix log label and confirm whether rejection needs server/state notification

  • Typo: log label says NEW_REJECT_FILE; action is REJECT_FILE.
  • Confirm the design: If this action is used by the UI, do we also need to notify the solution server and update chat state via handleFileResponse("reject", …)? If this action is intentionally ephemeral, no change needed.

Apply this tiny fix:

-      logger.error("Error handling NEW_REJECT_FILE:", error);
+      logger.error("Error handling REJECT_FILE:", error);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled
  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d0b6c41 and 1b66031.

📒 Files selected for processing (17)
  • tests/e2e/pages/vscode.page.ts (1 hunks)
  • vscode/src/utilities/ModifiedFiles/handleFileResponse.ts (1 hunks)
  • vscode/src/utilities/ModifiedFiles/handleQuickResponse.ts (0 hunks)
  • vscode/src/webviewMessageHandler.ts (4 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/DiffLegend.tsx (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/DiffLinesRenderer.tsx (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/EnhancedDiffRenderer.tsx (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/HunkSelectionInterface.tsx (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileActions.tsx (1 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (5 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileModal.tsx (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileModalHeader.css (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileModalHeader.tsx (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/SingleHunkDisplay.tsx (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/enhancedSyntaxHighlighting.css (0 hunks)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/index.ts (1 hunks)
  • webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx (4 hunks)
💤 Files with no reviewable changes (10)
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileModal.tsx
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/EnhancedDiffRenderer.tsx
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/DiffLinesRenderer.tsx
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileModalHeader.tsx
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileModalHeader.css
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/SingleHunkDisplay.tsx
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/enhancedSyntaxHighlighting.css
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/DiffLegend.tsx
  • vscode/src/utilities/ModifiedFiles/handleQuickResponse.ts
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/HunkSelectionInterface.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/e2e/pages/vscode.page.ts
  • vscode/src/utilities/ModifiedFiles/handleFileResponse.ts
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/index.ts
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileActions.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-01T13:53:20.774Z
Learnt from: djzager
PR: konveyor/editor-extensions#645
File: vscode/src/webviewMessageHandler.ts:575-577
Timestamp: 2025-08-01T13:53:20.774Z
Learning: The TOGGLE_AGENT_MODE action handler in vscode/src/webviewMessageHandler.ts correctly calls toggleAgentMode() without parameters, as the toggleAgentMode function in vscode/src/utilities/configuration.ts has been updated to internally handle reading and toggling the agent mode configuration value.

Applied to files:

  • vscode/src/webviewMessageHandler.ts
  • webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx
📚 Learning: 2025-08-12T02:13:55.897Z
Learnt from: djzager
PR: konveyor/editor-extensions#678
File: vscode/src/commands.ts:135-139
Timestamp: 2025-08-12T02:13:55.897Z
Learning: In vscode/src/commands.ts, the solutionState is conditionally reset to "none" after successful getSolution completion, but only when !state.isWaitingForUserInteraction. This prevents permanent blocking of future getSolution calls while still maintaining proper concurrency control during active workflows.

Applied to files:

  • webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx
🧬 Code graph analysis (3)
vscode/src/webviewMessageHandler.ts (2)
vscode/src/client/tracer.ts (1)
  • logger (60-65)
vscode/src/utilities/ModifiedFiles/handleFileResponse.ts (1)
  • handleFileResponse (92-225)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (3)
shared/src/types/types.ts (2)
  • ModifiedFileMessageValue (294-304)
  • LocalChange (99-108)
webview-ui/src/components/ResolutionsPage/ModifiedFile/useModifiedFileData.ts (1)
  • useModifiedFileData (46-107)
webview-ui/src/context/ExtensionStateContext.tsx (1)
  • useExtensionStateContext (102-108)
webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx (7)
webview-ui/src/hooks/useScrollManagement.ts (1)
  • useScrollManagement (5-259)
shared/src/types/types.ts (3)
  • Incident (5-10)
  • ToolMessageValue (292-292)
  • ModifiedFileMessageValue (294-304)
webview-ui/src/hooks/actions.ts (1)
  • openFile (52-58)
webview-ui/src/components/ResolutionsPage/MessageWrapper.tsx (1)
  • MessageWrapper (9-11)
webview-ui/src/components/ResolutionsPage/ToolMessage.tsx (1)
  • ToolMessage (116-181)
webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (1)
  • ModifiedFileMessage (17-253)
webview-ui/src/components/ResolutionsPage/ReceivedMessage.tsx (1)
  • ReceivedMessage (22-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Test (linux)
  • GitHub Check: Test (windows)
  • GitHub Check: Test (macos)
🔇 Additional comments (10)
webview-ui/src/components/ResolutionsPage/ResolutionsPage.tsx (5)

43-51: Readable, correct empty/content checks

The simplified hasNothingToView and hasContent conditions are clear and correct for the new chat-first flow.


126-128: Nice consolidation to a single data surface

Pulling just the fields needed by the page improves readability and reduces prop churn.


202-202: Consistent page class naming

The resolutions-page class aligns with existing CSS naming; good for consistency.


231-235: Good UX for empty state

Showing a concise “No resolutions available.” message when nothing is present is a helpful baseline.


175-196: Disable ‘run-analysis’ during analysis — good

The quick-response disabling based on isAnalyzing prevents duplicate runs. Solid guard.

webview-ui/src/components/ResolutionsPage/ModifiedFile/ModifiedFileMessage.tsx (2)

188-206: Minimized state shows “Processing…” but local handlers set final states immediately

You render a processing variant, but local apply/reject set “applied”/“rejected” right away. If the host rejects or errors, UI may briefly be inconsistent. Consider setting “processing” until you receive the corresponding status from global state.

I can wire this end-to-end if you confirm the host will always emit a final status event for apply/reject.


237-247: Props passed to ModifiedFileActions look correct

Handlers and state flags cover the new decorator-based flow well.

vscode/src/webviewMessageHandler.ts (3)

43-43: TOGGLE_AGENT_MODE import and usage LGTM

Importing toggleAgentMode and invoking it with no arguments (see Lines 248-250) matches the current configuration API and our retrieved learning from Aug 1, 2025. No changes needed.


189-205: SHOW_DIFF_WITH_DECORATORS delegation looks good

Cleanly delegates to konveyor.showDiffWithDecorations with a minimal wrapper and proper error surfacing. Matches the new vertical-diff architecture.


175-176: No lingering references to removed diff actions
I ran targeted searches for the old action constants (APPLY_FILE, DISCARD_FILE, SHOW_MAXIMIZED_DIFF, VIEW_FILE) and legacy command IDs (konveyor.applyDiscardDiff, konveyor.discardDiffView, etc.) and found no code or webview callers outside of the comment at lines 175–176 in webviewMessageHandler.ts. The only matches in package.json are the new konveyor.diffView.* commands introduced for the unified decorator flow, which is expected.

Everything related to the old diff actions has been cleanly removed—no further changes are required here.

pranavgaikwad
pranavgaikwad previously approved these changes Aug 21, 2025
Copy link
Contributor

@pranavgaikwad pranavgaikwad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this with latest commits and this is all working great. I can confirm all major issues we spotted so far have been resolved. There is one small issue in setupModelProvider thats moved to onWillSaveTextDocument which is causing the provider to not reload correctly when config changes on disk. Since I am out next week, dont want to block on my approval. But itd be great if you could keep the setup provider part in onDidSave as it was before so we dont need to have a follow up issue for it.

@pranavgaikwad pranavgaikwad self-requested a review August 22, 2025 16:04
Copy link
Member

@djzager djzager left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[bug] solution server regression: when agent mode is disabled we do not tell solution server
3 participants