| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import * as vscode from 'vscode'; |
| import { Ffx, FfxEventType, FuchsiaDevice } from './ffx'; |
| import * as logger from './logger'; |
| import { COMMAND_TARGET_SELECT_ID } from './common-config'; |
| |
| const STATUS_BAR_PRIORITY = 0; |
| |
| // Exported so it is accessible in the test module. |
| export const STATUS_PREFIX_EMPTY = '$(circle-slash)'; |
| export const STATUS_PREFIX_NOT_CONNECTED = '$(vm-outline)'; |
| export const STATUS_PREFIX_CONNECTED = '$(vm)'; |
| /** |
| * Class that holds quick pick item data for the function showQuickPick(). Each instance is |
| * presented as a row in a menu created when clicking on the target status bar widget. |
| */ |
| export class TargetPickAction implements vscode.QuickPickItem { |
| label: string; |
| picked?: boolean | undefined; |
| device: FuchsiaDevice; |
| command: string; |
| |
| constructor(label: string, device: FuchsiaDevice, command: string) { |
| this.label = label; |
| this.device = device; |
| this.command = command; |
| } |
| } |
| |
| /** |
| * Status bar widget to query and send commands to the target (fuchsia device). |
| */ |
| export class TargetStatusBarItem { |
| private ffxTools: Ffx; |
| private fuchsiaStatusBarItem: vscode.StatusBarItem; |
| |
| private setBarItemText(targetName: string | undefined, connected: boolean) { |
| let displayName: string; |
| let icon: string; |
| let backgroundColor: vscode.ThemeColor | undefined; |
| |
| if (targetName === undefined) { |
| icon = STATUS_PREFIX_EMPTY; |
| displayName = 'Not connected'; |
| backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground'); |
| } else if (!connected) { |
| icon = STATUS_PREFIX_NOT_CONNECTED; |
| displayName = `${targetName} (not connected)`; |
| backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground'); |
| } else { |
| icon = STATUS_PREFIX_CONNECTED; |
| displayName = targetName; |
| backgroundColor = undefined; |
| } |
| |
| this.fuchsiaStatusBarItem.text = `${icon} ${displayName}`; |
| this.fuchsiaStatusBarItem.tooltip = `Fuchsia target device: ${displayName}`; |
| this.fuchsiaStatusBarItem.backgroundColor = backgroundColor; |
| } |
| |
| // Build the list of QuickPickItems for targets. |
| // The first item in the list is the default target, followed by the actions |
| // that can be performed on that target. |
| // Items to set the other targets as the default follow. |
| private buildTargetQuickPickItems(targetList: { [key: string]: FuchsiaDevice }): |
| TargetPickAction[] { |
| let items: TargetPickAction[] = []; |
| for (let key in targetList) { |
| let device = targetList[key]; |
| // Ignore devices that are not connected. |
| if (!device.connected) { continue; } |
| |
| if (device.isDefault) { |
| // Add items to front of list. |
| let deviceItem = new TargetPickAction(`Default target: ${key}`, device, |
| 'fuchsia.internal.target.setDefault'); |
| deviceItem.picked = true; |
| let rebootItem = new TargetPickAction(`Reboot ${key}`, device, |
| 'fuchsia.target.reboot'); |
| let poweroffItem = new TargetPickAction(`Power off ${key}`, device, |
| 'fuchsia.target.powerOff'); |
| let logItem = new TargetPickAction(`Show log for ${key}`, device, |
| 'fuchsia.viewLogs'); |
| items = [deviceItem, logItem, rebootItem, poweroffItem, ...items]; |
| } else { |
| items.push(new TargetPickAction(`Set ${key} as default`, device, |
| 'fuchsia.internal.target.setDefault')); |
| } |
| } |
| return items; |
| } |
| |
| constructor(readonly contextSubscriptions: { dispose(): any }[], ffxTools: Ffx) { |
| this.ffxTools = ffxTools; |
| |
| // Menu for target actions |
| contextSubscriptions.push( |
| vscode.commands.registerCommand(COMMAND_TARGET_SELECT_ID, async () => { |
| // Try to find the ffx path again if it is not set. |
| if (this.ffxTools.hasValidFfxPath() === false) { |
| await this.ffxTools.toolFinder?.updateFfxPath(false); |
| } |
| |
| this.ffxTools.getTargetList().then(async targetList => { |
| let items = this.buildTargetQuickPickItems(targetList); |
| if (items.length > 0) { |
| let action = await vscode.window.showQuickPick(items); |
| if (action === undefined) { |
| return; |
| } |
| await vscode.commands.executeCommand(action.command, action.device); |
| } else { |
| let msg = Object.keys(targetList).length > 0 ? |
| 'No available devices found.' : 'No devices found.'; |
| logger.warn(msg); |
| void vscode.window.showWarningMessage(msg); |
| } |
| }) |
| // If there is a problem getting the list, just log it. The quickpick |
| // will be empty. |
| .catch(err => { |
| logger.warn(`Could not build target list: ${err}`); |
| void vscode.window.showWarningMessage('Failed to retrieve devices.'); |
| }); |
| }) |
| ); |
| |
| // Target status-bar item |
| this.fuchsiaStatusBarItem = vscode.window |
| .createStatusBarItem(vscode.StatusBarAlignment.Left, STATUS_BAR_PRIORITY); |
| this.fuchsiaStatusBarItem.command = COMMAND_TARGET_SELECT_ID; |
| this.fuchsiaStatusBarItem.tooltip = 'Fuchsia target device.'; |
| contextSubscriptions.push(this.fuchsiaStatusBarItem); |
| this.setBarItemText(/*targetName*/ undefined, /*Connected*/ false); |
| this.fuchsiaStatusBarItem.show(); |
| |
| // Clear the default target if there is no ffx found. |
| this.ffxTools.onDidChangeConfiguration(eventType => { |
| switch (eventType) { |
| case FfxEventType.ffxPathReset: |
| this.setBarItemText(/*targetName*/ undefined, /*Connected*/ false); |
| break; |
| } |
| }); |
| |
| // When the default target is set, update the item text. |
| this.ffxTools.onSetTarget(target => { |
| this.setBarItemText(target?.nodeName, target?.connected ?? false); |
| }); |
| } |
| } |