@@ -5,17 +5,36 @@ import { SymbolKind } from 'vscode';
5
5
import { Disposable } from 'vscode-jsonrpc' ;
6
6
import { ExecuteCommandRequest } from 'vscode-languageclient' ;
7
7
import { ExtensionState } from './ExtensionState' ;
8
- import { getOrAskForProgram } from './debugConfigProvider' ;
9
- import { adaExtState , mainOutputChannel } from './extension' ;
10
- import { getProjectFileRelPath } from './helpers' ;
8
+ import { AdaConfig , getOrAskForProgram , initializeConfig } from './debugConfigProvider' ;
9
+ import { adaExtState , logger , mainOutputChannel } from './extension' ;
10
+ import { findAdaMain , getProjectFileRelPath } from './helpers' ;
11
11
import {
12
12
CustomTaskDefinition ,
13
+ findBuildAndRunTask ,
13
14
getBuildAndRunTasks ,
14
15
getConventionalTaskLabel ,
15
16
getEnclosingSymbol ,
16
17
isFromWorkspace ,
17
18
} from './taskProviders' ;
18
19
20
+ /**
21
+ * Identifier for a hidden command used for building and running a project main.
22
+ * The command accepts a parameter which is the URI of the main source file.
23
+ * It is triggered by CodeLenses provided by the extension.
24
+ *
25
+ * @see {@link buildAndRunSpecifiedMain }
26
+ */
27
+ export const CMD_BUILD_AND_RUN_MAIN = 'ada.buildAndRunMain' ;
28
+
29
+ /**
30
+ * Identifier for a hidden command used for building and debugging a project main.
31
+ * The command accepts a parameter which is the URI of the main source file.
32
+ * It is triggered by CodeLenses provided by the extension.
33
+ *
34
+ * @see {@link buildAndDebugSpecifiedMain }
35
+ */
36
+ export const CMD_BUILD_AND_DEBUG_MAIN = 'ada.buildAndDebugMain' ;
37
+
19
38
export function registerCommands ( context : vscode . ExtensionContext , clients : ExtensionState ) {
20
39
context . subscriptions . push ( vscode . commands . registerCommand ( 'ada.otherFile' , otherFileHandler ) ) ;
21
40
context . subscriptions . push (
@@ -66,6 +85,17 @@ export function registerCommands(context: vscode.ExtensionContext, clients: Exte
66
85
}
67
86
)
68
87
) ;
88
+
89
+ /**
90
+ * The following commands are not defined in package.json and hence not
91
+ * exposed through the command palette but are called from CodeLenses.
92
+ */
93
+ context . subscriptions . push (
94
+ vscode . commands . registerCommand ( CMD_BUILD_AND_RUN_MAIN , buildAndRunSpecifiedMain )
95
+ ) ;
96
+ context . subscriptions . push (
97
+ vscode . commands . registerCommand ( CMD_BUILD_AND_DEBUG_MAIN , buildAndDebugSpecifiedMain )
98
+ ) ;
69
99
}
70
100
/**
71
101
* Add a subprogram box above the subprogram enclosing the cursor's position, if any.
@@ -439,3 +469,105 @@ export async function checkSrcDirectories(atStartup = false, displayYesNoPopup =
439
469
}
440
470
}
441
471
}
472
+
473
+ /*
474
+ * This is a command handler that builds and runs the main given as parameter.
475
+ * If the given URI does not match one of the project Mains an error is
476
+ * displayed.
477
+ *
478
+ * @param main - a URI of a document
479
+ */
480
+ async function buildAndRunSpecifiedMain ( main : vscode . Uri ) : Promise < void > {
481
+ const adaMain = await findAdaMain ( main . fsPath ) ;
482
+ if ( adaMain ) {
483
+ const task = await findBuildAndRunTask ( adaMain ) ;
484
+ if ( task ) {
485
+ lastUsedTaskInfo = { source : task . source , name : task . name } ;
486
+ await vscode . tasks . executeTask ( task ) ;
487
+ } else {
488
+ void vscode . window . showErrorMessage (
489
+ `Could not find the 'Build and Run' task for the project main ` +
490
+ `${ adaMain . mainRelPath ( ) } ` ,
491
+ { modal : true }
492
+ ) ;
493
+ }
494
+ } else {
495
+ void vscode . window . showErrorMessage (
496
+ `The document ${ vscode . workspace . asRelativePath ( main ) } does not match ` +
497
+ `any of the Mains of the project ${ await getProjectFileRelPath ( ) } ` ,
498
+ { modal : true }
499
+ ) ;
500
+ }
501
+ }
502
+
503
+ /**
504
+ * This is a command handler that builds the main given as parameter and starts
505
+ * a debug session on that main. If the given URI does not match one of the
506
+ * project Mains an error is displayed.
507
+ *
508
+ * @param main - a URI of a document
509
+ */
510
+ async function buildAndDebugSpecifiedMain ( main : vscode . Uri ) : Promise < void > {
511
+ function isMatchingConfig ( cfg : vscode . DebugConfiguration , configToMatch : AdaConfig ) : boolean {
512
+ return cfg . type == configToMatch . type && cfg . name == configToMatch . name ;
513
+ }
514
+
515
+ const wsFolder = vscode . workspace . getWorkspaceFolder ( main ) ;
516
+ const adaMain = await findAdaMain ( main . fsPath ) ;
517
+ if ( adaMain ) {
518
+ /**
519
+ * The vscode API doesn't provide a way to list both automatically
520
+ * provided and User-defined debug configurations. So instead, we
521
+ * inspect the launch.json data if it exists, and the dynamic configs
522
+ * provided by the exctension. We look for a debug config that matches
523
+ * the given main URI.
524
+ */
525
+ // Create a launch config for this main to help with matching
526
+ const configToMatch = initializeConfig ( adaMain ) ;
527
+ logger . debug ( 'Debug config to match:\n' + JSON . stringify ( configToMatch , null , 2 ) ) ;
528
+
529
+ let matchingConfig = undefined ;
530
+
531
+ {
532
+ // Find matching config in the list of workspace-defined launch configs
533
+ const configs : vscode . DebugConfiguration [ ] =
534
+ vscode . workspace . getConfiguration ( 'launch' ) . get ( 'configurations' ) ?? [ ] ;
535
+ logger . debug ( `Workspace debug configurations:\n${ JSON . stringify ( configs , null , 2 ) } ` ) ;
536
+ matchingConfig = configs . find ( ( cfg ) => isMatchingConfig ( cfg , configToMatch ) ) ;
537
+ }
538
+
539
+ if ( ! matchingConfig ) {
540
+ logger . debug ( 'Could not find matching config in workspace configs.' ) ;
541
+ // Look for a matching config among the configs dynamically provided by the extension
542
+ const dynamicConfigs =
543
+ await adaExtState . dynamicDebugConfigProvider . provideDebugConfigurations ( ) ;
544
+ logger . debug (
545
+ `Dynamic debug configurations:\n${ JSON . stringify ( dynamicConfigs , null , 2 ) } `
546
+ ) ;
547
+ matchingConfig = dynamicConfigs . find ( ( cfg ) => isMatchingConfig ( cfg , configToMatch ) ) ;
548
+ }
549
+
550
+ if ( matchingConfig ) {
551
+ logger . debug ( 'Found matching config: ' + JSON . stringify ( matchingConfig , null , 2 ) ) ;
552
+ const success = await vscode . debug . startDebugging ( wsFolder , matchingConfig ) ;
553
+ if ( ! success ) {
554
+ void vscode . window . showErrorMessage (
555
+ `Failed to start debug configuration: ${ matchingConfig . name } `
556
+ ) ;
557
+ }
558
+ } else {
559
+ logger . error ( 'Could not find matching config' ) ;
560
+ void vscode . window . showErrorMessage (
561
+ `Could not find a debug configuration for the project main ` +
562
+ `${ adaMain . mainRelPath ( ) } ` ,
563
+ { modal : true }
564
+ ) ;
565
+ }
566
+ } else {
567
+ void vscode . window . showErrorMessage (
568
+ `The document ${ vscode . workspace . asRelativePath ( main ) } does not match ` +
569
+ `any of the Mains of the project ${ await getProjectFileRelPath ( ) } ` ,
570
+ { modal : true }
571
+ ) ;
572
+ }
573
+ }
0 commit comments