Skip to content

Commit

Permalink
Roku Remote Control (#59)
Browse files Browse the repository at this point in the history
* Added configuration for host and sendRemoteCommand function

* Update host for remote

We look for the host in the following order:
1. It has been set in users configuration as `brightscript.remoteControl.host`
2. If not, it will prompt for the host IP

It will also get set or updated any time the debug launches

You can toggle the remote control by setitng a keybinding on `extension.brightscript.toggleRemote`

You can also set keybindings to send commands like:
```
{
    "key": "Backspace",
    "command": "extension.brightscript.sendRemoteCommand",
    "args": "Back",
    "when": "isInRemoteMode"
}
```

* Added Status bar color change and mesages when Remote is Active

* Clean up on activation

* Prompt for host when not set on debug launch

* Added documentation for the Roku remote

* Cleaned up code to pass linting ;)

* trying possible bug fix

src/BrightScriptCommands.ts:101:9 - error TS2322: Type '{}' is not assignable to type 'string'.

101         this.host = await this.context.workspaceState.get('remoteHost');

* Only update the current workspace’s settings when changing the bar color

* Removed the need to toggle the remote on and off

Also registered the basic 11 remote key commands and added default keybindings for them

* Added Roku Text send from popup text dialog

pressing `cmd+k` and you get a popup which you can input text and it will send the text to the roku

* lint cleanup

* Updated Documentation

Also changed the play button to cmd+Enter instead of cmd+p because it conflicted with the Command Palette

* Added Backspace and Updated documentation

* After text input, put focus on Output

* Changed command to `workbench.action.focusPanel`
  • Loading branch information
bvisin authored and TwitchBronBron committed Jan 4, 2019
1 parent 72a46d4 commit f425536
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 12 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"files.trimTrailingWhitespace": true,
"files.exclude": {
"out": false,
"node_modules": false,
Expand Down
48 changes: 42 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# BrightScript Extension for VSCode
# BrightScript Extension for VSCode
A VSCode extension to support Roku's BrightScript language.

[![Build Status](https://travis-ci.org/TwitchBronBron/vscode-brightscript-language.svg?branch=master)](https://travis-ci.org/TwitchBronBron/vscode-brightscript-language)
Expand All @@ -9,7 +9,7 @@ A VSCode extension to support Roku's BrightScript language.

- Syntax highlighting
- Code formatting (provided by [brightscript-formatter](https://github.com/TwitchBronBron/brightscript-formatter))
- Debugging support - Set breakpoints, launch and debug your source code running on the Roku device all from within VSCode
- Debugging support - Set breakpoints, launch and debug your source code running on the Roku device all from within VSCode
- Publish directly to a roku device from VSCode (provided by [roku-deploy](https://github.com/TwitchBronBron/roku-deploy))
- Basic symbol navigation for document and workspace ("APPLE/Ctrl + SHIFT + O" for document, "APPLE/Ctrl + T" for workspace)
- Goto definition (F12)
Expand All @@ -18,6 +18,7 @@ A VSCode extension to support Roku's BrightScript language.
- XML goto definition support which navigates to xml component, code behind function, or brs script import (F12)
- Method signature help (open bracket, or APPLE/Ctrl + SHIFT + SPACE)
- Brightscript output log (which is searchable and can be colorized with a plugin like this: [https://marketplace.visualstudio.com/items?itemName=IBM.output-colorizer](https://marketplace.visualstudio.com/items?itemName=IBM.output-colorizer)
- [Roku remote control from keyboard](#rokuRemote)


## Requirements
Expand Down Expand Up @@ -52,7 +53,7 @@ Here is a sample launch configuration
}
```

If your BrightScript project is located in a subdirectory of the workspace, you will need to update the launch configuration property called 'rootDir' to point to the root folder containing the manifest file.
If your BrightScript project is located in a subdirectory of the workspace, you will need to update the launch configuration property called 'rootDir' to point to the root folder containing the manifest file.

For example, if you have this structure:

Expand Down Expand Up @@ -86,9 +87,9 @@ then you would need change `rootDir` in your launch config to look like this:

### Debug source files with Custom build process

If you have a build process that moves files from a source directory to an output directory, by default you will need to place breakpoints in the output directory's versions of the files.
If you have a build process that moves files from a source directory to an output directory, by default you will need to place breakpoints in the output directory's versions of the files.

**IF** your build process does not change line numbers between source files and built files, this extension will allow you to place breakpoints in your source files, and launch/run your built files. Pair this with vscode's task system, and you can build your code, then launch and debug your code with ease.
**IF** your build process does not change line numbers between source files and built files, this extension will allow you to place breakpoints in your source files, and launch/run your built files. Pair this with vscode's task system, and you can build your code, then launch and debug your code with ease.

**Example:**
- src/
Expand All @@ -103,7 +104,7 @@ If you have a build process that moves files from a source directory to an outpu
- language.brs
- manifest

Here's a sample launch.json for this scenario:
Here's a sample launch.json for this scenario:

```
{
Expand Down Expand Up @@ -132,6 +133,41 @@ This extension contributes the following settings:
* `brightscript.format.compositeKeywords`: specify whether composite words (ie: "endif", "endfor") should be broken apart into their two-word format (ie: "end if", "end for")
* `brightscript.format.removeTrailingWhiteSpace`: specify whether trailing whitespace should be removed on format

## <a name="rokuRemote"></a></a>Roku Remote Control

This extension contributes keybindings to send keypresses to the Roku device through Roku's [External Control API](https://sdkdocs.roku.com/display/sdkdoc/External+Control+API#ExternalControlAPI-KeypressKeyValues) using `extension.brightscript.sendRemoteCommand` and passing the key to send as the `args`.

The basic 12 remote keys are already mapped with this extension as defined below. The keys are mapped using the `when` clause so it will only send the remote commands if the Panel has focus (`panelFocus`) AND the focus in NOT in the Debug Console REPL (`!inDebugRepl`) AND the Editor Find widget is NOT visible (`!findWidgetVisible`).


|Keyboard Key | Roku Remote Key | Keybinging Command|
|--|--|--|
|`Backspace` | Back Button | `extension.brightscript.pressBackButton` |
|`win+Backspace` (or `cmd+Backspace` on mac) | Backspace | `extension.brightscript.pressBackspaceButton` |
|`Escape` | Home Button | `extension.brightscript.pressHomeButton` |
|`up` | Up Button | `extension.brightscript.pressUpButton` |
|`down` | Down Button | `extension.brightscript.pressDownButton` |
|`right` | Right Button | `extension.brightscript.pressRightButton` |
|`left` | Left Button | `extension.brightscript.pressLeftButton` |
|`Enter` | Select Button (OK) | `extension.brightscript.pressSelectButton` |
|`win+Enter` (or `cmd+Enter` on mac) | Play Button | `extension.brightscript.pressPlayButton` |
|`win+left` (or `cmd+left` on mac) | Rev Button | `extension.brightscript.pressRevButton` |
|`win+right` (or `cmd+right` on mac) | Fwd Button | `extension.brightscript.pressFwdButton` |
|`win+8` (or `cmd+8` on mac) | Info Button | `extension.brightscript.pressStarButton` |


You can also press `win+k (or cmd+k on mac)` when the focus in in the Output Console and that will bring up a text input box to send text to the Roku device.

Example Keybindings for other keys:
```
{
"key": "Space",
"command": "extension.brightscript.sendRemoteCommand",
"args": "Lit_%20",
"when": "panelFocus && !inDebugRepl && !findWidgetVisible"
},
```

## Contributing

View our [developer guidelines](https://github.com/TwitchBronBron/vscode-brightscript-language/blob/master/developer-guidelines.md) for more information on how to contribute to this extension.
Expand Down
107 changes: 106 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,21 @@
"main": "./out/extension",
"activationEvents": [
"onDebug",
"onLanguage:brightscript"
"onLanguage:brightscript",
"onLanguage:xml",
"onCommand:extension.brightscript.sendRemoteCommand",
"onCommand:extension.brightscript.pressBackButton",
"onCommand:extension.brightscript.pressHomeButton",
"onCommand:extension.brightscript.pressUpButton",
"onCommand:extension.brightscript.pressDownButton",
"onCommand:extension.brightscript.pressRightButton",
"onCommand:extension.brightscript.pressLeftButton",
"onCommand:extension.brightscript.pressSelectButton",
"onCommand:extension.brightscript.pressPlayButton",
"onCommand:extension.brightscript.pressRevButton",
"onCommand:extension.brightscript.pressFwdButton",
"onCommand:extension.brightscript.pressStarButton",
"onCommand:extension.brightscript.sendRemoteText"
],
"contributes": {
"breakpoints": [
Expand Down Expand Up @@ -297,6 +311,12 @@
"default": true,
"description": "Removes all trailing whitespace at the end of each line.",
"scope": "resource"
},
"brightscript.remoteControl.host": {
"type": "string",
"default": "${promptForHost}",
"description": "IP address of the roku to remotely control",
"scope": "resource"
}
}
},
Expand All @@ -305,7 +325,92 @@
"command": "extension.brightscript.toggleXML",
"title": "Toggle xml/brs",
"category": "Brightscript"
},
{
"command": "extension.brightscript.sendRemoteCommand",
"title": "Send 'arg' button key press to Roku as a remote control",
"category": "Brightscript"
},
{
"command": "extension.brightscript.pressBackButton",
"title": "Press the Back button on the Roku remote",
"category": "Brightscript"
},
{
"command": "extension.brightscript.pressBackspaceButton",
"title": "Press the Backspace button on the Roku remote",
"category": "Brightscript"
},
{
"command": "extension.brightscript.pressHomeButton",
"title": "Press the Home button on the Roku remote",
"category": "Brightscript"
},
{
"command": "extension.brightscript.pressUpButton",
"title": "Press the Up button on the Roku remote",
"category": "Brightscript"
},
{
"command": "extension.brightscript.pressDownButton",
"title": "Press the Down button on the Roku remote",
"category": "Brightscript"
},
{
"command": "extension.brightscript.pressRightButton",
"title": "Press the Right button on the Roku remote",
"category": "Brightscript"
},
{
"command": "extension.brightscript.pressLeftButton",
"title": "Press the Left button on the Roku remote",
"category": "Brightscript"
},
{
"command": "extension.brightscript.pressSelectButton",
"title": "Press the Select button on the Roku remote",
"category": "Brightscript"
},
{
"command": "extension.brightscript.pressPlayButton",
"title": "Press the Play button on the Roku remote",
"category": "Brightscript"
},
{
"command": "extension.brightscript.pressRevButton",
"title": "Press the Rev button on the Roku remote",
"category": "Brightscript"
},
{
"command": "extension.brightscript.pressFwdButton",
"title": "Press the Fwd button on the Roku remote",
"category": "Brightscript"
},
{
"command": "extension.brightscript.pressStarButton",
"title": "Press the Star button on the Roku remote",
"category": "Brightscript"
},
{
"command": "extension.brightscript.sendRemoteText",
"title": "Send text characters to the Roku Device",
"category": "Brightscript"
}
],
"keybindings": [
{ "key": "Backspace", "command": "extension.brightscript.pressBackButton", "when": "panelFocus && !inDebugRepl && !findWidgetVisible"},
{ "key": "win+Backspace", "mac": "cmd+Backspace", "command": "extension.brightscript.pressBackspaceButton", "when": "panelFocus && !inDebugRepl && !findWidgetVisible"},
{ "key": "Escape", "command": "extension.brightscript.pressHomeButton", "when": "panelFocus && !inDebugRepl && !findWidgetVisible"},
{ "key": "up", "command": "extension.brightscript.pressUpButton", "when": "panelFocus && !inDebugRepl && !findWidgetVisible"},
{ "key": "down", "command": "extension.brightscript.pressDownButton", "when": "panelFocus && !inDebugRepl && !findWidgetVisible"},
{ "key": "right", "command": "extension.brightscript.pressRightButton", "when": "panelFocus && !inDebugRepl && !findWidgetVisible"},
{ "key": "left", "command": "extension.brightscript.pressLeftButton", "when": "panelFocus && !inDebugRepl && !findWidgetVisible"},
{ "key": "Enter", "command": "extension.brightscript.pressSelectButton", "when": "panelFocus && !inDebugRepl && !findWidgetVisible"},
{ "key": "win+Enter", "mac": "cmd+Enter", "command": "extension.brightscript.pressPlayButton", "when": "panelFocus && !inDebugRepl && !findWidgetVisible"},
{ "key": "win+left", "mac": "cmd+left", "command": "extension.brightscript.pressRevButton", "when": "panelFocus && !inDebugRepl && !findWidgetVisible"},
{ "key": "win+right", "mac": "cmd+right", "command": "extension.brightscript.pressFwdButton", "when": "panelFocus && !inDebugRepl && !findWidgetVisible"},
{ "key": "win+8", "mac": "cmd+8", "command": "extension.brightscript.pressStarButton", "when": "panelFocus && !inDebugRepl && !findWidgetVisible"},
{ "key": "win+k", "mac": "cmd+k", "command": "extension.brightscript.sendRemoteText", "when": "panelFocus && !inDebugRepl && !findWidgetVisible"}
]
},
"watch": {
Expand Down
100 changes: 99 additions & 1 deletion src/BrightScriptCommands.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import * as request from 'request';
import * as vscode from 'vscode';

// tslint:disable-next-line
export var __request: any = request;

import BrightScriptFileUtils from './BrightScriptFileUtils';

// georgejecook: I can't find a way to stub/mock a TypeScript class constructor
Expand All @@ -15,12 +19,71 @@ export default class BrightScriptCommands {
}

private fileUtils: BrightScriptFileUtils;
private context: vscode.ExtensionContext;
private host: string;
public function;

public registerCommands({ subscriptions }: vscode.ExtensionContext) {
public registerCommands(context: vscode.ExtensionContext) {
this.context = context;
let subscriptions = context.subscriptions;

subscriptions.push(vscode.commands.registerCommand('extension.brightscript.toggleXML', () => {
this.onToggleXml();
} ));
subscriptions.push(vscode.commands.registerCommand('extension.brightscript.sendRemoteCommand', (key: string) => {
this.sendRemoteCommand(key);
} ));

subscriptions.push(vscode.commands.registerCommand('extension.brightscript.sendRemoteText', async () => {
let stuffUserTyped: string = await vscode.window.showInputBox({
placeHolder: 'Press enter to send all typed characters to the Roku',
value: ''
});
if (stuffUserTyped) {
for (let character of stuffUserTyped) {
let commandToSend: string = 'Lit_' + encodeURIComponent(character);
await this.sendRemoteCommand(commandToSend);
}
}
vscode.commands.executeCommand('workbench.action.focusPanel');
} ));

subscriptions.push(vscode.commands.registerCommand('extension.brightscript.pressBackButton', () => {
this.sendRemoteCommand('Back');
}));
subscriptions.push(vscode.commands.registerCommand('extension.brightscript.pressBackspaceButton', () => {
this.sendRemoteCommand('Backspace');
}));
subscriptions.push(vscode.commands.registerCommand('extension.brightscript.pressHomeButton', () => {
this.sendRemoteCommand('Home');
}));
subscriptions.push(vscode.commands.registerCommand('extension.brightscript.pressUpButton', () => {
this.sendRemoteCommand('Up');
}));
subscriptions.push(vscode.commands.registerCommand('extension.brightscript.pressDownButton', () => {
this.sendRemoteCommand('Down');
}));
subscriptions.push(vscode.commands.registerCommand('extension.brightscript.pressRightButton', () => {
this.sendRemoteCommand('Right');
}));
subscriptions.push(vscode.commands.registerCommand('extension.brightscript.pressLeftButton', () => {
this.sendRemoteCommand('Left');
}));
subscriptions.push(vscode.commands.registerCommand('extension.brightscript.pressSelectButton', () => {
this.sendRemoteCommand('Select');
}));
subscriptions.push(vscode.commands.registerCommand('extension.brightscript.pressPlayButton', () => {
this.sendRemoteCommand('Play');
}));
subscriptions.push(vscode.commands.registerCommand('extension.brightscript.pressRevButton', () => {
this.sendRemoteCommand('Rev');
}));
subscriptions.push(vscode.commands.registerCommand('extension.brightscript.pressFwdButton', () => {
this.sendRemoteCommand('Fwd');
}));
subscriptions.push(vscode.commands.registerCommand('extension.brightscript.pressStarButton', () => {
this.sendRemoteCommand('Info');
}));
}

public async openFile(filename: string) {
Expand All @@ -38,4 +101,39 @@ export default class BrightScriptCommands {
}
}
}

public async sendRemoteCommand(key: string) {
await this.getRemoteHost();
if (this.host) {
let clickUrl = `http://${this.host}:8060/keypress/${key}`;
console.log(`send ${clickUrl}`);
return new Promise(function(resolve, reject) {
request.post(clickUrl, function(err, response) {
if (err) {
return reject(err);
}
return resolve(response);
});
});
}
}

public async getRemoteHost() {
this.host = await this.context.workspaceState.get('remoteHost');
if (!this.host) {
let config = await vscode.workspace.getConfiguration('brightscript.remoteControl', null);
this.host = config.get('host');
if (this.host === '${promptForHost}') {
this.host = await vscode.window.showInputBox({
placeHolder: 'The IP address of your Roku device',
value: ''
});
}
}
if (!this.host) {
throw new Error('Can\'t send command: host is required.');
} else {
await this.context.workspaceState.update('remoteHost', this.host);
}
}
}
17 changes: 13 additions & 4 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function activate(context: vscode.ExtensionContext) {
}, new Formatter());
outputChannel = vscode.window.createOutputChannel('BrightScript Log');

context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('brightscript', new BrightScriptConfigurationProvider()));
context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('brightscript', new BrightScriptConfigurationProvider(context)));

//register the definition provider
const debugErrorHandler: DebugErrorHandler = new DebugErrorHandler(outputChannel);
Expand Down Expand Up @@ -70,6 +70,13 @@ export function activate(context: vscode.ExtensionContext) {
}

class BrightScriptConfigurationProvider implements vscode.DebugConfigurationProvider {

public constructor(context: vscode.ExtensionContext) {
this.context = context;
}

public context: vscode.ExtensionContext;

/**
* Massage a debug configuration just before a debug session is being launched,
* e.g. add all missing attributes to the debug configuration.
Expand All @@ -94,9 +101,11 @@ class BrightScriptConfigurationProvider implements vscode.DebugConfigurationProv
placeHolder: 'The IP address of your Roku device',
value: ''
});
if (!config.host) {
throw new Error('Debug session terminated: host is required.');
}
}
if (!config.host) {
throw new Error('Debug session terminated: host is required.');
} else {
await this.context.workspaceState.update('remoteHost', config.host);
}
//prompt for password if not hardcoded
if (config.password === '${promptForPassword}') {
Expand Down

0 comments on commit f425536

Please sign in to comment.