Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
28 changes: 28 additions & 0 deletions packages/amazonq/src/app/inline/EditRendering/stringUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

/**
* Strips common indentation from each line of code that may contain HTML tags
* @param lines Array of code lines (may contain HTML tags)
* @returns Array of code lines with common indentation removed
*/
export function stripCommonIndentation(lines: string[]): string[] {
if (lines.length === 0) {
return lines
}
const removeFirstTag = (line: string) => line.replace(/^<[^>]*>/, '')
const getLeadingWhitespace = (text: string) => text.match(/^\s*/)?.[0] || ''

// Find minimum indentation across all lines
const minIndentLength = Math.min(...lines.map((line) => getLeadingWhitespace(removeFirstTag(line)).length))

// Remove common indentation from each line
return lines.map((line) => {
const firstTagRemovedLine = removeFirstTag(line)
const leadingWhitespace = getLeadingWhitespace(firstTagRemovedLine)
const reducedWhitespace = leadingWhitespace.substring(minIndentLength)
return line.replace(leadingWhitespace, reducedWhitespace)
})
}
13 changes: 9 additions & 4 deletions packages/amazonq/src/app/inline/EditRendering/svgGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { diffWordsWithSpace, diffLines } from 'diff'
import * as vscode from 'vscode'
import { ToolkitError, getLogger } from 'aws-core-vscode/shared'
import { diffUtilities } from 'aws-core-vscode/shared'
import { stripCommonIndentation } from './stringUtils'
type Range = { line: number; start: number; end: number }

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

const highlightRanges = this.generateHighlightRanges(removedLines, addedLines, modifiedLines)
const diffAddedWithHighlight = this.getHighlightEdit(addedLines, highlightRanges.addedRanges)
const normalizedDiffLines = stripCommonIndentation(diffAddedWithHighlight)

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

// Generate CSS for syntax highlighting HTML content based on theme
const styles = this.generateStyles(currentTheme)
const htmlContent = this.generateHtmlContent(diffAddedWithHighlight, styles, offset)
const htmlContent = this.generateHtmlContent(normalizedDiffLines, styles, offset)

// Create foreignObject to embed HTML
const foreignObject = draw.foreignObject(width + offset, height)
Expand Down Expand Up @@ -162,6 +164,9 @@ export class SvgGenerationService {
white-space: pre-wrap; /* Preserve whitespace */
background-color: ${diffAdded};
}
.diff-unchanged {
white-space: pre-wrap; /* Preserve indentation for unchanged lines */
}
`
}

Expand Down Expand Up @@ -229,7 +234,7 @@ export class SvgGenerationService {

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

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

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

result.push(highlightedLine)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'assert'
import { stripCommonIndentation } from '../../../../../../src/app/inline/EditRendering/stringUtils'

describe('stripCommonIndentation', () => {
it('should strip common leading whitespace', () => {
const input = [' line1 ', ' line2 ', ' line3 ']
const expected = ['line1 ', 'line2 ', ' line3 ']
assert.deepStrictEqual(stripCommonIndentation(input), expected)
})

it('should handle HTML tags', () => {
const input = [
'<span class="diff-unchanged> line1 </span>',
'<span class="diff-changed> line2 </span>',
]
const expected = ['<span class="diff-unchanged> line1 </span>', '<span class="diff-changed>line2 </span>']
assert.deepStrictEqual(stripCommonIndentation(input), expected)
})

it('should handle mixed indentation', () => {
const input = [' line1', ' line2', ' line3']
const expected = ['line1', ' line2', ' line3']
assert.deepStrictEqual(stripCommonIndentation(input), expected)
})

it('should handle empty lines', () => {
const input = [' line1', '', ' line2']
const expected = [' line1', '', ' line2']
assert.deepStrictEqual(stripCommonIndentation(input), expected)
})

it('should handle no indentation', () => {
const input = ['line1', 'line2']
const expected = ['line1', 'line2']
assert.deepStrictEqual(stripCommonIndentation(input), expected)
})

it('should handle single line', () => {
const input = [' single line']
const expected = ['single line']
assert.deepStrictEqual(stripCommonIndentation(input), expected)
})
})
Loading