diff --git a/extension/package.json b/extension/package.json index e11e74060..27b76035a 100644 --- a/extension/package.json +++ b/extension/package.json @@ -332,6 +332,11 @@ "command": "gradle.createProjectAdvanced", "category": "Gradle", "title": "Create a Gradle Java Project... (Advanced)" + }, + { + "command": "gradle.runTasks", + "category": "Gradle", + "title": "Run Gradle Tasks..." } ], "menus": { @@ -471,6 +476,10 @@ { "command": "gradle.createProject", "when": "!java:projectManagerActivated" + }, + { + "command": "gradle.runTasks", + "when": "false" } ], "view/title": [ @@ -653,6 +662,16 @@ "command": "gradle.showTasks", "group": "gradle@0" } + ], + "javaProject.gradle": [ + { + "command": "gradle.runTasks", + "group": "gradle@20" + }, + { + "command": "gradle.openBuildFile", + "group": "gradle@30" + } ] }, "configuration": { diff --git a/extension/src/commands/Commands.ts b/extension/src/commands/Commands.ts index d2e29111d..607271117 100644 --- a/extension/src/commands/Commands.ts +++ b/extension/src/commands/Commands.ts @@ -74,6 +74,7 @@ import { GradleDaemonsTreeDataProvider, GradleTasksTreeDataProvider, RecentTasks import { Command } from "./Command"; import { COMMAND_CREATE_PROJECT, COMMAND_CREATE_PROJECT_ADVANCED, CreateProjectCommand } from "./CreateProjectCommand"; import { HideStoppedDaemonsCommand, HIDE_STOPPED_DAEMONS } from "./HideStoppedDaemonsCommand"; +import { COMMAND_RUN_TASKS, RunTasksCommand } from "./RunTasksCommand"; import { ShowStoppedDaemonsCommand, SHOW_STOPPED_DAEMONS } from "./ShowStoppedDaemonsCommand"; export class Commands { @@ -184,5 +185,6 @@ export class Commands { this.registerCommand(HIDE_STOPPED_DAEMONS, new HideStoppedDaemonsCommand(this.gradleDaemonsTreeDataProvider)); this.registerCommand(COMMAND_CREATE_PROJECT, new CreateProjectCommand(this.client), [false]); this.registerCommand(COMMAND_CREATE_PROJECT_ADVANCED, new CreateProjectCommand(this.client), [true]); + this.registerCommand(COMMAND_RUN_TASKS, new RunTasksCommand(this.gradleTaskProvider)); } } diff --git a/extension/src/commands/OpenBuildFileCommand.ts b/extension/src/commands/OpenBuildFileCommand.ts index 0757497ca..b22fdf495 100644 --- a/extension/src/commands/OpenBuildFileCommand.ts +++ b/extension/src/commands/OpenBuildFileCommand.ts @@ -1,15 +1,36 @@ import * as vscode from "vscode"; +import * as path from "path"; +import * as fse from "fs-extra"; import { GradleTaskTreeItem } from "../views"; import { Command } from "./Command"; +import { GRADLE_BUILD_FILE_NAMES } from "../constant"; export const COMMAND_OPEN_BUILD_FILE = "gradle.openBuildFile"; -async function run(taskItem: GradleTaskTreeItem): Promise { - await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(taskItem.task.definition.buildFile)); +async function run(path: string): Promise { + await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(path)); } export class OpenBuildFileCommand extends Command { - async run(taskItem: GradleTaskTreeItem): Promise { - return run(taskItem); + async run(item: GradleTaskTreeItem | { uri: string }): Promise { + if (item instanceof GradleTaskTreeItem) { + return run(item.task.definition.buildFile); + } else if (item.uri) { + const buildFilePath = await ensureBuildFilePath(item.uri); + if (buildFilePath) { + return run(buildFilePath); + } + } } } + +export async function ensureBuildFilePath(projectUri: string): Promise { + const projectFsPath = vscode.Uri.parse(projectUri).fsPath; + for (const buildFileName of GRADLE_BUILD_FILE_NAMES) { + const buildFilePath: string = path.join(projectFsPath, buildFileName); + if (await fse.pathExists(buildFilePath)) { + return buildFilePath; + } + } + return undefined; +} diff --git a/extension/src/commands/RunTasksCommand.ts b/extension/src/commands/RunTasksCommand.ts new file mode 100644 index 000000000..bb50c4f16 --- /dev/null +++ b/extension/src/commands/RunTasksCommand.ts @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import * as vscode from "vscode"; +import { GradleTaskProvider } from "../tasks"; +import { getRunTasks } from "../util/input"; +import { Command } from "./Command"; + +export const COMMAND_RUN_TASKS = "gradle.runTasks"; + +export class RunTasksCommand extends Command { + constructor(private readonly gradleTaskProvider: GradleTaskProvider) { + super(); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + async run(item?: any): Promise { + if (item?.uri) { + const foundTaskName = await getRunTasks(this.gradleTaskProvider, item.uri); + if (foundTaskName) { + const vscodeTask = this.gradleTaskProvider.getTasks().find((task) => task.name === foundTaskName); + if (vscodeTask) { + await vscode.tasks.executeTask(vscodeTask); + } + } + } + } +} diff --git a/extension/src/constant.ts b/extension/src/constant.ts index 20f20c8fa..5a595c5ac 100644 --- a/extension/src/constant.ts +++ b/extension/src/constant.ts @@ -16,6 +16,8 @@ export const GRADLE_COMPLETION = "gradle.completion"; export const VSCODE_TRIGGER_COMPLETION = "editor.action.triggerSuggest"; +export const GRADLE_BUILD_FILE_NAMES = ["build.gradle", "settings.gradle", "build.gradle.kts", "settings.gradle.kts"]; + export enum CompletionKinds { DEPENDENCY_GROUP = "dependency_group", DEPENDENCY_ARTIFACT = "dependency_artifact", diff --git a/extension/src/stores/RootProjectsStore.ts b/extension/src/stores/RootProjectsStore.ts index 73b0e34d0..f694e8b6e 100644 --- a/extension/src/stores/RootProjectsStore.ts +++ b/extension/src/stores/RootProjectsStore.ts @@ -4,6 +4,7 @@ import { getNestedProjectsConfig } from "../util/config"; import { StoreMap } from "."; import { isGradleRootProject } from "../util"; import { RootProject } from "../rootProject/RootProject"; +import { GRADLE_BUILD_FILE_NAMES } from "../constant"; async function getNestedRootProjectFolders(): Promise { const matchingNestedWrapperFiles = await vscode.workspace.findFiles("**/{gradlew,gradlew.bat}"); @@ -78,10 +79,7 @@ export class RootProjectsStore extends StoreMap { if ( ( await vscode.workspace.findFiles( - new vscode.RelativePattern( - folder, - "{build.gradle,settings.gradle,build.gradle.kts,settings.gradle.kts}" - ) + new vscode.RelativePattern(folder, `{${GRADLE_BUILD_FILE_NAMES.join(",")}}`) ) )?.length ) { diff --git a/extension/src/util/input.ts b/extension/src/util/input.ts index c93ca33f2..6e0bffd95 100644 --- a/extension/src/util/input.ts +++ b/extension/src/util/input.ts @@ -1,4 +1,5 @@ import * as vscode from "vscode"; +import * as path from "path"; import { TaskArgs } from "../stores/types"; import { RootProject } from "../rootProject/RootProject"; import { RootProjectsStore } from "../stores"; @@ -46,6 +47,30 @@ export function getFindTask(gradleTaskProvider: GradleTaskProvider): Thenable { + const buildFilePath = path.join(vscode.Uri.parse(projectUri).fsPath, "build.gradle"); + return vscode.window.showQuickPick( + gradleTaskProvider.loadTasks().then((tasks) => + tasks + .filter((task) => { + const buildFile = task.definition.buildFile; + if (buildFile) { + if (vscode.Uri.file(buildFile).fsPath === buildFilePath) { + return true; + } + } + return false; + }) + .map((task) => task.name) + ), + { + placeHolder: "Select a Gradle task to run", + ignoreFocusOut: true, + canPickMany: false, + } + ); +} + export async function getRootProjectFolder(rootProjectsStore: RootProjectsStore): Promise { const rootProjects = await rootProjectsStore.getProjectRoots(); if (rootProjects.length === 1) {