33 * SPDX-License-Identifier: Apache-2.0
44 */
55
6- import { existsSync } from 'fs'
76import * as vscode from 'vscode'
7+ import fs from 'fs'
8+ import path from 'path'
89import { featureDevScheme } from '../../amazonqFeatureDev/constants'
10+ import { amazonQDiffScheme , tempDirPath } from '../../shared'
911
1012export async function openDiff ( leftPath : string , rightPath : string , tabId : string ) {
1113 const { left, right } = getFileDiffUris ( leftPath , rightPath , tabId )
@@ -18,7 +20,7 @@ export async function openDeletedDiff(filePath: string, name: string, tabId: str
1820}
1921
2022export function getOriginalFileUri ( fullPath : string , tabId : string ) {
21- return existsSync ( fullPath ) ? vscode . Uri . file ( fullPath ) : createAmazonQUri ( 'empty' , tabId )
23+ return fs . existsSync ( fullPath ) ? vscode . Uri . file ( fullPath ) : createAmazonQUri ( 'empty' , tabId )
2224}
2325
2426export function getFileDiffUris ( leftPath : string , rightPath : string , tabId : string ) {
@@ -32,3 +34,126 @@ export function createAmazonQUri(path: string, tabId: string) {
3234 // TODO change the featureDevScheme to a more general amazon q scheme
3335 return vscode . Uri . from ( { scheme : featureDevScheme , path, query : `tabID=${ tabId } ` } )
3436}
37+
38+ /**
39+ * Extracts the file path and selection context from the message.
40+ *
41+ * @param {any } message - The message object containing the file and selection context.
42+ * @returns {Object } - An object with `filePath` and `selection` properties.
43+ */
44+ export function extractFileAndCodeSelectionFromMessage ( message : any ) {
45+ const filePath = message ?. context ?. activeFileContext ?. filePath
46+ const selection = message ?. context ?. focusAreaContext ?. selectionInsideExtendedCodeBlock as vscode . Selection
47+ return { filePath, selection }
48+ }
49+
50+ /**
51+ * Indents the given code based on the current document's indentation at the selection start.
52+ *
53+ * @param {any } message - The message object containing the code.
54+ * @param {vscode.TextDocument } doc - The VSCode document where the code is applied.
55+ * @param {vscode.Selection } selection - The selection range in the document.
56+ * @returns {string } - The processed code to be applied to the document.
57+ */
58+ export function getIndentedCode ( message : any , doc : vscode . TextDocument , selection : vscode . Selection ) {
59+ const code = message . code as string
60+ const indentRange = new vscode . Range ( new vscode . Position ( selection . start . line , 0 ) , selection . active )
61+ let indent = doc . getText ( indentRange )
62+
63+ if ( indent . trim ( ) . length !== 0 ) {
64+ indent = ' ' . repeat ( indent . length - indent . trimStart ( ) . length )
65+ }
66+
67+ let trimmedCode = code
68+ if ( code . endsWith ( '\n' ) ) {
69+ const startLineText = doc . lineAt ( selection . start . line ) . text
70+ if ( ! startLineText . endsWith ( '\n' ) ) {
71+ trimmedCode = code . trimEnd ( )
72+ }
73+ }
74+
75+ const indentedCode = trimmedCode
76+ . split ( '\n' )
77+ . map ( ( line ) => indent + line )
78+ . join ( '\n' )
79+
80+ return indentedCode
81+ }
82+
83+ /**
84+ * Creates a selection range from the given document and selection.
85+ * If a user selects a partial code, this function generates the range from start line to end line.
86+ *
87+ * @param {vscode.TextDocument } doc - The VSCode document where the selection is applied.
88+ * @param {vscode.Selection } selection - The selection range in the document.
89+ * @returns {vscode.Range } - The VSCode range object representing the start and end of the selection.
90+ */
91+ export function getSelectionFromRange ( doc : vscode . TextDocument , selection : vscode . Selection ) {
92+ return new vscode . Range (
93+ new vscode . Position ( selection . start . line , 0 ) ,
94+ new vscode . Position ( selection . end . line , doc . lineAt ( selection . end . line ) . range . end . character )
95+ )
96+ }
97+
98+ /**
99+ * Applies the given code to the specified range in the document.
100+ * Saves the document after the edit is successfully applied.
101+ *
102+ * @param {vscode.TextDocument } doc - The VSCode document to which the changes are applied.
103+ * @param {vscode.Range } range - The range in the document where the code is replaced.
104+ * @param {string } code - The code to be applied to the document.
105+ * @returns {Promise<void> } - Resolves when the changes are successfully applied and the document is saved.
106+ */
107+ export async function applyChanges ( doc : vscode . TextDocument , range : vscode . Range , code : string ) {
108+ const edit = new vscode . WorkspaceEdit ( )
109+ edit . replace ( doc . uri , range , code )
110+ const successfulEdit = await vscode . workspace . applyEdit ( edit )
111+ if ( successfulEdit ) {
112+ await doc . save ( )
113+ }
114+ }
115+
116+ /**
117+ * Creates a temporary file for diff comparison by cloning the original file
118+ * and applying the proposed changes within the selected range.
119+ *
120+ * @param {vscode.Uri } originalFileUri - The URI of the original file.
121+ * @param {any } message - The message object containing the proposed code changes.
122+ * @param {vscode.Selection } selection - The selection range in the document where the changes are applied.
123+ * @returns {Promise<vscode.Uri> } - A promise that resolves to the URI of the temporary file with the applied changes.
124+ */
125+ export async function createTempFileForDiff ( originalFileUri : vscode . Uri , message : any , selection : vscode . Selection ) {
126+ const id = Date . now ( )
127+ const fileName = path . basename ( originalFileUri . path )
128+ const tempFilePath = path . join ( tempDirPath , `${ fileName } _proposed-${ id } ` )
129+ const tempFileUri = vscode . Uri . parse ( `${ amazonQDiffScheme } :${ tempFilePath } ` )
130+
131+ // Write original content to temp file
132+ fs . writeFileSync ( tempFilePath , fs . readFileSync ( originalFileUri . fsPath , 'utf-8' ) )
133+
134+ // Apply the proposed changes to the temp file
135+ const doc = await vscode . workspace . openTextDocument ( tempFileUri . path )
136+ const code = getIndentedCode ( message , doc , selection )
137+ const range = getSelectionFromRange ( doc , selection )
138+
139+ await applyChanges ( doc , range , code )
140+ return tempFileUri
141+ }
142+
143+ /**
144+ * Disposes of resources (content provider) when the temporary diff editor is closed.
145+ *
146+ * @param {vscode.Uri } tempFileUri - The URI of the temporary file used for diff comparison.
147+ * @param {vscode.Disposable } disposable - The disposable resource to be cleaned up (e.g., content provider).
148+ */
149+ export function disposeOnEditorClose ( tempFileUri : vscode . Uri , disposable : vscode . Disposable ) {
150+ vscode . window . onDidChangeVisibleTextEditors ( ( ) => {
151+ if (
152+ ! vscode . window . visibleTextEditors . some (
153+ ( editor ) => editor . document . uri . toString ( ) === tempFileUri . toString ( )
154+ )
155+ ) {
156+ disposable . dispose ( )
157+ }
158+ } )
159+ }
0 commit comments