Skip to content

Conversation

@ivikash
Copy link
Member

@ivikash ivikash commented Jul 18, 2024

Add Accept Diff & View Diff button to Amazon Q for auto generated code.

Screen.Recording.2024-10-02.at.9.10.45.AM.mov

License

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@ivikash ivikash requested review from a team as code owners July 18, 2024 05:45
const fileName = path.basename(right.path)

const diffScheme = 'amazon-q-accept-diff'
const left = Uri.parse(`${diffScheme}:${fileName}-` + randomUUID())
Copy link
Contributor

Choose a reason for hiding this comment

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

getNonexistentFilename may be useful

export async function getNonexistentFilename(

Copy link
Contributor

@justinmk3 justinmk3 left a comment

Choose a reason for hiding this comment

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

which Q team needs to review this?

@ivikash ivikash changed the title feat(amazonq): add button to accept code changes in diff view [Work In Progress] feat(amazonq): add button to accept code changes in diff view Jul 22, 2024
@github-actions
Copy link

This pull request modifies code in src/ but no tests were added/updated. Confirm whether tests should be added or ensure the PR description explains why tests are not required.

@ivikash ivikash changed the title [Work In Progress] feat(amazonq): add button to accept code changes in diff view feat(amazonq): add button to accept code changes in diff view Sep 19, 2024
@ivikash ivikash changed the title feat(amazonq): add button to accept code changes in diff view feat(amazonq): add button to view diff in IDE Sep 19, 2024
@ivikash ivikash force-pushed the feat/diff-view branch 10 times, most recently from 0374d02 to 80ac383 Compare October 2, 2024 16:01
* @param {vscode.Selection} selection - The selection range in the document.
* @returns {vscode.Range} - The VSCode range object representing the start and end of the selection.
*/
export function getSelectionFromRange(doc: vscode.TextDocument, selection: vscode.Selection) {
Copy link
Contributor

Choose a reason for hiding this comment

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

packages/core/src/shared/utilities/textDocumentUtilities.ts

Copy link
Contributor

Choose a reason for hiding this comment

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

for better or worse, the "commons" concept is named shared/ in this codebase. can we avoid introducing a different name?

q-specific common modules should live in packages/core/src/shared/amazonq/ if they are truly Q-only and can't live in the more specific packages/amazonq/src/shared/

cwsprChatHasProjectContext: this.responseWithProjectContext.get(message.messageId),
}
break
case 'view_diff':
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: seems strange that this uses snake_case, acceptDiff is camelCase and the others are hyphen-words

Copy link
Member Author

@ivikash ivikash Oct 2, 2024

Choose a reason for hiding this comment

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

Indeed thats very true. Even during development its weird.

export const amazonQVscodeMarketplace =
'https://marketplace.visualstudio.com/items?itemName=AmazonWebServices.amazon-q-vscode'

export const amazonQDiffScheme = 'amazon-q-view-diff'
Copy link
Contributor

Choose a reason for hiding this comment

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

we probably need a single schemes.fs file somewhere, but until we decide that, this can at least live next to the existing ones:

* URI scheme for CloudWatch Logs Virtual Documents
*/
export const CLOUDWATCH_LOGS_SCHEME = 'aws-cwl' // eslint-disable-line @typescript-eslint/naming-convention
export const AWS_SCHEME = 'aws' // eslint-disable-line @typescript-eslint/naming-convention

@ivikash ivikash force-pushed the feat/diff-view branch 2 times, most recently from ae5a75d to 051dd09 Compare October 2, 2024 18:44
*/
export const CLOUDWATCH_LOGS_SCHEME = 'aws-cwl' // eslint-disable-line @typescript-eslint/naming-convention
export const AWS_SCHEME = 'aws' // eslint-disable-line @typescript-eslint/naming-convention
export const AMAZON_Q_DIFF_SCHEME = 'amazon-q-view-diff' // eslint-disable-line @typescript-eslint/naming-convention
Copy link
Contributor

Choose a reason for hiding this comment

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

eslint-disable-line was a compromise to avoid code churn, not intended to be continued for new symobls

is "view" an imporant thing to mention in the scheme name?

Suggested change
export const AMAZON_Q_DIFF_SCHEME = 'amazon-q-view-diff' // eslint-disable-line @typescript-eslint/naming-convention
export const amazonQDiffScheme = 'amazon-q-diff'

const languageId = (await vscode.workspace.openTextDocument(originalFileUri)).languageId
const tempFile = _path.parse(originalFileUri.path)
const tempFilePath = _path.join(tempDirPath, `${tempFile.name}_proposed-${id}${tempFile.ext}`)
const tempFileUri = vscode.Uri.parse(`${AMAZON_Q_DIFF_SCHEME}:${tempFilePath}`)
Copy link
Contributor

Choose a reason for hiding this comment

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

scheme should probably be a parameter, but maybe that can be revisited later.

try {
await FeatureConfigProvider.instance.fetchFeatureConfigs()
featureConfigs = FeatureConfigProvider.getFeatureConfigs()
} catch (error) {}
Copy link
Contributor

Choose a reason for hiding this comment

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

should it not at least log a warning?

* const map = deserializeArrayToMap(array);
* // map is now a Map object with entries: { 'key1' => 'value1', 'key2' => 'value2' }
*/
export function deserializeArrayToMap(arr: [unknown, unknown][]) {
Copy link
Contributor

@justinmk3 justinmk3 Oct 4, 2024

Choose a reason for hiding this comment

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

This is a very long name, and docstring. If someone is trying to understand this function, does this docstring tell them something useful that the implementation (new Map()) does not?

Suggested change
export function deserializeArrayToMap(arr: [unknown, unknown][]) {
export function tryNewMap(arr: [unknown, unknown][]) {

constructor(private uri: vscode.Uri) {}

provideTextDocumentContent(_uri: vscode.Uri) {
return fs.readFileSync(this.uri.fsPath, 'utf-8')
Copy link
Contributor

Choose a reason for hiding this comment

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

Unless this needs to be synchronous, do not use fs-extra. The correct fs module is in our shared folder

Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at the api, this method can be async. The type eventually resolves to a possible "Thenable". So you can use our built in fs module


import * as _path from 'path'
import * as vscode from 'vscode'
import fs from 'fs-extra'
Copy link
Contributor

Choose a reason for hiding this comment

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

avoid fs-extra if possible. Info in previous comment

@ivikash ivikash force-pushed the feat/diff-view branch 2 times, most recently from b58d1f7 to e0d7464 Compare October 6, 2024 20:23
})
)
} catch (error) {
getLogger().error(`Diff: Unable to open the document:`, (error as Error).message)
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the behavior if we were to throw instead? If this code is initially triggered in a Command, then the error will be swallowed upstream, and logged.

Copy link
Member Author

Choose a reason for hiding this comment

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

Discussed offline.

Display an error Notification for the user. Update the logic to throw everywhere and catch at high level and display a generic error notification.


import * as _path from 'path'
import * as vscode from 'vscode'
import * as fs from 'fs'
Copy link
Contributor

Choose a reason for hiding this comment

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

This isn't the shared 'fs'. Use src/shared/fs/fs.ts

fs.mkdirSync(tempDirPath, { recursive: true })
}
} catch (error) {
getLogger().error('Diff: Unable to create directory %s: %s', tempDirPath, (error as Error).message)
Copy link
Contributor

@nkomonen-amazon nkomonen-amazon Oct 7, 2024

Choose a reason for hiding this comment

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

What is the reason for swallowing the errors? Especially in a utils function we shouldn't be swallowing, unless there is a good reason to. One reason is in telemetry when we fail and record the reason for the failure, we parse the entire error object for the chain of errors and then record it there. So if we only log then all that information is lost and requires the user to report it.

Instead you could make a function like this that wraps the error with context about the specific FS call that failed: https://github.com/aws/aws-toolkit-vscode/blob/master/packages/core/src/shared/crashMonitoring.ts#L589

Copy link
Contributor

@nkomonen-amazon nkomonen-amazon Oct 10, 2024

Choose a reason for hiding this comment

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

Looking back at the I think that you should remove the try/catch and just call fs.mkdir() since its behavior is mkdirp() where it creates missing subfolders if necessary and does not throw if it already exists.

const tempFile = _path.parse(originalFileUri.path)
const tempFilePath = _path.join(tempDirPath, `${tempFile.name}_proposed-${id}${tempFile.ext}`)
try {
if (!fs.existsSync(tempDirPath)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Avoid sync calls since they are blocking. See the shared fs mentioned above

@ivikash ivikash force-pushed the feat/diff-view branch 2 times, most recently from 615332d to bf8f4dd Compare October 7, 2024 17:39
)
} catch (error) {
void vscode.window.showInformationMessage(errorNotification)
ChatDiffError.chain(error, `Failed to Accept Diff`, { code: chatDiffCode })
Copy link
Contributor

Choose a reason for hiding this comment

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

need to add throw

}
} catch (error) {
void vscode.window.showInformationMessage(errorNotification)
ChatDiffError.chain(error, `Failed to Open Diff View`, { code: chatDiffCode })
Copy link
Contributor

Choose a reason for hiding this comment

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

need to add throw

if (!(error instanceof Error)) {
throw error
}
getLogger().error('Diff: Unable to write to file %s: %s', tempFilePath, (error as Error).message)
Copy link
Contributor

Choose a reason for hiding this comment

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

Dont need to log the error since below we will throw the error and it will be logged upstream

throw error
}
getLogger().error('Diff: Unable to write to file %s: %s', tempFilePath, (error as Error).message)
ToolkitError.chain(error, 'Unable to write to file', { code: errorCode })
Copy link
Contributor

Choose a reason for hiding this comment

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

add 'throw'. Also we want this error message to give context to why the FS call failed. So in this case it should be "Failed to write to temp file"

* const map = tryNewMap(array);
* // map is now a Map object with entries: { 'key1' => 'value1', 'key2' => 'value2' }
*/
export function tryNewMap(arr: [unknown, unknown][]) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like it should live in core and since this is map related would probably make sense in core/src/shared/utilities/map

Copy link
Contributor

@justinmk3 justinmk3 Oct 10, 2024

Choose a reason for hiding this comment

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

amazon/util doesn't look like the place for this. And, the docstring is still way too long and over-explains what could be stated as "tries to create map from x, else returns an empty map".

Also that pattern is easily generalized as tryCall<T>(...), so I don't see why we need a dedicated function for this.

@ivikash ivikash force-pushed the feat/diff-view branch 2 times, most recently from 155042e to c669d06 Compare October 10, 2024 03:04
import { extractFileAndCodeSelectionFromMessage, fs, getErrorMsg, getIndentedCode, ToolkitError } from '../../../shared'

class ContentProvider implements vscode.TextDocumentContentProvider {
constructor(private uri: vscode.Uri) {}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: private readonly uri: ... is typically preferred. But dont bother changing in this PR

@ivikash ivikash merged commit 62e9cfb into aws:master Oct 10, 2024
21 of 24 checks passed
@ivikash ivikash deleted the feat/diff-view branch October 10, 2024 17:20
"downvote",
"clickBodyLink"
"clickBodyLink",
"viewDiff"
Copy link
Contributor

Choose a reason for hiding this comment

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

* @param {vscode.Selection} selection - The selection range in the document.
* @returns {string} - The processed code to be applied to the document.
*/
export function getIndentedCode(message: any, doc: vscode.TextDocument, selection: vscode.Selection) {
Copy link
Contributor

Choose a reason for hiding this comment

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

should this live in packages/core/src/shared/utilities/textDocumentUtilities.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants