From 4aa7b059fe53582ec4349e056aafbf111ece6a89 Mon Sep 17 00:00:00 2001 From: maxu Date: Sat, 18 Nov 2017 18:44:54 +0800 Subject: [PATCH 1/9] . --- .editorconfig | 2 +- .vscode/tasks.json | 4 +- LICENSE.md | 16 --- README.md | 91 ------------------ package.json | 3 +- src/debug.ts | 56 +++++------ src/extension.ts | 235 ++++++++++++++++++++++++--------------------- 7 files changed, 158 insertions(+), 249 deletions(-) delete mode 100644 LICENSE.md delete mode 100644 README.md diff --git a/.editorconfig b/.editorconfig index 16f3fce4..51d91d0e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,5 +3,5 @@ root = true [*] end_of_line = lf indent_style = space -indent_size = 2 +indent_size = 4 insert_final_newline = true diff --git a/.vscode/tasks.json b/.vscode/tasks.json index fb7f662e..83fa44da 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -23,8 +23,8 @@ "args": ["run", "compile", "--loglevel", "silent"], // The tsc compiler is started in watching mode - "isWatching": true, + "isBackground": true, // use the standard tsc in watch mode problem matcher to find compile problems in the output. "problemMatcher": "$tsc-watch" -} \ No newline at end of file +} diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index fea25e5b..00000000 --- a/LICENSE.md +++ /dev/null @@ -1,16 +0,0 @@ -# MIT License - -Copyright © 2016 Andrew Short - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit -persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the -Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 23475729..00000000 --- a/README.md +++ /dev/null @@ -1,91 +0,0 @@ -# ROS VSCode Extension - -This [Visual Studio Code (VSCode)][vscode] extension provides support for [Robot Operating System (ROS)][ros] -development. - -* [Repository][repo] -* [Issues][issues] - -## Getting Started - -The extension will automatically start when you open a catkin workspace. -The build system (e.g. catkin_make or catkin build) will automatically be confirmed from the hidden files associated with -each system. -The ROS distro will automatically be confirmed from the parent environment, or you will be prompted to select a ROS -distro if this can't be done automatically. - -> You must build the catkin workspace at least once before the extension will recognise it. - -To start ROS master, use the "ROS: Start Core" command. The "ROS master" indicator in the bottom left will show if the -master is currently running, and you can click on this to view parameters etc. If you hit F5 you can create a debug -configuration to run a `rosrun` or `roslaunch` command. - -The first time you open the workspace the extension will automatically create build and test tasks and update the -C++ and Python paths. You can re-run this process later using the appropriate commands. - -## Features - -* Automatic ROS environment configuration. -* Allows starting, stopping and viewing the ROS master status. -* Automatically discover `catkin_make` or `catkin build` build tasks. -* Create catkin packages using `catkin_create_pkg` script or `catkin create pkg`. -* Run `rosrun` or `roslaunch` (breakpoints currently not supported). -* Syntax highlighting for `.msg`, `.urdf` and other ROS files. -* Automatically add the ROS C++ include and Python import paths. -* Format C++ using the ROS `clang-format` style. - -## Commands - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameCommandDescription
Create Catkin Packageros.createCatkinPackage - Create a catkin package. You can right click on a folder in the explorer to create it in a specific location. -
Create Terminalros.createTerminalCreate a terminal with ROS sourced.
Show Master Statusros.showMasterStatusOpen a detail view showing details about the ROS master.
Start Coreros.startCoreSpawn a ROS core
Stop Coreros.stopCoreKill the ROS core
Update C++ Propertiesros.updateCppPropertiesUpdate the C++ include path to include ROS.
Update Python Pathros.updatePythonPathUpdate the Python path to include ROS.
- -[issues]: https://github.com/ajshort/vscode-ros/issues -[repo]: https://github.com/ajshort/vscode-ros -[ros]: http://ros.org -[vscode]: https://code.visualstudio.com diff --git a/package.json b/package.json index 3f1b91aa..f241d5b7 100644 --- a/package.json +++ b/package.json @@ -142,8 +142,7 @@ "debugSettings": "${command:debugSettings}" } } - ], - "initialConfigurations": "ros.provideInitialConfigurations" + ] } ], "languages": [ diff --git a/src/debug.ts b/src/debug.ts index df2323ed..46f07cd5 100644 --- a/src/debug.ts +++ b/src/debug.ts @@ -3,49 +3,45 @@ import * as utils from "./utils"; import { basename } from "path"; import * as vscode from "vscode"; +import { WorkspaceFolder, DebugConfiguration, ProviderResult, CancellationToken } from 'vscode'; + /** * Gets stringified settings to pass to the debug server. */ export async function getDebugSettings() { - return JSON.stringify({ env: extension.env }); + return JSON.stringify({ env: extension.env }); } /** * Interacts with the user to create initial configurations. */ -export async function provideInitialConfigurations() { - const packages = utils.getPackages(); +export async function provideInitialConfigurations(config: DebugConfiguration) { + const packages = utils.getPackages(); - const command = await vscode.window.showQuickPick(["roslaunch", "rosrun"], { placeHolder: "Launch command" }); - const packageName = await vscode.window.showQuickPick(packages.then(Object.keys), { placeHolder: "Package" }); + const command = await vscode.window.showQuickPick(["roslaunch", "rosrun"], { placeHolder: "Launch command" }); + const packageName = await vscode.window.showQuickPick(packages.then(Object.keys), { placeHolder: "Package" }); - let target: string; + let target: string; - if (packageName) { - let basenames = (files: string[]) => files.map(file => basename(file)); + if (packageName) { + let basenames = (files: string[]) => files.map(file => basename(file)); - if (command === "roslaunch") { - const launches = utils.findPackageLaunchFiles(packageName).then(basenames); - target = await vscode.window.showQuickPick(launches, { placeHolder: "Launch file" }); + if (command === "roslaunch") { + const launches = utils.findPackageLaunchFiles(packageName).then(basenames); + target = await vscode.window.showQuickPick(launches, { placeHolder: "Launch file" }); + } else { + const executables = utils.findPackageExecutables(packageName).then(basenames); + target = await vscode.window.showQuickPick(executables, { placeHolder: "Executable" }); + } } else { - const executables = utils.findPackageExecutables(packageName).then(basenames); - target = await vscode.window.showQuickPick(executables, { placeHolder: "Executable" }); + target = await vscode.window.showInputBox({ placeHolder: "Target" }); } - } else { - target = await vscode.window.showInputBox({ placeHolder: "Target" }); - } - - return JSON.stringify({ - configurations: [ - { - command, - debugSettings: "${command:debugSettings}", - name: target, - package: packageName, - type: "ros", - target, - }, - ], - version: "0.1.0", - }, undefined, 2); + + config.name = target; + config.type = "ros"; + config.debugSettings = "${command:debugSettings}"; + config.package = packageName; + config.target = target; + config.command = command; + } diff --git a/src/extension.ts b/src/extension.ts index a05331d4..801d8984 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -10,6 +10,8 @@ import * as utils from "./utils"; import { dirname } from "path"; import * as vscode from "vscode"; +import { WorkspaceFolder, DebugConfiguration, ProviderResult, CancellationToken } from 'vscode'; + let context: vscode.ExtensionContext; /** @@ -42,51 +44,54 @@ export let onDidChangeEnv = onEnvChanged.event; let subscriptions = []; export async function activate(ctx: vscode.ExtensionContext) { - // Activate if we're in a catkin workspace. - context = ctx; + // Activate if we're in a catkin workspace. + context = ctx; - await determineBuildSystem(vscode.workspace.rootPath); + await determineBuildSystem(vscode.workspace.rootPath); - if (buildSystem == BuildSystem.None) { - return; - } + if (buildSystem == BuildSystem.None) { + return; + } - console.log(`Activating ROS extension in "${baseDir}"`); + console.log(`Activating ROS extension in "${baseDir}"`); - // Activate components when the ROS env is changed. - context.subscriptions.push(onDidChangeEnv(activateEnvironment)); + // register a configuration provider + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('ros', new RosConfigurationProvider())); - // Activate components which don't require the ROS env. - context.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider( - "cpp", new CppFormatter() - )); + // Activate components when the ROS env is changed. + context.subscriptions.push(onDidChangeEnv(activateEnvironment)); - // Source the environment, and re-source on config change. - let config = utils.getConfig(); + // Activate components which don't require the ROS env. + context.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider( + "cpp", new CppFormatter() + )); - context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => { - const updatedConfig = utils.getConfig(); - const fields = Object.keys(config).filter(k => !(config[k] instanceof Function)); - const changed = fields.some(key => updatedConfig[key] !== config[key]); + // Source the environment, and re-source on config change. + let config = utils.getConfig(); - if (changed) { - sourceRosAndWorkspace(); - } + context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => { + const updatedConfig = utils.getConfig(); + const fields = Object.keys(config).filter(k => !(config[k] instanceof Function)); + const changed = fields.some(key => updatedConfig[key] !== config[key]); - config = updatedConfig; - })); + if (changed) { + sourceRosAndWorkspace(); + } - sourceRosAndWorkspace(); + config = updatedConfig; + })); - return { - getBaseDir: () => baseDir, - getEnv: () => env, - onDidChangeEnv: (listener: () => any, thisArg: any) => onDidChangeEnv(listener, thisArg), - }; + sourceRosAndWorkspace(); + + return { + getBaseDir: () => baseDir, + getEnv: () => env, + onDidChangeEnv: (listener: () => any, thisArg: any) => onDidChangeEnv(listener, thisArg), + }; } export function deactivate() { - subscriptions.forEach(disposable => disposable.dispose()); + subscriptions.forEach(disposable => disposable.dispose()); } /** @@ -94,103 +99,119 @@ export function deactivate() { * auto-generated files. */ async function determineBuildSystem(dir: string): Promise { - while (dir && dirname(dir) !== dir) { - if (await pfs.exists(`${dir}/.catkin_workspace`)) { - baseDir = dir; - buildSystem = BuildSystem.CatkinMake; + while (dir && dirname(dir) !== dir) { + if (await pfs.exists(`${dir}/.catkin_workspace`)) { + baseDir = dir; + buildSystem = BuildSystem.CatkinMake; - return; - } else if (await pfs.exists(`${dir}/.catkin_tools`)) { - baseDir = dir; - buildSystem = BuildSystem.CatkinTools; + return; + } else if (await pfs.exists(`${dir}/.catkin_tools`)) { + baseDir = dir; + buildSystem = BuildSystem.CatkinTools; - return; - } + return; + } - dir = dirname(dir); - } + dir = dirname(dir); + } - buildSystem = BuildSystem.None; + buildSystem = BuildSystem.None; } /** * Activates components which require a ROS env. */ function activateEnvironment() { - // Clear existing disposables. - while (subscriptions.length > 0) { - subscriptions.pop().dispose(); - } - - if (typeof env.ROS_ROOT === "undefined") { - return; - } - - // Set up the master. - const masterApi = new master.XmlRpcApi(env.ROS_MASTER_URI); - const masterStatusItem = new master.StatusBarItem(masterApi); - const masterStatusProvider = new master.StatusDocumentProvider(context, masterApi); - - masterStatusItem.activate(); - - subscriptions.push(masterStatusItem); - subscriptions.push(vscode.workspace.registerTextDocumentContentProvider("ros-master", masterStatusProvider)); - subscriptions.push(vscode.workspace.registerTaskProvider("catkin", new CatkinTaskProvider())); - - // Register commands. - subscriptions.push( - vscode.commands.registerCommand(constants.CMD_CREATE_CATKIN_PACKAGE, catkin.createPackage), - vscode.commands.registerCommand(constants.CMD_CREATE_TERMINAL, utils.createTerminal), - vscode.commands.registerCommand(constants.CMD_GET_DEBUG_SETTINGS, debug.getDebugSettings), - vscode.commands.registerCommand(constants.CMD_PROVIDE_INITIAL_CONFIGURATIONS, debug.provideInitialConfigurations), - vscode.commands.registerCommand(constants.CMD_SHOW_MASTER_STATUS, master.showMasterStatus), - vscode.commands.registerCommand(constants.CMD_START_CORE, master.startCore), - vscode.commands.registerCommand(constants.CMD_STOP_CORE, () => master.stopCore(masterApi)), - vscode.commands.registerCommand(constants.CMD_UPDATE_CPP_PROPERTIES, build.updateCppProperties), - vscode.commands.registerCommand(constants.CMD_UPDATE_PYTHON_PATH, build.updatePythonPath), - ); - - // Generate config files if they don't already exist. - build.createConfigFiles(); + // Clear existing disposables. + while (subscriptions.length > 0) { + subscriptions.pop().dispose(); + } + + if (typeof env.ROS_ROOT === "undefined") { + return; + } + + // Set up the master. + const masterApi = new master.XmlRpcApi(env.ROS_MASTER_URI); + const masterStatusItem = new master.StatusBarItem(masterApi); + const masterStatusProvider = new master.StatusDocumentProvider(context, masterApi); + + masterStatusItem.activate(); + + subscriptions.push(masterStatusItem); + subscriptions.push(vscode.workspace.registerTextDocumentContentProvider("ros-master", masterStatusProvider)); + subscriptions.push(vscode.workspace.registerTaskProvider("catkin", new CatkinTaskProvider())); + + // Register commands. + subscriptions.push( + vscode.commands.registerCommand(constants.CMD_CREATE_CATKIN_PACKAGE, catkin.createPackage), + vscode.commands.registerCommand(constants.CMD_CREATE_TERMINAL, utils.createTerminal), + vscode.commands.registerCommand(constants.CMD_GET_DEBUG_SETTINGS, debug.getDebugSettings), + //vscode.commands.registerCommand(constants.CMD_PROVIDE_INITIAL_CONFIGURATIONS, debug.provideInitialConfigurations), + vscode.commands.registerCommand(constants.CMD_SHOW_MASTER_STATUS, master.showMasterStatus), + vscode.commands.registerCommand(constants.CMD_START_CORE, master.startCore), + vscode.commands.registerCommand(constants.CMD_STOP_CORE, () => master.stopCore(masterApi)), + vscode.commands.registerCommand(constants.CMD_UPDATE_CPP_PROPERTIES, build.updateCppProperties), + vscode.commands.registerCommand(constants.CMD_UPDATE_PYTHON_PATH, build.updatePythonPath), + ); + + // Generate config files if they don't already exist. + build.createConfigFiles(); } /** * Loads the ROS environment, and prompts the user to select a distro if required. */ async function sourceRosAndWorkspace(): Promise { - env = undefined; + env = undefined; + + const config = utils.getConfig(); + const distro = config.get("distro", ""); + + if (distro) { + try { + env = await utils.sourceSetupFile(`/opt/ros/${distro}/setup.bash`, {}); + } catch (err) { + vscode.window.showErrorMessage(`Could not source the setup file for ROS distro "${distro}".`); + } + } else if (typeof process.env.ROS_ROOT !== "undefined") { + env = process.env; + } else { + const message = "The ROS distro is not configured."; + const configure = "Configure"; + + if (await vscode.window.showErrorMessage(message, configure) === configure) { + config.update("distro", await vscode.window.showQuickPick(utils.getDistros())); + } + } - const config = utils.getConfig(); - const distro = config.get("distro", ""); + // Source the workspace setup over the top. + const workspaceSetup = `${baseDir}/devel/setup.bash`; - if (distro) { - try { - env = await utils.sourceSetupFile(`/opt/ros/${distro}/setup.bash`, {}); - } catch (err) { - vscode.window.showErrorMessage(`Could not source the setup file for ROS distro "${distro}".`); + if (env && typeof env.ROS_ROOT !== "undefined" && await pfs.exists(workspaceSetup)) { + try { + env = await utils.sourceSetupFile(workspaceSetup, env); + } catch (err) { + vscode.window.showWarningMessage("Could not source the workspace setup file."); + } } - } else if (typeof process.env.ROS_ROOT !== "undefined") { - env = process.env; - } else { - const message = "The ROS distro is not configured."; - const configure = "Configure"; - - if (await vscode.window.showErrorMessage(message, configure) === configure) { - config.update("distro", await vscode.window.showQuickPick(utils.getDistros())); - } - } - // Source the workspace setup over the top. - const workspaceSetup = `${baseDir}/devel/setup.bash`; + // Notify listeners the environment has changed. + onEnvChanged.fire(); +} + +class RosConfigurationProvider implements vscode.DebugConfigurationProvider { + + /** + * Massage a debug configuration just before a debug session is being launched, + * e.g. add all missing attributes to the debug configuration. + */ + resolveDebugConfiguration(folder: WorkspaceFolder | undefined, config: DebugConfiguration, token?: CancellationToken): ProviderResult { + + debug.provideInitialConfigurations(config); + + return config; - if (env && typeof env.ROS_ROOT !== "undefined" && await pfs.exists(workspaceSetup)) { - try { - env = await utils.sourceSetupFile(workspaceSetup, env); - } catch (err) { - vscode.window.showWarningMessage("Could not source the workspace setup file."); } - } - // Notify listeners the environment has changed. - onEnvChanged.fire(); } From 1ad6d76a7b7780c56c4b255c4d83d2c2144cb443 Mon Sep 17 00:00:00 2001 From: maxu Date: Mon, 20 Nov 2017 20:44:14 +0800 Subject: [PATCH 2/9] . --- package.json | 6 ++-- src/build.ts | 83 +++++++++++++++++++++++++++------------------------- 2 files changed, 46 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index f241d5b7..2d31d929 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "displayName": "ROS", "icon": "assets/icon.png", "description": "Development support for Robot Operating System (ROS)", - "version": "0.2.0", - "publisher": "ajshort", + "version": "0.3.1", + "publisher": "maxu", "license": "MIT", "repository": { "type": "git", - "url": "https://github.com/ajshort/vscode-ros.git" + "url": "https://github.com/imxood/vscode-ros.git" }, "engines": { "vscode": "^1.18.0" diff --git a/src/build.ts b/src/build.ts index bab2b98a..d1db3c6a 100644 --- a/src/build.ts +++ b/src/build.ts @@ -11,64 +11,67 @@ const PYTHON_AUTOCOMPLETE_PATHS = "python.autoComplete.extraPaths"; * Creates config files which don't exist. */ export async function createConfigFiles() { - const config = vscode.workspace.getConfiguration(); + const config = vscode.workspace.getConfiguration(); - // Update the Python path if required. - if (config.get(PYTHON_AUTOCOMPLETE_PATHS, []).length === 0) { - updatePythonPath(); - } + // Update the Python path if required. + if (config.get(PYTHON_AUTOCOMPLETE_PATHS, []).length === 0) { + updatePythonPath(); + } - // Ensure the ".vscode" directory exists then update the C++ path. - const dir = path.join(vscode.workspace.rootPath, ".vscode"); - - if (!await pfs.exists(dir)) { - await pfs.mkdir(dir); - } + // Ensure the ".vscode" directory exists then update the C++ path. + const dir = path.join(vscode.workspace.rootPath, ".vscode"); - pfs.exists(path.join(dir, "c_cpp_properties.json")).then(exists => { - if (!exists) { - updateCppProperties(); + if (!await pfs.exists(dir)) { + await pfs.mkdir(dir); } - }); + + pfs.exists(path.join(dir, "c_cpp_properties.json")).then(exists => { + if (!exists) { + updateCppProperties(); + } + }); } /** * Updates the `c_cpp_properties.json` file with ROS include paths. */ export async function updateCppProperties(): Promise { - const includes = await utils.getIncludeDirs(); - const filename = vscode.workspace.rootPath + "/.vscode/c_cpp_properties.json"; + const includes = await utils.getIncludeDirs(); + const filename = vscode.workspace.rootPath + "/.vscode/c_cpp_properties.json"; - // Get all packages within the workspace, and check if they have an include - // directory. If so, add them to the list. - const packages = await utils.getPackages().then( - pkgs => _.values(pkgs).filter(pkg => pkg.startsWith(extension.baseDir)) - ); + // Get all packages within the workspace, and check if they have an include + // directory. If so, add them to the list. + const packages = await utils.getPackages().then( + pkgs => _.values(pkgs) //.filter(pkg => pkg.startsWith(extension.baseDir)) + ); - await Promise.all(packages.map(pkg => { - const include = path.join(pkg, "include"); + await Promise.all(packages.map(pkg => { + const include = path.join(pkg, "include"); - return pfs.exists(include).then(exists => { - if (exists) { - includes.push(include); - } - }); - })); + return pfs.exists(include).then(exists => { + if (exists) { + includes.push(include); + } + }); + })); + + console.log( "cpp include: ", includes ); - await pfs.writeFile(filename, JSON.stringify({ - configurations: [ - { - browse: { databaseFilename: "", limitSymbolsToIncludedHeaders: true }, - includePath: [...includes, "/usr/include"], - name: "Linux", - }, - ], - }, undefined, 2)); + await pfs.writeFile(filename, JSON.stringify({ + configurations: [ + { + browse: { databaseFilename: "", limitSymbolsToIncludedHeaders: true }, + includePath: [...includes, "/usr/include"], + name: "Linux", + }, + ], + }, undefined, 2)); } /** * Updates the python autocomplete path to support ROS. */ export function updatePythonPath() { - vscode.workspace.getConfiguration().update(PYTHON_AUTOCOMPLETE_PATHS, extension.env.PYTHONPATH.split(":")); + vscode.workspace.getConfiguration().update(PYTHON_AUTOCOMPLETE_PATHS, extension.env.PYTHONPATH.split(":")); + console.log( "python path: ", vscode.workspace.getConfiguration().get(PYTHON_AUTOCOMPLETE_PATHS) ); } From 0ffc4f25b76a1277af22db3e71cef5e456157df6 Mon Sep 17 00:00:00 2001 From: maxu Date: Thu, 23 Nov 2017 09:48:00 +0800 Subject: [PATCH 3/9] . --- .gitignore | 1 + package.json | 6 +++--- src/build.ts | 29 +++++++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 5df8049b..448a1861 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ out node_modules +.vscode diff --git a/package.json b/package.json index 2d31d929..9f347b65 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "displayName": "ROS", "icon": "assets/icon.png", "description": "Development support for Robot Operating System (ROS)", - "version": "0.3.1", - "publisher": "maxu", + "version": "0.3.3", + "publisher": "ajshort", "license": "MIT", "repository": { "type": "git", - "url": "https://github.com/imxood/vscode-ros.git" + "url": "https://github.com/ajshort/vscode-ros.git" }, "engines": { "vscode": "^1.18.0" diff --git a/src/build.ts b/src/build.ts index d1db3c6a..890f25f0 100644 --- a/src/build.ts +++ b/src/build.ts @@ -71,7 +71,32 @@ export async function updateCppProperties(): Promise { /** * Updates the python autocomplete path to support ROS. */ -export function updatePythonPath() { - vscode.workspace.getConfiguration().update(PYTHON_AUTOCOMPLETE_PATHS, extension.env.PYTHONPATH.split(":")); +export async function updatePythonPath() { + + const pathon_paths: string[] = []; + + // Get all packages within the workspace, and check if they have an include + // directory. If so, add them to the list. + const packages = await utils.getPackages().then( + pkgs => _.values(pkgs) //.filter(pkg => pkg.startsWith(extension.baseDir)) + ); + + await Promise.all(packages.map(pkg => { + const pkg_name = pkg.substring(pkg.lastIndexOf("/")+1); + const pkg_path = path.join(pkg, "src", pkg_name); + + console.log( "pkg_path: ", pkg_path ); + + return pfs.exists( path.join(pkg_path, "__init__.py")).then(exists => { + if (exists) { + pathon_paths.push(pkg_path); + } + }); + })); + + pathon_paths.push.apply(pathon_paths, process.env.PYTHONPATH.split(":")); + + vscode.workspace.getConfiguration().update(PYTHON_AUTOCOMPLETE_PATHS, pathon_paths); + console.log( "python path: ", vscode.workspace.getConfiguration().get(PYTHON_AUTOCOMPLETE_PATHS) ); } From 77068e6c70fc1fd176a118ae357df630615c2e73 Mon Sep 17 00:00:00 2001 From: maxu Date: Thu, 23 Nov 2017 10:10:48 +0800 Subject: [PATCH 4/9] . --- .gitignore | 1 + package.json | 2 +- src/build.ts | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 448a1861..85e63edb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ out node_modules .vscode +ros-* diff --git a/package.json b/package.json index 9f347b65..e9dc1e55 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "ROS", "icon": "assets/icon.png", "description": "Development support for Robot Operating System (ROS)", - "version": "0.3.3", + "version": "0.3.4", "publisher": "ajshort", "license": "MIT", "repository": { diff --git a/src/build.ts b/src/build.ts index 890f25f0..0d3d46cc 100644 --- a/src/build.ts +++ b/src/build.ts @@ -83,11 +83,11 @@ export async function updatePythonPath() { await Promise.all(packages.map(pkg => { const pkg_name = pkg.substring(pkg.lastIndexOf("/")+1); - const pkg_path = path.join(pkg, "src", pkg_name); + const pkg_path = path.join(pkg, "src"); console.log( "pkg_path: ", pkg_path ); - return pfs.exists( path.join(pkg_path, "__init__.py")).then(exists => { + return pfs.exists( path.join(pkg_path, pkg_name, "__init__.py")).then(exists => { if (exists) { pathon_paths.push(pkg_path); } From 419389731e1d920cf28fe0684d92f979fe3b9566 Mon Sep 17 00:00:00 2001 From: maxu Date: Thu, 23 Nov 2017 12:18:19 +0800 Subject: [PATCH 5/9] . --- LICENSE.md | 16 +++++ package.json | 2 +- src/debug.ts | 8 +-- src/extension.ts | 153 +++++++++++++++++++++-------------------------- 4 files changed, 85 insertions(+), 94 deletions(-) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..fea25e5b --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,16 @@ +# MIT License + +Copyright © 2016 Andrew Short + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/package.json b/package.json index 094fd71d..d53b6e64 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "ROS", "icon": "assets/icon.png", "description": "Development support for Robot Operating System (ROS)", - "version": "0.3.1", + "version": "0.3.0", "publisher": "ajshort", "license": "MIT", "repository": { diff --git a/src/debug.ts b/src/debug.ts index e2590ee2..a3c8b50e 100644 --- a/src/debug.ts +++ b/src/debug.ts @@ -10,19 +10,16 @@ import { WorkspaceFolder, } from "vscode"; -import { WorkspaceFolder, DebugConfiguration, ProviderResult, CancellationToken } from 'vscode'; - /** * Gets stringified settings to pass to the debug server. */ export async function getDebugSettings() { - return JSON.stringify({ env: extension.env }); + return JSON.stringify({ env: extension.env }); } /** * Interacts with the user to create a `roslaunch` or `rosrun` configuration. */ - export class RosDebugConfigProvider implements DebugConfigurationProvider { provideDebugConfigurations(folder: WorkspaceFolder | undefined, token?: CancellationToken) { return []; @@ -34,11 +31,9 @@ export class RosDebugConfigProvider implements DebugConfigurationProvider { const command = await window.showQuickPick(["roslaunch", "rosrun"], { placeHolder: "Launch command" }); const packageName = await window.showQuickPick(packages.then(Object.keys), { placeHolder: "Package" }); - let target: string; if (packageName) { - let basenames = (files: string[]) => files.map(file => basename(file)); if (command === "roslaunch") { @@ -62,5 +57,4 @@ export class RosDebugConfigProvider implements DebugConfigurationProvider { return config; } - } diff --git a/src/extension.ts b/src/extension.ts index 090f4c81..9ede0b82 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -43,49 +43,46 @@ export async function activate(context: vscode.ExtensionContext) { // Activate if we're in a catkin workspace. await determineBuildSystem(vscode.workspace.rootPath); - if (buildSystem == BuildSystem.None) { - return; - } + if (buildSystem == BuildSystem.None) { + return; + } - console.log(`Activating ROS extension in "${baseDir}"`); + console.log(`Activating ROS extension in "${baseDir}"`); // Activate components when the ROS env is changed. context.subscriptions.push(onDidChangeEnv(activateEnvironment.bind(null, context))); - // Activate components when the ROS env is changed. - context.subscriptions.push(onDidChangeEnv(activateEnvironment)); + // Activate components which don't require the ROS env. + context.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider( + "cpp", new CppFormatter() + )); - // Activate components which don't require the ROS env. - context.subscriptions.push(vscode.languages.registerDocumentFormattingEditProvider( - "cpp", new CppFormatter() - )); + // Source the environment, and re-source on config change. + let config = utils.getConfig(); - // Source the environment, and re-source on config change. - let config = utils.getConfig(); + context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => { + const updatedConfig = utils.getConfig(); + const fields = Object.keys(config).filter(k => !(config[k] instanceof Function)); + const changed = fields.some(key => updatedConfig[key] !== config[key]); - context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => { - const updatedConfig = utils.getConfig(); - const fields = Object.keys(config).filter(k => !(config[k] instanceof Function)); - const changed = fields.some(key => updatedConfig[key] !== config[key]); - - if (changed) { - sourceRosAndWorkspace(); - } + if (changed) { + sourceRosAndWorkspace(); + } - config = updatedConfig; - })); + config = updatedConfig; + })); - sourceRosAndWorkspace(); + sourceRosAndWorkspace(); - return { - getBaseDir: () => baseDir, - getEnv: () => env, - onDidChangeEnv: (listener: () => any, thisArg: any) => onDidChangeEnv(listener, thisArg), - }; + return { + getBaseDir: () => baseDir, + getEnv: () => env, + onDidChangeEnv: (listener: () => any, thisArg: any) => onDidChangeEnv(listener, thisArg), + }; } export function deactivate() { - subscriptions.forEach(disposable => disposable.dispose()); + subscriptions.forEach(disposable => disposable.dispose()); } /** @@ -93,23 +90,23 @@ export function deactivate() { * auto-generated files. */ async function determineBuildSystem(dir: string): Promise { - while (dir && dirname(dir) !== dir) { - if (await pfs.exists(`${dir}/.catkin_workspace`)) { - baseDir = dir; - buildSystem = BuildSystem.CatkinMake; + while (dir && dirname(dir) !== dir) { + if (await pfs.exists(`${dir}/.catkin_workspace`)) { + baseDir = dir; + buildSystem = BuildSystem.CatkinMake; - return; - } else if (await pfs.exists(`${dir}/.catkin_tools`)) { - baseDir = dir; - buildSystem = BuildSystem.CatkinTools; + return; + } else if (await pfs.exists(`${dir}/.catkin_tools`)) { + baseDir = dir; + buildSystem = BuildSystem.CatkinTools; - return; - } - - dir = dirname(dir); + return; } - buildSystem = BuildSystem.None; + dir = dirname(dir); + } + + buildSystem = BuildSystem.None; } /** @@ -157,55 +154,39 @@ function activateEnvironment(context: vscode.ExtensionContext) { * Loads the ROS environment, and prompts the user to select a distro if required. */ async function sourceRosAndWorkspace(): Promise { - env = undefined; - - const config = utils.getConfig(); - const distro = config.get("distro", ""); - - if (distro) { - try { - env = await utils.sourceSetupFile(`/opt/ros/${distro}/setup.bash`, {}); - } catch (err) { - vscode.window.showErrorMessage(`Could not source the setup file for ROS distro "${distro}".`); - } - } else if (typeof process.env.ROS_ROOT !== "undefined") { - env = process.env; - } else { - const message = "The ROS distro is not configured."; - const configure = "Configure"; - - if (await vscode.window.showErrorMessage(message, configure) === configure) { - config.update("distro", await vscode.window.showQuickPick(utils.getDistros())); - } - } + env = undefined; - // Source the workspace setup over the top. - const workspaceSetup = `${baseDir}/devel/setup.bash`; + const config = utils.getConfig(); + const distro = config.get("distro", ""); - if (env && typeof env.ROS_ROOT !== "undefined" && await pfs.exists(workspaceSetup)) { - try { - env = await utils.sourceSetupFile(workspaceSetup, env); - } catch (err) { - vscode.window.showWarningMessage("Could not source the workspace setup file."); - } + if (distro) { + try { + env = await utils.sourceSetupFile(`/opt/ros/${distro}/setup.bash`, {}); + } catch (err) { + vscode.window.showErrorMessage(`Could not source the setup file for ROS distro "${distro}".`); } + } else if (typeof process.env.ROS_ROOT !== "undefined") { + env = process.env; + } else { + const message = "The ROS distro is not configured."; + const configure = "Configure"; + + if (await vscode.window.showErrorMessage(message, configure) === configure) { + config.update("distro", await vscode.window.showQuickPick(utils.getDistros())); + } + } - // Notify listeners the environment has changed. - onEnvChanged.fire(); -} - -class RosConfigurationProvider implements vscode.DebugConfigurationProvider { - - /** - * Massage a debug configuration just before a debug session is being launched, - * e.g. add all missing attributes to the debug configuration. - */ - resolveDebugConfiguration(folder: WorkspaceFolder | undefined, config: DebugConfiguration, token?: CancellationToken): ProviderResult { - - debug.provideInitialConfigurations(config); - - return config; + // Source the workspace setup over the top. + const workspaceSetup = `${baseDir}/devel/setup.bash`; + if (env && typeof env.ROS_ROOT !== "undefined" && await pfs.exists(workspaceSetup)) { + try { + env = await utils.sourceSetupFile(workspaceSetup, env); + } catch (err) { + vscode.window.showWarningMessage("Could not source the workspace setup file."); } + } + // Notify listeners the environment has changed. + onEnvChanged.fire(); } From a26b5b97de02703aceb875a3d4140638849ffe7f Mon Sep 17 00:00:00 2001 From: maxu Date: Thu, 23 Nov 2017 12:22:58 +0800 Subject: [PATCH 6/9] . --- .editorconfig | 2 +- .gitignore | 2 - .vscode/tasks.json | 4 +- README.md | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 README.md diff --git a/.editorconfig b/.editorconfig index 51d91d0e..16f3fce4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,5 +3,5 @@ root = true [*] end_of_line = lf indent_style = space -indent_size = 4 +indent_size = 2 insert_final_newline = true diff --git a/.gitignore b/.gitignore index 85e63edb..5df8049b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ out node_modules -.vscode -ros-* diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 83fa44da..fb7f662e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -23,8 +23,8 @@ "args": ["run", "compile", "--loglevel", "silent"], // The tsc compiler is started in watching mode - "isBackground": true, + "isWatching": true, // use the standard tsc in watch mode problem matcher to find compile problems in the output. "problemMatcher": "$tsc-watch" -} +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..23475729 --- /dev/null +++ b/README.md @@ -0,0 +1,91 @@ +# ROS VSCode Extension + +This [Visual Studio Code (VSCode)][vscode] extension provides support for [Robot Operating System (ROS)][ros] +development. + +* [Repository][repo] +* [Issues][issues] + +## Getting Started + +The extension will automatically start when you open a catkin workspace. +The build system (e.g. catkin_make or catkin build) will automatically be confirmed from the hidden files associated with +each system. +The ROS distro will automatically be confirmed from the parent environment, or you will be prompted to select a ROS +distro if this can't be done automatically. + +> You must build the catkin workspace at least once before the extension will recognise it. + +To start ROS master, use the "ROS: Start Core" command. The "ROS master" indicator in the bottom left will show if the +master is currently running, and you can click on this to view parameters etc. If you hit F5 you can create a debug +configuration to run a `rosrun` or `roslaunch` command. + +The first time you open the workspace the extension will automatically create build and test tasks and update the +C++ and Python paths. You can re-run this process later using the appropriate commands. + +## Features + +* Automatic ROS environment configuration. +* Allows starting, stopping and viewing the ROS master status. +* Automatically discover `catkin_make` or `catkin build` build tasks. +* Create catkin packages using `catkin_create_pkg` script or `catkin create pkg`. +* Run `rosrun` or `roslaunch` (breakpoints currently not supported). +* Syntax highlighting for `.msg`, `.urdf` and other ROS files. +* Automatically add the ROS C++ include and Python import paths. +* Format C++ using the ROS `clang-format` style. + +## Commands + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameCommandDescription
Create Catkin Packageros.createCatkinPackage + Create a catkin package. You can right click on a folder in the explorer to create it in a specific location. +
Create Terminalros.createTerminalCreate a terminal with ROS sourced.
Show Master Statusros.showMasterStatusOpen a detail view showing details about the ROS master.
Start Coreros.startCoreSpawn a ROS core
Stop Coreros.stopCoreKill the ROS core
Update C++ Propertiesros.updateCppPropertiesUpdate the C++ include path to include ROS.
Update Python Pathros.updatePythonPathUpdate the Python path to include ROS.
+ +[issues]: https://github.com/ajshort/vscode-ros/issues +[repo]: https://github.com/ajshort/vscode-ros +[ros]: http://ros.org +[vscode]: https://code.visualstudio.com From 5925de5390296f7b23ee885881d679afe8dcdc95 Mon Sep 17 00:00:00 2001 From: maxu Date: Thu, 23 Nov 2017 22:30:08 +0800 Subject: [PATCH 7/9] . --- package.json | 2 +- src/build.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index d53b6e64..0de7b70f 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "ROS", "icon": "assets/icon.png", "description": "Development support for Robot Operating System (ROS)", - "version": "0.3.0", + "version": "0.3.10", "publisher": "ajshort", "license": "MIT", "repository": { diff --git a/src/build.ts b/src/build.ts index 0d3d46cc..173f520a 100644 --- a/src/build.ts +++ b/src/build.ts @@ -85,8 +85,6 @@ export async function updatePythonPath() { const pkg_name = pkg.substring(pkg.lastIndexOf("/")+1); const pkg_path = path.join(pkg, "src"); - console.log( "pkg_path: ", pkg_path ); - return pfs.exists( path.join(pkg_path, pkg_name, "__init__.py")).then(exists => { if (exists) { pathon_paths.push(pkg_path); @@ -97,6 +95,8 @@ export async function updatePythonPath() { pathon_paths.push.apply(pathon_paths, process.env.PYTHONPATH.split(":")); vscode.workspace.getConfiguration().update(PYTHON_AUTOCOMPLETE_PATHS, pathon_paths); + + await pfs.writeFile(vscode.workspace.rootPath + "/.env", "PYTHONPATH=" + pathon_paths.join(":")); console.log( "python path: ", vscode.workspace.getConfiguration().get(PYTHON_AUTOCOMPLETE_PATHS) ); } From 3262c8a4a397244f4d91645326de5e4bf4f0da94 Mon Sep 17 00:00:00 2001 From: maxu Date: Fri, 24 Nov 2017 16:18:17 +0800 Subject: [PATCH 8/9] . --- src/build.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/build.ts b/src/build.ts index 173f520a..15f1f94f 100644 --- a/src/build.ts +++ b/src/build.ts @@ -92,11 +92,9 @@ export async function updatePythonPath() { }); })); + await pfs.writeFile(vscode.workspace.rootPath + "/.env", "PYTHONPATH=" + pathon_paths.join(":")); + pathon_paths.push.apply(pathon_paths, process.env.PYTHONPATH.split(":")); vscode.workspace.getConfiguration().update(PYTHON_AUTOCOMPLETE_PATHS, pathon_paths); - - await pfs.writeFile(vscode.workspace.rootPath + "/.env", "PYTHONPATH=" + pathon_paths.join(":")); - - console.log( "python path: ", vscode.workspace.getConfiguration().get(PYTHON_AUTOCOMPLETE_PATHS) ); } From 4e07ebdf74a1cf80c03d8f047fea0282d0c8c95e Mon Sep 17 00:00:00 2001 From: maxu Date: Tue, 12 Dec 2017 15:26:19 +0800 Subject: [PATCH 9/9] . --- src/catkin-task-provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/catkin-task-provider.ts b/src/catkin-task-provider.ts index eede9e45..bc58b9db 100644 --- a/src/catkin-task-provider.ts +++ b/src/catkin-task-provider.ts @@ -13,7 +13,7 @@ export default class CatkinTaskProvider implements vscode.TaskProvider { buildCommand = `catkin_make --directory "${extension.baseDir}"`; testCommand = `${buildCommand} run_tests`; } else if (extension.buildSystem === extension.BuildSystem.CatkinTools) { - buildCommand = `catkin build --workspace "${extension.baseDir}"`; + buildCommand = `catkin build -DCMAKE_BUILD_TYPE=Debug --workspace "${extension.baseDir}"`; testCommand = `${buildCommand} --catkin-make-args run_tests`; }