Skip to content

Commit

Permalink
Add file and package code lenses
Browse files Browse the repository at this point in the history
  • Loading branch information
firelizzard18 committed Nov 16, 2024
1 parent 7367fff commit 948d8ee
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 31 deletions.
27 changes: 24 additions & 3 deletions src/test/codeLens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,31 @@ export class CodeLensProvider implements vscode.CodeLensProvider<GoCodeLens> {
* Provide code lenses for a file.
*/
*#fileCodeLenses(file: TestFile) {
const mode = this.#mode(file.uri);
const runPkg = new GoCodeLens(new Range(0, 0, 0, 0), file.package, 'run');
const debugPkg = new GoCodeLens(new Range(0, 0, 0, 0), file.package, 'debug');
const runFile = new GoCodeLens(new Range(0, 0, 0, 0), file, 'run');
const debugFile = new GoCodeLens(new Range(0, 0, 0, 0), file, 'debug');
switch (mode) {
case 'run':
yield runFile;
yield runPkg;
break;
case 'debug':
yield debugFile;
yield debugPkg;
break;
default:
yield runFile;
yield debugFile;
yield runPkg;
yield debugPkg;
break;
}

// Depending on the mode, create a run and/or debug code lens for each
// test case that has a range. We can't do this for dynamic test cases
// because `go test` does not provide a range for those.
const mode = this.#mode(file.uri);
for (const test of file.tests) {
if (test instanceof StaticTestCase && test.range) {
const run = new GoCodeLens(test.range, test, 'run');
Expand Down Expand Up @@ -92,10 +113,10 @@ export class CodeLensProvider implements vscode.CodeLensProvider<GoCodeLens> {
lens.command = {
title: `${lens.kind} ${lens.item.kind}`,
command: `goExp.test.${lens.kind}`,
arguments: [await this.#manager.resolveTestItem(lens.item)],
arguments: await this.#manager.resolveTestItem(lens.item, { children: true }),
};
if (!(lens.item instanceof TestCase)) {
lens.command.title += ' files';
lens.command.title += ' tests';
}
return lens;
}
Expand Down
18 changes: 9 additions & 9 deletions src/test/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { TestRunProfileKind, Uri, TestRunRequest as VSCTestRunRequest, Cancellat
import type { CancellationToken, Disposable, TestItem, TestTag } from 'vscode';
import type vscode from 'vscode';
import { Context, doSafe, Tail, TestController } from './testing';
import { TestResolver } from './resolver';
import { ResolveOptions, TestResolver } from './resolver';
import { GoTestItem } from './item';
import { TestRunner } from './runner';
import { TestRunRequest } from './testRun';
Expand Down Expand Up @@ -121,16 +121,15 @@ export class TestManager {
/**
* Run a test.
*/
runTest(item: TestItem) {
this.#executeTestRun(this.#run, new VSCTestRunRequest([item]));
runTests(...items: TestItem[]) {
this.#executeTestRun(this.#run, new VSCTestRunRequest(items));
}

/**
* Debug a test.
*/
debugTest(item: TestItem) {
if (!this.#debug) return;
this.#executeTestRun(this.#debug, new VSCTestRunRequest([item]));
debugTests(...items: TestItem[]) {
this.#executeTestRun(this.#debug, new VSCTestRunRequest(items));
}

/**
Expand Down Expand Up @@ -248,9 +247,10 @@ export class TestManager {
}

resolveTestItem(goItem: GoTestItem): Promise<TestItem | undefined>;
resolveTestItem(goItem: GoTestItem, create: true): Promise<TestItem>;
resolveTestItem(goItem: GoTestItem, create = false) {
return this.#resolver!.resolveViewItems(goItem, create);
resolveTestItem(goItem: GoTestItem, opts: ResolveOptions & { create: true }): Promise<TestItem>;
resolveTestItem(goItem: GoTestItem, opts: ResolveOptions & { children: true }): Promise<TestItem[]>;
resolveTestItem(goItem: GoTestItem, opts?: ResolveOptions) {
return this.#resolver!.resolveViewItems(goItem, opts);
}

resolveGoTestItem(id: string) {
Expand Down
4 changes: 2 additions & 2 deletions src/test/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ async function registerTestController(ctx: ExtensionContext, testCtx: Context) {
command('goExp.testExplorer.refresh', (item: TestItem) => manager.enabled && manager.reloadViewItem(item));

// [Command] Run Test, Debug Test
command('goExp.test.run', (item: TestItem) => manager.enabled && manager.runTest(item));
command('goExp.test.debug', (item: TestItem) => manager.enabled && manager.debugTest(item));
command('goExp.test.run', (...item: TestItem[]) => manager.enabled && manager.runTests(...item));
command('goExp.test.debug', (...item: TestItem[]) => manager.enabled && manager.debugTests(...item));

// [Command] Browser navigation
command('goExp.browser.back', () => Browser.active?.back());
Expand Down
59 changes: 46 additions & 13 deletions src/test/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ import { TestConfig } from './config';
import { CapturedProfile, ProfileContainer, ProfileSet } from './profile';
import { EventEmitter } from '../utils/eventEmitter';

export type ResolveOptions = {
/**
* If this is true and the {@link TestItem} corresponding to a
* {@link GoTestItem} has not been created yet, it will be created.
*/
create?: boolean;

/**
* If this is true and a {@link GoTestItem} does not have a corresponding
* {@link TestItem} (e.g. a file when showFiles is false), the GoTestItem
* will resolve to its children instead of its parent.
*/
children?: boolean;
};

/**
* Maps between Go items ({@link GoTestItem}) and view items ({@link TestItem})
* and manages view updates.
Expand Down Expand Up @@ -188,7 +203,7 @@ export class TestResolver {
}

// Create a TestItem for each GoTestItem, including its ancestors, and refresh
const items = await this.resolveViewItems(item, true);
const items = await this.resolveViewItems(item, { create: true });
await Promise.all(items.map((x) => this.reloadViewItem(x)));
await this.#didChangeTestItem.fire(item);
}
Expand All @@ -212,7 +227,7 @@ export class TestResolver {
}

// Update the view
const items = await this.resolveViewItems(reload, true);
const items = await this.resolveViewItems(reload, { create: true });
await Promise.all(items.map((x) => this.reloadViewItem(x)));
invalidate && this.#ctrl.invalidateTestResults?.(items);

Expand All @@ -231,39 +246,57 @@ export class TestResolver {
* @param create - Whether to create missing {@link TestItem}(s).
* @returns The corresponding {@link TestItem}(s).
*/
resolveViewItems(goItems: GoTestItem, create?: boolean): Promise<TestItem | undefined>;
resolveViewItems(goItems: GoTestItem, create: true): Promise<TestItem>;
resolveViewItems(goItems: Iterable<GoTestItem>, create?: boolean): Promise<TestItem[]>;
resolveViewItems(goItems: GoTestItem): Promise<TestItem | undefined>;
resolveViewItems(goItems: GoTestItem, opts: ResolveOptions & { create: true }): Promise<TestItem>;
resolveViewItems(goItems: GoTestItem, opts: ResolveOptions & { children: true }): Promise<TestItem[]>;
resolveViewItems(goItems: GoTestItem, opts?: ResolveOptions): Promise<TestItem | undefined> | Promise<TestItem[]>;
resolveViewItems(goItems: Iterable<GoTestItem>, opts?: ResolveOptions): Promise<TestItem[]>;
resolveViewItems(
goItems: GoTestItem | Iterable<GoTestItem>,
create?: boolean,
opts?: ResolveOptions,
): Promise<TestItem | undefined> | Promise<TestItem[]> {
if (!(Symbol.iterator in goItems)) {
if (opts?.children) {
return this.resolveViewItems([goItems], opts);
}
return (async () => {
const [item] = await this.resolveViewItems([goItems], create);
const [item] = await this.resolveViewItems([goItems], opts);
return item;
})();
}

const toResolve = [];
const config = new TestConfig(this.#context.workspace);
for (let item of goItems) {
const toResolve: GoTestItem[] = [];
const add = (item: GoTestItem) => {
// If the item is a file and showFiles is disabled, resolve the
// parent instead of the file
if (item instanceof TestFile && !config.for(item.uri).showFiles()) {
item = item.getParent();
addHidden(item);
return;
}

// If the item is a package and is the self-package of a root,
// resolve the root instead of the package
if (item instanceof Package && item.isRootPkg) {
item = item.getParent();
addHidden(item);
return;
}

toResolve.push(item);
};
const addHidden = (item: { getParent(): GoTestItem; getChildren(): GoTestItem[] }) => {
if (opts?.children) {
item.getChildren().forEach((x) => add(x));
} else {
add(item.getParent());
}
};

const config = new TestConfig(this.#context.workspace);
for (const item of goItems) {
add(item);
}

if (create) {
if (opts?.create) {
return Promise.all(toResolve.map((x) => this.#getOrCreateAll(x)));
}

Expand Down
10 changes: 6 additions & 4 deletions src/test/testRun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export class TestRunRequest {
[...include.values()].map((x) =>
Promise.all(
x.map(async (y) => {
const item = await this.manager.resolveTestItem(y, true);
const item = await this.manager.resolveTestItem(y, { create: true });
testItems.push(item);
}),
),
Expand All @@ -186,7 +186,7 @@ export class TestRunRequest {

async *packages(run: TestRun) {
for (const pkg of this.#packages) {
const pkgItem = await this.manager.resolveTestItem(pkg, true);
const pkgItem = await this.manager.resolveTestItem(pkg, { create: true });
const include = await this.#resolveTestItems(this.include.get(pkg) || pkg.getTests());
const exclude = await this.#resolveTestItems(this.exclude.get(pkg) || []);

Expand All @@ -197,7 +197,9 @@ export class TestRunRequest {
async #resolveTestItems<T extends GoTestItem>(goItems: T[]) {
return new Map(
await Promise.all(
goItems.map(async (x): Promise<[T, TestItem]> => [x, await this.manager.resolveTestItem(x, true)]),
goItems.map(
async (x): Promise<[T, TestItem]> => [x, await this.manager.resolveTestItem(x, { create: true })],
),
),
);
}
Expand Down Expand Up @@ -252,7 +254,7 @@ export class PackageTestRun {
// Resolve the named test case and its associated test item
// const test = msg.Test ? await this.#resolveTestCase(this.goItem, msg.Test) : undefined;
const test = msg.Test ? this.goItem.findTest(msg.Test, true, this.#run) : undefined;
const item = test && (await this.#request.manager.resolveTestItem(test, true));
const item = test && (await this.#request.manager.resolveTestItem(test, { create: true }));

const elapsed = typeof msg.Elapsed === 'number' ? msg.Elapsed * 1000 : undefined;
if (msg.Action === 'output') {
Expand Down

0 comments on commit 948d8ee

Please sign in to comment.