Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: run all tests button and context menus #191

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 110 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@
},
{
"command": "mesonbuild.build",
"title": "Meson: Build"
"title": "Meson: Build",
"icon": {
"dark": "res/build-icon-dark.svg",
"light": "res/build-icon-light.svg"
}
},
{
"command": "mesonbuild.test",
Expand All @@ -87,6 +91,36 @@
{
"command": "mesonbuild.restartLanguageServer",
"title": "Meson: Restart Language Server"
},
{
"command": "mesonbuild.node.reconfigure",
"title": "Reconfigure",
"icon": {
"dark": "res/meson_32.svg",
"light": "res/meson_32.svg"
}
},
{
"command": "mesonbuild.node.build",
"title": "Build",
"icon": {
"dark": "res/build-icon-dark.svg",
"light": "res/build-icon-light.svg"
}
},
{
"command": "mesonbuild.node.clean",
"title": "Clean"
},
{
"command": "mesonbuild.node.runAll",
tristan957 marked this conversation as resolved.
Show resolved Hide resolved
"title": "Run all",
"icon": "$(testing-run-all-icon)"
},
{
"command": "mesonbuild.node.run",
"title": "Run",
"icon": "$(testing-run-icon)"
}
],
"configuration": {
Expand Down Expand Up @@ -388,10 +422,65 @@
},
"menus": {
"view/item/context": [
{
"command": "mesonbuild.node.reconfigure",
"when": "view == meson-project && viewItem == meson-projectroot",
"group": "build@0"
},
{
"command": "mesonbuild.node.build",
"when": "view == meson-project && viewItem == meson-projectroot",
"group": "inline"
},
{
"command": "mesonbuild.node.build",
"when": "view == meson-project && viewItem == meson-projectroot",
"group": "build@1"
},
{
"command": "mesonbuild.node.clean",
"when": "view == meson-project && viewItem == meson-projectroot",
"group": "build@2"
},
{
"command": "mesonbuild.node.build",
"when": "view == meson-project && viewItem == meson-target",
"group": "inline@0"
},
{
"command": "mesonbuild.openBuildFile",
"when": "view == meson-project && viewItem == meson-target",
"group": "inline@1"
},
{
"command": "mesonbuild.node.build",
"when": "view == meson-project && viewItem == meson-target",
"group": "build@0"
},
{
"command": "mesonbuild.openBuildFile",
"when": "view == meson-project && viewItem == meson-target",
"group": "build@1"
},
{
"command": "mesonbuild.node.runAll",
"when": "view == meson-project && viewItem == meson-test-root",
"group": "inline"
},
{
"command": "mesonbuild.node.runAll",
"when": "view == meson-project && viewItem == meson-test-root",
"group": "run"
},
{
"command": "mesonbuild.node.run",
"when": "view == meson-project && viewItem == meson-test",
"group": "inline"
},
{
"command": "mesonbuild.node.run",
"when": "view == meson-project && viewItem == meson-test",
"group": "run"
}
],
"view/title": [
Expand All @@ -405,6 +494,26 @@
{
"command": "mesonbuild.openBuildFile",
"when": "false"
},
{
"command": "mesonbuild.node.reconfigure",
"when": "false"
},
{
"command": "mesonbuild.node.build",
"when": "false"
},
{
"command": "mesonbuild.node.clean",
"when": "false"
},
{
"command": "mesonbuild.node.run",
"when": "false"
},
{
"command": "mesonbuild.node.runAll",
"when": "false"
}
]
},
Expand Down
8 changes: 8 additions & 0 deletions res/build-icon-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions res/build-icon-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { activateFormatters } from "./formatters";
import { SettingsKey, TaskQuickPickItem } from "./types";
import { createLanguageServerClient } from "./lsp/common";
import { dirname, relative } from "path";
import { IBuildableNode, IRunnableNode } from "./treeview/nodes/base";

export let extensionPath: string;
export let workspaceState: vscode.Memento;
Expand Down Expand Up @@ -201,6 +202,31 @@ export async function activate(ctx: vscode.ExtensionContext) {
}),
);

ctx.subscriptions.push(
vscode.commands.registerCommand("mesonbuild.node.reconfigure", async () => {
runFirstTask("reconfigure");
}),
);

ctx.subscriptions.push(
vscode.commands.registerCommand("mesonbuild.node.build", async (node: IBuildableNode) => node.build()),
);

ctx.subscriptions.push(
vscode.commands.registerCommand("mesonbuild.node.clean", async () => {
runFirstTask("clean");
}),
);

// Two commands just to have different icons.
ctx.subscriptions.push(
vscode.commands.registerCommand("mesonbuild.node.runAll", async (node: IRunnableNode) => node.run()),
);

ctx.subscriptions.push(
vscode.commands.registerCommand("mesonbuild.node.run", async (node: IRunnableNode) => node.run()),
);

if (!checkMesonIsConfigured(buildDir)) {
let configureOnOpen = configurationChosen || extensionConfiguration(SettingsKey.configureOnOpen);
if (configureOnOpen === "ask") {
Expand Down
12 changes: 6 additions & 6 deletions src/tasks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as vscode from "vscode";
import { getMesonTargets, getMesonTests, getMesonBenchmarks } from "./introspection";
import { extensionConfiguration, getOutputChannel, getTargetName, getEnvDict } from "./utils";
import { Test, Target } from "./types";
import { Test, Target, pseudoAllTarget } from "./types";
import { checkMesonIsConfigured } from "./utils";
import { workspaceState } from "./extension";

Expand Down Expand Up @@ -68,29 +68,29 @@ function createReconfigureTask(buildDir: string, sourceDir: string) {
export async function getMesonTasks(buildDir: string, sourceDir: string) {
try {
const defaultBuildTask = new vscode.Task(
{ type: "meson", mode: "build" },
{ type: "meson", mode: "build", target: pseudoAllTarget },
"Build all targets",
"Meson",
new vscode.ShellExecution(extensionConfiguration("mesonPath"), ["compile", "-C", buildDir]),
"$meson-gcc",
);
const defaultTestTask = new vscode.Task(
{ type: "meson", mode: "test" },
{ type: "meson", mode: "test", target: pseudoAllTarget },
"Run all tests",
"Meson",
new vscode.ShellExecution(
extensionConfiguration("mesonPath"),
["test", ...extensionConfiguration("testOptions")],
["test", ...extensionConfiguration("testOptions"), pseudoAllTarget],
{ cwd: buildDir },
),
);
const defaultBenchmarkTask = new vscode.Task(
{ type: "meson", mode: "benchmark" },
{ type: "meson", mode: "benchmark", target: pseudoAllTarget },
"Run all benchmarks",
"Meson",
new vscode.ShellExecution(
extensionConfiguration("mesonPath"),
["test", "--benchmark", ...extensionConfiguration("benchmarkOptions")],
["test", "--benchmark", ...extensionConfiguration("benchmarkOptions"), pseudoAllTarget],
{ cwd: buildDir },
),
);
Expand Down
10 changes: 10 additions & 0 deletions src/treeview/nodes/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,13 @@ export abstract class BaseDirectoryNode<T> extends BaseNode {

abstract buildFileTree(fpaths: T[]): FolderMap<T> | Thenable<FolderMap<T>>;
}

// A node in the meson tree view that can be built.
export interface IBuildableNode {
build(): Thenable<any>;
}

// A node in the meson tree view that can be run.
export interface IRunnableNode {
run(): Thenable<any>;
}
18 changes: 7 additions & 11 deletions src/treeview/nodes/targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { BaseNode } from "../basenode";
import { Target, Targets } from "../../types";
import { TargetSourcesRootNode, TargetGeneratedSourcesRootNode } from "./sources";
import { extensionRelative, getTargetName } from "../../utils";
import { BaseDirectoryNode } from "./base";
import { BaseDirectoryNode, IBuildableNode } from "./base";

export class TargetDirectoryNode extends BaseDirectoryNode<Target> {
constructor(parentId: string, folder: string, targets: Targets) {
Expand Down Expand Up @@ -72,7 +72,7 @@ export class TargetDirectoryNode extends BaseDirectoryNode<Target> {
}
}

export class TargetNode extends BaseNode {
export class TargetNode extends BaseNode implements IBuildableNode {
constructor(
parentId: string,
private readonly target: Target,
Expand Down Expand Up @@ -103,7 +103,7 @@ export class TargetNode extends BaseNode {
}
}

override async getTreeItem() {
override getTreeItem() {
const item = super.getTreeItem() as vscode.TreeItem;

item.label = this.target.name;
Expand All @@ -112,17 +112,13 @@ export class TargetNode extends BaseNode {
item.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;
item.contextValue = "meson-target";

const targetName = await getTargetName(this.target);

item.command = {
title: `Build ${this.target.name}`,
command: "mesonbuild.build",
arguments: [targetName],
};

return item;
}

async build() {
return vscode.commands.executeCommand("mesonbuild.build", await getTargetName(this.target));
}

private getIconPath() {
switch (this.target.type) {
case "executable":
Expand Down
42 changes: 33 additions & 9 deletions src/treeview/nodes/tests.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import * as vscode from "vscode";

import { BaseNode } from "../basenode";
import { Test, Tests } from "../../types";
import { Test, Tests, pseudoAllTarget } from "../../types";
import { extensionRelative } from "../../utils";
import { IRunnableNode } from "./base";

export class TestRootNode extends BaseNode {
function getTestCommand(isBenchmark: boolean): string {
return isBenchmark ? "benchmark" : "test";
}

export class TestRootNode extends BaseNode implements IRunnableNode {
constructor(
parentId: string,
private readonly tests: Tests,
private readonly isBenchmark: boolean,
) {
super(`${parentId}-${isBenchmark ? "benchmarks" : "tests"}`);
super(`${parentId}-${getTestCommand(isBenchmark)}`);
}

override getTreeItem() {
Expand All @@ -21,39 +26,58 @@ export class TestRootNode extends BaseNode {
item.collapsibleState =
this.tests.length === 0 ? vscode.TreeItemCollapsibleState.None : vscode.TreeItemCollapsibleState.Collapsed;

// To key in to "when": "view == meson-project && viewItem == meson-test-root" in package.json.
item.contextValue = "meson-test-root";

return item;
}

override getChildren() {
return this.tests.map((test) => new TestNode(this.id, test, this.isBenchmark));
}

run() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a return type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can do, though it's from the IRunnable interface; not sure what the gain is other than more stuff to edit if the interface changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, the gain is explicit return types. Helps when reading code. Everyone has different preferences though.

return vscode.commands.executeCommand(`mesonbuild.${getTestCommand(this.isBenchmark)}`, pseudoAllTarget);
}
}

class TestNode extends BaseNode {
class TestNode extends BaseNode implements IRunnableNode {
private readonly taskName: string;
private readonly command: string;

constructor(
parentId: string,
private readonly test: Test,
private readonly isBenchmark: boolean,
) {
super(`${parentId}-${test.suite[0]}-${test.name}`);

this.command = getTestCommand(this.isBenchmark);
const project = this.test.suite[0].split(":")[0];
this.taskName = `${project}:${this.test.name}`;
}

override getTreeItem() {
const item = super.getTreeItem() as vscode.TreeItem;
const project = this.test.suite[0].split(":")[0];
const name = `${project}:${this.test.name}`;

item.label = this.test.name;
item.iconPath = extensionRelative("res/meson_32.svg");
item.command = {
title: `Run ${this.isBenchmark ? "benchmark" : "test"}`,
command: `mesonbuild.${this.isBenchmark ? "benchmark" : "test"}`,
arguments: [name],
title: `Run ${this.command}`,
command: `mesonbuild.${this.command}`,
arguments: [this.taskName],
};

// No children currently, so don't display toggle.
item.collapsibleState = vscode.TreeItemCollapsibleState.None;

// To key in to "when": "view == meson-project && viewItem == meson-test" in package.json.
item.contextValue = "meson-test";

return item;
}

run() {
return vscode.commands.executeCommand(`mesonbuild.${this.command}`, this.taskName);
}
}
Loading