@@ -1460,15 +1460,207 @@ def generate_visual_studio_run_configs(self, configs):
14601460 with open (launch_path , "w" ) as f :
14611461 json .dump (launch_data , f , indent = 4 )
14621462
1463+ def generate_vscode_run_configs (self , configs ):
1464+ # Visual Studio launch configs are stored in .vs/launch.vs.json
1465+ vscode_dir = os .path .join (self .options .mrdocs_src_dir , ".vscode" )
1466+ os .makedirs (vscode_dir , exist_ok = True )
1467+ launch_path = os .path .join (vscode_dir , "launch.json" )
1468+ tasks_path = os .path .join (vscode_dir , "tasks.json" )
1469+
1470+ # Load existing configs if present
1471+ if os .path .exists (launch_path ):
1472+ with open (launch_path , "r" ) as f :
1473+ launch_data = json .load (f )
1474+ else :
1475+ launch_data = {"version" : "0.2.0" , "configurations" : []}
1476+
1477+ if os .path .exists (tasks_path ):
1478+ with open (tasks_path , "r" ) as f :
1479+ tasks_data = json .load (f )
1480+ else :
1481+ tasks_data = {"version" : "2.0.0" , "tasks" : []}
1482+
1483+ # Build a dict for quick lookup by name
1484+ vs_configs_by_name = {cfg .get ("name" ): cfg for cfg in launch_data .get ("configurations" , [])}
1485+ vs_tasks_by_name = {task .get ("label" ): task for task in tasks_data .get ("tasks" , [])}
1486+
1487+ # Replace with config placeholders
1488+ def replace_with_placeholders (new_config ):
1489+ for key , value in new_config .items ():
1490+ if isinstance (value , str ):
1491+ new_config [key ] = value .replace (self .options .mrdocs_src_dir , "${workspaceFolder}" )
1492+ elif isinstance (value , list ):
1493+ for i in range (len (value )):
1494+ if isinstance (value [i ], str ):
1495+ value [i ] = value [i ].replace (self .options .mrdocs_src_dir , "${workspaceFolder}" )
1496+ elif isinstance (value , dict ):
1497+ for sub_key , sub_value in value .items ():
1498+ if isinstance (sub_value , str ):
1499+ value [sub_key ] = sub_value .replace (self .options .mrdocs_src_dir , "${workspaceFolder}" )
1500+
1501+ bootstrap_refresh_config_name = self .options .mrdocs_preset_name or self .options .mrdocs_build_type or "debug"
1502+ for config in configs :
1503+ is_python_script = 'script' in config and config ['script' ].endswith ('.py' )
1504+ is_js_script = 'script' in config and config ['script' ].endswith ('.js' )
1505+ is_config = 'target' in config or is_python_script or is_js_script
1506+ if is_config :
1507+ new_cfg = {
1508+ "name" : config ["name" ],
1509+ "type" : None ,
1510+ "request" : "launch" ,
1511+ "program" : config .get ("script" , "" ) or config .get ("target" , "" ),
1512+ "args" : config ["args" ],
1513+ "cwd" : config .get ('cwd' , self .options .mrdocs_build_dir )
1514+ }
1515+
1516+ if 'target' in config :
1517+ # new_cfg["projectTarget"] = config["target"]
1518+ new_cfg ["name" ] += f" ({ bootstrap_refresh_config_name } )"
1519+ new_cfg ["type" ] = "cppdbg"
1520+ if 'program' in config :
1521+ new_cfg ["program" ] = config ["program" ]
1522+ else :
1523+ new_cfg ["program" ] = os .path .join (self .options .mrdocs_build_dir , config ["target" ])
1524+ new_cfg ["environment" ] = []
1525+ new_cfg ["stopAtEntry" ] = False
1526+ new_cfg ["externalConsole" ] = False
1527+ new_cfg ["preLaunchTask" ] = f"CMake Build { config ['target' ]} ({ bootstrap_refresh_config_name } )"
1528+ if self .compiler_info ["CMAKE_CXX_COMPILER_ID" ].lower () != "clang" :
1529+ lldb_path = shutil .which ("lldb" )
1530+ if lldb_path :
1531+ new_cfg ["MIMode" ] = "lldb"
1532+ else :
1533+ clang_path = self .compiler_info ["CMAKE_CXX_COMPILER" ]
1534+ if clang_path and os .path .exists (clang_path ):
1535+ lldb_path = os .path .join (os .path .dirname (clang_path ), "lldb" )
1536+ if os .path .exists (lldb_path ):
1537+ new_cfg ["MIMode" ] = "lldb"
1538+ elif self .compiler_info ["CMAKE_CXX_COMPILER_ID" ].lower () == "gcc" :
1539+ gdb_path = shutil .which ("gdb" )
1540+ if gdb_path :
1541+ new_cfg ["MIMode" ] = "gdb"
1542+ else :
1543+ gcc_path = self .compiler_info ["CMAKE_CXX_COMPILER" ]
1544+ if gcc_path and os .path .exists (gcc_path ):
1545+ gdb_path = os .path .join (os .path .dirname (gcc_path ), "gdb" )
1546+ if os .path .exists (gdb_path ):
1547+ new_cfg ["MIMode" ] = "gdb"
1548+ if 'script' in config :
1549+ new_cfg ["program" ] = config ["script" ]
1550+ # set type
1551+ if config ["script" ].endswith (".py" ):
1552+ new_cfg ["type" ] = "debugpy"
1553+ new_cfg ["console" ] = "integratedTerminal"
1554+ new_cfg ["stopOnEntry" ] = False
1555+ new_cfg ["justMyCode" ] = True
1556+ new_cfg ["env" ] = {}
1557+ elif config ["script" ].endswith (".js" ):
1558+ new_cfg ["type" ] = "node"
1559+ new_cfg ["console" ] = "integratedTerminal"
1560+ new_cfg ["internalConsoleOptions" ] = "neverOpen"
1561+ new_cfg ["skipFiles" ] = [
1562+ "<node_internals>/**"
1563+ ]
1564+ new_cfg ["sourceMaps" ] = True
1565+ new_cfg ["env" ] = {}
1566+ for key , value in config .get ("env" , {}).items ():
1567+ new_cfg ["env" ][key ] = value
1568+ else :
1569+ raise ValueError (
1570+ f"Unsupported script type for configuration '{ config ['name' ]} ': { config ['script' ]} . "
1571+ "Only Python (.py) and JavaScript (.js) scripts are supported."
1572+ )
1573+
1574+ # Any property that begins with the value of mrdocs_src_dir is replaced with ${workspaceFolder}
1575+ replace_with_placeholders (new_cfg )
1576+
1577+ # Replace or add
1578+ vs_configs_by_name [new_cfg ["name" ]] = new_cfg
1579+ else :
1580+ # This is a script configuration, we will create a task for it
1581+ new_task = {
1582+ "label" : config ["name" ],
1583+ "type" : "shell" ,
1584+ "command" : config ["script" ],
1585+ "args" : config ["args" ],
1586+ "options" : {},
1587+ "problemMatcher" : [],
1588+ }
1589+ if 'cwd' in config and config ["cwd" ] != self .options .mrdocs_src_dir :
1590+ new_task ["options" ]["cwd" ] = config ["cwd" ]
1591+
1592+ # Any property that begins with the value of mrdocs_src_dir is replaced with ${workspaceFolder}
1593+ replace_with_placeholders (new_task )
1594+
1595+ # Replace or add
1596+ vs_tasks_by_name [new_task ["label" ]] = new_task
1597+
1598+ # Create tasks for the cmake config and build steps
1599+ cmake_config_args = [
1600+ "-S" , "${workspaceFolder}"
1601+ ]
1602+ if self .options .mrdocs_preset_name :
1603+ cmake_config_args .extend (["--preset" , self .options .mrdocs_preset_name ])
1604+ else :
1605+ cmake_config_args .extend (["-B" , self .options .mrdocs_build_dir ])
1606+ if self .options .ninja_path :
1607+ cmake_config_args .extend (["-G" , "Ninja" ])
1608+ cmake_config_task = {
1609+ "label" : f"CMake Configure ({ bootstrap_refresh_config_name } )" ,
1610+ "type" : "shell" ,
1611+ "command" : "cmake" ,
1612+ "args" : cmake_config_args ,
1613+ "options" : {
1614+ "cwd" : "${workspaceFolder}"
1615+ }
1616+ }
1617+ replace_with_placeholders (cmake_config_task )
1618+ vs_tasks_by_name [cmake_config_task ["label" ]] = cmake_config_task
1619+
1620+ unique_targets = set ()
1621+ for config in configs :
1622+ if 'target' in config :
1623+ unique_targets .add (config ['target' ])
1624+ for target in unique_targets :
1625+ build_args = [
1626+ "--build" , self .options .mrdocs_build_dir ,
1627+ "--target" , target
1628+ ]
1629+ cmake_build_task = {
1630+ "label" : f"CMake Build { target } ({ bootstrap_refresh_config_name } )" ,
1631+ "type" : "shell" ,
1632+ "command" : "cmake" ,
1633+ "args" : build_args ,
1634+ "options" : {
1635+ "cwd" : "${workspaceFolder}"
1636+ },
1637+ "dependsOn" : f"CMake Configure ({ bootstrap_refresh_config_name } )" ,
1638+ "dependsOrder" : "sequence" ,
1639+ "group" : "build"
1640+ }
1641+ replace_with_placeholders (cmake_build_task )
1642+ vs_tasks_by_name [cmake_build_task ["label" ]] = cmake_build_task
1643+
1644+ # Write back all configs
1645+ launch_data ["configurations" ] = list (vs_configs_by_name .values ())
1646+ with open (launch_path , "w" ) as f :
1647+ json .dump (launch_data , f , indent = 4 )
1648+
1649+ tasks_data ["tasks" ] = list (vs_tasks_by_name .values ())
1650+ with open (tasks_path , "w" ) as f :
1651+ json .dump (tasks_data , f , indent = 4 )
1652+
14631653 def generate_run_configs (self ):
14641654 # Configurations using MrDocs executable
14651655 configs = [{
14661656 "name" : "MrDocs Version" ,
14671657 "target" : "mrdocs" ,
1658+ "program" : os .path .join (self .options .mrdocs_build_dir , "mrdocs" ),
14681659 "args" : ["--version" ]
14691660 }, {
14701661 "name" : "MrDocs Help" ,
14711662 "target" : "mrdocs" ,
1663+ "program" : os .path .join (self .options .mrdocs_build_dir , "mrdocs" ),
14721664 "args" : ["--help" ]
14731665 }]
14741666
@@ -1477,6 +1669,7 @@ def generate_run_configs(self):
14771669 configs .append ({
14781670 "name" : "MrDocs Unit Tests" ,
14791671 "target" : "mrdocs-test" ,
1672+ "program" : os .path .join (self .options .mrdocs_build_dir , "mrdocs-test" ),
14801673 "args" : [
14811674 '--unit=true'
14821675 ]
@@ -1488,6 +1681,7 @@ def generate_run_configs(self):
14881681 configs .append ({
14891682 "name" : f"MrDocs { verb .title ()} Test Fixtures ({ generator .upper ()} )" ,
14901683 "target" : "mrdocs-test" ,
1684+ "program" : os .path .join (self .options .mrdocs_build_dir , "mrdocs-test" ),
14911685 "folder" : "MrDocs Test Fixtures" ,
14921686 "args" : [
14931687 f'"{ self .options .mrdocs_src_dir } /test-files/golden-tests"' ,
@@ -1514,6 +1708,7 @@ def generate_run_configs(self):
15141708 configs .append ({
15151709 "name" : f"Boost.{ lib .title ()} Documentation" ,
15161710 "target" : "mrdocs" ,
1711+ "program" : os .path .join (self .options .mrdocs_build_dir , "mrdocs" ),
15171712 "args" : [
15181713 '../CMakeLists.txt' ,
15191714 f'--config={ self .options .boost_src_dir } /libs/{ lib } /doc/mrdocs.yml' ,
@@ -1537,6 +1732,7 @@ def generate_run_configs(self):
15371732 configs .append ({
15381733 "name" : f"MrDocs Self-Reference" ,
15391734 "target" : "mrdocs" ,
1735+ "program" : os .path .join (self .options .mrdocs_build_dir , "mrdocs" ),
15401736 "args" : [
15411737 '../CMakeLists.txt' ,
15421738 f'--config={ self .options .mrdocs_src_dir } /docs/mrdocs.yml' ,
@@ -1733,6 +1929,8 @@ def generate_run_configs(self):
17331929
17341930 print ("Generating CLion run configurations for MrDocs..." )
17351931 self .generate_clion_run_configs (configs )
1932+ print ("Generating Visual Studio Code run configurations for MrDocs..." )
1933+ self .generate_vscode_run_configs (configs )
17361934 print ("Generating Visual Studio run configurations for MrDocs..." )
17371935 self .generate_visual_studio_run_configs (configs )
17381936
0 commit comments