@@ -17,6 +17,7 @@ import { IConfiguration, IConfigurationSnippet, DebuggerType, DebuggerEvent, MIC
1717import { parse } from 'comment-json' ;
1818import { PlatformInformation } from '../platform' ;
1919import { Environment , ParsedEnvironmentFile } from './ParsedEnvironmentFile' ;
20+ import { CppSettings } from '../LanguageServer/settings' ;
2021import { configPrefix } from '../LanguageServer/extension' ;
2122
2223nls . config ( { messageFormat : nls . MessageFormat . bundle , bundleFormat : nls . BundleFormat . standalone } ) ( ) ;
@@ -107,7 +108,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
107108
108109 const items : MenuItem [ ] = configs . map < MenuItem > ( config => {
109110 const reducedConfig : vscode . DebugConfiguration = { ...config } ;
110- // Remove the "detail" property from the DebugConfiguration that will be written in launch.json .
111+ // Remove the extra properties that are not a part of the DebugConfiguration .
111112 reducedConfig . detail = undefined ;
112113 reducedConfig . existing = undefined ;
113114 reducedConfig . isDefault = undefined ;
@@ -129,11 +130,6 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
129130 this . showErrorIfClNotAvailable ( selection . label ) ;
130131 }
131132
132- await this . resolvePreLaunchTask ( folder , configs [ 0 ] , DebuggerEvent . debugPanel ) ;
133- if ( ! folder ) {
134- // In case of singleFile, remove the preLaunch task.
135- selection . configuration . preLaunchTask = undefined ;
136- }
137133 return [ selection . configuration ] ;
138134 }
139135
@@ -143,43 +139,38 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
143139 * If return "null", the debugging will be aborted and launch.json will be opened.
144140 * resolveDebugConfigurationWithSubstitutedVariables will be automatically called after this function.
145141 */
146- resolveDebugConfiguration ( folder : vscode . WorkspaceFolder | undefined , config : vscode . DebugConfiguration , token ?: vscode . CancellationToken ) : vscode . ProviderResult < vscode . DebugConfiguration > {
142+ async resolveDebugConfiguration ( folder : vscode . WorkspaceFolder | undefined , config : vscode . DebugConfiguration , token ?: vscode . CancellationToken ) : Promise < vscode . DebugConfiguration | null | undefined > {
143+ const isIntelliSenseDisabled : boolean = new CppSettings ( ( vscode . workspace . workspaceFolders && vscode . workspace . workspaceFolders . length > 0 ) ? vscode . workspace . workspaceFolders [ 0 ] ?. uri : undefined ) . intelliSenseEngine === "Disabled" ;
147144 if ( ! config || ! config . type ) {
148145 // When DebugConfigurationProviderTriggerKind is Dynamic, this function will be called with an empty config.
149146 // Hence, providing debug configs, and start debugging should be done manually.
150147 // resolveDebugConfiguration will be automatically called after calling provideDebugConfigurations.
151- this . provideDebugConfigurations ( folder ) . then ( async configs => {
152- if ( ! configs || configs . length === 0 ) {
153- Telemetry . logDebuggerEvent ( DebuggerEvent . debugPanel , { "debugType" : "debug" , "folderMode" : folder ? "folder" : "singleFile" , "cancelled" : "true" } ) ;
154- return undefined ; // aborts debugging silently
155- } else {
156- // Currently, we expect only one debug config to be selected.
157- console . assert ( configs . length === 1 , "More than one debug config is selected." ) ;
158- // await this.resolvePreLaunchTask(folder, configs[0], DebuggerEvent.debugPanel);
159- // await this.startDebugging(folder, configs[0], DebuggerEvent.debugPanel);
160- return configs [ 0 ] ;
161- }
162- } ) ;
163- } else {
164- if ( config . type === 'cppvsdbg' ) {
165- // Handle legacy 'externalConsole' bool and convert to console: "externalTerminal"
166- if ( config . hasOwnProperty ( "externalConsole" ) ) {
167- logger . getOutputChannelLogger ( ) . showWarningMessage ( localize ( "debugger.deprecated.config" , "The key '{0}' is deprecated. Please use '{1}' instead." , "externalConsole" , "console" ) ) ;
168- if ( config . externalConsole && ! config . console ) {
169- config . console = "externalTerminal" ;
170- }
171- delete config . externalConsole ;
172- }
173-
174- // Fail if cppvsdbg type is running on non-Windows
175- if ( os . platform ( ) !== 'win32' ) {
176- logger . getOutputChannelLogger ( ) . showWarningMessage ( localize ( "debugger.not.available" , "Debugger of type: '{0}' is only available on Windows. Use type: '{1}' on the current OS platform." , "cppvsdbg" , "cppdbg" ) ) ;
177- return undefined ; // Stop debugging
178- }
148+ const configs : vscode . DebugConfiguration [ ] = await this . provideDebugConfigurations ( folder ) ;
149+ if ( ! configs || configs . length === 0 ) {
150+ Telemetry . logDebuggerEvent ( DebuggerEvent . debugPanel , { "debugType" : "debug" , "folderMode" : folder ? "folder" : "singleFile" , "cancelled" : "true" } ) ;
151+ return undefined ; // aborts debugging silently
152+ } else {
153+ // Currently, we expect only one debug config to be selected.
154+ console . assert ( configs . length === 1 , "More than one debug config is selected." ) ;
155+ config = configs [ 0 ] ;
156+ // Keep track of the entry point where the debug config has been selected, for telemetry purposes.
157+ config . debuggerEvent = DebuggerEvent . debugPanel ;
179158 }
180- // resolveDebugConfigurationWithSubstitutedVariables will be automatically called after this return.
181- return config ;
182159 }
160+ if ( ( ! folder || isIntelliSenseDisabled ) && config . preLaunchTask ) {
161+ /* There are two cases where folder is undefined:
162+ * when debugging is done on a single file where there is no folder open,
163+ * or when the debug configuration is defined at the User level.
164+ */
165+ await this . resolvePreLaunchTask ( undefined , config ) ;
166+ config . preLaunchTask = undefined ;
167+ } else {
168+ await this . resolvePreLaunchTask ( folder , config ) ;
169+ }
170+ await this . sendDebugTelemetry ( folder , config ) ;
171+
172+ // resolveDebugConfigurationWithSubstitutedVariables will be automatically called after this return.
173+ return config ;
183174 }
184175
185176 /**
@@ -192,10 +183,25 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
192183 */
193184 resolveDebugConfigurationWithSubstitutedVariables ( folder : vscode . WorkspaceFolder | undefined , config : vscode . DebugConfiguration , token ?: vscode . CancellationToken ) : vscode . ProviderResult < vscode . DebugConfiguration > {
194185 if ( ! config || ! config . type ) {
195- return undefined ;
186+ return undefined ; // Abort debugging silently.
196187 }
197188
198189 if ( config . type === 'cppvsdbg' ) {
190+ // Fail if cppvsdbg type is running on non-Windows
191+ if ( os . platform ( ) !== 'win32' ) {
192+ logger . getOutputChannelLogger ( ) . showWarningMessage ( localize ( "debugger.not.available" , "Debugger of type: '{0}' is only available on Windows. Use type: '{1}' on the current OS platform." , "cppvsdbg" , "cppdbg" ) ) ;
193+ return undefined ; // Abort debugging silently.
194+ }
195+
196+ // Handle legacy 'externalConsole' bool and convert to console: "externalTerminal"
197+ if ( config . hasOwnProperty ( "externalConsole" ) ) {
198+ logger . getOutputChannelLogger ( ) . showWarningMessage ( localize ( "debugger.deprecated.config" , "The key '{0}' is deprecated. Please use '{1}' instead." , "externalConsole" , "console" ) ) ;
199+ if ( config . externalConsole && ! config . console ) {
200+ config . console = "externalTerminal" ;
201+ }
202+ delete config . externalConsole ;
203+ }
204+
199205 // Disable debug heap by default, enable if 'enableDebugHeap' is set.
200206 if ( ! config . enableDebugHeap ) {
201207 const disableDebugHeapEnvSetting : Environment = { "name" : "_NO_DEBUG_HEAP" , "value" : "1" } ;
@@ -392,32 +398,24 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
392398 } ) ;
393399 } ) ) ;
394400 configs . push ( defaultTemplateConfig ) ;
395- // Get existing debug configurations from launch.json.
396- if ( folder ) {
397- const existingConfigs : vscode . DebugConfiguration [ ] | undefined = ( await this . getLaunchConfigs ( folder , type ) ) ?. map ( config => ( {
398- name : config . name ,
399- type : config . type ,
400- request : config . request ,
401- detail : config . detail ? config . detail :
402- config . preLaunchTask ? localize ( "pre.Launch.Task" , "preLaunchTask: {0}" , config . preLaunchTask ) : undefined ,
403- existing : TaskConfigStatus . configured ,
404- preLaunchTask : config . preLaunchTask
405- } ) ) ;
406- if ( existingConfigs ) {
407- const areEqual = ( config1 : vscode . DebugConfiguration , config2 : vscode . DebugConfiguration ) : boolean =>
408- ( config1 . preLaunchTask === config2 . preLaunchTask
409- && config1 . type === config2 . type && config1 . request === config2 . request ) ;
410- // Remove the detected configs that are already configured once in launch.json.
411- const dedupExistingConfigs : vscode . DebugConfiguration [ ] = configs . filter ( detectedConfig => ! existingConfigs . some ( config => {
412- if ( areEqual ( config , detectedConfig ) ) {
413- // Carry the default task information.
414- config . isDefault = detectedConfig . isDefault ? detectedConfig . isDefault : undefined ;
415- return true ;
416- }
417- return false ;
418- } ) ) ;
419- configs = existingConfigs . concat ( dedupExistingConfigs ) ;
401+ const existingConfigs : vscode . DebugConfiguration [ ] | undefined = this . getLaunchConfigs ( folder , type ) ?. map ( config => {
402+ if ( ! config . detail && config . preLaunchTask ) {
403+ config . detail = localize ( "pre.Launch.Task" , "preLaunchTask: {0}" , config . preLaunchTask ) ;
420404 }
405+ config . existing = TaskConfigStatus . configured ;
406+ return config ;
407+ } ) ;
408+ if ( existingConfigs ) {
409+ // Remove the detected configs that are already configured once in launch.json.
410+ const dedupExistingConfigs : vscode . DebugConfiguration [ ] = configs . filter ( detectedConfig => ! existingConfigs . some ( config => {
411+ if ( config . preLaunchTask === detectedConfig . preLaunchTask && config . type === detectedConfig . type && config . request === detectedConfig . request ) {
412+ // Carry the default task information.
413+ config . isDefault = detectedConfig . isDefault ? detectedConfig . isDefault : undefined ;
414+ return true ;
415+ }
416+ return false ;
417+ } ) ) ;
418+ configs = existingConfigs . concat ( dedupExistingConfigs ) ;
421419 }
422420 return configs ;
423421 }
@@ -574,15 +572,27 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
574572 }
575573 }
576574
577- public async getLaunchConfigs ( folder : vscode . WorkspaceFolder | undefined , type : DebuggerType ) : Promise < vscode . WorkspaceConfiguration [ ] | undefined > {
578- let configs : vscode . WorkspaceConfiguration [ ] | undefined = vscode . workspace . getConfiguration ( 'launch' , folder ) . get ( 'configurations' ) ;
575+ public getLaunchConfigs ( folder ?: vscode . WorkspaceFolder , type ?: DebuggerType ) : vscode . DebugConfiguration [ ] | undefined {
576+ // Get existing debug configurations from launch.json or user/workspace "launch" settings.
577+ let configs : any = vscode . workspace . getConfiguration ( 'launch' , folder ) . get ( 'configurations' ) ;
579578 if ( ! configs ) {
580579 return undefined ;
581580 }
582- configs = configs . filter ( config => ( config . name && config . request === "launch" && config . type === type ) ) ;
581+ configs = configs . filter ( ( config : any ) => ( config . name && config . request === "launch" && type ? ( config . type === type ) : true ) ) ;
583582 return configs ;
584583 }
585584
585+ public checkDebugConfigExists ( configName : string , folder ?: vscode . WorkspaceFolder , type ?: DebuggerType ) : boolean {
586+ const configs : vscode . DebugConfiguration [ ] | undefined = this . getLaunchConfigs ( folder , type ) ;
587+ if ( configs && configs . length > 0 ) {
588+ const selectedConfig : any | undefined = configs . find ( ( config : any ) => config . name && config . name === configName ) ;
589+ if ( selectedConfig ) {
590+ return true ;
591+ }
592+ }
593+ return false ;
594+ }
595+
586596 public async buildAndRun ( textEditor : vscode . TextEditor ) : Promise < void > {
587597 // Turn off the debug mode.
588598 return this . buildAndDebug ( textEditor , false ) ;
@@ -627,33 +637,28 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
627637 } ) ;
628638 }
629639
630- const debuggerEvent : string = DebuggerEvent . launchPlayButton ;
631640 if ( ! selection ) {
632- Telemetry . logDebuggerEvent ( debuggerEvent , { "debugType" : debugModeOn ? "debug" : "run" , "folderMode" : folder ? "folder" : "singleFile" , "cancelled" : "true" } ) ;
641+ Telemetry . logDebuggerEvent ( DebuggerEvent . launchPlayButton , { "debugType" : debugModeOn ? "debug" : "run" , "folderMode" : folder ? "folder" : "singleFile" , "cancelled" : "true" } ) ;
633642 return ; // User canceled it.
634643 }
635644
636645 if ( this . isClConfiguration ( selection . label ) && this . showErrorIfClNotAvailable ( selection . label ) ) {
637646 return ;
638647 }
639648
640- // Resolve config before start debugging.
641- let resolvedConfig : vscode . DebugConfiguration | undefined | null ;
642- if ( selection . configuration && selection . configuration . type ) {
643- resolvedConfig = await this . resolveDebugConfiguration ( folder , selection . configuration ) ;
644- if ( resolvedConfig ) {
645- resolvedConfig = await this . resolveDebugConfigurationWithSubstitutedVariables ( folder , resolvedConfig ) ;
646- }
647- }
648- if ( resolvedConfig ) {
649- await this . resolvePreLaunchTask ( folder , resolvedConfig , debuggerEvent , debugModeOn ) ;
650- await this . startDebugging ( folder , resolvedConfig , debuggerEvent , debugModeOn ) ;
651- }
649+ // Keep track of the entry point where the debug has been selected, for telemetry purposes.
650+ selection . configuration . debuggerEvent = DebuggerEvent . launchPlayButton ;
651+ // startDebugging will trigger a call to resolveDebugConfiguration.
652+ await vscode . debug . startDebugging ( folder , selection . configuration , { noDebug : ! debugModeOn } ) ;
653+
652654 }
653655
654- private async resolvePreLaunchTask ( folder : vscode . WorkspaceFolder | undefined , configuration : vscode . DebugConfiguration , debuggerEvent : string , debugModeOn : boolean = true ) : Promise < void > {
656+ private async resolvePreLaunchTask ( folder : vscode . WorkspaceFolder | undefined , configuration : vscode . DebugConfiguration , debugModeOn : boolean = true ) : Promise < void > {
655657 const debugType : string = debugModeOn ? "debug" : "run" ;
656658 const folderMode : string = folder ? "folder" : "singleFile" ;
659+ // if configuration.debuggerEvent === undefined, it means this configuration is already defined in launch.json and is shown in debugPanel.
660+ // All the other configs that are selected by user, have a debuggerEvent element.
661+ const debuggerEvent : string = configuration . debuggerEvent ? configuration . debuggerEvent : DebuggerEvent . debugPanel ;
657662 if ( configuration . preLaunchTask ) {
658663 try {
659664 if ( folder ) {
@@ -673,31 +678,14 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
673678 }
674679 }
675680
676- private async startDebugging ( folder : vscode . WorkspaceFolder | undefined , configuration : vscode . DebugConfiguration , debuggerEvent : string , debugModeOn : boolean = true ) : Promise < void > {
681+ private async sendDebugTelemetry ( folder : vscode . WorkspaceFolder | undefined , configuration : vscode . DebugConfiguration , debugModeOn : boolean = true ) : Promise < void > {
677682 const debugType : string = debugModeOn ? "debug" : "run" ;
678683 const folderMode : string = folder ? "folder" : "singleFile" ;
679- if ( ! folder ) {
680- // In case of singleFile, remove the preLaunch task.
681- configuration . preLaunchTask = undefined ;
682- }
683- try {
684- // Check if the debug configuration exists in launch.json.
685- await cppBuildTaskProvider . checkDebugConfigExists ( configuration . name ) ;
686- try {
687- await vscode . debug . startDebugging ( folder , configuration . name , { noDebug : ! debugModeOn } ) ;
688- Telemetry . logDebuggerEvent ( debuggerEvent , { "debugType" : debugType , "folderMode" : folderMode , "config" : "launchConfig" , "success" : "true" } ) ;
689- } catch ( e ) {
690- Telemetry . logDebuggerEvent ( debuggerEvent , { "debugType" : debugType , "folderMode" : folderMode , "config" : "launchConfig" , "success" : "false" } ) ;
691- }
692- } catch ( e ) {
693- try {
694- await vscode . debug . startDebugging ( folder , configuration , { noDebug : ! debugModeOn } ) ;
695- Telemetry . logDebuggerEvent ( debuggerEvent , { "debugType" : debugType , "folderMode" : folderMode , "config" : "noLaunchConfig" , "success" : "true" } ) ;
696- } catch ( e ) {
697- Telemetry . logDebuggerEvent ( debuggerEvent , { "debugType" : debugType , "folderMode" : folderMode , "config" : "noLaunchConfig" , "success" : "false" } ) ;
698- }
699- }
684+ const configMode : string = this . checkDebugConfigExists ( configuration . name ) ? "launchConfig" : "noLaunchConfig" ;
685+ const debuggerEvent : string = configuration . debuggerEvent ? configuration . debuggerEvent : "unknownDebuggerEvent" ;
686+ Telemetry . logDebuggerEvent ( debuggerEvent , { "debugType" : debugType , "folderMode" : folderMode , "config" : configMode } ) ;
700687 }
688+
701689}
702690
703691export interface IConfigurationAssetProvider {
0 commit comments