@@ -14,7 +14,7 @@ import * as Telemetry from '../telemetry';
1414import { cppBuildTaskProvider , configPrefix } from '../LanguageServer/extension' ;
1515import * as logger from '../logger' ;
1616import * as nls from 'vscode-nls' ;
17- import { IConfiguration , IConfigurationSnippet , DebuggerType , DebuggerEvent , MIConfigurations , WindowsConfigurations , WSLConfigurations , PipeTransportConfigurations , TaskConfigStatus } from './configurations' ;
17+ import { IConfiguration , IConfigurationSnippet , DebuggerType , DebuggerEvent , MIConfigurations , WindowsConfigurations , WSLConfigurations , PipeTransportConfigurations } from './configurations' ;
1818import { parse } from 'comment-json' ;
1919import { PlatformInformation } from '../platform' ;
2020import { Environment , ParsedEnvironmentFile } from './ParsedEnvironmentFile' ;
@@ -26,6 +26,26 @@ function isDebugLaunchStr(str: string): boolean {
2626return str . startsWith ( "(gdb) " ) || str . startsWith ( "(lldb) " ) || str . startsWith ( "(Windows) " ) ;
2727}
2828
29+ interface MenuItem extends vscode . QuickPickItem {
30+ configuration :vscode . DebugConfiguration ;
31+ }
32+
33+ export enum TaskConfigStatus {
34+ recentlyUsed = "Recently Used Task" ,
35+ configured = "Configured Task" , // The tasks that are configured in tasks.json file.
36+ detected = "Detected Task" // The tasks that are available based on detected compilers.
37+ }
38+
39+ const localizeConfigs = ( items :MenuItem [ ] ) :MenuItem [ ] => {
40+ items . map ( ( item :MenuItem ) => {
41+ item . detail = ( item . detail === TaskConfigStatus . recentlyUsed ) ?localize ( "recently.used.task" , "Recently Used Task" ) :
42+ ( item . detail === TaskConfigStatus . configured ) ?localize ( "configured.task" , "Configured Task" ) :
43+ ( item . detail === TaskConfigStatus . detected ) ?localize ( "detected.task" , "Detected Task" ) :item . detail ;
44+
45+ } ) ;
46+ return items ;
47+ } ;
48+
2949/*
3050 * Retrieves configurations from a provider and displays them in a quickpick menu to be selected.
3151 * Ensures that the selected configuration's preLaunchTask (if existent) is populated in the user's task.json.
@@ -60,9 +80,6 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
6080if ( ! editor || ! util . isCppOrCFile ( editor . document . uri ) || configs . length <= 1 ) {
6181return [ defaultConfig ] ;
6282}
63- interface MenuItem extends vscode . QuickPickItem {
64- configuration :vscode . DebugConfiguration ;
65- }
6683
6784// Find the recently used task and place it at the top of quickpick list.
6885let recentlyUsedConfig :vscode . DebugConfiguration | undefined ;
@@ -92,7 +109,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
92109return menuItem ;
93110} ) ;
94111
95- const selection :MenuItem | undefined = await vscode . window . showQuickPick ( items , { placeHolder :localize ( "select.configuration" , "Select a configuration" ) } ) ;
112+ const selection :MenuItem | undefined = await vscode . window . showQuickPick ( localizeConfigs ( items ) , { placeHolder :localize ( "select.configuration" , "Select a configuration" ) } ) ;
96113if ( ! selection ) {
97114Telemetry . logDebuggerEvent ( DebuggerEvent . debugPanel , { "debugType" :"debug" , "folderMode" :folder ?"folder" :"singleFile" , "cancelled" :"true" } ) ;
98115return [ ] ; // User canceled it.
@@ -283,7 +300,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
283300
284301// Generate new configurations for each build task.
285302// Generating a task is async, therefore we must *await* *all* map(task => config) Promises to resolve.
286- const configs :vscode . DebugConfiguration [ ] = await Promise . all ( buildTasks . map < Promise < vscode . DebugConfiguration > > ( async task => {
303+ let configs :vscode . DebugConfiguration [ ] = await Promise . all ( buildTasks . map < Promise < vscode . DebugConfiguration > > ( async task => {
287304const definition :CppBuildTaskDefinition = task . definition as CppBuildTaskDefinition ;
288305const compilerPath :string = definition . command ;
289306const compilerName :string = path . basename ( compilerPath ) ;
@@ -297,12 +314,15 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
297314newConfig . console = "externalTerminal" ;
298315}
299316const isWindows :boolean = platformInfo . platform === 'win32' ;
300- const exeName :string = path . join ( "${fileDirname}" , "${fileBasenameNoExtension}" ) ;
301- newConfig . program = isWindows ?exeName + ".exe" :exeName ;
317+ // Extract the .exe path from the defined task.
318+ const definedExePath :string | undefined = util . findExePathInArgs ( task . definition . args ) ;
319+ newConfig . program = definedExePath ?definedExePath :util . defaultExePath ( ) ;
302320// Add the "detail" property to show the compiler path in QuickPickItem.
303321// This property will be removed before writing the DebugConfiguration in launch.json.
304322newConfig . detail = localize ( "pre.Launch.Task" , "preLaunchTask: {0}" , task . name ) ;
305- newConfig . existing = ( task . name === DebugConfigurationProvider . recentBuildTaskLabelStr ) ?TaskConfigStatus . recentlyUsed :( task . existing ?TaskConfigStatus . configured :TaskConfigStatus . detected ) ;
323+ newConfig . existing = ( task . name === DebugConfigurationProvider . recentBuildTaskLabelStr ) ?
324+ TaskConfigStatus . recentlyUsed :
325+ ( task . existing ?TaskConfigStatus . configured :TaskConfigStatus . detected ) ;
306326if ( task . isDefault ) {
307327newConfig . isDefault = true ;
308328}
@@ -355,6 +375,33 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
355375} ) ;
356376} ) ) ;
357377configs . push ( defaultConfig ) ;
378+ // Get existing debug configurations from launch.json.
379+ if ( folder ) {
380+ const existingConfigs :vscode . DebugConfiguration [ ] | undefined = ( await this . getLaunchConfigs ( folder , type ) ) ?. map ( config => ( {
381+ name :config . name ,
382+ type :config . type ,
383+ request :config . request ,
384+ detail :config . detail ?config . detail :
385+ config . preLaunchTask ?localize ( "pre.Launch.Task" , "preLaunchTask: {0}" , config . preLaunchTask ) :undefined ,
386+ existing :TaskConfigStatus . configured ,
387+ preLaunchTask :config . preLaunchTask
388+ } ) ) ;
389+ if ( existingConfigs ) {
390+ const areEqual = ( config1 :vscode . DebugConfiguration , config2 :vscode . DebugConfiguration ) :boolean =>
391+ ( config1 . preLaunchTask === config2 . preLaunchTask
392+ && config1 . type === config2 . type && config1 . request === config2 . request ) ;
393+ // Remove the detected configs that are already configured once in launch.json.
394+ const dedupExistingConfigs :vscode . DebugConfiguration [ ] = configs . filter ( detectedConfig => ! existingConfigs . some ( config => {
395+ if ( areEqual ( config , detectedConfig ) ) {
396+ // Carry the default task information.
397+ config . isDefault = detectedConfig . isDefault ?detectedConfig . isDefault :undefined ;
398+ return true ;
399+ }
400+ return false ;
401+ } ) ) ;
402+ configs = existingConfigs . concat ( dedupExistingConfigs ) ;
403+ }
404+ }
358405return configs ;
359406}
360407
@@ -510,12 +557,12 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
510557}
511558}
512559
513- public async getLaunchConfigs ( folder :vscode . WorkspaceFolder | undefined ) :Promise < vscode . WorkspaceConfiguration [ ] | undefined > {
560+ public async getLaunchConfigs ( folder :vscode . WorkspaceFolder | undefined , type : DebuggerType ) :Promise < vscode . WorkspaceConfiguration [ ] | undefined > {
514561let configs :vscode . WorkspaceConfiguration [ ] | undefined = vscode . workspace . getConfiguration ( 'launch' , folder ) . get ( 'configurations' ) ;
515562if ( ! configs ) {
516563return undefined ;
517564}
518- configs = configs . filter ( config => ( config . name && config . request === "launch" && ( config . type === DebuggerType . cppvsdbg || config . type === DebuggerType . cppdbg ) ) ) ;
565+ configs = configs . filter ( config => ( config . name && config . request === "launch" && config . type === type ) ) ;
519566return configs ;
520567}
521568
@@ -538,36 +585,14 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
538585configs = configs . concat ( await this . provideDebugConfigurationsForType ( DebuggerType . cppvsdbg , folder ) ) ;
539586}
540587
541- if ( folder ) {
542- // Get existing debug configurations from launch.json.
543- const existingConfigs :vscode . DebugConfiguration [ ] | undefined = ( await this . getLaunchConfigs ( folder ) ) ?. map ( config => ( {
544- name :config . name ,
545- type :config . type ,
546- request :config . request ,
547- detail :config . detail ?config . detail :localize ( "pre.Launch.Task" , "preLaunchTask: {0}" , config . preLaunchTask ) ,
548- preLaunchTask :config . preLaunchTask ,
549- existing :TaskConfigStatus . configured
550- } ) ) ;
551- if ( existingConfigs ) {
552- const areEqual = ( config1 :vscode . DebugConfiguration , config2 :vscode . DebugConfiguration ) :boolean =>
553- ( config1 . name === config2 . name && config1 . preLaunchTask === config2 . preLaunchTask
554- && config1 . type === config2 . type && config1 . request === config2 . request ) ;
555- // Remove the detected configs that are already configured once in launch.json.
556- const dedupExistingConfigs :vscode . DebugConfiguration [ ] = configs . filter ( detectedConfig => ! existingConfigs . some ( config => areEqual ( config , detectedConfig ) ) ) ;
557- configs = existingConfigs . concat ( dedupExistingConfigs ) ;
558- }
559- }
560-
561588const defaultConfig :vscode . DebugConfiguration [ ] = configs . filter ( ( config :vscode . DebugConfiguration ) => ( config . hasOwnProperty ( "isDefault" ) && config . isDefault ) ) ;
562- interface MenuItem extends vscode . QuickPickItem {
563- configuration :vscode . DebugConfiguration ;
564- }
565589
566590const items :MenuItem [ ] = configs . map < MenuItem > ( config => ( { label :config . name , configuration :config , description :config . detail , detail :config . existing } ) ) ;
567591
568592let selection :MenuItem | undefined ;
569593
570- if ( defaultConfig . length !== 0 ) {
594+ // if there was only one config for the default task, choose that config, otherwise ask the user to choose.
595+ if ( defaultConfig . length === 1 ) {
571596selection = { label :defaultConfig [ 0 ] . name , configuration :defaultConfig [ 0 ] , description :defaultConfig [ 0 ] . detail , detail :defaultConfig [ 0 ] . existing } ;
572597} else {
573598let sortedItems :MenuItem [ ] = [ ] ;
@@ -579,7 +604,8 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
579604}
580605sortedItems = sortedItems . concat ( items . filter ( item => item . detail === TaskConfigStatus . configured ) ) ;
581606sortedItems = sortedItems . concat ( items . filter ( item => item . detail === TaskConfigStatus . detected ) ) ;
582- selection = await vscode . window . showQuickPick ( sortedItems , {
607+
608+ selection = await vscode . window . showQuickPick ( localizeConfigs ( sortedItems ) , {
583609placeHolder :( items . length === 0 ?localize ( "no.compiler.found" , "No compiler found" ) :localize ( "select.debug.configuration" , "Select a debug configuration" ) )
584610} ) ;
585611}