Skip to content

Commit

Permalink
Add multi-folder workspace support.
Browse files Browse the repository at this point in the history
  • Loading branch information
agrojean-ledger committed Jul 5, 2023
1 parent b1e820c commit 965c509
Show file tree
Hide file tree
Showing 9 changed files with 462 additions and 293 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.0.5] - 2023-07-05

### Added

- Add multi-folder workspace support. User can now choose which app to build from a quickpick menu.
## [0.0.4] - 2023-07-04

### Added
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ This extension contributes the following settings:

## Release Notes

### 0.0.5

Add multi-folder workspace support. User can now choose which app to build from a quickpick menu.

### 0.0.4

Adds container terminal task to side bar items.
Expand Down
11 changes: 6 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "ledger-dev-tools",
"displayName": "ledger-dev-tools",
"description": "Tools to accelerate development of apps for Ledger devices.",
"version": "0.0.4",
"version": "0.0.5",
"publisher": "LedgerHQ",
"license": "Apache",
"icon": "resources/ledger-square.png",
Expand Down Expand Up @@ -61,17 +61,16 @@
{
"command": "selectTarget",
"title": "Select the device you want to build your app for."
},
{
"command": "showAppList",
"title": "Select the app you want to build."
}
],

"configuration": {
"title": "Ledger Developer Tools",
"properties": {
"ledgerDevTools.buildDirRelativePath": {
"type": "string",
"default": true,
"description": "Set the build directory relative path of your app."
},
"ledgerDevTools.dockerImage": {
"type": "string",
"default": "ghcr.io/ledgerhq/ledger-app-builder/ledger-app-dev-tools:latest",
Expand Down Expand Up @@ -100,6 +99,7 @@
"@typescript-eslint/parser": "^5.59.8",
"eslint": "^8.41.0",
"glob": "^8.1.0",
"fast-glob": "^3.3.0",
"mocha": "^10.2.0",
"typescript": "^5.1.3",
"ts-loader": "^9.4.3",
Expand Down
73 changes: 73 additions & 0 deletions src/appSelector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as vscode from "vscode";
import * as fs from "fs";
import * as path from "path";
import * as fg from "fast-glob";
import { StatusBarManager } from "./statusBar";
import { TreeDataProvider } from "./treeView";
import { TaskProvider } from "./taskProvider";
const APP_DETECTION_FILE: string = "Makefile";
const APP_DETECTION_STRING: string = "include $(BOLOS_SDK)/Makefile.defines";

export interface App {
appName: string;
appFolder: vscode.WorkspaceFolder;
containerName: string;
buildDirPath: string;
}

let appList: App[] = [];
let selectedApp: App | undefined;

export function findAppsInWorkspace(): App[] | undefined {
const workspaceFolders = vscode.workspace.workspaceFolders;

if (workspaceFolders) {
workspaceFolders.forEach((folder) => {
const appFolder = folder;
const appName = path.basename(appFolder.uri.fsPath);
const containerName = `${appName}-container`;
const searchPattern = path.join(folder.uri.fsPath, `**/${APP_DETECTION_FILE}`);
const makefiles = fg.sync(searchPattern, { onlyFiles: true, deep: 2 });

makefiles.forEach((makefile) => {
const buildDirPath = path.dirname(makefile);
const fileContent = fs.readFileSync(makefile, "utf-8");
if (fileContent.includes(APP_DETECTION_STRING)) {
appList.push({ appName: appName, appFolder: appFolder, containerName: containerName, buildDirPath: buildDirPath });
}
});
});
}

return appList;
}

export async function showAppSelectorMenu(
statusManager: StatusBarManager,
treeDataProvider: TreeDataProvider,
taskProvider: TaskProvider
) {
const appNames = appList.map((app) => app.appName);
const result = await vscode.window.showQuickPick(appNames, {
placeHolder: "Please select an app",
onDidSelectItem: (item) => {
selectedApp = appList.find((app) => app.appName === item);
},
});
taskProvider.generateTasks();
statusManager.autoUpdateDevImageItem();
treeDataProvider.updateTargetLabel();
return result;
}

export function getSelectedApp() {
return selectedApp;
}

export function setSelectedApp(app: App) {
selectedApp = app;
}

export function getAppList() {
return appList;
}
53 changes: 27 additions & 26 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
"use strict";

import * as vscode from "vscode";
import { TaskProvider } from "./taskProvider";
import { TaskProvider, taskType } from "./taskProvider";
import { TreeDataProvider } from "./treeView";
import { showTargetSelectorMenu } from "./targetSelector";
import { StatusBarManager, DevImageStatus } from "./statusBar";
import { findAppsInWorkspace, getSelectedApp, setSelectedApp, showAppSelectorMenu } from "./appSelector";

console.log("Ledger: Loading extension");

export function activate(context: vscode.ExtensionContext) {
console.log(`Ledger: activating extension in mode`);

let treeProvider = new TreeDataProvider();
vscode.window.registerTreeDataProvider("exampleView", treeProvider);
const appList = findAppsInWorkspace();
if (appList) {
setSelectedApp(appList[0]);
}

let taskProvider = new TaskProvider();
context.subscriptions.push(vscode.tasks.registerTaskProvider(TaskProvider.taskType, taskProvider));
context.subscriptions.push(vscode.tasks.registerTaskProvider(taskType, taskProvider));

let statusBarManager = new StatusBarManager();
statusBarManager.updateDevImageItem(getContainerStatus());
statusBarManager.autoUpdateDevImageItem();

let treeProvider = new TreeDataProvider();
vscode.window.registerTreeDataProvider("exampleView", treeProvider);

context.subscriptions.push(
vscode.commands.registerCommand("selectTarget", () => {
Expand All @@ -32,6 +38,12 @@ export function activate(context: vscode.ExtensionContext) {
})
);

context.subscriptions.push(
vscode.commands.registerCommand("showAppList", () => {
showAppSelectorMenu(statusBarManager, treeProvider, taskProvider);
})
);

vscode.tasks.onDidStartTask((event) => {
const taskName = event.execution.task.name;
if (taskName.startsWith("Run dev-tools image")) {
Expand All @@ -42,7 +54,16 @@ export function activate(context: vscode.ExtensionContext) {
vscode.tasks.onDidEndTask((event) => {
const taskName = event.execution.task.name;
if (taskName.startsWith("Run dev-tools image")) {
statusBarManager.updateDevImageItem(getContainerStatus());
statusBarManager.autoUpdateDevImageItem();
}
});

vscode.workspace.onDidChangeWorkspaceFolders(() => {
const appList = findAppsInWorkspace();
if (appList) {
if (!getSelectedApp()) {
setSelectedApp(appList[0]);
}
}
});

Expand All @@ -62,23 +83,3 @@ function executeTaskByName(taskProvider: TaskProvider, taskName: string) {
vscode.tasks.executeTask(task);
}
}

import { execSync } from "child_process";

function getContainerStatus(): DevImageStatus {
try {
const workspaceName = `${vscode.workspace.workspaceFolders![0].name}`;
const containerName = `${workspaceName}-container`;
const command = `docker inspect -f '{{ .State.Status }}' ${containerName}`;
const containerStatus = execSync(command).toString().trim();
if (containerStatus === "running") {
return DevImageStatus.running;
} else if (containerStatus === "starting" || containerStatus === "restarting") {
return DevImageStatus.syncing;
} else {
return DevImageStatus.stopped;
}
} catch (error: any) {
return DevImageStatus.stopped;
}
}
78 changes: 54 additions & 24 deletions src/statusBar.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"use strict";

import * as vscode from "vscode";
import { execSync } from "child_process";
import { getSelectedTarget } from "./targetSelector";
import { getSelectedApp } from "./appSelector";

export enum DevImageStatus {
running = "sync",
Expand All @@ -17,11 +19,10 @@ export class StatusBarManager {

constructor() {
this.targetItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
this.targetItem.text = `$(target) ${getSelectedTarget()}`;
this.targetItem.tooltip = "Click to select another device.";
this.targetItem.command = "selectTarget";
this.targetItem.backgroundColor = new vscode.ThemeColor("statusBarItem.prominentBackground");
this.targetItem.show();
this.updateTargetItem();

this.devImageItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
// Create a Command object with command and arguments
Expand All @@ -37,32 +38,61 @@ export class StatusBarManager {
}

public updateTargetItem() {
this.targetItem.text = `$(target) ${getSelectedTarget()}`;
this.targetItem.text = `$(target) L : ${getSelectedTarget()}`;
this.targetItem.show();
}

public updateDevImageItem(status: DevImageStatus): void {
const workspaceName = `${vscode.workspace.workspaceFolders![0].name}`;
const containerName = `${workspaceName}-container`;
this.devImageItem.text = `$(${status.toString()}) ${containerName}`;
let statusText = "[stopped] ";
switch (status) {
case DevImageStatus.running:
this.devImageItem.backgroundColor = new vscode.ThemeColor("statusBarItem.prominentBackground");
statusText = "[running] ";
break;
case DevImageStatus.syncing:
statusText = "[syncing] ";
this.devImageItem.backgroundColor = new vscode.ThemeColor("statusBarItem.warningBackground");
break;
case DevImageStatus.stopped:
this.devImageItem.backgroundColor = new vscode.ThemeColor("statusBarItem.errorBackground");
break;
default:
this.devImageItem.backgroundColor = new vscode.ThemeColor("statusBarItem.errorBackground");
break;
const currentApp = getSelectedApp();
if (currentApp) {
this.devImageItem.text = `$(${status.toString()}) L : ${currentApp.appName}`;
let statusText = "[stopped] ";
switch (status) {
case DevImageStatus.running:
this.devImageItem.backgroundColor = new vscode.ThemeColor("statusBarItem.prominentBackground");
statusText = "[running] ";
break;
case DevImageStatus.syncing:
statusText = "[syncing] ";
this.devImageItem.backgroundColor = new vscode.ThemeColor("statusBarItem.warningBackground");
break;
case DevImageStatus.stopped:
this.devImageItem.backgroundColor = new vscode.ThemeColor("statusBarItem.errorBackground");
break;
default:
this.devImageItem.backgroundColor = new vscode.ThemeColor("statusBarItem.errorBackground");
break;
}
this.devImageItem.tooltip = statusText + imageToolTip;
this.devImageItem.show();
} else {
this.devImageItem.hide();
}
}

public autoUpdateDevImageItem(): void {
this.updateDevImageItem(this.getContainerStatus());
}

private getContainerStatus(): DevImageStatus {
try {
const currentApp = getSelectedApp();
if (currentApp) {
const containerName = currentApp.containerName;
const command = `docker inspect -f '{{ .State.Status }}' ${containerName}`;
const containerStatus = execSync(command).toString().trim();
if (containerStatus === "running") {
return DevImageStatus.running;
} else if (containerStatus === "starting" || containerStatus === "restarting") {
return DevImageStatus.syncing;
} else {
return DevImageStatus.stopped;
}
} else {
return DevImageStatus.stopped;
}
} catch (error: any) {
return DevImageStatus.stopped;
}
this.devImageItem.tooltip = statusText + imageToolTip;
this.devImageItem.show();
}
}
Loading

0 comments on commit 965c509

Please sign in to comment.