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

Fix nested projects extension activate #737

Merged
merged 4 commits into from
Oct 7, 2020
Merged
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
2 changes: 0 additions & 2 deletions extension/src/client/GradleClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ export class GradleClient implements vscode.Disposable {
) {
this.server.onDidStart(this.handleServerStart);
this.server.onDidStop(this.handleServerStop);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.server.start();
}

private handleServerStop = (): void => {
Expand Down
1 change: 0 additions & 1 deletion extension/src/commands/RefreshCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export class RefreshCommand extends Command {
}
async run(): Promise<void> {
this.gradleTaskProvider.clearTasksCache();
// Explicitly load tasks as the views might not be visible
void this.gradleTaskProvider.loadTasks();
this.gradleTasksTreeDataProvider.refresh();
this.pinnedTasksTreeDataProvider.refresh();
Expand Down
2 changes: 1 addition & 1 deletion extension/src/commands/StopDaemonsCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class StopDaemonsCommand extends Command {
) {
return;
}
const gradleRootFolders = await this.rootProjectsStore.buildAndGetProjectRootsWithUniqueVersions();
const gradleRootFolders = await this.rootProjectsStore.getProjectRootsWithUniqueVersions();
const promises: Promise<StopDaemonsReply | void>[] = gradleRootFolders.map(
(rootProject) =>
this.client.stopDaemons(rootProject.getProjectUri().fsPath)
Expand Down
31 changes: 19 additions & 12 deletions extension/src/extension/Extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,15 @@ export class Extension {
this.gradleTasksTreeView
);

this.activate();
this.storeSubscriptions();
this.registerCommands();
this.handleTaskEvents();
this.handleWatchEvents();
this.handleEditorEvents();
this.loadTasks();

this.client.onDidConnect(() => this.refresh());

void this.activate();
}

private storeSubscriptions(): void {
Expand All @@ -200,20 +202,22 @@ export class Extension {
);
}

private activate(): void {
// Used for showing the view container in the activity bar
// eslint-disable-next-line @typescript-eslint/no-floating-promises
vscode.commands.executeCommand('setContext', 'gradle:activated', true);
private async activate(): Promise<void> {
const activated = !!(await this.rootProjectsStore.getProjectRoots()).length;
if (activated && !this.server.isReady()) {
await this.server.start();
}
await vscode.commands.executeCommand(
'setContext',
'gradle:activated',
activated
);
}

private registerCommands(): void {
this.commands.register();
}

private loadTasks(): void {
this.client.onDidConnect(() => this.refresh());
}

private handleTaskEvents(): void {
this.gradleTaskManager.onDidStartTask(async (task: vscode.Task) => {
if (this.gradleTasksTreeView.visible && getConfigFocusTaskInExplorer()) {
Expand All @@ -240,8 +244,10 @@ export class Extension {
}

private async restartServer(): Promise<void> {
await this.client.cancelBuilds();
await this.server.restart();
if (this.server.isReady()) {
await this.client.cancelBuilds();
await this.server.restart();
}
}

private refresh(): Thenable<void> {
Expand All @@ -264,6 +270,7 @@ export class Extension {
) {
this.rootProjectsStore.clear();
await this.refresh();
await this.activate();
}
if (event.affectsConfiguration('gradle.debug')) {
const debug = getConfigIsDebugEnabled();
Expand Down
2 changes: 1 addition & 1 deletion extension/src/input/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function getGradleCommand(): Thenable<TaskArgs | undefined> {
export async function getRootProjectFolder(
rootProjectsStore: RootProjectsStore
): Promise<RootProject | undefined> {
const rootProjects = await rootProjectsStore.buildAndGetProjectRoots();
const rootProjects = await rootProjectsStore.getProjectRoots();
if (rootProjects.length === 1) {
return Promise.resolve(rootProjects[0]);
}
Expand Down
7 changes: 6 additions & 1 deletion extension/src/server/GradleServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,14 @@ export class GradleServer implements vscode.Disposable {
this._onDidStart.fire(null);
}

public dispose(): void {
public stop(): void {
this.process?.removeAllListeners();
this.killProcess();
this.ready = false;
}

public dispose(): void {
this.stop();
this._onDidStart.dispose();
this._onDidStop.dispose();
}
Expand Down
86 changes: 55 additions & 31 deletions extension/src/stores/RootProjectsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ import { StoreMap } from '.';
import { isGradleRootProject } from '../util';
import { RootProject } from '../rootProject/RootProject';

async function getNestedRootProjectFolders(): Promise<RootProject[]> {
const files = await vscode.workspace.findFiles(
'**/{gradlew,gradlew.bat}',
'/{gradlew,gradlew.bat}' // ignore root wrapper scripts
async function getNestedRootProjectFolders(): Promise<string[]> {
const matchingNestedWrapperFiles = await vscode.workspace.findFiles(
'**/{gradlew,gradlew.bat}'
);
const projectFolders = [
...new Set(files.map((uri) => path.dirname(uri.fsPath))),
return [
...new Set(
matchingNestedWrapperFiles.map((uri) => path.dirname(uri.fsPath))
),
];
return projectFolders.map((folder) =>
buildRootFolder(vscode.Uri.file(folder))
);
}

function buildRootFolder(folderUri: vscode.Uri): RootProject {
Expand All @@ -24,28 +22,47 @@ function buildRootFolder(folderUri: vscode.Uri): RootProject {
return new RootProject(workspaceFolder, folderUri, javaDebug);
}

function getGradleProjectFoldersOutsideRoot(
configNestedFolders: boolean | ReadonlyArray<string>,
gradleProjectFolders: string[],
workspaceFolder: vscode.WorkspaceFolder
): string[] {
if (configNestedFolders === true) {
return gradleProjectFolders.filter(
(projectFolder) => projectFolder !== workspaceFolder.uri.fsPath
);
} else if (Array.isArray(configNestedFolders)) {
return configNestedFolders.map((nestedfolder) => {
return path.join(workspaceFolder.uri.fsPath, nestedfolder);
});
}
return [];
}

export class RootProjectsStore extends StoreMap<string, RootProject> {
private async populate(): Promise<void> {
private isPopulated = false;
private populatePromise: Promise<void> | undefined = undefined;

public async populate(): Promise<void> {
const workspaceFolders: ReadonlyArray<vscode.WorkspaceFolder> =
vscode.workspace.workspaceFolders || [];
workspaceFolders.forEach((workspaceFolder) =>
this.setRootProjectFolder(buildRootFolder(workspaceFolder.uri))
);
const gradleProjectFolders = await getNestedRootProjectFolders();

for (const workspaceFolder of workspaceFolders) {
const configNestedFolders = getNestedProjectsConfig(workspaceFolder);
if (configNestedFolders === true) {
(await getNestedRootProjectFolders()).forEach(
this.setRootProjectFolder
);
} else if (Array.isArray(configNestedFolders)) {
configNestedFolders
.map((nestedfolder) => {
const fsPath = path.join(workspaceFolder.uri.fsPath, nestedfolder);
return buildRootFolder(vscode.Uri.file(fsPath));
})
.forEach(this.setRootProjectFolder);
const gradleProjectFoldersOutsideRoot = getGradleProjectFoldersOutsideRoot(
configNestedFolders,
gradleProjectFolders,
workspaceFolder
);
if (gradleProjectFolders.includes(workspaceFolder.uri.fsPath)) {
this.setRootProjectFolder(buildRootFolder(workspaceFolder.uri));
}
gradleProjectFoldersOutsideRoot
.map((folder) => buildRootFolder(vscode.Uri.file(folder)))
.forEach(this.setRootProjectFolder);
}
this.isPopulated = true;
this.fireOnDidChange(null);
}

Expand All @@ -55,18 +72,20 @@ export class RootProjectsStore extends StoreMap<string, RootProject> {
}
};

public async buildAndGetProjectRoots(): Promise<RootProject[]> {
if (!this.getData().size) {
await this.populate();
public async getProjectRoots(): Promise<RootProject[]> {
if (!this.isPopulated) {
if (!this.populatePromise) {
this.populatePromise = this.populate();
}
await this.populatePromise;
this.populatePromise = undefined;
}
return [...this.getData().values()];
}

public async buildAndGetProjectRootsWithUniqueVersions(): Promise<
RootProject[]
> {
public async getProjectRootsWithUniqueVersions(): Promise<RootProject[]> {
const gradleVersionIds: string[] = [];
return (await this.buildAndGetProjectRoots()).filter((rootProject) => {
return (await this.getProjectRoots()).filter((rootProject) => {
const version = rootProject
.getEnvironment()
?.getGradleEnvironment()
Expand All @@ -81,4 +100,9 @@ export class RootProjectsStore extends StoreMap<string, RootProject> {
return false;
});
}

public clear(fireOnDidChange = true): void {
super.clear(fireOnDidChange);
this.isPopulated = false;
}
}
2 changes: 1 addition & 1 deletion extension/src/tasks/GradleTaskProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class GradleTaskProvider
}
logger.debug('Refreshing tasks');
this._onDidStartRefresh.fire(null);
const folders = await this.rootProjectsStore.buildAndGetProjectRoots();
const folders = await this.rootProjectsStore.getProjectRoots();
if (!folders.length) {
this.cachedTasks = [];
return Promise.resolve(this.cachedTasks);
Expand Down
24 changes: 12 additions & 12 deletions extension/src/test/integration/nested-projects/extension.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ describe(getSuiteName('Extension'), () => {
extension = vscode.extensions.getExtension(EXTENSION_NAME);
});

before(async () => {
await vscode.workspace
.getConfiguration('gradle')
.update('nestedProjects', false);
});

after(async () => {
await vscode.workspace
.getConfiguration('gradle')
.update('nestedProjects', false);
});

it('should be present', () => {
assert.ok(extension);
});
Expand Down Expand Up @@ -42,12 +54,6 @@ describe(getSuiteName('Extension'), () => {
.update('nestedProjects', true);
});

after(async () => {
await vscode.workspace
.getConfiguration('gradle')
.update('nestedProjects', false);
});

beforeEach(async () => {
tasks = await vscode.tasks.fetchTasks({ type: 'gradle' });
});
Expand Down Expand Up @@ -89,12 +95,6 @@ describe(getSuiteName('Extension'), () => {
.update('nestedProjects', ['gradle-groovy-default-build-file']);
});

after(async () => {
await vscode.workspace
.getConfiguration('gradle')
.update('nestedProjects', false);
});

beforeEach(async () => {
tasks = await vscode.tasks.fetchTasks({ type: 'gradle' });
});
Expand Down
2 changes: 1 addition & 1 deletion extension/src/test/runTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { runTests, downloadAndUnzipVSCode } from 'vscode-test';

const extensionDevelopmentPath = path.resolve(__dirname, '../../');
// TODO: consider also testing using insiders
const VSCODE_VERSION = '1.49.0';
const VSCODE_VERSION = '1.49.3';

async function runTestsWithGradle(
vscodeExecutablePath: string,
Expand Down
8 changes: 8 additions & 0 deletions extension/src/test/testUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,22 @@ export function stubWorkspaceFolders(
vscode.workspace,
'getWorkspaceFolder'
);
const dirnameStub = sinon.stub(path, 'dirname');
workspaceFolders.forEach((workspaceFolder) => {
existsSyncStub
.withArgs(path.join(workspaceFolder.uri.fsPath, 'gradlew'))
.returns(true);
getWorkspaceFolderStub
.withArgs(sinon.match.has('fsPath', workspaceFolder.uri.fsPath))
.returns(workspaceFolder);
dirnameStub
.withArgs(workspaceFolder.uri.fsPath)
.returns(workspaceFolder.uri.fsPath);
});
sinon
.stub(vscode.workspace, 'findFiles')
.withArgs('**/{gradlew,gradlew.bat}')
.returns(Promise.resolve(workspaceFolders.map((folder) => folder.uri)));
}

export function buildMockContext(): any {
Expand Down
Loading