diff --git a/docs/variable-substitutions.md b/docs/variable-substitutions.md new file mode 100644 index 00000000..51638521 --- /dev/null +++ b/docs/variable-substitutions.md @@ -0,0 +1,90 @@ +# Variable Substitutions + +BrightScript extension supports variable substitutions in launch.json configurations to make your debugging workflow more flexible and dynamic. + +## Overview + +Variable substitutions allow you to avoid hardcoding values in your launch configurations. Instead of specifying static IP addresses or passwords, you can use variables that are resolved at runtime. + +## Available Variable Substitutions + +| Variable | Description | Behavior | +|----------|-------------|----------| +| `${promptForHost}` | Prompts you to enter or select a host IP address | Shows input dialog or device picker when debugging starts | +| `${promptForPassword}` | Prompts you to enter the device password | Shows password input dialog when debugging starts | +| `${activeHost}` | Uses the currently active device | Automatically uses pre-configured device, or prompts if none set | +| `${host}` | References the resolved host value | Can be used in other fields like `deepLinkUrl` | + +## `${promptForHost}` - Interactive Host Selection + +The most common variable substitution. When used, VS Code will: +- Show a list of discovered Roku devices (if device discovery is enabled) +- Allow manual IP entry +- Remember the last used device + +```json +{ + "host": "${promptForHost}" +} +``` + +## `${promptForPassword}` - Interactive Password Entry + +Prompts for the developer password when debugging starts: + +```json +{ + "password": "${promptForPassword}" +} +``` + +## `${activeHost}` - Smart Device Selection + +**New!** Uses the currently active device without prompting, but gracefully falls back to prompting if no device is set. This provides the best of both worlds: convenience when you have a preferred device, flexibility when you don't. + +### Basic Usage + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "type": "brightscript", + "name": "Launch with Current Device", + "request": "launch", + "host": "${activeHost}", + "password": "${promptForPassword}", + "rootDir": "${workspaceFolder}", + "files": [ + "manifest", + "source/**/*.*", + "components/**/*.*", + "images/**/*.*" + ] + } + ] +} +``` + +### Requirements + +To use `${activeHost}` optimally, you should set an active device using one of these methods: + +1. **Via Command Palette:** + - Open Command Palette (`Ctrl+Shift+P` / `Cmd+Shift+P`) + - Type "BrightScript: Set Active Device" + - Enter the IP address of your Roku device + +2. **Via Device List:** + - Use the Roku Devices view in the sidebar to select a device (if device discovery is enabled) + +3. **Via Debugging Session:** + - The active device is automatically set when you start a debugging session with `${promptForHost}` + +### Fallback Handling + +If you use `${activeHost}` but no active device is set, it will automatically fallback to prompting for host selection (same behavior as `${promptForHost}`). + +This provides a seamless experience: +- **When active device is set**: Uses it automatically without prompting +- **When no active device**: Falls back to the device picker/input dialog diff --git a/package.json b/package.json index 2f4e7f34..b7d065d6 100644 --- a/package.json +++ b/package.json @@ -458,7 +458,7 @@ }, "deepLinkUrl": { "type": "string", - "description": "A full deep link url to start the debugging session. There's no pretty way of launching directly to a deep link, so the app must be side-loaded, it auto-runs, we stop the app, and then launch it again using the deep link. You may use ${promptForHost}, ${host}, ${promptForQueryParams} which only asks for the URL-encoded querystring, and ${promptForDeepLinkUrl} to enter the entire deep link url at launch-time.", + "description": "A full deep link url to start the debugging session. There's no pretty way of launching directly to a deep link, so the app must be side-loaded, it auto-runs, we stop the app, and then launch it again using the deep link. You may use ${promptForHost}, ${activeHost}, ${host}, ${promptForQueryParams} which only asks for the URL-encoded querystring, and ${promptForDeepLinkUrl} to enter the entire deep link url at launch-time.", "default": "http://${host}:8060/launch/dev?${promptForQueryParams}" }, "password": { @@ -2101,7 +2101,7 @@ }, "brightscript.debug.deepLinkUrl": { "type": "string", - "description": "A full deep link url to start the debugging session. There's no pretty way of launching directly to a deep link, so the app must be side-loaded, it auto-runs, we stop the app, and then launch it again using the deep link. You may use ${promptForHost}, ${host}, ${promptForQueryParams} which only asks for the URL-encoded querystring, and ${promptForDeepLinkUrl} to enter the entire deep link url at launch-time.", + "description": "A full deep link url to start the debugging session. There's no pretty way of launching directly to a deep link, so the app must be side-loaded, it auto-runs, we stop the app, and then launch it again using the deep link. You may use ${promptForHost}, ${activeHost}, ${host}, ${promptForQueryParams} which only asks for the URL-encoded querystring, and ${promptForDeepLinkUrl} to enter the entire deep link url at launch-time.", "default": "http://${host}:8060/launch/dev?${promptForQueryParams}", "scope": "resource" }, diff --git a/src/DebugConfigurationProvider.spec.ts b/src/DebugConfigurationProvider.spec.ts index 302524b9..539f4b41 100644 --- a/src/DebugConfigurationProvider.spec.ts +++ b/src/DebugConfigurationProvider.spec.ts @@ -53,7 +53,8 @@ describe('BrightScriptConfigurationProvider', () => { activeDeviceManager, null, vscode.window.createOutputChannel('Extension'), - userInputManager + userInputManager, + null // BrightScriptCommands is not used in this test ); }); diff --git a/src/DebugConfigurationProvider.ts b/src/DebugConfigurationProvider.ts index a3015c99..8e8fe9ce 100644 --- a/src/DebugConfigurationProvider.ts +++ b/src/DebugConfigurationProvider.ts @@ -21,6 +21,7 @@ import cloneDeep = require('clone-deep'); import { rokuDeploy } from 'roku-deploy'; import type { DeviceInfo } from 'roku-deploy'; import type { UserInputManager } from './managers/UserInputManager'; +import type { BrightScriptCommands } from './BrightScriptCommands'; export class BrightScriptDebugConfigurationProvider implements DebugConfigurationProvider { @@ -30,7 +31,8 @@ export class BrightScriptDebugConfigurationProvider implements DebugConfiguratio private activeDeviceManager: ActiveDeviceManager, private telemetryManager: TelemetryManager, private extensionOutputChannel: vscode.OutputChannel, - private userInputManager: UserInputManager + private userInputManager: UserInputManager, + private brightScriptCommands: BrightScriptCommands ) { this.context = context; this.activeDeviceManager = activeDeviceManager; @@ -429,6 +431,9 @@ export class BrightScriptDebugConfigurationProvider implements DebugConfiguratio } else { config.host = await this.userInputManager.promptForHostManual(); } + } else if (config.host.trim() === '${activeHost}') { + // Get the current remote host from workspace state (it will prompt for host as a fallback) + config.host = await this.brightScriptCommands.getRemoteHost(); } //check the host and throw error if not provided or update the workspace to set last host diff --git a/src/extension.ts b/src/extension.ts index 77b6f0cf..3e0cec07 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -146,7 +146,7 @@ export class Extension { ); //register the debug configuration provider - let configProvider = new BrightScriptDebugConfigurationProvider(context, activeDeviceManager, this.telemetryManager, this.extensionOutputChannel, userInputManager); + let configProvider = new BrightScriptDebugConfigurationProvider(context, activeDeviceManager, this.telemetryManager, this.extensionOutputChannel, userInputManager, this.brightScriptCommands); context.subscriptions.push( vscode.debug.registerDebugConfigurationProvider('brightscript', configProvider) );