• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rokucommunity / vscode-brightscript-language / #2719

24 Aug 2022 12:51PM UTC coverage: 41.691% (+0.02%) from 41.675%
#2719

push

TwitchBronBron
2.35.0

477 of 1427 branches covered (33.43%)

Branch coverage included in aggregate %.

1126 of 2418 relevant lines covered (46.57%)

7.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

40.1
/src/BrightScriptCommands.ts
1
import * as request from 'request';
1✔
2
import * as vscode from 'vscode';
1✔
3
import BrightScriptFileUtils from './BrightScriptFileUtils';
1✔
4
import { GlobalStateManager } from './GlobalStateManager';
1✔
5
import { brighterScriptPreviewCommand } from './commands/BrighterScriptPreviewCommand';
1✔
6
import { languageServerInfoCommand } from './commands/LanguageServerInfoCommand';
1✔
7
import { util } from './util';
1✔
8
import { util as rokuDebugUtil } from 'roku-debug/dist/util';
1✔
9
import type { RemoteControlManager, RemoteControlModeInitiator } from './managers/RemoteControlManager';
10
import type { WhatsNewManager } from './managers/WhatsNewManager';
11
import type { ActiveDeviceManager } from './ActiveDeviceManager';
12

13
export class BrightScriptCommands {
1✔
14

15
    constructor(
16
        private remoteControlManager: RemoteControlManager,
10✔
17
        private whatsNewManager: WhatsNewManager,
10✔
18
        private context: vscode.ExtensionContext,
10✔
19
        private activeDeviceManager: ActiveDeviceManager
10✔
20
    ) {
21
        this.fileUtils = new BrightScriptFileUtils();
10✔
22
    }
23

24
    private fileUtils: BrightScriptFileUtils;
25
    private host: string;
26

27
    public registerCommands() {
28

29
        brighterScriptPreviewCommand.register(this.context);
6✔
30
        languageServerInfoCommand.register(this.context);
6✔
31

32
        this.registerGeneralCommands();
6✔
33

34
        this.registerCommand('sendRemoteCommand', async (key: string) => {
6✔
35
            await this.sendRemoteCommand(key);
×
36
        });
37

38
        //the "Refresh" button in the Devices list
39
        this.registerCommand('refreshDeviceList', (key: string) => {
6✔
40
            this.activeDeviceManager.refresh();
×
41
        });
42

43
        this.registerCommand('sendRemoteText', async () => {
6✔
44
            let items: vscode.QuickPickItem[] = [];
×
45
            for (const item of new GlobalStateManager(this.context).sendRemoteTextHistory) {
×
46
                items.push({ label: item });
×
47
            }
48

49
            const stuffUserTyped = await util.showQuickPickInputBox({
×
50
                placeholder: 'Press enter to send all typed characters to the Roku',
51
                items: items
52
            });
53
            console.log('userInput', stuffUserTyped);
×
54

55
            if (stuffUserTyped) {
×
56
                new GlobalStateManager(this.context).addTextHistory(stuffUserTyped);
×
57
                let fallbackToHttp = true;
×
58
                await this.getRemoteHost();
×
59
                //TODO fix SceneGraphDebugCommandController to not timeout so quickly
60
                // try {
61
                //     let commandController = new SceneGraphDebugCommandController(this.host);
62
                //     let response = await commandController.type(stuffUserTyped);
63
                //     if (!response.error) {
64
                //         fallbackToHttp = false;
65
                //     }
66
                // } catch (error) {
67
                //     console.error(error);
68
                //     // Let this fallback to the old HTTP based logic
69
                // }
70

71
                if (fallbackToHttp) {
×
72
                    for (let character of stuffUserTyped) {
×
73
                        await this.sendAsciiToDevice(character);
×
74
                    }
75
                }
76
            }
77
            await vscode.commands.executeCommand('workbench.action.focusPanel');
×
78
        });
79

80
        this.registerCommand('toggleRemoteControlMode', (initiator: RemoteControlModeInitiator) => {
6✔
81
            return this.remoteControlManager.toggleRemoteControlMode(initiator);
×
82
        });
83

84
        this.registerCommand('enableRemoteControlMode', () => {
6✔
85
            return this.remoteControlManager.setRemoteControlMode(true, 'command');
×
86
        });
87

88
        this.registerCommand('disableRemoteControlMode', () => {
6✔
89
            return this.remoteControlManager.setRemoteControlMode(false, 'command');
×
90
        });
91

92
        this.registerCommand('pressBackButton', async () => {
6✔
93
            await this.sendRemoteCommand('Back');
×
94
        });
95

96
        this.registerCommand('pressBackspaceButton', async () => {
6✔
97
            await this.sendRemoteCommand('Backspace');
×
98
        });
99

100
        this.registerCommand('pressHomeButton', async () => {
6✔
101
            await this.sendRemoteCommand('Home');
×
102
        });
103

104
        this.registerCommand('pressUpButton', async () => {
6✔
105
            await this.sendRemoteCommand('Up');
×
106
        });
107

108
        this.registerCommand('pressDownButton', async () => {
6✔
109
            await this.sendRemoteCommand('Down');
×
110
        });
111

112
        this.registerCommand('pressRightButton', async () => {
6✔
113
            await this.sendRemoteCommand('Right');
×
114
        });
115

116
        this.registerCommand('pressLeftButton', async () => {
6✔
117
            await this.sendRemoteCommand('Left');
×
118
        });
119

120
        this.registerCommand('pressSelectButton', async () => {
6✔
121
            await this.sendRemoteCommand('Select');
×
122
        });
123

124
        this.registerCommand('pressPlayButton', async () => {
6✔
125
            await this.sendRemoteCommand('Play');
×
126
        });
127

128
        this.registerCommand('pressRevButton', async () => {
6✔
129
            await this.sendRemoteCommand('Rev');
×
130
        });
131

132
        this.registerCommand('pressFwdButton', async () => {
6✔
133
            await this.sendRemoteCommand('Fwd');
×
134
        });
135

136
        this.registerCommand('pressStarButton', async () => {
6✔
137
            await this.sendRemoteCommand('Info');
×
138
        });
139

140
        this.registerCommand('pressInstantReplayButton', async () => {
6✔
141
            await this.sendRemoteCommand('InstantReplay');
×
142
        });
143

144
        this.registerCommand('pressSearchButton', async () => {
6✔
145
            await this.sendRemoteCommand('Search');
×
146
        });
147

148
        this.registerCommand('pressEnterButton', async () => {
6✔
149
            await this.sendRemoteCommand('Enter');
×
150
        });
151

152
        this.registerCommand('pressFindRemote', async () => {
6✔
153
            await this.sendRemoteCommand('FindRemote');
×
154
        });
155

156
        this.registerCommand('pressVolumeDown', async () => {
6✔
157
            await this.sendRemoteCommand('VolumeDown');
×
158
        });
159

160
        this.registerCommand('pressVolumeMute', async () => {
6✔
161
            await this.sendRemoteCommand('VolumeMute');
×
162
        });
163

164
        this.registerCommand('pressVolumeUp', async () => {
6✔
165
            await this.sendRemoteCommand('FindVolumeUp');
×
166
        });
167

168
        this.registerCommand('pressPowerOff', async () => {
6✔
169
            await this.sendRemoteCommand('PowerOff');
×
170
        });
171

172
        this.registerCommand('pressChannelUp', async () => {
6✔
173
            await this.sendRemoteCommand('ChannelUp');
×
174
        });
175

176
        this.registerCommand('pressChannelDown', async () => {
6✔
177
            await this.sendRemoteCommand('ChannelDown');
×
178
        });
179

180
        this.registerCommand('changeTvInput', async (host?: string) => {
6✔
181
            const selectedInput = await vscode.window.showQuickPick([
×
182
                'InputHDMI1',
183
                'InputHDMI2',
184
                'InputHDMI3',
185
                'InputHDMI4',
186
                'InputAV1',
187
                'InputTuner'
188
            ]);
189

190
            if (selectedInput) {
×
191
                await this.sendRemoteCommand(selectedInput, host);
×
192
            }
193
        });
194

195
        this.registerKeyboardInputs();
6✔
196
    }
197

198
    /**
199
     * Registers all the commands for a-z, A-Z, 0-9, and all the primary character such as !, @, #, ', ", etc...
200
     */
201
    private registerKeyboardInputs() {
202
        // Get all the keybindings from our package.json
203
        const extension = vscode.extensions.getExtension('RokuCommunity.brightscript');
6✔
204
        const keybindings = (extension.packageJSON.contributes.keybindings as Array<{
6✔
205
            key: string;
206
            command: string;
207
            when: string;
208
            args: any;
209
        }>);
210

211
        for (let keybinding of keybindings) {
6✔
212
            // Find every keybinding that is related to sending text characters to the device
213
            if (keybinding.command.includes('.sendAscii+')) {
870✔
214

215
                if (!keybinding.args) {
576!
216
                    throw new Error(`Can not register command: ${keybinding.command}. Missing Arguments.`);
×
217
                }
218

219
                // Dynamically register the the command defined in the keybinding
220
                this.registerCommand(keybinding.command, async (character: string) => {
576✔
221
                    await this.sendAsciiToDevice(character);
×
222
                });
223
            }
224
        }
225
    }
226

227
    private registerGeneralCommands() {
228
        //a command that does absolutely nothing. It's here to allow us to absorb unsupported keypresses when in **remote control mode**.
229
        this.registerCommand('doNothing', () => { });
6✔
230

231
        this.registerCommand('toggleXML', async () => {
6✔
232
            await this.onToggleXml();
×
233
        });
234

235
        this.registerCommand('clearGlobalState', async () => {
6✔
236
            new GlobalStateManager(this.context).clear();
×
237
            await vscode.window.showInformationMessage('BrightScript Language extension global state cleared');
×
238
        });
239

240
        this.registerCommand('copyToClipboard', async (value: string) => {
6✔
241
            try {
×
242
                await vscode.env.clipboard.writeText(value);
×
243
                await vscode.window.showInformationMessage(`Copied to clipboard: ${value}`);
×
244
            } catch (error) {
245
                await vscode.window.showErrorMessage(`Could not copy value to clipboard`);
×
246
            }
247
        });
248

249
        this.registerCommand('openUrl', async (url: string) => {
6✔
250
            try {
×
251
                await vscode.env.openExternal(vscode.Uri.parse(url));
×
252
            } catch (error) {
253
                await vscode.window.showErrorMessage(`Tried to open url but failed: ${url}`);
×
254
            }
255
        });
256

257
        this.registerCommand('showReleaseNotes', () => {
6✔
258
            this.whatsNewManager.showReleaseNotes();
×
259
        });
260
    }
261

262
    public async openFile(filename: string, range: vscode.Range = null, preview = false): Promise<boolean> {
×
263
        let uri = vscode.Uri.file(filename);
×
264
        try {
×
265
            let doc = await vscode.workspace.openTextDocument(uri); // calls back into the provider
×
266
            await vscode.window.showTextDocument(doc, { preview: preview });
×
267
            if (range) {
×
268
                await this.gotoRange(range);
×
269
            }
270
        } catch (e) {
271
            return false;
×
272
        }
273
        return true;
×
274
    }
275

276
    private async gotoRange(range: vscode.Range) {
277
        let editor = vscode.window.activeTextEditor;
×
278
        editor.selection = new vscode.Selection(
×
279
            range.start.line,
280
            range.start.character,
281
            range.start.line,
282
            range.start.character
283
        );
284
        await vscode.commands.executeCommand('revealLine', {
×
285
            lineNumber: range.start.line,
286
            at: 'center'
287
        });
288
    }
289

290
    public async onToggleXml() {
291
        if (vscode.window.activeTextEditor) {
3✔
292
            const currentDocument = vscode.window.activeTextEditor.document;
2✔
293
            let alternateFileName = this.fileUtils.getAlternateFileName(currentDocument.fileName);
2✔
294
            if (alternateFileName) {
2✔
295
                if (
1!
296
                    !await this.openFile(alternateFileName) &&
2✔
297
                    alternateFileName.toLowerCase().endsWith('.brs')
298
                ) {
299
                    await this.openFile(this.fileUtils.getBsFileName(alternateFileName));
×
300
                }
301
            }
302
        }
303
    }
304

305
    public async sendRemoteCommand(key: string, host?: string) {
306
        // do we have a temporary override?
307
        if (!host) {
×
308
            // Get the long lived host ip
309
            await this.getRemoteHost();
×
310
            host = this.host;
×
311
        }
312

313
        if (host) {
×
314
            let clickUrl = `http://${host}:8060/keypress/${key}`;
×
315
            console.log(`send ${clickUrl}`);
×
316
            return new Promise((resolve, reject) => {
×
317
                request.post(clickUrl, (err, response) => {
×
318
                    if (err) {
×
319
                        return reject(err);
×
320
                    }
321
                    return resolve(response);
×
322
                });
323
            });
324
        }
325
    }
326

327
    public async getRemoteHost() {
328
        this.host = await this.context.workspaceState.get('remoteHost');
×
329
        if (!this.host) {
×
330
            let config = vscode.workspace.getConfiguration('brightscript.remoteControl', null);
×
331
            this.host = config.get('host');
×
332
            // eslint-disable-next-line no-template-curly-in-string
333
            if (this.host === '${promptForHost}') {
×
334
                this.host = await vscode.window.showInputBox({
×
335
                    placeHolder: 'The IP address of your Roku device',
336
                    value: ''
337
                });
338
            }
339
        }
340
        if (!this.host) {
×
341
            throw new Error('Can\'t send command: host is required.');
×
342
        } else {
343
            await this.context.workspaceState.update('remoteHost', this.host);
×
344
        }
345
        if (this.host) {
×
346
            this.host = await rokuDebugUtil.dnsLookup(this.host);
×
347
        }
348
    }
349

350
    private registerCommand(name: string, callback: (...args: any[]) => any, thisArg?: any) {
351
        const prefix = 'extension.brightscript.';
786✔
352
        const commandName = name.startsWith(prefix) ? name : prefix + name;
786✔
353
        this.context.subscriptions.push(vscode.commands.registerCommand(commandName, callback, thisArg));
786✔
354
    }
355

356
    private async sendAsciiToDevice(character: string) {
357
        let commandToSend: string = 'Lit_' + encodeURIComponent(character);
×
358
        await this.sendRemoteCommand(commandToSend);
×
359
    }
360
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc