Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
698 changes: 688 additions & 10 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@playwright/browser-chromium": "^1.43.1",
"@stylistic/eslint-plugin": "^2.11.0",
"@types/he": "^1.2.3",
"@types/jaro-winkler": "^0.2.4",
"@types/vscode": "^1.68.0",
"@types/vscode-webview": "^1.57.1",
"@types/webpack-env": "^1.18.5",
Expand Down Expand Up @@ -74,6 +75,7 @@
},
"dependencies": {
"@types/node": "^22.7.5",
"jaro-winkler": "^0.2.8",
"vscode-nls": "^5.2.0",
"vscode-nls-dev": "^4.0.4"
}
Expand Down
34 changes: 34 additions & 0 deletions packages/amazonq/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,18 @@
"command": "aws.amazonq.clearCache",
"title": "%AWS.amazonq.clearCache%",
"category": "%AWS.amazonq.title%"
},
{
"command": "aws.amazonq.inline.acceptEdit",
"title": "%aws.amazonq.inline.acceptEdit%"
},
{
"command": "aws.amazonq.inline.rejectEdit",
"title": "%aws.amazonq.inline.rejectEdit%"
},
{
"command": "aws.amazonq.toggleNextEditPredictionPanel",
"title": "%aws.amazonq.toggleNextEditPredictionPanel%"
}
],
"keybindings": [
Expand All @@ -837,6 +849,18 @@
"mac": "cmd+alt+i",
"linux": "meta+alt+i"
},
{
"command": "aws.amazonq.inline.debugAcceptEdit",
"key": "ctrl+alt+a",
"mac": "cmd+alt+a",
"when": "editorTextFocus"
},
{
"command": "aws.amazonq.inline.debugRejectEdit",
"key": "ctrl+alt+r",
"mac": "cmd+alt+r",
"when": "editorTextFocus"
},
{
"command": "aws.amazonq.explainCode",
"win": "win+alt+e",
Expand Down Expand Up @@ -917,6 +941,16 @@
"command": "aws.amazonq.inline.waitForUserDecisionRejectAll",
"key": "escape",
"when": "editorTextFocus && aws.codewhisperer.connected && amazonq.inline.codelensShortcutEnabled"
},
{
"command": "aws.amazonq.inline.acceptEdit",
"key": "tab",
"when": "editorTextFocus && aws.amazonq.editSuggestionActive"
},
{
"command": "aws.amazonq.inline.rejectEdit",
"key": "escape",
"when": "editorTextFocus && aws.amazonq.editSuggestionActive"
}
],
"icons": {
Expand Down
142 changes: 142 additions & 0 deletions packages/amazonq/src/app/inline/EditRendering/diffUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
// TODO: deprecate this file in favor of core/shared/utils/diffUtils
import { applyPatch } from 'diff'
Copy link
Contributor

Choose a reason for hiding this comment

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

followup: consolidate with packages/core/src/shared/utilities/diffUtils.ts


export type LineDiff =
| { type: 'added'; content: string }
| { type: 'removed'; content: string }
| { type: 'modified'; before: string; after: string }

/**
* Apply a unified diff to original code to generate modified code
* @param originalCode The original code as a string
* @param unifiedDiff The unified diff content
* @returns The modified code after applying the diff
*/
export function applyUnifiedDiff(
docText: string,
unifiedDiff: string
): { appliedCode: string; addedCharacterCount: number; deletedCharacterCount: number } {
try {
const { addedCharacterCount, deletedCharacterCount } = getAddedAndDeletedCharCount(unifiedDiff)
// First try the standard diff package
try {
const result = applyPatch(docText, unifiedDiff)
if (result !== false) {
return {
appliedCode: result,
addedCharacterCount: addedCharacterCount,
deletedCharacterCount: deletedCharacterCount,
}
}
} catch (error) {}

// Parse the unified diff to extract the changes
const diffLines = unifiedDiff.split('\n')
let result = docText

// Find all hunks in the diff
const hunkStarts = diffLines
.map((line, index) => (line.startsWith('@@ ') ? index : -1))
.filter((index) => index !== -1)

// Process each hunk
for (const hunkStart of hunkStarts) {
// Parse the hunk header
const hunkHeader = diffLines[hunkStart]
const match = hunkHeader.match(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/)

if (!match) {
continue
}

const oldStart = parseInt(match[1])
const oldLines = parseInt(match[2])

// Extract the content lines for this hunk
let i = hunkStart + 1
const contentLines = []
while (i < diffLines.length && !diffLines[i].startsWith('@@')) {
contentLines.push(diffLines[i])
i++
}

// Build the old and new text
let oldText = ''
let newText = ''

for (const line of contentLines) {
if (line.startsWith('-')) {
oldText += line.substring(1) + '\n'
} else if (line.startsWith('+')) {
newText += line.substring(1) + '\n'
} else if (line.startsWith(' ')) {
oldText += line.substring(1) + '\n'
newText += line.substring(1) + '\n'
}
}

// Remove trailing newline if it was added
oldText = oldText.replace(/\n$/, '')
newText = newText.replace(/\n$/, '')

// Find the text to replace in the document
const docLines = docText.split('\n')
const startLine = oldStart - 1 // Convert to 0-based
const endLine = startLine + oldLines

// Extract the text that should be replaced
const textToReplace = docLines.slice(startLine, endLine).join('\n')

// Replace the text
result = result.replace(textToReplace, newText)
}
return {
appliedCode: result,
addedCharacterCount: addedCharacterCount,
deletedCharacterCount: deletedCharacterCount,
}
} catch (error) {
return {
appliedCode: docText, // Return original text if all methods fail
addedCharacterCount: 0,
deletedCharacterCount: 0,
}
}
}

export function getAddedAndDeletedCharCount(diff: string): {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is no longer needed. I will make a followup PR to remove it. But it is not a blocker.

Copy link
Contributor

Choose a reason for hiding this comment

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

ok will followup

addedCharacterCount: number
deletedCharacterCount: number
} {
let addedCharacterCount = 0
let deletedCharacterCount = 0
let i = 0
const lines = diff.split('\n')
while (i < lines.length) {
const line = lines[i]
if (line.startsWith('+') && !line.startsWith('+++')) {
addedCharacterCount += line.length - 1
} else if (line.startsWith('-') && !line.startsWith('---')) {
const removedLine = line.substring(1)
deletedCharacterCount += removedLine.length

// Check if this is a modified line rather than a pure deletion
const nextLine = lines[i + 1]
if (nextLine && nextLine.startsWith('+') && !nextLine.startsWith('+++') && nextLine.includes(removedLine)) {
// This is a modified line, not a pure deletion
// We've already counted the deletion, so we'll just increment i to skip the next line
// since we'll process the addition on the next iteration
i += 1
}
}
i += 1
}
return {
addedCharacterCount,
deletedCharacterCount,
}
}
Loading
Loading