Skip to content

Commit 03ba103

Browse files
authored
Merge pull request #81 from zapta/main
Various refactorings and tweaks. See individual commits for details.
2 parents f404c9c + 3d94245 commit 03ba103

File tree

9 files changed

+529
-343
lines changed

9 files changed

+529
-343
lines changed

CHANGELOG.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44

55
### Changed
66

7-
- Added right-click context menu command that allows to test or simulate
8-
the selected testbench.
7+
- Added right-click context menu commands for `apio format`, `apio sim`, and
8+
`apio test`. The context menu works in the VS Code explorer and in the
9+
editor.
910

10-
- Added a menu command to test only the default testbench, similar to the
11-
behavior of the sim command which tests the `default-testbench` defined
12-
in `apio.ini`, or the existing testbench if the project has exactly one
11+
- Added a menu command to test only the default testbench,
12+
similar to the behavior of the sim command which tests the `default-testbench`
13+
defined in `apio.ini`, or the existing testbench if the project has exactly one
1314
testbench.
1415

1516
- The `sim` command now creates automatically a `.gtkw` default file to have
@@ -18,7 +19,7 @@
1819
saved a a user created `.gtkw` file. See more details in the documentation
1920
of the `sim` command at <https://fpgawars.github.io/apio/docs/cmd-apio-sim>.
2021

21-
- Added a command `report (detached)` that prints additional information such
22+
- Added a command `report verbose` that prints additional information such
2223
as critical nets.
2324

2425
- Now exiting with an error if a testbench contains `$dumpfile(...)` (used to

package.json

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,40 +78,56 @@
7878
]
7979
},
8080
"commands": [
81+
8182
{
82-
"command": "apio.simContext",
83+
"command": "apio.context.sim",
8384
"title": "Apio sim",
8485
"enablement": "apio.apioIniExists"
8586
},
8687
{
87-
"command": "apio.testContext",
88+
"command": "apio.context.test",
8889
"title": "Apio test",
8990
"enablement": "apio.apioIniExists"
91+
}, {
92+
"command": "apio.context.format",
93+
"title": "Apio format",
94+
"enablement": "apio.apioIniExists"
9095
}
9196
],
9297
"menus": {
9398
"explorer/context": [
9499
{
95-
"command": "apio.simContext",
96-
"group": "2_workspace@10",
100+
"command": "apio.context.sim",
101+
"group": "2_workspace@1",
97102
"when": "apio.apioIniExists && resourceFilename =~ /_tb\\.[s]?v$/ && !explorerResourceIsFolder && resourceScheme == file"
98103
},
99104
{
100-
"command": "apio.testContext",
101-
"group": "2_workspace@10",
105+
"command": "apio.context.test",
106+
"group": "2_workspace@2",
102107
"when": "apio.apioIniExists && resourceFilename =~ /_tb\\.[s]?v$/ && !explorerResourceIsFolder && resourceScheme == file"
108+
},
109+
{
110+
"command": "apio.context.format",
111+
"group": "2_workspace@3",
112+
"when": "apio.apioIniExists && resourceFilename =~ /\\.[s]?v$/ && !explorerResourceIsFolder && resourceScheme == file"
103113
}
104114
],
105115
"editor/context": [
116+
106117
{
107-
"command": "apio.simContext",
108-
"group": "1_apio@5",
118+
"command": "apio.context.sim",
119+
"group": "1_apio@1",
109120
"when": "apio.apioIniExists && resourceFilename =~ /_tb\\.[s]?v$/ && resourceScheme == file"
110121
},
111122
{
112-
"command": "apio.testContext",
113-
"group": "1_apio@5",
123+
"command": "apio.context.test",
124+
"group": "1_apio@2",
114125
"when": "apio.apioIniExists && resourceFilename =~ /_tb\\.[s]?v$/ && resourceScheme == file"
126+
},
127+
{
128+
"command": "apio.context.format",
129+
"group": "1_apio@3",
130+
"when": "apio.apioIniExists && resourceFilename =~ /\\.[s]?v$/ && resourceScheme == file"
115131
}
116132
]
117133
}

src/actions.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Handle apio actions launching.
2+
3+
// Standard imports
4+
const vscode = require("vscode");
5+
6+
// Local imports.
7+
const envSelector = require("./env-selector.js");
8+
const downloader = require("./downloader.js");
9+
const tasks = require("./tasks.js");
10+
const apioLog = require("./apio-log.js");
11+
const utils = require("./utils.js");
12+
13+
// A function to execute an action. Action can have commands anr/or url.
14+
// Cmds include the pre commands but may contain placeholders that need
15+
// to be expanded.
16+
async function _launchAction(
17+
taskTitle,
18+
taskCmds,
19+
taskCompletionMsgs,
20+
urlToOpen,
21+
cmdIdToInvoke,
22+
) {
23+
// Handle the optional commands.
24+
if (taskCmds != null) {
25+
// Get the optional apio --env flag for current apio env.
26+
apioEnvFlag = envSelector.getApioEnvFlag();
27+
28+
29+
30+
// Expand the placeholders.
31+
taskCmds = taskCmds.map((cmd) => cmd.replace("{env-flag}", apioEnvFlag));
32+
taskCmds = taskCmds.map((cmd) =>
33+
cmd.replace("{apio-bin}", utils.apioBinaryPath()),
34+
);
35+
36+
// Execute the commands and wait for completion.
37+
const aborted = await tasks.execCommandsInATask(
38+
taskTitle,
39+
taskCmds,
40+
taskCompletionMsgs,
41+
false, // preserveExitCode
42+
);
43+
44+
if (aborted) {
45+
apioLog.msg("Terminal commands aborted or timeout.");
46+
return;
47+
}
48+
}
49+
50+
// Handle url aspect of the action. Launch in a browser if exists.
51+
if (urlToOpen != null) {
52+
apioLog.msg(`Opening URL: ${urlToOpen}`);
53+
vscode.env.openExternal(vscode.Uri.parse(urlToOpen));
54+
}
55+
56+
// Handle command id aspect of the action, if exists. This is
57+
// for example how we launch the get example wizard.
58+
if (cmdIdToInvoke) {
59+
apioLog.msg(`Launching command: ${cmdIdToInvoke}`);
60+
vscode.commands.executeCommand(cmdIdToInvoke);
61+
}
62+
}
63+
64+
// A wrapper that first download the apio binary if needed and
65+
// only then invoked execAction
66+
function getActionLauncher(
67+
taskTitle,
68+
taskCmds,
69+
taskCompletionMsgs,
70+
urlToOpen,
71+
cmdIdToInvoke,
72+
) {
73+
// This wrapper is called when the user invokes the command. It
74+
// downloads and installs apio if needed and then executes
75+
// the command.
76+
async function _launchWrapper() {
77+
apioLog.msg("-----");
78+
79+
// Make sure the apio binary exists. If not, download and install it.
80+
await downloader.ensureApioBinary();
81+
82+
// Execute the command. Note that this is asynchronous such that
83+
// the execution of the commands may continues after this returns.
84+
try {
85+
await _launchAction(
86+
taskTitle,
87+
taskCmds,
88+
taskCompletionMsgs,
89+
urlToOpen,
90+
cmdIdToInvoke,
91+
);
92+
} catch (err) {
93+
console.error("[APIO] Failed to start the command:", err);
94+
vscode.window.showErrorMessage("Apio failed to launch the command.");
95+
}
96+
}
97+
98+
return _launchWrapper;
99+
}
100+
101+
// Export for require()
102+
module.exports = {
103+
getActionLauncher,
104+
};

src/context-cmds.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Handle the context commands. This are commands that are
2+
// launched from right-click menus.
3+
4+
// Standard imports
5+
const vscode = require("vscode");
6+
7+
// Local imports.
8+
const apioLog = require("./apio-log.js");
9+
const actions = require("./actions.js");
10+
11+
// A callback for handling commands invocations from vscode context
12+
// right click menu.
13+
async function contextCmdHandler(taskTitle, taskCmds, contextUri) {
14+
let targetUri;
15+
16+
apioLog.msg("fileContextHandler() invoked");
17+
apioLog.msg(`contextUri=${contextUri}`);
18+
19+
// Determine the target URI.
20+
if (contextUri instanceof vscode.Uri) {
21+
// We got the uri from the explorer.
22+
targetUri = contextUri;
23+
} else {
24+
// Otherwise, the invocation from the editor editor.
25+
const activeEditor = vscode.window.activeTextEditor;
26+
if (activeEditor) {
27+
targetUri = activeEditor.document.uri;
28+
}
29+
}
30+
31+
// Error if we didn't figure out the.
32+
if (!targetUri) {
33+
vscode.window.showWarningMessage("No file selected or no active editor.");
34+
return;
35+
}
36+
37+
// Convert the uri to a path relative to the workspace.
38+
const contextPath = vscode.workspace.asRelativePath(targetUri);
39+
40+
// Expand the {context-path} placeholder in the task title and
41+
// in the commands.
42+
taskTitle = taskTitle.replace("{context-path}", contextPath);
43+
taskCmds = taskCmds.map((cmd) => cmd.replace("{context-path}", contextPath));
44+
45+
// Launch the commands as a task.
46+
launcher = actions.getActionLauncher(
47+
(taskTitle = taskTitle),
48+
(taskCmds = taskCmds),
49+
(taskCompletionMsgs = null),
50+
(urlToOpen = null),
51+
(cmdIdToInvoke = null),
52+
);
53+
54+
await launcher();
55+
}
56+
57+
// Register the handlers for the file context operations
58+
function registerFileContextHandlers(context, preCmds) {
59+
// Per context command cmdId, taskTitle, and taskCmds.
60+
const contextCmds = [
61+
[
62+
"apio.context.sim",
63+
"CONTEXT / SIM",
64+
[...preCmds, `{apio-bin} sim "{context-path}" {env-flag}`],
65+
],
66+
[
67+
"apio.context.test",
68+
"CONTEXT / TEST",
69+
[...preCmds, `{apio-bin} test "{context-path}" {env-flag}`],
70+
],
71+
[
72+
"apio.context.format",
73+
"CONTEXT / FORMAT",
74+
[...preCmds, `{apio-bin} format "{context-path}" {env-flag}`],
75+
],
76+
];
77+
78+
for (const [cmdId, taskTitle, taskCmds] of contextCmds) {
79+
context.subscriptions.push(
80+
vscode.commands.registerCommand(cmdId, (contextUri) =>
81+
contextCmdHandler(taskTitle, taskCmds, contextUri),
82+
),
83+
);
84+
}
85+
}
86+
87+
// Export for require()
88+
module.exports = {
89+
registerFileContextHandlers,
90+
};

src/demo-cmd.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Handles the apio-shell command.
2+
3+
const vscode = require("vscode");
4+
5+
// Local imports
6+
const downloader = require("./downloader.js");
7+
const utils = require("./utils.js");
8+
const tasks = require("./tasks.js");
9+
10+
/**
11+
* Registers the command "apio.demoProject"
12+
*/
13+
function registerDemoProjectCommand(context) {
14+
// Register the command handler.
15+
const disposable = vscode.commands.registerCommand(
16+
"apio.demoProject",
17+
async () => {
18+
// Make sure the apio binary exists. If not, download and install it.
19+
await downloader.ensureApioBinary();
20+
21+
const demoDir = await utils.prepareEmptyApioDemoDir();
22+
23+
// Populate the demo directory with the given example and open it in VSCode.
24+
// Does not return if successful since VSCode leaves this workspace.
25+
await tasks.openProjectFromExample(
26+
context,
27+
"alhambra-ii",
28+
"getting-started",
29+
demoDir,
30+
(callback = (ok, text) => {
31+
if (!ok) throw Error(text);
32+
}),
33+
);
34+
},
35+
);
36+
37+
context.subscriptions.push(disposable);
38+
}
39+
40+
// Export for require()
41+
module.exports = {
42+
registerDemoProjectCommand,
43+
};

0 commit comments

Comments
 (0)