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,124 @@ 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+ const indentedCode = code
68+ . split ( '\n' )
69+ . map ( ( line ) => indent + line )
70+ . join ( '\n' )
71+
72+ if ( indentedCode . endsWith ( '\n' ) ) {
73+ const startLineText = doc . lineAt ( selection . start . line ) . text
74+ if ( ! startLineText . endsWith ( '\n' ) ) {
75+ return indentedCode . trimEnd ( )
76+ }
77+ }
78+ return indentedCode
79+ }
80+
81+ /**
82+ * Creates a selection range from the given document and selection.
83+ * If a user selects a partial code, this function generates the range from start line to end line.
84+ *
85+ * @param {vscode.TextDocument } doc - The VSCode document where the selection is applied.
86+ * @param {vscode.Selection } selection - The selection range in the document.
87+ * @returns {vscode.Range } - The VSCode range object representing the start and end of the selection.
88+ */
89+ export function getSelectionFromRange ( doc : vscode . TextDocument , selection : vscode . Selection ) {
90+ return new vscode . Range (
91+ new vscode . Position ( selection . start . line , 0 ) ,
92+ new vscode . Position ( selection . end . line , doc . lineAt ( selection . end . line ) . range . end . character )
93+ )
94+ }
95+
96+ /**
97+ * Applies the given code to the specified range in the document.
98+ * Saves the document after the edit is successfully applied.
99+ *
100+ * @param {vscode.TextDocument } doc - The VSCode document to which the changes are applied.
101+ * @param {vscode.Range } range - The range in the document where the code is replaced.
102+ * @param {string } code - The code to be applied to the document.
103+ * @returns {Promise<void> } - Resolves when the changes are successfully applied and the document is saved.
104+ */
105+ export async function applyChanges ( doc : vscode . TextDocument , range : vscode . Range , code : string ) {
106+ const edit = new vscode . WorkspaceEdit ( )
107+ edit . replace ( doc . uri , range , code )
108+ const successfulEdit = await vscode . workspace . applyEdit ( edit )
109+ if ( successfulEdit ) {
110+ await doc . save ( )
111+ }
112+ }
113+
114+ /**
115+ * Creates a temporary file for diff comparison by cloning the original file
116+ * and applying the proposed changes within the selected range.
117+ *
118+ * @param {vscode.Uri } originalFileUri - The URI of the original file.
119+ * @param {any } message - The message object containing the proposed code changes.
120+ * @param {vscode.Selection } selection - The selection range in the document where the changes are applied.
121+ * @returns {Promise<vscode.Uri> } - A promise that resolves to the URI of the temporary file with the applied changes.
122+ */
123+ export async function createTempFileForDiff ( originalFileUri : vscode . Uri , message : any , selection : vscode . Selection ) {
124+ const id = Date . now ( )
125+ const fileName = path . basename ( originalFileUri . path )
126+ const tempFilePath = path . join ( tempDirPath , `${ fileName } _proposed-${ id } ` )
127+ const tempFileUri = vscode . Uri . parse ( `${ amazonQDiffScheme } :${ tempFilePath } ` )
128+
129+ // Write original content to temp file
130+ fs . writeFileSync ( tempFilePath , fs . readFileSync ( originalFileUri . fsPath , 'utf-8' ) )
131+
132+ // Apply the proposed changes to the temp file
133+ const doc = await vscode . workspace . openTextDocument ( tempFileUri . path )
134+ const code = getIndentedCode ( message , doc , selection )
135+ const range = getSelectionFromRange ( doc , selection )
136+
137+ await applyChanges ( doc , range , code )
138+ return tempFileUri
139+ }
140+
141+ /**
142+ * Disposes of resources (content provider) when the temporary diff editor is closed.
143+ *
144+ * @param {vscode.Uri } tempFileUri - The URI of the temporary file used for diff comparison.
145+ * @param {vscode.Disposable } disposable - The disposable resource to be cleaned up (e.g., content provider).
146+ */
147+ export function disposeOnEditorClose ( tempFileUri : vscode . Uri , disposable : vscode . Disposable ) {
148+ vscode . window . onDidChangeVisibleTextEditors ( ( ) => {
149+ if (
150+ ! vscode . window . visibleTextEditors . some (
151+ ( editor ) => editor . document . uri . toString ( ) === tempFileUri . toString ( )
152+ )
153+ ) {
154+ disposable . dispose ( )
155+ }
156+ } )
157+ }
0 commit comments