Skip to content

Commit 1269e76

Browse files
committed
fix(amazonq): fix identation for edits multi-line suggestion
1 parent 1c2686f commit 1269e76

File tree

3 files changed

+62
-4
lines changed

3 files changed

+62
-4
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* Strips common indentation from each line of code that may contain HTML tags
3+
* @param lines Array of code lines (may contain HTML tags)
4+
* @returns Array of code lines with common indentation removed
5+
*/
6+
export function stripCommonIndentation(lines: string[]): string[] {
7+
const getTextOnly = (line: string) => line.replace(/^<[^>]*>/g, '')
8+
const minIndent = Math.min(...lines.map((line) => getTextOnly(line).match(/^\s*/)?.[0].length || 0))
9+
return lines.map((line) => {
10+
const leadingSpaces = getTextOnly(line).match(/^\s*/)?.[0] || ''
11+
return line.replace(leadingSpaces, leadingSpaces.substring(minIndent))
12+
})
13+
}

packages/amazonq/src/app/inline/EditRendering/svgGenerator.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { diffWordsWithSpace, diffLines } from 'diff'
77
import * as vscode from 'vscode'
88
import { ToolkitError, getLogger } from 'aws-core-vscode/shared'
99
import { diffUtilities } from 'aws-core-vscode/shared'
10+
import { stripCommonIndentation } from './stringUtils'
1011
type Range = { line: number; start: number; end: number }
1112

1213
const logger = getLogger('nextEditPrediction')
@@ -78,6 +79,7 @@ export class SvgGenerationService {
7879

7980
const highlightRanges = this.generateHighlightRanges(removedLines, addedLines, modifiedLines)
8081
const diffAddedWithHighlight = this.getHighlightEdit(addedLines, highlightRanges.addedRanges)
82+
const normalizedDiffLines = stripCommonIndentation(diffAddedWithHighlight)
8183

8284
// Create SVG window, document, and container
8385
const window = createSVGWindow()
@@ -90,7 +92,7 @@ export class SvgGenerationService {
9092

9193
// Generate CSS for syntax highlighting HTML content based on theme
9294
const styles = this.generateStyles(currentTheme)
93-
const htmlContent = this.generateHtmlContent(diffAddedWithHighlight, styles, offset)
95+
const htmlContent = this.generateHtmlContent(normalizedDiffLines, styles, offset)
9496

9597
// Create foreignObject to embed HTML
9698
const foreignObject = draw.foreignObject(width + offset, height)
@@ -162,6 +164,9 @@ export class SvgGenerationService {
162164
white-space: pre-wrap; /* Preserve whitespace */
163165
background-color: ${diffAdded};
164166
}
167+
.diff-unchanged {
168+
white-space: pre-wrap; /* Preserve indentation for unchanged lines */
169+
}
165170
`
166171
}
167172

@@ -229,7 +234,7 @@ export class SvgGenerationService {
229234

230235
// If no ranges for this line, leave it as-is with HTML escaping
231236
if (lineRanges.length === 0) {
232-
result.push(this.escapeHtml(line))
237+
result.push(`<span class="diff-unchanged">${this.escapeHtml(line)}</span>`)
233238
continue
234239
}
235240

@@ -244,7 +249,7 @@ export class SvgGenerationService {
244249
// Add text before the current range (with HTML escaping)
245250
if (range.start > currentPos) {
246251
const beforeText = line.substring(currentPos, range.start)
247-
highlightedLine += this.escapeHtml(beforeText)
252+
highlightedLine += `<span class="diff-unchanged">${this.escapeHtml(beforeText)}</span>`
248253
}
249254

250255
// Add the highlighted part (with HTML escaping)
@@ -258,7 +263,7 @@ export class SvgGenerationService {
258263
// Add any remaining text after the last range (with HTML escaping)
259264
if (currentPos < line.length) {
260265
const afterText = line.substring(currentPos)
261-
highlightedLine += this.escapeHtml(afterText)
266+
highlightedLine += `<span class="diff-unchanged">${this.escapeHtml(afterText)}</span>`
262267
}
263268

264269
result.push(highlightedLine)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import assert from 'assert'
2+
import { stripCommonIndentation } from '../../../../../../src/app/inline/EditRendering/stringUtils'
3+
4+
describe('stripCommonIndentation', () => {
5+
it('should strip common leading whitespace', () => {
6+
const input = [' line1 ', ' line2 ', ' line3 ']
7+
const expected = ['line1 ', 'line2 ', ' line3 ']
8+
assert.strictEqual(stripCommonIndentation(input), expected)
9+
})
10+
11+
it('should handle HTML tags', () => {
12+
const input = ['<span>line1</span> line1 </span>', '<span>line2</span> line2 </span>']
13+
const expected = ['<span>line1</span> line1 </span>', '<span>line2</span> line2 </span>']
14+
assert.strictEqual(stripCommonIndentation(input), expected)
15+
})
16+
17+
it('should handle mixed indentation', () => {
18+
const input = [' line1', ' line2', ' line3']
19+
const expected = ['line1', ' line2', ' line3']
20+
assert.strictEqual(stripCommonIndentation(input), expected)
21+
})
22+
23+
it('should handle empty lines', () => {
24+
const input = [' line1', '', ' line2']
25+
const expected = ['line1', '', 'line2']
26+
assert.strictEqual(stripCommonIndentation(input), expected)
27+
})
28+
29+
it('should handle no indentation', () => {
30+
const input = ['line1', 'line2']
31+
const expected = ['line1', 'line2']
32+
assert.strictEqual(stripCommonIndentation(input), expected)
33+
})
34+
35+
it('should handle single line', () => {
36+
const input = [' single line']
37+
const expected = ['single line']
38+
assert.strictEqual(stripCommonIndentation(input), expected)
39+
})
40+
})

0 commit comments

Comments
 (0)