From fb05a7987188957bf25b695d3347888488ef164b Mon Sep 17 00:00:00 2001 From: Shane Dell Date: Wed, 29 Nov 2023 13:05:02 -0500 Subject: [PATCH] Launch Wizard - Allow copying of configs Closes #670 --- src/launchWizard/launchWizard.js | 213 +++++++++++++++++++++++++------ src/launchWizard/launchWizard.ts | 119 +++++++++++++++-- src/styles/styles.css | 14 ++ 3 files changed, 297 insertions(+), 49 deletions(-) diff --git a/src/launchWizard/launchWizard.js b/src/launchWizard/launchWizard.js index fc9024c6c..ce8d20459 100644 --- a/src/launchWizard/launchWizard.js +++ b/src/launchWizard/launchWizard.js @@ -27,8 +27,12 @@ function getConfigIndex() { if (configSelectedValue === 'New Config') { document.getElementById('nameLabel').style = 'margin-top: 10px; visibility: visible;' + document.getElementById('copyLaunchConfigButton').style = + 'visibility: hidden;' } else { document.getElementById('nameLabel').style = 'visibility: hidden;' + document.getElementById('copyLaunchConfigButton').style = + 'visibility: visible;' } return configSelectedValue === 'New Config' @@ -36,6 +40,75 @@ function getConfigIndex() { : configSelectionBox.selectedIndex } +function getConfigValues() { + var configSelectionBox = document.getElementById('configSelected') + var configSelectedValue = + configSelectionBox.options[configSelectionBox.selectedIndex].value + const name = + configSelectedValue === 'New Config' + ? document.getElementById('name').value + : configSelectedValue + const data = document.getElementById('data').value + const debugServer = parseInt(document.getElementById('debugServer').value) + const infosetFormat = document.getElementById('infosetFormat').value + const infosetOutputFilePath = document.getElementById( + 'infosetOutputFilePath' + ).value + const infosetOutputType = document.getElementById('infosetOutputType').value + const tdmlAction = document.getElementById('tdmlAction').value + const tdmlName = document.getElementById('tdmlName').value + const tdmlDescription = document.getElementById('tdmlDescription').value + const tdmlPath = document.getElementById('tdmlPath').value + const openHexView = document.getElementById('openHexView').checked + const openInfosetDiffView = document.getElementById( + 'openInfosetDiffView' + ).checked + const openInfosetView = document.getElementById('openInfosetView').checked + const program = document.getElementById('program').value + const stopOnEntry = document.getElementById('stopOnEntry').checked + const trace = document.getElementById('trace').checked + const useExistingServer = document.getElementById('useExistingServer').checked + const dataEditorPort = parseInt( + document.getElementById('dataEditorPort').value + ) + const dataEditorLogFile = document.getElementById('dataEditorLogFile').value + const dataEditorLogLevel = document.getElementById('dataEditorLogLevel').value + const dfdlDebuggerLogFile = document.getElementById( + 'dfdlDebuggerLogFile' + ).value + const dfdlDebuggerLogLevel = document.getElementById( + 'dfdlDebuggerLogLevel' + ).value + + const daffodilDebugClasspath = getDaffodilDebugClasspathString() + + return [ + name, + data, + debugServer, + infosetFormat, + infosetOutputFilePath, + infosetOutputType, + tdmlAction, + tdmlName, + tdmlDescription, + tdmlPath, + openHexView, + openInfosetDiffView, + openInfosetView, + program, + stopOnEntry, + trace, + useExistingServer, + dataEditorPort, + dataEditorLogFile, + dataEditorLogLevel, + dfdlDebuggerLogFile, + dfdlDebuggerLogLevel, + daffodilDebugClasspath, + ] +} + // Function get daffodil debug classpath string function getDaffodilDebugClasspathString() { let childNodes = document.getElementById( @@ -188,43 +261,32 @@ function save() { configSelectionBox.options[configSelectionBox.selectedIndex].value var updateOrCreate = configSelectedValue === 'New Config' ? 'create' : 'update' - const name = - configSelectedValue === 'New Config' - ? document.getElementById('name').value - : configSelectedValue - const data = document.getElementById('data').value - const debugServer = parseInt(document.getElementById('debugServer').value) - const infosetFormat = document.getElementById('infosetFormat').value - const infosetOutputFilePath = document.getElementById( - 'infosetOutputFilePath' - ).value - const infosetOutputType = document.getElementById('infosetOutputType').value - const tdmlAction = document.getElementById('tdmlAction').value - const tdmlName = document.getElementById('tdmlName').value - const tdmlDescription = document.getElementById('tdmlDescription').value - const tdmlPath = document.getElementById('tdmlPath').value - const openHexView = document.getElementById('openHexView').checked - const openInfosetDiffView = document.getElementById( - 'openInfosetDiffView' - ).checked - const openInfosetView = document.getElementById('openInfosetView').checked - const program = document.getElementById('program').value - const stopOnEntry = document.getElementById('stopOnEntry').checked - const trace = document.getElementById('trace').checked - const useExistingServer = document.getElementById('useExistingServer').checked - const dataEditorPort = parseInt( - document.getElementById('dataEditorPort').value - ) - const dataEditorLogFile = document.getElementById('dataEditorLogFile').value - const dataEditorLogLevel = document.getElementById('dataEditorLogLevel').value - const dfdlDebuggerLogFile = document.getElementById( - 'dfdlDebuggerLogFile' - ).value - const dfdlDebuggerLogLevel = document.getElementById( - 'dfdlDebuggerLogLevel' - ).value - const daffodilDebugClasspath = getDaffodilDebugClasspathString() + const [ + name, + data, + debugServer, + infosetFormat, + infosetOutputFilePath, + infosetOutputType, + tdmlAction, + tdmlName, + tdmlDescription, + tdmlPath, + openHexView, + openInfosetDiffView, + openInfosetView, + program, + stopOnEntry, + trace, + useExistingServer, + dataEditorPort, + dataEditorLogFile, + dataEditorLogLevel, + dfdlDebuggerLogFile, + dfdlDebuggerLogLevel, + daffodilDebugClasspath, + ] = getConfigValues() var obj = { version: '0.2.0', @@ -278,6 +340,85 @@ function save() { }) } +// Function to copy selected config +function copyConfig() { + const [ + name, + data, + debugServer, + infosetFormat, + infosetOutputFilePath, + infosetOutputType, + tdmlAction, + tdmlName, + tdmlDescription, + tdmlPath, + openHexView, + openInfosetDiffView, + openInfosetView, + program, + stopOnEntry, + trace, + useExistingServer, + dataEditorPort, + dataEditorLogFile, + dataEditorLogLevel, + dfdlDebuggerLogFile, + dfdlDebuggerLogLevel, + daffodilDebugClasspath, + ] = getConfigValues() + + var obj = { + version: '0.2.0', + configurations: [ + { + request: 'launch', + type: 'dfdl', + name: `${name}`, + program: program, + data: data, + debugServer: debugServer, + infosetFormat: infosetFormat, + infosetOutput: { + type: infosetOutputType, + path: infosetOutputFilePath, + }, + tdmlConfig: { + action: tdmlAction, + name: tdmlName, + description: tdmlDescription, + path: tdmlPath, + }, + trace: trace, + stopOnEntry: stopOnEntry, + useExistingServer: useExistingServer, + openHexView: openHexView, + openInfosetView: openInfosetView, + openInfosetDiffView: openInfosetDiffView, + daffodilDebugClasspath: daffodilDebugClasspath, + dataEditor: { + port: dataEditorPort, + logging: { + file: dataEditorLogFile, + level: dataEditorLogLevel, + }, + }, + dfdlDebugger: { + logging: { + file: dfdlDebuggerLogFile, + level: dfdlDebuggerLogLevel, + }, + }, + }, + ], + } + + vscode.postMessage({ + command: 'copyConfig', + data: JSON.stringify(obj, null, 4), + }) +} + // Function to update config values in the webview async function updateConfigValues(config) { document.getElementById('name').value = config.name diff --git a/src/launchWizard/launchWizard.ts b/src/launchWizard/launchWizard.ts index 665b69c75..07ef84c2c 100644 --- a/src/launchWizard/launchWizard.ts +++ b/src/launchWizard/launchWizard.ts @@ -23,6 +23,8 @@ import { VSCodeLaunchConfigArgs } from '../classes/vscode-launch' import { DataEditorConfig } from '../classes/dataEditor' import { parse as jsoncParse } from 'jsonc-parser' +let launchWizard: LaunchWizard | undefined + const defaultConf = getConfig({ name: 'Wizard Config', request: 'launch', @@ -33,7 +35,7 @@ const defaultConf = getConfig({ export async function activate(ctx: vscode.ExtensionContext) { ctx.subscriptions.push( vscode.commands.registerCommand('launch.config', async () => { - await createWizard(ctx) + launchWizard = await createWizard(ctx) }) ) } @@ -138,6 +140,71 @@ async function createUpdateConfigFile(data, updateOrCreate) { vscode.window.showTextDocument(vscode.Uri.parse(launchPath)) } +// Function to create a copy of the passed config +async function copyConfig(data) { + let rootPath = vscode.workspace.workspaceFolders + ? vscode.workspace.workspaceFolders[0].uri.fsPath + : vscode.Uri.parse('').fsPath + + if (!fs.existsSync(`${rootPath}/.vscode`)) { + fs.mkdirSync(`${rootPath}/.vscode`) + } + + // Create launch.json if it doesn't exist already + if (!fs.existsSync(`${rootPath}/.vscode/launch.json`)) { + fs.writeFileSync(`${rootPath}/.vscode/launch.json`, data) + return + } + + let newConf = JSON.parse(data).configurations[0] + let fileData = jsoncParse( + fs.readFileSync(`${rootPath}/.vscode/launch.json`).toString() + ) + + fileData.configurations.forEach((element) => { + if (element.name === newConf.name) { + if (newConf.name.endsWith('Copy')) { + // Second copy + newConf.name = `${newConf.name} 1` + } else if (new RegExp('Copy.*$').test(newConf.name)) { + // Any copy after the first one to add a number + let items = newConf.name.split(' ') + let count = parseInt(items[items.length - 1]) + newConf.name = `${items.slice(0, -1).join(' ')} ${count + 1}` + } else { + // First copy + newConf.name = `${newConf.name} Copy` + } + } + }) + + // Add new config to launch.json + fileData.configurations.push(newConf) + fs.writeFileSync( + `${rootPath}/.vscode/launch.json`, + JSON.stringify(fileData, null, 4) + ) + + vscode.window.showInformationMessage('Launch Configuration Copied!') + + await reloadLaunchWizardUI(fileData, newConf) +} + +// Function to reload the launch wizard UI when a config is copied +async function reloadLaunchWizardUI(fileData, newConf) { + if (launchWizard !== undefined) { + let panel = launchWizard.getPanel() + let index: number | undefined = undefined + fileData.configurations.forEach((conf, i) => { + if (conf.name == newConf.name) { + index = i + } + }) + + panel.webview.html = launchWizard.getWebViewContent(index) + } +} + // Function to update the config values in the webview panel async function updateWebViewConfigValues(configIndex) { let rootPath = vscode.workspace.workspaceFolders @@ -186,9 +253,9 @@ async function openFilePicker( // Function that will create webview async function createWizard(ctx: vscode.ExtensionContext) { - let launchWizard = new LaunchWizard(ctx) - let panel = launchWizard.getPanel() - panel.webview.html = launchWizard.getWebViewContent() + let launchWiz = new LaunchWizard(ctx) + let panel = launchWiz.getPanel() + panel.webview.html = launchWiz.getWebViewContent() panel.webview.onDidReceiveMessage( async (message) => { @@ -197,6 +264,9 @@ async function createWizard(ctx: vscode.ExtensionContext) { await createUpdateConfigFile(message.data, message.updateOrCreate) panel.dispose() return + case 'copyConfig': + await copyConfig(message.data) + return case 'updateConfigValue': var configValues = await updateWebViewConfigValues( message.configIndex @@ -244,6 +314,8 @@ async function createWizard(ctx: vscode.ExtensionContext) { undefined, ctx.subscriptions ) + + return launchWiz } // Class for creating launch wizard webview @@ -293,7 +365,7 @@ class LaunchWizard { } // Method to set html for the hex view - getWebViewContent() { + getWebViewContent(selectedConfigNumber: number | undefined = undefined) { const scriptUri = vscode.Uri.parse( this.ctx.asAbsolutePath('./src/launchWizard/launchWizard.js') ) @@ -311,7 +383,11 @@ class LaunchWizard { let configSelect = '' let newConfig = !fs.existsSync(`${rootPath}/.vscode/launch.json`) - let configIndex = fs.existsSync(`${rootPath}/.vscode/launch.json`) ? 0 : -1 + let configIndex = selectedConfigNumber + ? selectedConfigNumber + : fs.existsSync(`${rootPath}/.vscode/launch.json`) + ? 0 + : -1 let fileData = JSON.parse('{}') if (fs.existsSync(`${rootPath}/.vscode/launch.json`)) { @@ -319,18 +395,25 @@ class LaunchWizard { fs.readFileSync(`${rootPath}/.vscode/launch.json`).toString() ) - fileData.configurations.forEach((element) => { - configSelect += `` + fileData.configurations.forEach((element, i) => { + const selectedText = i == configIndex ? 'selected="selected"' : '' + configSelect += `` }) } + let selectedText = !fs.existsSync(`${rootPath}/.vscode/launch.json`) + ? 'selected="selected"' + : '' + configSelect += `` + let defaultValues = getConfigValues(fileData, configIndex) let nameVisOrHiddenStyle = newConfig ? 'margin-top: 10px; visibility: visible;' : 'visibility: hidden;' - - configSelect += `` + let copyConfigVisOrHiddenStyle = newConfig + ? 'visibility: hidden;' + : 'visibility: visible;' let openHexView = defaultValues.openHexView ? 'checked' : '' let openInfosetDiffView = defaultValues.openInfosetDiffView ? 'checked' : '' @@ -453,9 +536,19 @@ class LaunchWizard {

Launch Config:

Launch config to be updated in the launch.json. 'New Config' will allow for a new one to be made.

- +

+ + +

+

New Config Name: diff --git a/src/styles/styles.css b/src/styles/styles.css index 65de734f9..5a5798653 100644 --- a/src/styles/styles.css +++ b/src/styles/styles.css @@ -124,6 +124,20 @@ margin-top: -10px; } +.copy-config-button { + background-color: lightgray; + border: none; + border-radius: 12px; + color: black; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 12px; + cursor: pointer; + width: 80px; + height: 30px; +} + .browse-button { background-color: lightgray; border: none;