diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0325be8..96c2640 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,29 +1,27 @@ // For format details, see https://aka.ms/devcontainer.json. For config options, see the // README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node { - "name": "foxglove-mcap-dev", - // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/typescript-node:latest", - "features": { - "ghcr.io/devcontainers-contrib/features/pnpm": { - "version": "latest" - }, - "ghcr.io/devcontainers/features/git": {} - }, - // Features to add to the dev container. More info: https://containers.dev/features. - // "features": {}, - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "pnpm i && npm i -g @vscode/vsce", - // Configure tool-specific properties. - "customizations": { - "vscode": { - "extensions": [ - "esbenp.prettier-vscode" - ] - } - }, - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" -} \ No newline at end of file + "name": "foxglove-mcap-dev", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/typescript-node:latest", + // Features to add to the dev container. More info: https://containers.dev/features. + "features": { + "ghcr.io/devcontainers-contrib/features/pnpm": { + "version": "latest" + }, + "ghcr.io/devcontainers/features/git": {}, + "ghcr.io/tiwaojo/features/mcap-cli": {} + }, + "workspaceFolder": "/home/node/${localWorkspaceFolderBasename}", + "workspaceMount": "source=${localWorkspaceFolder},target=/home/node/${localWorkspaceFolderBasename},type=bind", + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "pnpm i && npm i -g @vscode/vsce", + // Configure tool-specific properties. + "customizations": { + "vscode": { + "extensions": ["esbenp.prettier-vscode","GitHub.copilot"] + } + } + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66f4d40..517145f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,8 +3,9 @@ name: Build and Publish on: push: - branches: - - main + push: + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 release: types: - created diff --git a/.vscode/launch.json b/.vscode/launch.json index 670d6e6..3a5ff65 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,7 +15,7 @@ "outFiles": [ "${workspaceFolder}/out/**/*.js" ], - "preLaunchTask": "${defaultBuildTask}" + "preLaunchTask": "${defaultBuildTask}" }, { "name": "Extension Tests", diff --git a/.vscode/settings.json b/.vscode/settings.json index 30bf8c2..a16b4f5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,6 @@ "out": true // set this to false to include "out" folder in search results }, // Turn off tsc task auto detection since we have the necessary tasks as npm scripts - "typescript.tsc.autoDetect": "off" + "typescript.tsc.autoDetect": "off", + "mcap.mcapPath": "mcap" } \ No newline at end of file diff --git a/package.json b/package.json index 8162fc7..76127c9 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "properties": { "mcap.mcapPath": { "type": "string", - "default": "/workspaces/foxglove-mcap/mcap", + "default": "mcap", "description": "Complete functions with their parameter signature.", "scope": "resource" }, diff --git a/src/extension.ts b/src/extension.ts index 0e50af4..b85216e 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,292 +1,289 @@ // The module 'vscode' contains the VS Code extensibility API // Import the module and reference it with the alias vscode in your code below import * as vscode from "vscode"; -import * as cp from "child_process"; import { MCAPCommand } from "./types"; const config = vscode.workspace.getConfiguration(); // get configuration +async function executeCommandWithSelectedFile() { + const activeEditor = vscode.window.activeTextEditor; + + if (!activeEditor) { + vscode.window.showErrorMessage("No file selected."); + return; + } + + const filePath = activeEditor.document.uri.fsPath; + console.log("Selected FILE PATH: ", activeEditor.document.uri); + + console.log("Selected FILE PATH: ", filePath); + return filePath; +} + // Get the properties saved in vscode config settings function extentionInit(): boolean { - try { - const workspaceFolders = vscode.workspace.workspaceFolders; - if ( - !workspaceFolders || - workspaceFolders.length === 0 || - !config.get("mcap.mcapPath") - ) { - vscode.window.showErrorMessage("MCAP binary not detected in workspace."); - return false; - } - - const rootPath = workspaceFolders[0].uri.fsPath; - - config - .update("mcap.mcapPath", rootPath, vscode.ConfigurationTarget.Global) // Set the new value globally - .then(() => { - vscode.window.showInformationMessage( - "Configuration value updated successfully!" - ); - }); - } catch (error) { - vscode.window.showErrorMessage("An error has occured"); - return false; - } - return true; + try { + const workspaceFolders = vscode.workspace.workspaceFolders; + if ( + !workspaceFolders || + workspaceFolders.length === 0 || + !config.get("mcap.mcapPath") + ) { + vscode.window.showErrorMessage("MCAP binary not detected in workspace."); + return false; + } + + const rootPath = workspaceFolders[0].uri.fsPath; + + config + .update("mcap.mcapPath", rootPath, vscode.ConfigurationTarget.Global) // Set the new value globally + .then(() => { + vscode.window.showInformationMessage( + "Configuration value updated successfully!" + ); + }); + } catch (error) { + vscode.window.showErrorMessage("An error has occured"); + return false; + } + return true; } // Prepares a terminal and executes the composed mcap command async function runMCAP( - mcapCommands: string[], - mcapFlags: string[], - files: string[] + mcapCommands: string[], + mcapFlags: string[], + files: string[] ) { - const mcapClearOutputConf = vscode.workspace - .getConfiguration() - .get("mcap.clearOutputBeforeCommand"); - - const terminalName = "MCAP Output"; - - // Find if our terminal exists so it can be reused - const terminal = vscode.window.terminals.find((terminal) => terminal.name === terminalName); - - // Compose mcap command that will be used in the terminal - const mcapCommand = `${config.get("mcap.mcapPath")} ${mcapCommands.join( - " " - )} ${mcapFlags.join(" ")} ${files.join(" ")}`; - console.log("MCAP COMMAND: ", mcapCommand); - - if (mcapClearOutputConf) { - await vscode.commands.executeCommand("workbench.action.terminal.clear"); - } - - if (terminal) { - terminal.show(false); - terminal.sendText(mcapCommand, true); - } else { - const mcapTerminal = vscode.window.createTerminal(terminalName); - mcapTerminal.show(false); - mcapTerminal.sendText(mcapCommand); - } + const mcapClearOutputConf = vscode.workspace + .getConfiguration() + .get("mcap.clearOutputBeforeCommand"); + + const terminalName = "MCAP Output"; + + // Find if our terminal exists so it can be reused + const terminal = vscode.window.terminals.find( + (terminal) => terminal.name === terminalName + ); + + // Compose mcap command that will be used in the terminal + const mcapCommand = `${config.get("mcap.mcapPath")} ${mcapCommands.join( + " " + )} ${mcapFlags.join(" ")} ${files.join(" ")}`; + console.log("MCAP COMMAND: ", mcapCommand); + + if (mcapClearOutputConf) { + await vscode.commands.executeCommand("workbench.action.terminal.clear"); + } + + if (terminal) { + terminal.show(false); + terminal.sendText(mcapCommand, true); + } else { + const mcapTerminal = vscode.window.createTerminal(terminalName); + mcapTerminal.show(false); + mcapTerminal.sendText(mcapCommand); + } } +// async function runMCAP(mcapCommands: string[], mcapFlags: string[], files: string[]) { +// const mcapClearOutputConf = config.get("mcap.clearOutputBeforeCommand"); + +// // Find if our terminal exists so it can be reused +// const terminalName = "MCAP Output"; +// const terminal = vscode.window.terminals.find((terminal) => terminal.name === terminalName) || vscode.window.createTerminal(terminalName); + +// const mcapCommand = `${config.get("mcap.mcapPath")} ${mcapCommands.join(" ")} ${mcapFlags.join(" ")} ${files.join(" ")}`; + +// if (mcapClearOutputConf) { +// await vscode.commands.executeCommand("workbench.action.terminal.clear"); +// } + +// terminal.show(false); +// terminal.sendText(mcapCommand, true); +// } + // Prepare a quick pick of .mcap files in the workspace async function selectMCAPFile() { - const files = await vscode.workspace.findFiles("**/*.mcap"); - - if (files.length === 0) { - vscode.window.showInformationMessage( - "No .mcap files found in the workspace." - ); - return; - } - - const fileItems: vscode.QuickPickItem[] = files.map((file) => ({ - label: file.path, - description: file.fsPath, - })); - - console.log(fileItems); - - const selectedFiles = await vscode.window.showQuickPick(fileItems, { - placeHolder: "Select a .mcap file", - onDidSelectItem(item) { - item.toString(); - }, - matchOnDetail: true, - }); - return selectedFiles; + const files = await vscode.workspace.findFiles("**/*.mcap"); + + if (files.length === 0) { + vscode.window.showInformationMessage( + "No .mcap files found in the workspace." + ); + return; + } + + const fileItems = files.map((file) => ({ + label: file.path, + description: file.fsPath, + })); + + const selectedFiles = await vscode.window.showQuickPick(fileItems, { + placeHolder: "Select a .mcap file", + matchOnDetail: true, + }); + + return selectedFiles?.label; } // Prepare commands -async function commandSetup(cmd: MCAPCommand, subCmds: MCAPCommand[]) { - const subCmdItems: vscode.QuickPickItem[] = subCmds.map((e) => ({ - label: e.command as string, - description: e.description as string, - })); - let selectedSubCmd: vscode.QuickPickItem | undefined; - if (subCmdItems.length !== 0) { - // Select sub-commands - selectedSubCmd = await vscode.window - .showQuickPick(subCmdItems, { - matchOnDetail: true, - canPickMany: false, - placeHolder: "attachment", title: cmd.description - }) - .then((val) => { - return val; - }); - } - - // Select command flags - const flagsInput = await vscode.window - .showInputBox({ - prompt: "Insert optional flags here with their respective arguments", - placeHolder: "e.g. '--end-secs 9223372036854775807 --topics=/imu/data'", ignoreFocusOut: false - }) - .then((val) => { - return val; - }); - - // Select file - const file = await selectMCAPFile().then((res) => { - return res?.label; - }); - - - if (file?.length as number <= 0) { - - return; - } - - runMCAP( - [cmd.command, selectedSubCmd?.label as string], - [flagsInput as string], - [file as string] - ); +async function commandSetup( + cmd: string, + subCmds: MCAPCommand[], + reqSubCmd: boolean +) { + const subCmdItems = subCmds.map((e) => ({ + label: e.command, + description: e.description, + })); + + // If the command requires a sub-command, but no sub-commands are found, show an error message + if (subCmdItems.length > 0 && reqSubCmd) { + vscode.window.showErrorMessage( + `No sub-commands found for ${cmd}. Sub-commands are required for this command.` + ); + const selectedSubCmd = await vscode.window.showQuickPick(subCmdItems, { + matchOnDetail: true, + canPickMany: false, + placeHolder: "attachment", + title: cmd, + }); + + /** + * Executes a command with the selected file, or prompts the user to select an MCAP file if no file is selected. + * @returns The file path of the selected file, or null if no file is selected. + */ + const filePath = + (await executeCommandWithSelectedFile()) ?? (await selectMCAPFile()); + + if (!filePath || !selectedSubCmd) { + return; + } + const flagsInput = await vscode.window.showInputBox({ + prompt: "Insert optional flags here with their respective arguments", + placeHolder: "--end-secs=9223372036854775807 --topics=/imu/data,/gps", + ignoreFocusOut: false,valueSelection: [0, 0] + }); + runMCAP([cmd, selectedSubCmd?.label || ""], [flagsInput || ""], [filePath]); + } else if (subCmdItems.length === 0 && !reqSubCmd) { + // if there are no subcommands and they are not required, run the command + /** + * Executes a command with the selected file, or prompts the user to select an MCAP file if no file is selected. + * @returns The file path of the selected file, or null if no file is selected. + */ + const filePath = + (await executeCommandWithSelectedFile()) ?? (await selectMCAPFile()); + + if (!filePath) { + return; + } + const flagsInput = await vscode.window.showInputBox({ + prompt: "Insert optional flags here with their respective arguments", + placeHolder: "--end-secs=9223372036854775807 --topics=/imu/data,/gps", + ignoreFocusOut: false, + }); + runMCAP([cmd, ""], [flagsInput || ""], [filePath]); + } else { + // If the command does not require a sub-command, but sub-commands are found, show an error message + vscode.window.showErrorMessage( + `No sub-commands found for ${cmd}. Sub-commands are not required for this command.` + ); + } } export function activate(context: vscode.ExtensionContext) { - - // if (extentionInit() !== true) { - // return; - // } - - console.log("MCAP path: ", config.get("mcap.mcapPath")); - // Use the console to output diagnostic information (console.log) and errors (console.error) - // This line of code will only be executed once when your extension is activated - console.log( - 'Congratulations, your extension "mcap-cli-vscode" is now active!' - ); - - // MCAP: Add - let mcapAdd = vscode.commands.registerCommand( - "mcap-cli-vscode.add", - async () => { - const subCmds: MCAPCommand[] = [ - { command: "attachment", description: "Add an attachment to an MCAP file", }, - { command: "metadata", description: "Add metadata to an MCAP file" }, - ]; - await commandSetup({ command: "add", description: "Add records to an existing MCAP file" }, subCmds); - } - ); - - // MCAP: Cat - let mcapCat = vscode.commands.registerCommand( - "mcap-cli-vscode.cat", - async () => { - await commandSetup({ command: 'cat', description: 'Cat the messages in an MCAP file to stdout' }, []); - vscode.window.showInformationMessage("Success from mcap-cli cat"); - } - ); - - // MCAP: List - let mcapList = vscode.commands.registerCommand( - "mcap-cli-vscode.list", - async () => { - const subCmds: MCAPCommand[] = [ - { command: "attachment", description: "List attachments in an MCAP file", }, - { command: "channels", description: "List channels in an MCAP file" }, - { command: "chunks", description: "List chunks in an MCAP file" }, - { command: "metadata", description: "List metadata in an MCAP file" }, - { command: "schemas", description: "List schemas in an MCAP file" }, - ]; - await commandSetup({ command: "list", description: "List records of an MCAP file" }, subCmds); - vscode.window.showInformationMessage("Success from mcap-cli list"); - } - ); - - // MCAP: Info - let mcapInfo = vscode.commands.registerCommand( - "mcap-cli-vscode.info", - async () => { - - await commandSetup({ command: "info", description: "Report statistics about an MCAP file" }, []); - vscode.window.showInformationMessage("Success from mcap-cli info"); - } - ); - - // MCAP: Help - let mcapHelp = vscode.commands.registerCommand( - "mcap-cli-vscode.help", - async () => { - await commandSetup({ command: "help", description: "Help about any command" }, []); - vscode.window.showInformationMessage("Success from mcap-cli help"); - } - ); - - // MCAP: Doctor - let mcapDoctor = vscode.commands.registerCommand( - "mcap-cli-vscode.doctor", - async () => { - await commandSetup({ command: "doctor", description: "Check an MCAP file structure" }, []); - vscode.window.showInformationMessage("Success from mcap-cli doctor"); - } - ); - - // MCAP: Get - let mcapGet = vscode.commands.registerCommand( - "mcap-cli-vscode.get", - async () => { - const subCmds: MCAPCommand[] = [ - { command: "attachment", description: "Get an attachment by name or offset", }, - { command: "metadata", description: "Get metadata by name" }, - ]; - await commandSetup({ command: "get", description: "Get a record from an MCAP file" }, subCmds); - vscode.window.showInformationMessage("Success from mcap-cli get"); - } - ); - - // MCAP: Compress - let mcapCompress = vscode.commands.registerCommand( - "mcap-cli-vscode.compress", - async () => { - vscode.window.showInformationMessage("🚧 Comming soon..."); - } - ); - - // MCAP: Decompress - let mcapDecompress = vscode.commands.registerCommand( - "mcap-cli-vscode.decompress", - async () => { - vscode.window.showInformationMessage("🚧 Comming soon..."); - } - ); - - // MCAP: Convert - let mcapConvert = vscode.commands.registerCommand( - "mcap-cli-vscode.convert", - async () => { - vscode.window.showInformationMessage("🚧 Comming soon..."); - } - ); - - // MCAP: Recover - let mcapRecover = vscode.commands.registerCommand( - "mcap-cli-vscode.recover", - async () => { - vscode.window.showInformationMessage("🚧 Comming soon..."); - } - ); - - // MCAP: Filter - let mcapFilter = vscode.commands.registerCommand( - "mcap-cli-vscode.filter", - async () => { - vscode.window.showInformationMessage("🚧 Comming soon..."); - } - ); - - // MCAP: Merge - let mcapMerge = vscode.commands.registerCommand( - "mcap-cli-vscode.merge", - async () => { - vscode.window.showInformationMessage("🚧 Comming soon..."); - } - ); - - context.subscriptions.push(mcapAdd, mcapCat, mcapList, mcapInfo, mcapHelp, mcapDoctor, mcapGet, mcapCompress, mcapDecompress, mcapConvert, mcapRecover, mcapFilter, mcapMerge); + // if (extentionInit() !== true) { + // return; + // } + console.log("MCAP path: ", config.get("mcap.mcapPath")); + console.log( + 'Congratulations, your extension "mcap-cli-vscode" is now active!' + ); + + context.subscriptions.push( + vscode.commands.registerCommand("mcap-cli-vscode.add", async () => { + await commandSetup( + "add", + [ + { + command: "attachment", + description: "Add an attachment to an MCAP file", + }, + { command: "metadata", description: "Add metadata to an MCAP file" }, + ], + true + ); + }), + vscode.commands.registerCommand("mcap-cli-vscode.cat", async () => { + await commandSetup("cat", [], false); + vscode.window.showInformationMessage("Success from mcap-cli cat"); + }), + vscode.commands.registerCommand("mcap-cli-vscode.list", async () => { + await commandSetup( + "list", + [ + { + command: "attachment", + description: "List attachments in an MCAP file", + }, + { command: "channels", description: "List channels in an MCAP file" }, + { command: "chunks", description: "List chunks in an MCAP file" }, + { command: "metadata", description: "List metadata in an MCAP file" }, + { command: "schemas", description: "List schemas in an MCAP file" }, + ], + true + ); + vscode.window.showInformationMessage("Success from mcap-cli list"); + }), + vscode.commands.registerCommand("mcap-cli-vscode.info", async (uri: vscode.Uri) => { + console.log("URI: ", uri.fsPath); + + await commandSetup("info", [], false); + vscode.window.showInformationMessage("Success from mcap-cli info"); + }), + vscode.commands.registerCommand("mcap-cli-vscode.help", async () => { + await commandSetup("help", [], false); + vscode.window.showInformationMessage("Success from mcap-cli help"); + }), + vscode.commands.registerCommand("mcap-cli-vscode.doctor", async () => { + await commandSetup("doctor", [], false); + vscode.window.showInformationMessage("Success from mcap-cli doctor"); + }), + vscode.commands.registerCommand("mcap-cli-vscode.get", async () => { + await commandSetup( + "get", + [ + { + command: "attachment", + description: "Get an attachment by name or offset", + }, + { command: "metadata", description: "Get metadata by name" }, + ], + true + ); + vscode.window.showInformationMessage("Success from mcap-cli get"); + }), + vscode.commands.registerCommand("mcap-cli-vscode.compress", async () => { + vscode.window.showInformationMessage("🚧 Coming soon..."); + }), + vscode.commands.registerCommand("mcap-cli-vscode.decompress", async () => { + vscode.window.showInformationMessage("🚧 Coming soon..."); + }), + vscode.commands.registerCommand("mcap-cli-vscode.convert", async () => { + vscode.window.showInformationMessage("🚧 Coming soon..."); + }), + vscode.commands.registerCommand("mcap-cli-vscode.recover", async () => { + vscode.window.showInformationMessage("🚧 Coming soon..."); + }), + vscode.commands.registerCommand("mcap-cli-vscode.filter", async () => { + vscode.window.showInformationMessage("🚧 Coming soon..."); + }), + vscode.commands.registerCommand("mcap-cli-vscode.merge", async () => { + vscode.window.showInformationMessage("🚧 Coming soon..."); + }) + ); } // This method is called when your extension is deactivated -export function deactivate() { } +export function deactivate() {}