@@ -10,16 +10,13 @@ import * as vscode from 'vscode';
10
10
import { SettingsManager , SettingsManagerImpl } from './lib/settings' ;
11
11
import * as targeting from './lib/targeting'
12
12
import { DiagnosticManager , DiagnosticManagerImpl } from './lib/diagnostics' ;
13
- import { DiffCreateAction } from './lib/actions/diff-create-action' ;
14
- import { DiffAcceptAction } from './lib/actions/diff-accept-action' ;
15
- import { DiffRejectAction } from './lib/actions/diff-reject-action' ;
16
13
import { messages } from './lib/messages' ;
17
14
import { Fixer } from './lib/fixer' ;
18
15
import { CoreExtensionService } from './lib/core-extension-service' ;
19
16
import * as Constants from './lib/constants' ;
20
17
import * as path from 'path' ;
21
18
import * as ApexGuruFunctions from './lib/apexguru/apex-guru-service' ;
22
- import { AgentforceViolationsFixer } from './lib/agentforce/agentforce-violations -fixer'
19
+ import { AgentforceViolationFixer } from './lib/agentforce/agentforce-violation -fixer'
23
20
import {
24
21
CODEGENIE_UNIFIED_DIFF_ACCEPT ,
25
22
CODEGENIE_UNIFIED_DIFF_ACCEPT_ALL ,
@@ -34,6 +31,10 @@ import {TelemetryService} from "./lib/external-services/telemetry-service";
34
31
import { DfaRunner } from "./lib/dfa-runner" ;
35
32
import { CodeAnalyzerRunner } from "./lib/code-analyzer-runner" ;
36
33
import { CodeActionProvider , CodeActionProviderMetadata , DocumentSelector } from "vscode" ;
34
+ import { AgentforceCodeActionProvider } from "./lib/agentforce/agentforce-code-action-provider" ;
35
+ import { UnifiedDiffActions } from "./lib/unified-diff/unified-diff-actions" ;
36
+ import { CodeGenieUnifiedDiffTool , UnifiedDiffTool } from "./lib/unified-diff/unified-diff-tool" ;
37
+ import { FixSuggestion } from "./lib/fix-suggestion" ;
37
38
38
39
39
40
// Object to hold the state of our extension for a specific activation context, to be returned by our activate function
@@ -99,7 +100,13 @@ export async function activate(context: vscode.ExtensionContext): Promise<SFCAEx
99
100
if ( ! vscode . window . activeTextEditor ) {
100
101
throw new Error ( messages . targeting . error . noFileSelected ) ;
101
102
}
102
- return codeAnalyzerRunner . runAndDisplay ( Constants . COMMAND_RUN_ON_ACTIVE_FILE , [ vscode . window . activeTextEditor . document . fileName ] ) ;
103
+
104
+ // Note that the active editor window could be the output window instead of the actual file editor, so we
105
+ // force focus it first to ensure we are getting the correct editor
106
+ await vscode . commands . executeCommand ( 'workbench.action.focusActiveEditorGroup' ) ;
107
+ const document : vscode . TextDocument = vscode . window . activeTextEditor . document ;
108
+
109
+ return codeAnalyzerRunner . runAndDisplay ( Constants . COMMAND_RUN_ON_ACTIVE_FILE , [ document . fileName ] ) ;
103
110
} ) ;
104
111
// ... also invoked by opening a file if the user has set things to do so.
105
112
onDidOpenTextDocument ( async ( textDocument : vscode . TextDocument ) => {
@@ -178,7 +185,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<SFCAEx
178
185
dfaRunner . clearSavedFilesCache ( ) ;
179
186
} ) ;
180
187
181
- onDidSaveTextDocument ( ( document : vscode . TextDocument ) => dfaRunner . addSavedFileToCache ( document . uri . fsPath ) ) ;
188
+ onDidSaveTextDocument ( ( document : vscode . TextDocument ) => dfaRunner . addSavedFileToCache ( document . fileName ) ) ;
182
189
183
190
184
191
// =================================================================================================================
@@ -219,67 +226,104 @@ export async function activate(context: vscode.ExtensionContext): Promise<SFCAEx
219
226
220
227
221
228
// =================================================================================================================
222
- // == Agentforce for Developers Integration and Unified Diff Functionality
229
+ // == Agentforce for Developers Integration
223
230
// =================================================================================================================
224
- const agentforceViolationsFixer = new AgentforceViolationsFixer ( externalServiceProvider , logger ) ;
225
- registerCodeActionsProvider ( { pattern : '**/*.cls' } , agentforceViolationsFixer ,
231
+ const agentforceCodeActionProvider : AgentforceCodeActionProvider = new AgentforceCodeActionProvider ( externalServiceProvider , logger ) ;
232
+ const agentforceViolationFixer : AgentforceViolationFixer = new AgentforceViolationFixer ( externalServiceProvider , logger ) ;
233
+ const unifiedDiffTool : UnifiedDiffTool < DiffHunk > = new CodeGenieUnifiedDiffTool ( ) ;
234
+ const unifiedDiffActions : UnifiedDiffActions < DiffHunk > = new UnifiedDiffActions < DiffHunk > ( unifiedDiffTool , telemetryService , logger ) ;
235
+
236
+ registerCodeActionsProvider ( { language : 'apex' } , agentforceCodeActionProvider ,
226
237
{ providedCodeActionKinds : [ vscode . CodeActionKind . QuickFix ] } ) ;
227
238
228
- registerCommand ( Constants . UNIFIED_DIFF , async ( source : string , code : string , file ?: string ) => {
229
- await ( new DiffCreateAction ( `${ source } .${ Constants . UNIFIED_DIFF } ` , {
230
- callback : ( code : string , file ?: string ) => VSCodeUnifiedDiff . singleton . unifiedDiff ( code , file ) ,
231
- telemetryService
232
- } ) ) . run ( code , file ) ;
233
- await VSCodeUnifiedDiff . singleton . unifiedDiff ( code , file ) ;
239
+ // Invoked by the "quick fix" buttons on A4D enabled diagnostics
240
+ registerCommand ( Constants . QF_COMMAND_A4D_FIX , async ( document : vscode . TextDocument , diagnostic : vscode . Diagnostic ) => {
241
+ const fixSuggestion : FixSuggestion = await agentforceViolationFixer . suggestFix ( document , diagnostic ) ;
242
+ if ( ! fixSuggestion ) {
243
+ return ;
244
+ }
245
+
246
+ diagnosticManager . clearDiagnostic ( document . uri , diagnostic ) ;
247
+
248
+ // TODO: We really need to either improve or replace the CodeGenie unified diff tool. Ideally, we would be
249
+ // passing the fixSuggestion to some sort of callback that when the diff is rejected, restore the diagnostic
250
+ // that we just removed that is associated with the fix but the CodeGenie diff tool doesn't allow us to do that.
251
+
252
+ // Display the diff with buttons that call through to the commands:
253
+ // CODEGENIE_UNIFIED_DIFF_ACCEPT, CODEGENIE_UNIFIED_DIFF_REJECT, CODEGENIE_UNIFIED_DIFF_ACCEPT_ALL, CODEGENIE_UNIFIED_DIFF_REJECT_ALL
254
+ const commandSource : string = Constants . QF_COMMAND_A4D_FIX ;
255
+ await unifiedDiffActions . createDiff ( commandSource , document , fixSuggestion . getFixedDocumentCode ( ) ) ;
256
+
257
+ // We should consider a better way to display the explanation associated with the fix.
258
+ if ( fixSuggestion . hasExplanation ( ) ) {
259
+ logger . log ( `Explanation of the fix supplied by Agentforce:\n${ indent ( fixSuggestion . getExplanation ( ) ) } ` ) ;
260
+ }
261
+
262
+ logger . trace ( `=== ORIGINAL CODE ===:\n${ fixSuggestion . getOriginalCodeToBeFixed ( ) } \n\n` +
263
+ `=== FIXED CODE ===:\n${ fixSuggestion . getFixedCode ( ) } ` ) ;
234
264
} ) ;
235
265
236
- registerCommand ( CODEGENIE_UNIFIED_DIFF_ACCEPT , async ( hunk : DiffHunk ) => {
237
- // TODO: The use of the prefix shouldn't be hardcoded. Ideally, it should be passed in as an argument to the command.
238
- // But that would require us to make changes to the underlying UnifiedDiff code that we're not currently in a position to make.
239
- await ( new DiffAcceptAction ( `${ Constants . A4D_PREFIX } .${ CODEGENIE_UNIFIED_DIFF_ACCEPT } ` , {
240
- callback : async ( diffHunk : DiffHunk ) => {
241
- await VSCodeUnifiedDiff . singleton . unifiedDiffAccept ( diffHunk ) ;
242
- return diffHunk . lines . length ;
243
- } ,
244
- telemetryService
245
- } ) ) . run ( hunk ) ;
246
- // For accept & accept all, it is tricky to track the diagnostics and the changed lines as multiple fixes are requested.
247
- // Hence, we save the file and rerun the scan instead.
248
- await vscode . window . activeTextEditor . document . save ( ) ;
249
- return codeAnalyzerRunner . runAndDisplay ( Constants . COMMAND_RUN_ON_ACTIVE_FILE , [ vscode . window . activeTextEditor . document . fileName ] ) ;
266
+
267
+ // =================================================================================================================
268
+ // == CodeGenie Unified Diff Integration
269
+ // =================================================================================================================
270
+
271
+ VSCodeUnifiedDiff . singleton . activate ( context ) ;
272
+
273
+ // Invoked by the "Accept" button on the CodeGenie Unified Diff tool
274
+ registerCommand ( CODEGENIE_UNIFIED_DIFF_ACCEPT , async ( diffHunk : DiffHunk ) => {
275
+ // Unfortunately the CodeGenie diff tool doesn't pass in the original source of the diff, so we hardcode
276
+ // this for now since A4D is the only source for the unified diff so far.
277
+ const commandSource : string = `${ Constants . QF_COMMAND_A4D_FIX } >${ CODEGENIE_UNIFIED_DIFF_ACCEPT } ` ;
278
+ // Also, CodeGenie diff tool does not pass in the document, and so we assume it is the active one since the user clicked the button.
279
+ const document : vscode . TextDocument = vscode . window . activeTextEditor . document ;
280
+
281
+ await unifiedDiffActions . acceptDiffHunk ( commandSource , document , diffHunk ) ;
250
282
} ) ;
251
283
252
- registerCommand ( CODEGENIE_UNIFIED_DIFF_REJECT , async ( hunk : DiffHunk ) => {
253
- // TODO: The use of the prefix shouldn't be hardcoded. Ideally, it should be passed in as an argument to the command.
254
- // But that would require us to make changes to the underlying UnifiedDiff code that we're not currently in a position to make.
255
- await ( new DiffRejectAction ( `${ Constants . A4D_PREFIX } .${ CODEGENIE_UNIFIED_DIFF_REJECT } ` , {
256
- callback : ( diffHunk : DiffHunk ) => VSCodeUnifiedDiff . singleton . unifiedDiffReject ( diffHunk ) ,
257
- telemetryService
258
- } ) ) . run ( hunk ) ;
284
+ // Invoked by the "Reject" button on the CodeGenie Unified Diff tool
285
+ registerCommand ( CODEGENIE_UNIFIED_DIFF_REJECT , async ( diffHunk : DiffHunk ) => {
286
+ // Unfortunately the CodeGenie diff tool doesn't pass in the original source of the diff, so we hardcode
287
+ // this for now since A4D is the only source for the unified diff so far.
288
+ const commandSource : string = `${ Constants . QF_COMMAND_A4D_FIX } >${ CODEGENIE_UNIFIED_DIFF_REJECT } ` ;
289
+ // Also, CodeGenie diff tool does not pass in the document, and so we assume it is the active one since the user clicked the button.
290
+ const document : vscode . TextDocument = vscode . window . activeTextEditor . document ;
291
+ await unifiedDiffActions . rejectDiffHunk ( commandSource , document , diffHunk ) ;
292
+
293
+ // Work Around: For reject & reject all, we really should be restoring the diagnostic that we removed
294
+ // but CodeGenie doesn't let us keep the diagnostic information around at this point. So instead we must
295
+ // rerun the scan instead to get the diagnostic restored.
296
+ await document . save ( ) ; // TODO: saving the document should be built in to the runAndDisplay command in my opinion
297
+ return codeAnalyzerRunner . runAndDisplay ( Constants . COMMAND_RUN_ON_ACTIVE_FILE , [ document . fileName ] ) ;
259
298
} ) ;
260
299
300
+ // Invoked by the "Accept All" button on the CodeGenie Unified Diff tool
261
301
registerCommand ( CODEGENIE_UNIFIED_DIFF_ACCEPT_ALL , async ( ) => {
262
- // TODO: The use of the prefix shouldn't be hardcoded. Ideally, it should be passed in as an argument to the command.
263
- // But that would require us to make changes to the underlying UnifiedDiff code that we're not currently in a position to make.
264
- await ( new DiffAcceptAction ( `${ Constants . A4D_PREFIX } .${ CODEGENIE_UNIFIED_DIFF_ACCEPT_ALL } ` , {
265
- callback : ( ) => VSCodeUnifiedDiff . singleton . unifiedDiffAcceptAll ( ) ,
266
- telemetryService
267
- } ) ) . run ( ) ;
268
- await vscode . window . activeTextEditor . document . save ( ) ;
269
- return codeAnalyzerRunner . runAndDisplay ( Constants . COMMAND_RUN_ON_ACTIVE_FILE , [ vscode . window . activeTextEditor . document . fileName ] ) ;
302
+ // Unfortunately the CodeGenie diff tool doesn't pass in the original source of the diff, so we hardcode
303
+ // this for now since A4D is the only source for the unified diff so far.
304
+ const commandSource : string = `${ Constants . QF_COMMAND_A4D_FIX } >${ CODEGENIE_UNIFIED_DIFF_ACCEPT_ALL } ` ;
305
+ // Also, CodeGenie diff tool does not pass in the document, and so we assume it is the active one since the user clicked the button.
306
+ const document : vscode . TextDocument = vscode . window . activeTextEditor . document ;
307
+
308
+ await unifiedDiffActions . acceptAll ( commandSource , document ) ;
270
309
} ) ;
271
310
311
+ // Invoked by the "Reject All" button on the CodeGenie Unified Diff tool
272
312
registerCommand ( CODEGENIE_UNIFIED_DIFF_REJECT_ALL , async ( ) => {
273
- // TODO: The use of the prefix shouldn't be hardcoded. Ideally, it should be passed in as an argument to the command.
274
- // But that would require us to make changes to the underlying UnifiedDiff code that we're not currently in a position to make.
275
- await ( new DiffRejectAction ( `${ Constants . A4D_PREFIX } .${ CODEGENIE_UNIFIED_DIFF_REJECT_ALL } ` , {
276
- callback : ( ) => VSCodeUnifiedDiff . singleton . unifiedDiffRejectAll ( ) ,
277
- telemetryService
278
- } ) ) . run ( ) ;
313
+ // Unfortunately the CodeGenie diff tool doesn't pass in the original source of the diff, so we hardcode
314
+ // this for now since A4D is the only source for the unified diff so far.
315
+ const commandSource : string = `${ Constants . QF_COMMAND_A4D_FIX } >${ CODEGENIE_UNIFIED_DIFF_REJECT_ALL } ` ;
316
+ // Also, CodeGenie diff tool does not pass in the document, and so we assume it is the active one since the user clicked the button.
317
+ const document : vscode . TextDocument = vscode . window . activeTextEditor . document ;
318
+ await unifiedDiffActions . rejectAll ( commandSource , document ) ;
319
+
320
+ // Work Around: For reject & reject all, we really should be restoring the diagnostic that we removed
321
+ // but CodeGenie doesn't let us keep the diagnostic information around at this point. So instead we must
322
+ // rerun the scan instead to get the diagnostic restored.
323
+ await document . save ( ) ; // TODO: saving the document should be built in to the runAndDisplay command in my opinion
324
+ return codeAnalyzerRunner . runAndDisplay ( Constants . COMMAND_RUN_ON_ACTIVE_FILE , [ document . fileName ] ) ;
279
325
} ) ;
280
326
281
- VSCodeUnifiedDiff . singleton . activate ( context ) ;
282
-
283
327
284
328
// =================================================================================================================
285
329
// == Finalize activation
@@ -317,3 +361,8 @@ async function establishVariableInContext(varUsedInPackageJson: string, getValue
317
361
await vscode . commands . executeCommand ( 'setContext' , varUsedInPackageJson , await getValueFcn ( ) ) ;
318
362
} ) ;
319
363
}
364
+
365
+ export function indent ( value : string , indentation = ' ' ) : string {
366
+ return indentation + value . replace ( / \n / g, `\n${ indentation } ` ) ;
367
+ }
368
+
0 commit comments