Skip to content

Commit 63b99ae

Browse files
committed
fix: fix duplicate code
1 parent a65e8e7 commit 63b99ae

File tree

2 files changed

+100
-175
lines changed

2 files changed

+100
-175
lines changed

packages/amazonq/src/lsp/chat/commands.ts

Lines changed: 70 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -22,78 +22,24 @@ export function registerCommands(provider: AmazonQChatViewProvider) {
2222
registerGenericCommand('aws.amazonq.optimizeCode', 'Optimize', provider),
2323
registerGenericCommand('aws.amazonq.generateUnitTests', 'Generate Tests', provider),
2424

25-
Commands.register('aws.amazonq.explainIssue', async (issue: CodeScanIssue, filePath: string) => {
26-
void focusAmazonQPanel().then(async () => {
27-
if (issue && filePath) {
28-
const range = new vscode.Range(issue.startLine, 0, issue.endLine, 0)
29-
await vscode.workspace.openTextDocument(filePath).then((doc) => {
30-
void vscode.window.showTextDocument(doc, {
31-
selection: range,
32-
viewColumn: vscode.ViewColumn.One,
33-
preview: true,
34-
})
35-
})
36-
}
37-
38-
const lineRange =
39-
issue.startLine === issue.endLine - 1
40-
? `[${issue.startLine + 1}]`
41-
: `[${issue.startLine + 1}, ${issue.endLine}]`
42-
const visibleMessageInChat = `_Explain **${issue.title}** issue in **${path.basename(filePath)}** at \`${lineRange}\`_`
43-
44-
// The message that gets sent to the backend
45-
const contextMessage = `Provide a small description of the issue. You must not attempt to fix the issue. You should only give a small summary of it to the user. Code issue - ${JSON.stringify(issue)}`
46-
47-
void provider.webview?.postMessage({
48-
command: 'sendToPrompt',
49-
params: {
50-
selection: '',
51-
triggerType: 'contextMenu',
52-
prompt: {
53-
prompt: visibleMessageInChat, // what gets sent to the user
54-
escapedPrompt: contextMessage, // what gets sent to the backend
55-
},
56-
autoSubmit: true,
57-
},
58-
})
59-
})
60-
}),
61-
Commands.register('aws.amazonq.generateFix', async (issue: CodeScanIssue, filePath: string) => {
62-
void focusAmazonQPanel().then(async () => {
63-
if (issue && filePath) {
64-
const range = new vscode.Range(issue.startLine, 0, issue.endLine, 0)
65-
await vscode.workspace.openTextDocument(filePath).then((doc) => {
66-
void vscode.window.showTextDocument(doc, {
67-
selection: range,
68-
viewColumn: vscode.ViewColumn.One,
69-
preview: true,
70-
})
71-
})
72-
}
73-
74-
const lineRange =
75-
issue.startLine === issue.endLine - 1
76-
? `[${issue.startLine + 1}]`
77-
: `[${issue.startLine + 1}, ${issue.endLine}]`
78-
const visibleMessageInChat = `_Fix **${issue.title}** issue in **${path.basename(filePath)}** at \`${lineRange}\`_`
79-
80-
// The message that gets sent to the backend
81-
const contextMessage = `Generate a fix for the following code issue. You must not explain the issue, just generate and explain the fix. The user should have the option to accept or reject the fix before any code is changed. Code issue - ${JSON.stringify(issue)}`
82-
83-
void provider.webview?.postMessage({
84-
command: 'sendToPrompt',
85-
params: {
86-
selection: '',
87-
triggerType: 'contextMenu',
88-
prompt: {
89-
prompt: visibleMessageInChat, // what gets sent to the user
90-
escapedPrompt: contextMessage, // what gets sent to the backend
91-
},
92-
autoSubmit: true,
93-
},
94-
})
95-
})
96-
}),
25+
Commands.register('aws.amazonq.explainIssue', (issue: CodeScanIssue, filePath: string) =>
26+
handleIssueCommand(
27+
issue,
28+
filePath,
29+
'Explain',
30+
'Provide a small description of the issue. You must not attempt to fix the issue. You should only give a small summary of it to the user.',
31+
provider
32+
)
33+
),
34+
Commands.register('aws.amazonq.generateFix', (issue: CodeScanIssue, filePath: string) =>
35+
handleIssueCommand(
36+
issue,
37+
filePath,
38+
'Fix',
39+
'Generate a fix for the following code issue. You must not explain the issue, just generate and explain the fix. The user should have the option to accept or reject the fix before any code is changed.',
40+
provider
41+
)
42+
),
9743
Commands.register('aws.amazonq.sendToPrompt', (data) => {
9844
const triggerType = getCommandTriggerType(data)
9945
const selection = getSelectedText()
@@ -116,6 +62,53 @@ export function registerCommands(provider: AmazonQChatViewProvider) {
11662
)
11763
}
11864

65+
async function handleIssueCommand(
66+
issue: CodeScanIssue,
67+
filePath: string,
68+
action: string,
69+
contextPrompt: string,
70+
provider: AmazonQChatViewProvider
71+
) {
72+
await focusAmazonQPanel()
73+
74+
if (issue && filePath) {
75+
await openFileWithSelection(issue, filePath)
76+
}
77+
78+
const lineRange = createLineRangeText(issue)
79+
const visibleMessageInChat = `_${action} **${issue.title}** issue in **${path.basename(filePath)}** at \`${lineRange}\`_`
80+
const contextMessage = `${contextPrompt} Code issue - ${JSON.stringify(issue)}`
81+
82+
void provider.webview?.postMessage({
83+
command: 'sendToPrompt',
84+
params: {
85+
selection: '',
86+
triggerType: 'contextMenu',
87+
prompt: {
88+
prompt: visibleMessageInChat,
89+
escapedPrompt: contextMessage,
90+
},
91+
autoSubmit: true,
92+
},
93+
})
94+
}
95+
96+
async function openFileWithSelection(issue: CodeScanIssue, filePath: string) {
97+
const range = new vscode.Range(issue.startLine, 0, issue.endLine, 0)
98+
const doc = await vscode.workspace.openTextDocument(filePath)
99+
await vscode.window.showTextDocument(doc, {
100+
selection: range,
101+
viewColumn: vscode.ViewColumn.One,
102+
preview: true,
103+
})
104+
}
105+
106+
function createLineRangeText(issue: CodeScanIssue): string {
107+
return issue.startLine === issue.endLine - 1
108+
? `[${issue.startLine + 1}]`
109+
: `[${issue.startLine + 1}, ${issue.endLine}]`
110+
}
111+
119112
function getSelectedText(): string {
120113
const editor = window.activeTextEditor
121114
if (editor) {
@@ -134,15 +127,14 @@ function getCommandTriggerType(data: any): string {
134127
}
135128

136129
function registerGenericCommand(commandName: string, genericCommand: string, provider: AmazonQChatViewProvider) {
137-
return Commands.register(commandName, (data) => {
130+
return Commands.register(commandName, async (data) => {
138131
const triggerType = getCommandTriggerType(data)
139132
const selection = getSelectedText()
140133

141-
void focusAmazonQPanel().then(() => {
142-
void provider.webview?.postMessage({
143-
command: 'genericCommand',
144-
params: { genericCommand, selection, triggerType },
145-
})
134+
await focusAmazonQPanel()
135+
void provider.webview?.postMessage({
136+
command: 'genericCommand',
137+
params: { genericCommand, selection, triggerType },
146138
})
147139
})
148140
}

packages/amazonq/test/unit/codewhisperer/service/securityIssueHoverProvider.test.ts

Lines changed: 30 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,25 @@ describe('securityIssueHoverProvider', () => {
2121
token = new vscode.CancellationTokenSource()
2222
})
2323

24+
function buildCommandLink(command: string, args: any[], label: string, tooltip: string): string {
25+
return `[$(${command.includes('comment') ? 'comment' : 'error'}) ${label}](command:${command}?${encodeURIComponent(JSON.stringify(args))} '${tooltip}')`
26+
}
27+
28+
function buildExpectedContent(issue: any, fileName: string, description: string, severity?: string): string {
29+
const severityBadge = severity ? ` ![${severity}](severity-${severity.toLowerCase()}.svg)` : ' '
30+
const commands = [
31+
buildCommandLink('aws.amazonq.explainIssue', [issue, fileName], 'Explain', 'Explain with Amazon Q'),
32+
buildCommandLink('aws.amazonq.generateFix', [issue, fileName], 'Fix', 'Fix with Amazon Q'),
33+
buildCommandLink('aws.amazonq.security.ignore', [issue, fileName, 'hover'], 'Ignore', 'Ignore Issue'),
34+
buildCommandLink('aws.amazonq.security.ignoreAll', [issue, 'hover'], 'Ignore All', 'Ignore Similar Issues'),
35+
]
36+
return `## title${severityBadge}\n${description}\n\n${commands.join('\n | ')}\n`
37+
}
38+
39+
function setupIssues(issues: any[]): void {
40+
securityIssueProvider.issues = [{ filePath: mockDocument.fileName, issues }]
41+
}
42+
2443
it('should return hover for each issue for the current position', () => {
2544
const issues = [
2645
createCodeScanIssue({ findingId: 'finding-1', detectorId: 'language/detector-1', ruleId: 'Rule-123' }),
@@ -32,49 +51,17 @@ describe('securityIssueHoverProvider', () => {
3251
}),
3352
]
3453

35-
securityIssueProvider.issues = [
36-
{
37-
filePath: mockDocument.fileName,
38-
issues,
39-
},
40-
]
41-
54+
setupIssues(issues)
4255
const actual = securityIssueHoverProvider.provideHover(mockDocument, new vscode.Position(0, 0), token.token)
4356

4457
assert.strictEqual(actual.contents.length, 2)
4558
assert.strictEqual(
4659
(actual.contents[0] as vscode.MarkdownString).value,
47-
'## title ![High](severity-high.svg)\n' +
48-
'fix\n\n' +
49-
`[$(comment) Explain](command:aws.amazonq.explainIssue?${encodeURIComponent(
50-
JSON.stringify([issues[0], mockDocument.fileName])
51-
)} 'Explain with Amazon Q')\n` +
52-
` | [$(comment) Fix](command:aws.amazonq.generateFix?${encodeURIComponent(
53-
JSON.stringify([issues[0], mockDocument.fileName])
54-
)} 'Fix with Amazon Q')\n` +
55-
` | [$(error) Ignore](command:aws.amazonq.security.ignore?${encodeURIComponent(
56-
JSON.stringify([issues[0], mockDocument.fileName, 'hover'])
57-
)} 'Ignore Issue')\n` +
58-
` | [$(error) Ignore All](command:aws.amazonq.security.ignoreAll?${encodeURIComponent(
59-
JSON.stringify([issues[0], 'hover'])
60-
)} 'Ignore Similar Issues')\n`
60+
buildExpectedContent(issues[0], mockDocument.fileName, 'fix', 'High')
6161
)
6262
assert.strictEqual(
6363
(actual.contents[1] as vscode.MarkdownString).value,
64-
'## title ![High](severity-high.svg)\n' +
65-
'recommendationText\n\n' +
66-
`[$(comment) Explain](command:aws.amazonq.explainIssue?${encodeURIComponent(
67-
JSON.stringify([issues[1], mockDocument.fileName])
68-
)} 'Explain with Amazon Q')\n` +
69-
` | [$(comment) Fix](command:aws.amazonq.generateFix?${encodeURIComponent(
70-
JSON.stringify([issues[1], mockDocument.fileName])
71-
)} 'Fix with Amazon Q')\n` +
72-
` | [$(error) Ignore](command:aws.amazonq.security.ignore?${encodeURIComponent(
73-
JSON.stringify([issues[1], mockDocument.fileName, 'hover'])
74-
)} 'Ignore Issue')\n` +
75-
` | [$(error) Ignore All](command:aws.amazonq.security.ignoreAll?${encodeURIComponent(
76-
JSON.stringify([issues[1], 'hover'])
77-
)} 'Ignore Similar Issues')\n`
64+
buildExpectedContent(issues[1], mockDocument.fileName, 'recommendationText', 'High')
7865
)
7966
assertTelemetry('codewhisperer_codeScanIssueHover', [
8067
{ findingId: 'finding-1', detectorId: 'language/detector-1', ruleId: 'Rule-123', includesFix: true },
@@ -83,58 +70,28 @@ describe('securityIssueHoverProvider', () => {
8370
})
8471

8572
it('should return empty contents if there is no issue on the current position', () => {
86-
securityIssueProvider.issues = [
87-
{
88-
filePath: mockDocument.fileName,
89-
issues: [createCodeScanIssue()],
90-
},
91-
]
92-
73+
setupIssues([createCodeScanIssue()])
9374
const actual = securityIssueHoverProvider.provideHover(mockDocument, new vscode.Position(2, 0), token.token)
9475
assert.strictEqual(actual.contents.length, 0)
9576
})
9677

9778
it('should skip issues not in the current file', () => {
9879
securityIssueProvider.issues = [
99-
{
100-
filePath: 'some/path',
101-
issues: [createCodeScanIssue()],
102-
},
103-
{
104-
filePath: mockDocument.fileName,
105-
issues: [createCodeScanIssue()],
106-
},
80+
{ filePath: 'some/path', issues: [createCodeScanIssue()] },
81+
{ filePath: mockDocument.fileName, issues: [createCodeScanIssue()] },
10782
]
10883
const actual = securityIssueHoverProvider.provideHover(mockDocument, new vscode.Position(0, 0), token.token)
10984
assert.strictEqual(actual.contents.length, 1)
11085
})
11186

11287
it('should not show severity badge if undefined', () => {
11388
const issues = [createCodeScanIssue({ severity: undefined, suggestedFixes: [] })]
114-
securityIssueProvider.issues = [
115-
{
116-
filePath: mockDocument.fileName,
117-
issues,
118-
},
119-
]
89+
setupIssues(issues)
12090
const actual = securityIssueHoverProvider.provideHover(mockDocument, new vscode.Position(0, 0), token.token)
12191
assert.strictEqual(actual.contents.length, 1)
12292
assert.strictEqual(
12393
(actual.contents[0] as vscode.MarkdownString).value,
124-
'## title \n' +
125-
'recommendationText\n\n' +
126-
`[$(comment) Explain](command:aws.amazonq.explainIssue?${encodeURIComponent(
127-
JSON.stringify([issues[0], mockDocument.fileName])
128-
)} 'Explain with Amazon Q')\n` +
129-
` | [$(comment) Fix](command:aws.amazonq.generateFix?${encodeURIComponent(
130-
JSON.stringify([issues[0], mockDocument.fileName])
131-
)} 'Fix with Amazon Q')\n` +
132-
` | [$(error) Ignore](command:aws.amazonq.security.ignore?${encodeURIComponent(
133-
JSON.stringify([issues[0], mockDocument.fileName, 'hover'])
134-
)} 'Ignore Issue')\n` +
135-
` | [$(error) Ignore All](command:aws.amazonq.security.ignoreAll?${encodeURIComponent(
136-
JSON.stringify([issues[0], 'hover'])
137-
)} 'Ignore Similar Issues')\n`
94+
buildExpectedContent(issues[0], mockDocument.fileName, 'recommendationText')
13895
)
13996
})
14097

@@ -149,41 +106,17 @@ describe('securityIssueHoverProvider', () => {
149106
],
150107
}),
151108
]
152-
securityIssueProvider.issues = [
153-
{
154-
filePath: mockDocument.fileName,
155-
issues,
156-
},
157-
]
109+
setupIssues(issues)
158110
const actual = securityIssueHoverProvider.provideHover(mockDocument, new vscode.Position(0, 0), token.token)
159111
assert.strictEqual(actual.contents.length, 1)
160112
assert.strictEqual(
161113
(actual.contents[0] as vscode.MarkdownString).value,
162-
'## title ![High](severity-high.svg)\n' +
163-
'fix\n\n' +
164-
`[$(comment) Explain](command:aws.amazonq.explainIssue?${encodeURIComponent(
165-
JSON.stringify([issues[0], mockDocument.fileName])
166-
)} 'Explain with Amazon Q')\n` +
167-
` | [$(comment) Fix](command:aws.amazonq.generateFix?${encodeURIComponent(
168-
JSON.stringify([issues[0], mockDocument.fileName])
169-
)} 'Fix with Amazon Q')\n` +
170-
` | [$(error) Ignore](command:aws.amazonq.security.ignore?${encodeURIComponent(
171-
JSON.stringify([issues[0], mockDocument.fileName, 'hover'])
172-
)} 'Ignore Issue')\n` +
173-
` | [$(error) Ignore All](command:aws.amazonq.security.ignoreAll?${encodeURIComponent(
174-
JSON.stringify([issues[0], 'hover'])
175-
)} 'Ignore Similar Issues')\n`
114+
buildExpectedContent(issues[0], mockDocument.fileName, 'fix', 'High')
176115
)
177116
})
178117

179118
it('should not show issues that are not visible', () => {
180-
const issues = [createCodeScanIssue({ visible: false })]
181-
securityIssueProvider.issues = [
182-
{
183-
filePath: mockDocument.fileName,
184-
issues,
185-
},
186-
]
119+
setupIssues([createCodeScanIssue({ visible: false })])
187120
const actual = securityIssueHoverProvider.provideHover(mockDocument, new vscode.Position(0, 0), token.token)
188121
assert.strictEqual(actual.contents.length, 0)
189122
})

0 commit comments

Comments
 (0)