blob: 4704241e74e0deb8f814520a78a5d0e047110f38 [file] [log] [blame] [edit]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* Keep all general utility functions here, or in common_util.
*/
import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';
import {BoardOrHost} from './common/chromiumos/board_or_host';
import * as commonUtil from './common/common_util';
import {Chroot} from './common/common_util';
import * as cros from './common/cros';
import * as config from './services/config';
const loggerInstance = vscode.window.createOutputChannel(
'ChromiumIDE: UI Actions'
);
/**
* Return the logger that should be used by actions done in UI. For example,
* navigating to CodeSearch, opening listing packages worked on (view), and so on.
*
* Tasks that run in background or produce lots of logs should create their own loggers.
* See cros lint and C++ code completion for examples.
*/
// TODO(ttylenda): Move this function to a separate file in ui/.
export function getUiLogger(): vscode.OutputChannel {
return loggerInstance;
}
export const SHOW_UI_LOG: vscode.Command = {
command: 'chromiumide.showUiLog',
title: '',
};
/**
* Get the target board, or ask the user to select one.
*
* @returns The targe board name. null if the user ignores popup. NoBoardError if there is no
* available board.
*/
export async function getOrSelectTargetBoard(
chroot: cros.WrapFs<Chroot>
): Promise<BoardOrHost | null | NoBoardError> {
const board = config.board.get();
if (board) {
return BoardOrHost.parse(board);
}
return await selectAndUpdateTargetBoard(chroot, {suggestMostRecent: true});
}
export class NoBoardError extends Error {
constructor() {
super(
'no board has been setup; run setup_board for the board you want to use, ' +
'and revisit the editor'
);
}
}
/**
* Ask user to select the board to use. If user selects a board, the config
* is updated with the board name.
*
* @params options If options.suggestMostRecent is true, the board most recently
* used is proposed to the user, before showing the board picker.
*/
export async function selectAndUpdateTargetBoard(
chroot: cros.WrapFs<Chroot>,
options: {
suggestMostRecent: boolean;
}
): Promise<BoardOrHost | null | NoBoardError> {
const boards = await cros.getSetupBoardsRecentFirst(
chroot,
new cros.WrapFs(commonUtil.crosOutDir(commonUtil.sourceDir(chroot.root)))
);
const board = await selectBoard(boards, options.suggestMostRecent);
if (board instanceof Error) {
return board;
}
if (board) {
// TODO(oka): This should be per chroot (i.e. Remote) setting, instead of global (i.e. User).
await config.board.update(board.toString());
}
return board;
}
async function selectBoard(
boards: string[],
suggestMostRecent: boolean
): Promise<BoardOrHost | null | NoBoardError> {
if (boards.length === 0) {
return new NoBoardError();
}
if (suggestMostRecent) {
const mostRecent = boards[0];
const selection = await commonUtil.withTimeout(
vscode.window.showWarningMessage(
`Target board is not set. Do you use ${mostRecent}?`,
{
title: 'Yes',
},
{
title: 'Customize',
}
),
30 * 1000
);
if (!selection) {
return null;
}
switch (selection.title) {
case 'Yes':
return BoardOrHost.parse(mostRecent);
case 'Customize':
break;
default:
return null;
}
}
const choice = await vscode.window.showQuickPick(boards, {
title: 'Target board',
});
return typeof choice === 'string' ? BoardOrHost.parse(choice) : null;
}
/**
* Returns VSCode executable given appRoot and the name of the executable under bin directory.
* Returns Error if executable is not found.
*/
function findExecutable(appRoot: string, name: string): string | Error {
let dir = appRoot;
while (dir !== '/') {
const exe = path.join(dir, 'bin', name);
if (fs.existsSync(exe)) {
return exe;
}
dir = path.dirname(dir);
}
return new Error(`${name} was not found for ${appRoot}`);
}
/**
* Returns VSCode executable path, or error in case it's not found.
*/
export function vscodeExecutablePath(
appRoot = vscode.env.appRoot,
appName = vscode.env.appName,
remoteName = vscode.env.remoteName
): string | Error {
let executableName;
// code-server's appName differs depending on the version.
if (appName === 'code-server' || appName === 'Code - OSS') {
executableName = 'code-server';
} else if (appName === 'Visual Studio Code') {
executableName = 'code';
} else if (appName === 'Visual Studio Code - Insiders') {
executableName = 'code-insiders';
} else {
return new Error(`vscode app name not recognized: ${appName}`);
}
const executableSubPath =
remoteName === 'ssh-remote'
? path.join('remote-cli', executableName)
: executableName;
return findExecutable(appRoot, executableSubPath);
}
export function isCodeServer(appHost = vscode.env.appHost): boolean {
// vscode.env.appHost stores the hosted location of the application.
// On desktop this is 'desktop'. In the web it is the specified embedder.
// See https://code.visualstudio.com/api/references/vscode-api#env
// TODO(b/232050207): Check if the IDE is run on code-server or on the
// desktop app more reliably.
return appHost !== 'desktop';
}