diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8ac475241..4f805b355 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,7 @@ name: Build & Publish on: push: + branches: [master] pull_request: branches: [master] release: @@ -15,7 +16,7 @@ jobs: - name: Use Java 8 uses: actions/setup-java@v1 with: - java-version: '8' + java-version: "8" architecture: x64 - name: Use Node 12.16.2 uses: actions/setup-node@v1 @@ -34,7 +35,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_SONARCLOUD_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - JAVA_HOME: '' + JAVA_HOME: "" - name: Upload lib uses: actions/upload-artifact@v2 with: @@ -45,12 +46,12 @@ jobs: # lib generated in the previous step for a better real-world test. test-extension: needs: [build-and-analyse] - name: 'Test Java ${{ matrix.java-version }} - Node ${{ matrix.node-version }} - ${{ matrix.os }}' + name: "Test Java ${{ matrix.java-version }} - Node ${{ matrix.node-version }} - ${{ matrix.os }}" runs-on: ${{ matrix.os }} strategy: matrix: node-version: [12.16.2] - java-version: ['8', '11'] + java-version: ["8", "11"] os: [ubuntu-latest, windows-latest] # os: [ubuntu-latest, windows-latest, macos-latest] steps: @@ -70,7 +71,7 @@ jobs: arguments: extension:build wrapper-cache-enabled: false env: - NODE_OPTIONS: '--max-old-space-size=4096' + NODE_OPTIONS: "--max-old-space-size=4096" - name: Download lib uses: actions/download-artifact@v2 with: @@ -112,8 +113,8 @@ jobs: arguments: testVsCode wrapper-cache-enabled: false env: - DISPLAY: ':99.0' - CI: 'true' + DISPLAY: ":99.0" + CI: "true" - name: Stop Gradle daemon for root project uses: eskatos/gradle-command-action@v1 with: diff --git a/README.md b/README.md index 367f9c485..f6b5ac72c 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ A Gradle build can have one or more projects. Projects are listed in a flat list When you expand a project, tasks are listed in a tree, grouped by the task group. You can toggle the display of the tasks by clicking on the `Show Flat List`/`Show Tree` button in the treeview header. -Gradle Tasks View +Gradle Tasks View
Run tasks @@ -47,7 +47,7 @@ Tasks can be run via: A running task will be shown with an animated "spinner" icon in the treeviews, along with `Cancel Task` & `Restart Task` buttons. The `Cancel Task` button will gracefully cancel the task. The `Restart Task` button will first cancel the task, then restart it. -Gradle Tasks Running +Gradle Tasks Running A task will be run a vscode terminal where you can view the task output. @@ -60,6 +60,23 @@ Tasks run via the `Run a Gradle Build` command are not reflected in any of the t Run Gradle Build
+ +
Control task terminal behaviour + +`"gradle.reuseTerminals": "task"` (default): + + + +`"gradle.reuseTerminals": "all"`: + + + +`"gradle.reuseTerminals": "off"`: + + + +
+
Debug JavaExec tasks This extension provides an experimental feature to debug [JavaExec](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html) tasks. Before using this feature you need to install the [Debugger for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-debug) and [Language Support for Java](https://marketplace.visualstudio.com/items?itemName=redhat.java) extensions. @@ -153,12 +170,13 @@ The extension uses the Gradle wrapper to list daemons, and is quite a slow proce This extension contributes the following settings: -- `gradle.autoDetect`: Automatically detect Gradle tasks -- `gradle.focusTaskInExplorer`: Focus the task in the explorer when running a task +- `gradle.autoDetect`: Automatically detect Gradle tasks ("on" or "off") +- `gradle.focusTaskInExplorer`: Focus the task in the explorer when running a task (boolean) - `gradle.nestedProjects`: Process nested projects (boolean or an array of directories) +- `gradle.reuseTerminals`: Reuse task terminals ("task" [default], "all", or "off") - `gradle.javaDebug`: Debug JavaExec tasks (see below for usage) -- `gradle.debug`: Show extra debug info in the output panel -- `gradle.disableConfirmations`: Disable the warning confirm messages when performing batch actions (eg clear tasks, stop daemons etc) +- `gradle.debug`: Show extra debug info in the output panel (boolean) +- `gradle.disableConfirmations`: Disable the warning confirm messages when performing batch actions (eg clear tasks, stop daemons etc) (boolean) ## Gradle & Java Settings @@ -166,7 +184,7 @@ Set Gradle & Java options with standard environment variables or standard Gradle ### Example Environment Variables -- `JAVE_HOME` +- `JAVA_HOME` - `GRADLE_USER_HOME` _Note, the VS Code settings take precedence over the environment variables._ diff --git a/extension/package.json b/extension/package.json index 659621bfe..d1a3baef3 100644 --- a/extension/package.json +++ b/extension/package.json @@ -592,6 +592,16 @@ "default": false, "description": "Discover Gradle projects in nested sub-directories" }, + "gradle.reuseTerminals": { + "enum": [ + "task", + "off", + "all" + ], + "default": "task", + "scope": "window", + "description": "Reuse task terminals behaviour" + }, "gradle.debug": { "type": "boolean", "default": false, diff --git a/extension/src/async/index.ts b/extension/src/async/index.ts deleted file mode 100644 index 3d438d3b7..000000000 --- a/extension/src/async/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Deferred'; diff --git a/extension/src/client/GradleClient.ts b/extension/src/client/GradleClient.ts index e6504f0a0..6ebc707fa 100644 --- a/extension/src/client/GradleClient.ts +++ b/extension/src/client/GradleClient.ts @@ -26,10 +26,10 @@ import { import { GradleClient as GrpcClient } from '../proto/gradle_grpc_pb'; import { logger, LoggerStream, LogVerbosity, Logger } from '../logger'; -import { EventWaiter } from '../events'; +import { EventWaiter } from '../util'; import { GradleServer } from '../server'; import { ProgressHandler } from '../progress'; -import { getGradleConfig, getConfigJavaDebug } from '../config'; +import { getGradleConfig, getConfigJavaDebug } from '../util'; import { removeCancellingTask, restartQueuedTask } from '../tasks/taskUtil'; import { COMMAND_REFRESH_DAEMON_STATUS, diff --git a/extension/src/commands/ClearAllPinnedTasksCommand.ts b/extension/src/commands/ClearAllPinnedTasksCommand.ts index 50a96219b..b3817fe14 100644 --- a/extension/src/commands/ClearAllPinnedTasksCommand.ts +++ b/extension/src/commands/ClearAllPinnedTasksCommand.ts @@ -1,4 +1,4 @@ -import { confirmModal } from '../input'; +import { confirmModal } from '../util/input'; import { PinnedTasksStore } from '../stores'; import { Command } from './Command'; diff --git a/extension/src/commands/ClearAllRecentTasksCommand.ts b/extension/src/commands/ClearAllRecentTasksCommand.ts index aedd49a42..f6554939e 100644 --- a/extension/src/commands/ClearAllRecentTasksCommand.ts +++ b/extension/src/commands/ClearAllRecentTasksCommand.ts @@ -1,4 +1,4 @@ -import { confirmModal } from '../input'; +import { confirmModal } from '../util/input'; import { RecentTasksStore } from '../stores'; import { Command } from './Command'; diff --git a/extension/src/commands/CloseAllTaskTerminalsCommand.ts b/extension/src/commands/CloseAllTaskTerminalsCommand.ts index e828712b9..2ca199630 100644 --- a/extension/src/commands/CloseAllTaskTerminalsCommand.ts +++ b/extension/src/commands/CloseAllTaskTerminalsCommand.ts @@ -1,4 +1,4 @@ -import { confirmModal } from '../input'; +import { confirmModal } from '../util/input'; import { TaskTerminalsStore } from '../stores'; import { Command } from './Command'; diff --git a/extension/src/commands/PinTaskWithArgsCommand.ts b/extension/src/commands/PinTaskWithArgsCommand.ts index 4fac13bc5..0c93e6fc3 100644 --- a/extension/src/commands/PinTaskWithArgsCommand.ts +++ b/extension/src/commands/PinTaskWithArgsCommand.ts @@ -1,6 +1,6 @@ import { GradleTaskTreeItem, PinnedTasksTreeDataProvider } from '../views'; import { GradleTaskDefinition } from '../tasks'; -import { getTaskArgs } from '../input'; +import { getTaskArgs } from '../util/input'; import { Command } from './Command'; export const COMMAND_PIN_TASK_WITH_ARGS = 'gradle.pinTaskWithArgs'; diff --git a/extension/src/commands/RunBuildCommand.ts b/extension/src/commands/RunBuildCommand.ts index d248ce837..e47a75dc1 100644 --- a/extension/src/commands/RunBuildCommand.ts +++ b/extension/src/commands/RunBuildCommand.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; import { parseArgsStringToArgv } from 'string-argv'; -import { getGradleCommand, getRootProjectFolder } from '../input'; +import { getGradleCommand, getRootProjectFolder } from '../util/input'; import { GradleRunnerTerminal } from '../terminal'; import { getRunBuildCancellationKey } from '../client/CancellationKeys'; import { Command } from './Command'; diff --git a/extension/src/commands/StopDaemonCommand.ts b/extension/src/commands/StopDaemonCommand.ts index 7da8d2c6f..96b139a7d 100644 --- a/extension/src/commands/StopDaemonCommand.ts +++ b/extension/src/commands/StopDaemonCommand.ts @@ -1,5 +1,5 @@ import { GradleDaemonTreeItem } from '../views'; -import { confirmModal } from '../input'; +import { confirmModal } from '../util/input'; import { logger } from '../logger'; import { Command } from './Command'; import { GradleClient } from '../client'; diff --git a/extension/src/commands/StopDaemonsCommand.ts b/extension/src/commands/StopDaemonsCommand.ts index 958b8d33f..3d039cf37 100644 --- a/extension/src/commands/StopDaemonsCommand.ts +++ b/extension/src/commands/StopDaemonsCommand.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { confirmModal } from '../input'; +import { confirmModal } from '../util/input'; import { StopDaemonsReply } from '../proto/gradle_pb'; import { logger } from '../logger'; import { Command } from './Command'; diff --git a/extension/src/decorators/index.ts b/extension/src/decorators/index.ts deleted file mode 100644 index b7a1cf14d..000000000 --- a/extension/src/decorators/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './decorators'; diff --git a/extension/src/events/index.ts b/extension/src/events/index.ts deleted file mode 100644 index 7353629c5..000000000 --- a/extension/src/events/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './EventWaiter'; diff --git a/extension/src/extension/Extension.ts b/extension/src/extension/Extension.ts index 570d74d8c..49fbb4f48 100644 --- a/extension/src/extension/Extension.ts +++ b/extension/src/extension/Extension.ts @@ -4,10 +4,7 @@ import { Api } from '../api'; import { GradleClient } from '../client'; import { GradleServer } from '../server'; import { Icons } from '../icons'; -import { - getConfigFocusTaskInExplorer, - getConfigIsDebugEnabled, -} from '../config'; +import { getConfigFocusTaskInExplorer, getConfigIsDebugEnabled } from '../util'; import { GradleDaemonsTreeDataProvider, PinnedTasksTreeDataProvider, @@ -32,7 +29,7 @@ import { RECENT_TASKS_VIEW, } from '../views/constants'; import { focusTaskInGradleTasksTree } from '../views/viewUtil'; -import { FileWatcher } from '../watcher'; +import { FileWatcher } from '../util'; import { COMMAND_RENDER_TASK, COMMAND_REFRESH } from '../commands'; import { Commands } from '../commands/Commands'; @@ -263,16 +260,16 @@ export class Extension { event.affectsConfiguration('java.import.gradle.java.home') ) { await this.restartServer(); - } - if ( + } else if ( event.affectsConfiguration('gradle.javaDebug') || event.affectsConfiguration('gradle.nestedProjects') ) { this.rootProjectsStore.clear(); await this.refresh(); await this.activate(); - } - if (event.affectsConfiguration('gradle.debug')) { + } else if (event.affectsConfiguration('gradle.reuseTerminals')) { + await this.refresh(); + } else if (event.affectsConfiguration('gradle.debug')) { const debug = getConfigIsDebugEnabled(); Logger.setLogVerbosity( debug ? LogVerbosity.DEBUG : LogVerbosity.INFO diff --git a/extension/src/rootProject/RootProject.ts b/extension/src/rootProject/RootProject.ts index 568f02654..66a064799 100644 --- a/extension/src/rootProject/RootProject.ts +++ b/extension/src/rootProject/RootProject.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; import { Environment } from '../proto/gradle_pb'; -import { JavaDebug } from '../config'; +import { JavaDebug } from '../util'; export class RootProject { private environment?: Environment; diff --git a/extension/src/rootProject/index.ts b/extension/src/rootProject/index.ts new file mode 100644 index 000000000..69e5c08e6 --- /dev/null +++ b/extension/src/rootProject/index.ts @@ -0,0 +1 @@ +export * from './RootProject'; diff --git a/extension/src/server/serverUtil.ts b/extension/src/server/serverUtil.ts index d7b307716..35cec43e4 100644 --- a/extension/src/server/serverUtil.ts +++ b/extension/src/server/serverUtil.ts @@ -1,4 +1,4 @@ -import { getConfigGradleJavaHome } from '../config'; +import { getConfigGradleJavaHome } from '../util'; export function getGradleServerCommand(): string { const platform = process.platform; diff --git a/extension/src/stores/EventedStore.ts b/extension/src/stores/EventedStore.ts index a0ebaf2dd..ad9b803d6 100644 --- a/extension/src/stores/EventedStore.ts +++ b/extension/src/stores/EventedStore.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { debounce } from '../decorators'; +import { debounce } from '../util'; export abstract class EventedStore implements vscode.Disposable { private readonly _onDidChange: vscode.EventEmitter = new vscode.EventEmitter< diff --git a/extension/src/stores/RootProjectsStore.ts b/extension/src/stores/RootProjectsStore.ts index fea69c113..782fac224 100644 --- a/extension/src/stores/RootProjectsStore.ts +++ b/extension/src/stores/RootProjectsStore.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; import * as path from 'path'; -import { getNestedProjectsConfig, getConfigJavaDebug } from '../config'; +import { getNestedProjectsConfig, getConfigJavaDebug } from '../util'; import { StoreMap } from '.'; import { isGradleRootProject } from '../util'; import { RootProject } from '../rootProject/RootProject'; diff --git a/extension/src/tasks/GradleTaskProvider.ts b/extension/src/tasks/GradleTaskProvider.ts index a5df07467..135c4d440 100644 --- a/extension/src/tasks/GradleTaskProvider.ts +++ b/extension/src/tasks/GradleTaskProvider.ts @@ -1,12 +1,12 @@ import * as vscode from 'vscode'; -import { EventWaiter } from '../events'; +import { EventWaiter } from '../util'; import { GradleTaskDefinition } from '.'; import { logger } from '../logger'; import { createTaskFromDefinition, loadTasksForProjectRoots } from './taskUtil'; import { TaskId } from '../stores/types'; import { RootProjectsStore, TaskTerminalsStore } from '../stores'; import { RootProject } from '../rootProject/RootProject'; -import { getConfigJavaDebug } from '../config'; +import { getConfigJavaDebug, getConfigReuseTerminals } from '../util'; import { GradleClient } from '../client'; export class GradleTaskProvider @@ -64,11 +64,13 @@ export class GradleTaskProvider vscode.Uri.file(gradleTaskDefinition.projectFolder), javaDebug ); + const reuseTerminals = getConfigReuseTerminals(); return createTaskFromDefinition( this.taskTerminalsStore, gradleTaskDefinition, rootProject, - this.client + this.client, + reuseTerminals ); } diff --git a/extension/src/tasks/taskUtil.ts b/extension/src/tasks/taskUtil.ts index 5c370588a..9c947e371 100644 --- a/extension/src/tasks/taskUtil.ts +++ b/extension/src/tasks/taskUtil.ts @@ -5,7 +5,12 @@ import { TaskArgs } from '../stores/types'; import { GradleTaskDefinition } from '.'; import { GradleRunnerTerminal } from '../terminal'; import { logger } from '../logger'; -import { getGradleConfig, getConfigIsAutoDetectionEnabled } from '../config'; +import { + getGradleConfig, + getConfigIsAutoDetectionEnabled, + getConfigReuseTerminals, + ReuseTerminalsValue, +} from '../util'; import { getJavaLanguageSupportExtension, getJavaDebuggerExtension, @@ -13,8 +18,8 @@ import { JAVA_DEBUGGER_EXTENSION_ID, isJavaDebuggerExtensionActivated, isJavaLanguageSupportExtensionActivated, -} from '../compat'; -import { getTaskArgs } from '../input'; +} from '../util/compat'; +import { getTaskArgs } from '../util/input'; import { COMMAND_RENDER_TASK } from '../commands'; import { RootProject } from '../rootProject/RootProject'; import { getRunTaskCommandCancellationKey } from '../client/CancellationKeys'; @@ -151,11 +156,55 @@ export function buildTaskName(definition: GradleTaskDefinition): string { return `${definition.script}${argsLabel}`; } +function disposePreviousTerminals( + definition: GradleTaskDefinition, + taskTerminalsStore: TaskTerminalsStore, + reuseTerminals: ReuseTerminalsValue +): void { + if (reuseTerminals === 'task') { + const previousTerminals = taskTerminalsStore.get( + definition.id + definition.args + ); + if (previousTerminals) { + for (const previousTerminal of previousTerminals.values()) { + previousTerminal.dispose(); + } + previousTerminals.clear(); + } + } else if (reuseTerminals === 'all') { + const store = taskTerminalsStore.getData(); + for (const taskTerminals of store.values()) { + for (const previousTerminal of taskTerminals.values()) { + previousTerminal.dispose(); + } + taskTerminals.clear(); + } + } +} + +function handleTaskStart( + definition: GradleTaskDefinition, + taskTerminalsStore: TaskTerminalsStore, + reuseTerminals: ReuseTerminalsValue +): void { + disposePreviousTerminals(definition, taskTerminalsStore, reuseTerminals); + const disposable = vscode.window.onDidOpenTerminal( + (openedTerminal: vscode.Terminal) => { + disposable.dispose(); + taskTerminalsStore.addEntry( + definition.id + definition.args, + openedTerminal + ); + } + ); +} + export function createTaskFromDefinition( taskTerminalsStore: TaskTerminalsStore, definition: Required, rootProject: RootProject, - client: GradleClient + client: GradleClient, + reuseTerminals: ReuseTerminalsValue ): vscode.Task { const args = [definition.script] .concat(parseArgsStringToArgv(definition.args.trim())) @@ -165,6 +214,7 @@ export function createTaskFromDefinition( rootProject.getProjectUri().fsPath, definition.script ); + const terminal = new GradleRunnerTerminal( rootProject, args, @@ -178,15 +228,7 @@ export function createTaskFromDefinition( 'gradle', new vscode.CustomExecution( async (): Promise => { - const disposable = vscode.window.onDidOpenTerminal( - (openedTerminal: vscode.Terminal) => { - disposable.dispose(); - taskTerminalsStore.addEntry( - definition.id + definition.args, - openedTerminal - ); - } - ); + handleTaskStart(definition, taskTerminalsStore, reuseTerminals); return terminal; } ), @@ -209,6 +251,7 @@ function createVSCodeTaskFromGradleTask( gradleTask: GradleTask, rootProject: RootProject, client: GradleClient, + reuseTerminals: ReuseTerminalsValue, args = '', javaDebug = false ): vscode.Task { @@ -236,7 +279,8 @@ function createVSCodeTaskFromGradleTask( taskTerminalsStore, definition, rootProject, - client + client, + reuseTerminals ); } @@ -246,6 +290,7 @@ export function getVSCodeTasksFromGradleProject( gradleProject: GradleProject, client: GradleClient ): vscode.Task[] { + const reuseTerminals = getConfigReuseTerminals(); let projects: Array = [gradleProject]; const vsCodeTasks: vscode.Task[] = []; while (projects.length) { @@ -257,7 +302,8 @@ export function getVSCodeTasksFromGradleProject( taskTerminalsStore, gradleTask, rootProject, - client + client, + reuseTerminals ) ); } @@ -391,10 +437,12 @@ export function cloneTask( javaDebug, }; const rootProject = rootProjectsStore.get(definition.projectFolder); + const reuseTerminals = getConfigReuseTerminals(); return createTaskFromDefinition( taskTerminalsStore, definition, rootProject!, - client + client, + reuseTerminals ); } diff --git a/extension/src/terminal/GradleRunnerTerminal.ts b/extension/src/terminal/GradleRunnerTerminal.ts index c2a5eaff9..e095d6ef2 100644 --- a/extension/src/terminal/GradleRunnerTerminal.ts +++ b/extension/src/terminal/GradleRunnerTerminal.ts @@ -15,7 +15,7 @@ const NL = '\n'; const CR = '\r'; const nlRegExp = new RegExp(`${NL}([^${CR}]|$)`, 'g'); -export class GradleRunnerTerminal { +export class GradleRunnerTerminal implements vscode.Pseudoterminal { private readonly writeEmitter = new vscode.EventEmitter(); private stdOutLoggerStream: LoggerStream | undefined; private readonly closeEmitter = new vscode.EventEmitter(); diff --git a/extension/src/test/integration/gradle/extension.test.ts b/extension/src/test/integration/gradle/extension.test.ts index fe7e5d8c8..1ba356462 100644 --- a/extension/src/test/integration/gradle/extension.test.ts +++ b/extension/src/test/integration/gradle/extension.test.ts @@ -15,8 +15,23 @@ const fixturePath = vscode.Uri.file( path.resolve(__dirname, '../../../../test-fixtures', fixtureName) ); +const executeAndWaitForTask = (task: vscode.Task): Promise => { + return new Promise(async (resolve) => { + const disposable = vscode.tasks.onDidEndTaskProcess((e) => { + if (e.execution.task === task) { + disposable.dispose(); + resolve(); + } + }); + try { + await vscode.tasks.executeTask(task); + } catch (e) { + console.error('There was an error starting the task:', e.message); + } + }); +}; + describe(getSuiteName('Extension'), () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any let extension: vscode.Extension | undefined; before(() => { @@ -72,19 +87,7 @@ describe(getSuiteName('Extension'), () => { extension!.exports.getLogger(), 'appendLine' ); - await new Promise(async (resolve) => { - const disposable = vscode.tasks.onDidEndTaskProcess((e) => { - if (e.execution.task === task) { - disposable.dispose(); - resolve(); - } - }); - try { - await vscode.tasks.executeTask(task); - } catch (e) { - console.error('There was an error starting the task:', e.message); - } - }); + await executeAndWaitForTask(task); assert.ok(loggerAppendSpy.calledWith(sinon.match('Hello, World!'))); assert.ok( loggerAppendLineSpy.calledWith(sinon.match('Completed build: hello')) @@ -104,7 +107,6 @@ describe(getSuiteName('Extension'), () => { assert.ok(task); const spy = sinon.spy(extension.exports.getLogger(), 'append'); await new Promise(async (resolve) => { - // eslint-disable-next-line sonarjs/no-identical-functions const endDisposable = vscode.tasks.onDidEndTaskProcess((e) => { if (e.execution.task.definition.script === task.definition.script) { endDisposable.dispose(); @@ -151,4 +153,55 @@ describe(getSuiteName('Extension'), () => { assert.ok(hasMessage); }); }); + + describe('Reuse terminals config', () => { + const resetConfig = async (): Promise => + await vscode.workspace + .getConfiguration('gradle') + .update('reuseTerminals', 'off'); + + const executeAndWaitForTasks = async (): Promise => { + const tasks = await vscode.tasks.fetchTasks({ type: 'gradle' }); + const byeTask = tasks.find(({ name }) => name === 'bye'); + assert.ok(byeTask); + const helloTask = tasks.find(({ name }) => name === 'hello'); + assert.ok(helloTask); + await executeAndWaitForTask(byeTask); + await executeAndWaitForTask(byeTask); + await executeAndWaitForTask(helloTask); + }; + + before(async () => await resetConfig()); + after(async () => await resetConfig()); + + beforeEach(() => { + vscode.window.terminals.forEach((terminal) => { + terminal.dispose(); + }); + }); + + it('should generate a new terminal for every task run with reuseTerminals: "off"', async () => { + await vscode.workspace + .getConfiguration('gradle') + .update('reuseTerminals', 'off'); + await executeAndWaitForTasks(); + assert.strictEqual(vscode.window.terminals.length, 3); + }); + + it('should generate 1 terminal per task with reuseTerminals: "task"', async () => { + await vscode.workspace + .getConfiguration('gradle') + .update('reuseTerminals', 'task'); + await executeAndWaitForTasks(); + assert.strictEqual(vscode.window.terminals.length, 2); + }); + + it('should generate 1 terminal for all tasks with reuseTerminals: "all"', async () => { + await vscode.workspace + .getConfiguration('gradle') + .update('reuseTerminals', 'all'); + await executeAndWaitForTasks(); + assert.strictEqual(vscode.window.terminals.length, 1); + }); + }); }); diff --git a/extension/src/async/Deferred.ts b/extension/src/util/Deferred.ts similarity index 100% rename from extension/src/async/Deferred.ts rename to extension/src/util/Deferred.ts diff --git a/extension/src/events/EventWaiter.ts b/extension/src/util/EventWaiter.ts similarity index 100% rename from extension/src/events/EventWaiter.ts rename to extension/src/util/EventWaiter.ts diff --git a/extension/src/watcher/FileWatcher.ts b/extension/src/util/FileWatcher.ts similarity index 100% rename from extension/src/watcher/FileWatcher.ts rename to extension/src/util/FileWatcher.ts diff --git a/extension/src/compat.ts b/extension/src/util/compat.ts similarity index 100% rename from extension/src/compat.ts rename to extension/src/util/compat.ts diff --git a/extension/src/config.ts b/extension/src/util/config.ts similarity index 90% rename from extension/src/config.ts rename to extension/src/util/config.ts index a1ac16da6..b71e7d1db 100644 --- a/extension/src/config.ts +++ b/extension/src/util/config.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; -import { GradleConfig } from './proto/gradle_pb'; -import { RootProject } from './rootProject/RootProject'; +import { GradleConfig } from '../proto/gradle_pb'; +import { RootProject } from '../rootProject/RootProject'; type AutoDetect = 'on' | 'off'; @@ -60,6 +60,14 @@ export function getConfigIsDebugEnabled(): boolean { .get('debug', false); } +export type ReuseTerminalsValue = 'task' | 'off' | 'all'; + +export function getConfigReuseTerminals(): ReuseTerminalsValue { + return vscode.workspace + .getConfiguration('gradle') + .get('reuseTerminals', 'task'); +} + export function getDisableConfirmations(): boolean { return vscode.workspace .getConfiguration('gradle') diff --git a/extension/src/decorators/decorators.ts b/extension/src/util/decorators.ts similarity index 100% rename from extension/src/decorators/decorators.ts rename to extension/src/util/decorators.ts diff --git a/extension/src/util.ts b/extension/src/util/index.ts similarity index 87% rename from extension/src/util.ts rename to extension/src/util/index.ts index 0cd876d09..86447c939 100644 --- a/extension/src/util.ts +++ b/extension/src/util/index.ts @@ -2,7 +2,7 @@ import * as vscode from 'vscode'; import * as net from 'net'; import * as path from 'path'; import * as fs from 'fs'; -import { RootProject } from './rootProject/RootProject'; +import { RootProject } from '../rootProject'; export const isTest = (): boolean => process.env.VSCODE_TEST?.toLowerCase() === 'true'; @@ -64,3 +64,11 @@ export function isWorkspaceFolder( ): value is vscode.WorkspaceFolder { return value && typeof value !== 'number'; } + +export * from './compat'; +export * from './config'; +export * from './decorators'; +export * from './Deferred'; +export * from './EventWaiter'; +export * from './input'; +export * from './FileWatcher'; diff --git a/extension/src/input/index.ts b/extension/src/util/input.ts similarity index 97% rename from extension/src/input/index.ts rename to extension/src/util/input.ts index 07b24b645..234a4deed 100644 --- a/extension/src/input/index.ts +++ b/extension/src/util/input.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; import { TaskArgs } from '../stores/types'; -import { getDisableConfirmations } from '../config'; +import { getDisableConfirmations } from '../util'; import { RootProject } from '../rootProject/RootProject'; import { RootProjectsStore } from '../stores'; diff --git a/extension/src/views/gradleDaemons/GradleDaemonsTreeDataProvider.ts b/extension/src/views/gradleDaemons/GradleDaemonsTreeDataProvider.ts index 736b02310..dbd52d60e 100644 --- a/extension/src/views/gradleDaemons/GradleDaemonsTreeDataProvider.ts +++ b/extension/src/views/gradleDaemons/GradleDaemonsTreeDataProvider.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; import { GradleDaemonTreeItem } from '.'; -import { Deferred } from '../../async'; +import { Deferred } from '../../util'; import { RootProjectsStore } from '../../stores'; import { GradleClient } from '../../client'; diff --git a/extension/src/views/gradleTasks/GradleTaskTreeItem.ts b/extension/src/views/gradleTasks/GradleTaskTreeItem.ts index 269ffc1c5..622ed54f1 100644 --- a/extension/src/views/gradleTasks/GradleTaskTreeItem.ts +++ b/extension/src/views/gradleTasks/GradleTaskTreeItem.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { JavaDebug } from '../../config'; +import { JavaDebug } from '../../util'; import { Icons } from '../../icons'; import { TASK_STATE_RUNNING_REGEX } from '../constants'; import { getTreeItemState } from '../viewUtil'; diff --git a/extension/src/views/recentTasks/RecentTaskTreeItem.ts b/extension/src/views/recentTasks/RecentTaskTreeItem.ts index d86c828ac..cc5ce1ab5 100644 --- a/extension/src/views/recentTasks/RecentTaskTreeItem.ts +++ b/extension/src/views/recentTasks/RecentTaskTreeItem.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode'; import { GradleTaskTreeItem } from '..'; -import { JavaDebug } from '../../config'; +import { JavaDebug } from '../../util'; import { Icons } from '../../icons'; import { TaskTerminalsStore } from '../../stores'; import { GradleTaskDefinition } from '../../tasks'; diff --git a/extension/src/views/viewUtil.ts b/extension/src/views/viewUtil.ts index dd6a015a6..8eefe1af7 100644 --- a/extension/src/views/viewUtil.ts +++ b/extension/src/views/viewUtil.ts @@ -8,7 +8,7 @@ import { } from './constants'; import { GradleTaskDefinition } from '../tasks'; import { logger } from '../logger'; -import { JavaDebug } from '../config'; +import { JavaDebug } from '../util'; import { TaskArgs } from '../stores/types'; import { isTaskCancelling, isTaskRunning } from '../tasks/taskUtil'; import { diff --git a/extension/src/watcher/index.ts b/extension/src/watcher/index.ts deleted file mode 100644 index 78f81077c..000000000 --- a/extension/src/watcher/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './FileWatcher'; diff --git a/extension/test-fixtures/gradle-groovy-custom-build-file/my-custom-build.gradle b/extension/test-fixtures/gradle-groovy-custom-build-file/my-custom-build.gradle index 700664759..ac3836ba3 100644 --- a/extension/test-fixtures/gradle-groovy-custom-build-file/my-custom-build.gradle +++ b/extension/test-fixtures/gradle-groovy-custom-build-file/my-custom-build.gradle @@ -11,6 +11,12 @@ tasks.register("hello") { } } +tasks.register("bye") { + doLast { + println 'Goodbye cruel world' + } +} + tasks.register("helloProjectProperty") { doLast { println 'Hello, Project Property!' + customProp diff --git a/extension/test-fixtures/gradle-groovy-default-build-file/build.gradle b/extension/test-fixtures/gradle-groovy-default-build-file/build.gradle index bb84c6d31..69c80d02b 100644 --- a/extension/test-fixtures/gradle-groovy-default-build-file/build.gradle +++ b/extension/test-fixtures/gradle-groovy-default-build-file/build.gradle @@ -11,6 +11,12 @@ tasks.register("hello") { } } +tasks.register("bye") { + doLast { + println 'Goodbye cruel world' + } +} + tasks.register("helloProjectProperty") { doLast { println 'Hello, Project Property!' + customProp diff --git a/extension/test-fixtures/gradle-kotlin-default-build-file/build.gradle.kts b/extension/test-fixtures/gradle-kotlin-default-build-file/build.gradle.kts index 0a8bb57fa..9f2bd031e 100644 --- a/extension/test-fixtures/gradle-kotlin-default-build-file/build.gradle.kts +++ b/extension/test-fixtures/gradle-kotlin-default-build-file/build.gradle.kts @@ -11,6 +11,12 @@ tasks.register("hello") { } } +tasks.register("bye") { + doLast { + println("Goodbye cruel world") + } +} + tasks.register("helloKotlinDefault") { doLast { println("Hello, helloKotlinDefault!") diff --git a/images/reuse-terminals-all.gif b/images/reuse-terminals-all.gif new file mode 100644 index 000000000..c57764c15 Binary files /dev/null and b/images/reuse-terminals-all.gif differ diff --git a/images/reuse-terminals-off.gif b/images/reuse-terminals-off.gif new file mode 100644 index 000000000..ce28d170d Binary files /dev/null and b/images/reuse-terminals-off.gif differ diff --git a/images/reuse-terminals-task.gif b/images/reuse-terminals-task.gif new file mode 100644 index 000000000..4c250a553 Binary files /dev/null and b/images/reuse-terminals-task.gif differ diff --git a/npm-package/.eslintignore b/npm-package/.eslintignore index d5f19d89b..21a841fe4 100644 --- a/npm-package/.eslintignore +++ b/npm-package/.eslintignore @@ -1,2 +1,6 @@ node_modules package-lock.json +**/*.ts +**/*.js +!/*.ts +/*.d.ts diff --git a/npm-package/build.gradle b/npm-package/build.gradle index 9319de1c7..d86e0256a 100644 --- a/npm-package/build.gradle +++ b/npm-package/build.gradle @@ -106,6 +106,15 @@ task copyClientTypes(type: Copy) { into 'lib/client' } +task copyUtilTypes(type: Copy) { + dependsOn ':extension:buildProd' + group 'copy' + description 'Copies util' + from file("../extension/dist/util") + include "*.d.ts" + into 'lib/util' +} + task copyTasksTypes(type: Copy) { dependsOn ':extension:buildProd' group 'copy' @@ -152,7 +161,8 @@ task copyPublicApi(type: Copy) { copyLoggerTypes, copyRootProjectTypes, copyApiTypes, - copyIconTypes + copyIconTypes, + copyUtilTypes group 'copy' description 'Copies publc API types' from file("../extension/dist")