Skip to content

Commit df24e22

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

File tree

3 files changed

+80
-4
lines changed

3 files changed

+80
-4
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
/**
7+
* Strips common indentation from each line of code that may contain HTML tags
8+
* @param lines Array of code lines (may contain HTML tags)
9+
* @returns Array of code lines with common indentation removed
10+
*/
11+
export function stripCommonIndentation(lines: string[]): string[] {
12+
if (lines.length === 0) return lines
13+
14+
const extractTextContent = (line: string) => line.replace(/<[^>]*>/g, '')
15+
const getLeadingWhitespace = (text: string) => text.match(/^\s*/)?.[0] || ''
16+
17+
// Find minimum indentation across all lines
18+
const minIndentLength = Math.min(...lines.map((line) => getLeadingWhitespace(extractTextContent(line)).length))
19+
20+
// Remove common indentation from each line
21+
return lines.map((line) => {
22+
const textContent = extractTextContent(line)
23+
const leadingWhitespace = getLeadingWhitespace(textContent)
24+
const reducedWhitespace = leadingWhitespace.substring(minIndentLength)
25+
return line.replace(leadingWhitespace, reducedWhitespace)
26+
})
27+
}

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: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import assert from 'assert'
6+
import { stripCommonIndentation } from '../../../../../../src/app/inline/EditRendering/stringUtils'
7+
8+
describe('stripCommonIndentation', () => {
9+
it('should strip common leading whitespace', () => {
10+
const input = [' line1 ', ' line2 ', ' line3 ']
11+
const expected = ['line1 ', 'line2 ', ' line3 ']
12+
assert.deepStrictEqual(stripCommonIndentation(input), expected)
13+
})
14+
15+
it('should handle HTML tags', () => {
16+
const input = ['<span>line1</span> line1 </span>', '<span>line2</span> line2 </span>']
17+
const expected = ['<span>line1</span> line1 </span>', '<span>line2</span>line2 </span>']
18+
assert.deepStrictEqual(stripCommonIndentation(input), expected)
19+
})
20+
21+
it('should handle mixed indentation', () => {
22+
const input = [' line1', ' line2', ' line3']
23+
const expected = ['line1', ' line2', ' line3']
24+
assert.deepStrictEqual(stripCommonIndentation(input), expected)
25+
})
26+
27+
it('should handle empty lines', () => {
28+
const input = [' line1', '', ' line2']
29+
const expected = [' line1', '', ' line2']
30+
assert.deepStrictEqual(stripCommonIndentation(input), expected)
31+
})
32+
33+
it('should handle no indentation', () => {
34+
const input = ['line1', 'line2']
35+
const expected = ['line1', 'line2']
36+
assert.deepStrictEqual(stripCommonIndentation(input), expected)
37+
})
38+
39+
it('should handle single line', () => {
40+
const input = [' single line']
41+
const expected = ['single line']
42+
assert.deepStrictEqual(stripCommonIndentation(input), expected)
43+
})
44+
})

0 commit comments

Comments
 (0)