diff --git a/CHANGELOG.md b/CHANGELOG.md index bbddf8eab..10337d6d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Add macOS support for Swiftly toolchain management ([#1673](https://github.com/swiftlang/vscode-swift/pull/1673)) - Show revision hash or local/editing keyword in project panel dependency descriptions ([#1667](https://github.com/swiftlang/vscode-swift/pull/1667)) - Show files generated by build plugins under Target in Project Panel ([#1592](https://github.com/swiftlang/vscode-swift/pull/1592)) +- Added code lenses to run suites/tests, configurable with the `swift.showTestCodeLenses` setting ([#1698](https://github.com/swiftlang/vscode-swift/pull/1698)) ## 2.6.2 - 2025-07-02 @@ -16,6 +17,7 @@ - Fix test explorer tests not updating on document modification ([#1663](https://github.com/swiftlang/vscode-swift/pull/1663)) - Fix improper parenting of tests w/ identical names in explorer ([#1664](https://github.com/swiftlang/vscode-swift/pull/1664)) - Ensure document symbols are provided for folders in multi-root workspaces ([#1668](https://github.com/swiftlang/vscode-swift/pull/1668)) +- Fix comment regex not handling params/types/fn names containing `func` ([#1697](https://github.com/swiftlang/vscode-swift/pull/1697)) ## 2.6.1 - 2025-06-27 @@ -82,7 +84,7 @@ ### Fixed - Prevent duplicate reload extension notifications from appearing ([#1473](https://github.com/swiftlang/vscode-swift/pull/1473)) -- "Actual" and "Expected" values are shown in the right order on test failure ([#1444](https://github.com/swiftlang/vscode-swift/issues/1444)) +- Actual and Expected values are shown in the right order on test failure ([#1444](https://github.com/swiftlang/vscode-swift/issues/1444)) - Correctly set the `DEVELOPER_DIR` environment variable when selecting between two Xcode installs ([#1433](https://github.com/swiftlang/vscode-swift/pull/1433)) - Prompt to reload the extension when swiftEnvironmentVariables is changed ([#1430](https://github.com/swiftlang/vscode-swift/pull/1430)) - Search for Swift packages in sub-folders of the workspace ([#1425](https://github.com/swiftlang/vscode-swift/pull/1425)) @@ -208,8 +210,8 @@ The new extension id is `swiftlang.swift-vscode`. ### Changed -- Add setting to exclude files/directories from code coverage results. -- Updated and refined settings descriptions. +- Add setting to exclude files/directories from code coverage results +- Updated and refined settings descriptions - Added "throws" section to doc comment template ### Fixed @@ -268,7 +270,7 @@ The new extension id is `swiftlang.swift-vscode`. - Fix XCTest argument format when debugging multiple tests - Add user defined and optional sanitizer/diagnostics arguments to test builds -- Silence Terminal on test runs. +- Silence Terminal on test runs ## 1.10.0 - 2024-06-07 @@ -298,13 +300,16 @@ The new extension id is `swiftlang.swift-vscode`. ## 1.9.0 - 2024-04-15 ### Added + - Running tests in parallel. It is available from the drop-down next to the run button at the top of the TestExplorer. It is not available while debugging and parsing of XCT failure messages does not work prior to Swift 6. ### Changed + - If using Swift 5.10 allow for InlayHint text edits. - If using Swift 6 name of debug adapter has changed from `lldb-vscode` to `lldb-dap`. ### Fixed + - Don't check if line above is a comment if you are on line 0 in comment completion code. ## 1.8.1 - 2024-03-06 @@ -316,6 +321,7 @@ The new extension id is `swiftlang.swift-vscode`. ## 1.8.0 - 2024-02-21 ### Added + - Platform specific settings in the swift task definition. - Environment variables to set while running a swift task. - Setting to disable all Swift Package Manager integrations. @@ -323,6 +329,7 @@ The new extension id is `swiftlang.swift-vscode`. - Watch for changes to swift files in test targets and flag 'test discovery is required' if a file changes or is deleted. ### Changed + - Expand `~` in swift file path setting to home directory. - Don't create test explorer if project has no tests. - Only run test discovery after a build all task. @@ -333,21 +340,26 @@ The new extension id is `swiftlang.swift-vscode`. ## 1.7.2 - 2024-01-03 ### Added + - Setting to control action after a build error: focus on problems pane, focus on terminal or do nothing. -### Changes +### Changed + - Don't force show test pane when testing starts. Let `Testing: Open Testing` define when test pane should open. ### Fixed + - Setup of URI on readonly document viewer. This fixes jump to symbol in a swiftinterface on Windows ## 1.7.1 - 2023-12-02 ### Added + - Task queue operation to spawn a process and parse its output. Using this ensures a build task does not run at the same time. - Use spawn process task queue operation in test discovery and unedit of modules. -### Changes +### Changed + - Don't wait for SwiftPM plugin listing to finish before allowing build and run. - If auto-resolve is disabled then also disable the initial test discovery as this can cause a resolve @@ -358,7 +370,7 @@ The new extension id is `swiftlang.swift-vscode`. Merge debug adapter changes from v1.6.x prerelease builds into main release. -### Changes +### Changed - Consolidate common debug configurations when building debug configurations. @@ -368,8 +380,6 @@ Merge debug adapter changes from v1.6.x prerelease builds into main release. - Increase the size of stdout available to `llvm-cov` process. This fixes displaying test coverage for larger projects. - Build product instead of target when compiling Swift Snippets. This fixes running of Snippets on macOS. -### Fixed - ## 1.6.1 - 2023-10-04 (Toolchain debug adapter preview) ### Added @@ -428,17 +438,20 @@ Merge debug adapter changes from v1.6.x prerelease builds into main release. ## 1.4.0 - 2023-07-05 ### Added + - Add sanitizer build setting - Build tasks are not available while other tasks are running on the package - Add read-only document provider for swiftinterface files in preparation for go to definition for stdlib/framework symbols. ### Changed + - Add supported language configuration setting to replace Support C/C++ setting - deprecate Support C/C++ setting - Remove a number of unnecessary build arguments for Windows when using Swift 5.9 or later - Configure vscode-lldb to use native expressions ### Fixed + - Require a reload when using the select Xcode developer directory command - Reporting of errors returned by the compiler without a column number @@ -578,7 +591,7 @@ Merge debug adapter changes from v1.6.x prerelease builds into main release. ### Added - Support for Swift Snippets (requires Swift 5.7). Two new commands have been added `Run Swift Snippet` and `Debug Swift Snippet`. -- Sub menu to text editor right click menu. Includes commands not acccessible elsewhere `Run Swift Script`, Snippet commands and `Clean Build`. +- Sub menu to text editor right click menu. Includes commands not accessible elsewhere `Run Swift Script`, Snippet commands and `Clean Build`. - macOS: Command to choose between macOS, iOS, tvOS and watchOS targets. Switching to a non macOS target will give you symbol completion for that target, but building your package will have undefined results. - macOS: Command to choose between Swift toolchains from all versions of Xcode installed on your system. @@ -586,7 +599,7 @@ Merge debug adapter changes from v1.6.x prerelease builds into main release. - When working out project dependencies traverse local dependencies to get full dependency chain - Changed settings scope for a number of settings so they can be set per workspace folder -- Store hash of `Package.resolved` to compare with new `Package.resolved` whenever it has been updated, to ensure it has actaully changed before running `swift package resolve`. +- Store hash of `Package.resolved` to compare with new `Package.resolved` whenever it has been updated, to ensure it has actually changed before running `swift package resolve`. ### Fixed @@ -661,7 +674,6 @@ Merge debug adapter changes from v1.6.x prerelease builds into main release. ### Fixed - Running non-Swift LLDB on Windows - ## 0.5.0 - 2022-05-02 Version 0.5.0 of vscode-swift now requires v1.65.0 of Visual Studio Code @@ -758,10 +770,10 @@ Version 0.5.0 of vscode-swift now requires v1.65.0 of Visual Studio Code - Automatic generation of launch target for running tests. This is no longer needed now we have the test explorer. - ## 0.3.0 - 2022-02-22 ### Added + - Function documentation comment completion. Type "///" on line above function to activate. - Package dependency view has new right click menu. Menu entries include: - Use Local Version: Use local version of package dependency. @@ -773,6 +785,7 @@ Version 0.5.0 of vscode-swift now requires v1.65.0 of Visual Studio Code - Support for building development version of package via `npm run dev-package`. ### Changed + - Build terminal window is cleared before a build - When the Swift path or SourceKit-LSP path are changed the extension will restart to ensure the correct versions are used. @@ -784,6 +797,7 @@ Version 0.5.0 of vscode-swift now requires v1.65.0 of Visual Studio Code ## 0.2.0 - 2022-01-20 ### Added + - Build tasks for all folders in the workspace. - Resolve and update commands which update current folder. - Reset package and clean build commands. @@ -794,10 +808,12 @@ Version 0.5.0 of vscode-swift now requires v1.65.0 of Visual Studio Code - Cache contents of Package.resolved for use across different systems. ### Changed + - Cleanup Language client code - Package dependency view updates based on current folder ### Fixed + - Use correct workspace folder in launch.json program name ## 0.1.1 - 2021-12-27 diff --git a/package.json b/package.json index d02913af0..5c9a950f0 100644 --- a/package.json +++ b/package.json @@ -306,6 +306,27 @@ "category": "Test", "icon": "$(debug-coverage)" }, + { + "command": "swift.runTest", + "title": "Run Test", + "category": "Test", + "icon": "$(testing-run-icon)", + "enablement": "false" + }, + { + "command": "swift.debugTest", + "title": "Debug Test", + "category": "Test", + "icon": "$(testing-debug-icon)", + "enablement": "false" + }, + { + "command": "swift.runTestWithCoverage", + "title": "Run Test with Coverage", + "category": "Test", + "icon": "$(debug-coverage)", + "enablement": "false" + }, { "command": "swift.openDocumentation", "title": "Open Documentation", @@ -562,7 +583,7 @@ } }, { - "title": "Code Coverage", + "title": "Testing", "properties": { "swift.excludeFromCodeCoverage": { "description": "A list of paths to exclude from code coverage reports. Paths can be absolute or relative to the workspace root.", @@ -572,6 +593,23 @@ }, "default": [], "scope": "machine-overridable" + }, + "swift.showTestCodeLenses": { + "type": [ + "boolean", + "array" + ], + "default": true, + "markdownDescription": "Controls whether or not to show inline code lenses for running and debugging tests inline, above test and suite declarations. If set to an array, specify one or more of the following: 'run', 'debug', 'coverage'.", + "scope": "application", + "items": { + "type": "string", + "enum": [ + "run", + "debug", + "coverage" + ] + } } } }, @@ -1044,6 +1082,18 @@ "command": "swift.coverAllTests", "when": "swift.isActivated" }, + { + "command": "swift.runTest", + "when": "false" + }, + { + "command": "swift.debugTest", + "when": "false" + }, + { + "command": "swift.runTestWithCoverage", + "when": "false" + }, { "command": "swift.openEducationalNote", "when": "false" diff --git a/src/TestExplorer/TestCodeLensProvider.ts b/src/TestExplorer/TestCodeLensProvider.ts new file mode 100644 index 000000000..3699ffe04 --- /dev/null +++ b/src/TestExplorer/TestCodeLensProvider.ts @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the VS Code Swift open source project +// +// Copyright (c) 2021-2025 the VS Code Swift project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of VS Code Swift project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import * as vscode from "vscode"; +import { TestExplorer } from "./TestExplorer"; +import { flattenTestItemCollection } from "./TestUtils"; +import configuration, { ValidCodeLens } from "../configuration"; + +export class TestCodeLensProvider implements vscode.CodeLensProvider, vscode.Disposable { + private onDidChangeCodeLensesEmitter = new vscode.EventEmitter(); + public onDidChangeCodeLenses = this.onDidChangeCodeLensesEmitter.event; + private disposables: vscode.Disposable[] = []; + + constructor(private testExplorer: TestExplorer) { + this.disposables = [ + testExplorer.onTestItemsDidChange(() => this.onDidChangeCodeLensesEmitter.fire()), + vscode.languages.registerCodeLensProvider({ language: "swift", scheme: "file" }, this), + ]; + } + + dispose() { + this.disposables.forEach(disposable => disposable.dispose()); + } + + public provideCodeLenses( + document: vscode.TextDocument, + _token: vscode.CancellationToken + ): vscode.ProviderResult { + const config = configuration.showTestCodeLenses; + if (config === false || (Array.isArray(config) && config.length === 0)) { + return []; + } + + const items = flattenTestItemCollection(this.testExplorer.controller.items); + return items + .filter(item => item.uri?.fsPath === document.uri.fsPath) + .flatMap(item => this.codeLensesForTestItem(item, config)); + } + + private codeLensesForTestItem( + item: vscode.TestItem, + config: boolean | ValidCodeLens[] + ): vscode.CodeLens[] { + if (!item.range) { + return []; + } + + const lensConfigs: Array<{ + type: ValidCodeLens; + title: string; + command: string; + }> = [ + { + type: "run", + title: "$(play)\u00A0Run", + command: "swift.runTest", + }, + { + type: "debug", + title: "$(debug)\u00A0Debug", + command: "swift.debugTest", + }, + { + type: "coverage", + title: "$(debug-coverage)\u00A0Run w/ Coverage", + command: "swift.runTestWithCoverage", + }, + ]; + + return lensConfigs + .filter( + lensConfig => + config === true || (Array.isArray(config) && config.includes(lensConfig.type)) + ) + .map( + lensConfig => + new vscode.CodeLens(item.range!, { + title: lensConfig.title, + command: lensConfig.command, + arguments: [item], + }) + ); + } +} diff --git a/src/TestExplorer/TestExplorer.ts b/src/TestExplorer/TestExplorer.ts index dee218828..0a447ba08 100644 --- a/src/TestExplorer/TestExplorer.ts +++ b/src/TestExplorer/TestExplorer.ts @@ -27,15 +27,16 @@ import { TargetType } from "../SwiftPackage"; import { parseTestsFromSwiftTestListOutput } from "./SPMTestDiscovery"; import { parseTestsFromDocumentSymbols } from "./DocumentSymbolTestDiscovery"; import { flattenTestItemCollection } from "./TestUtils"; +import { TestCodeLensProvider } from "./TestCodeLensProvider"; /** Build test explorer UI */ export class TestExplorer { static errorTestItemId = "#Error#"; public controller: vscode.TestController; public testRunProfiles: vscode.TestRunProfile[]; + private lspTestDiscovery: LSPTestDiscovery; private subscriptions: { dispose(): unknown }[]; - private testFileEdited = true; private tokenSource = new vscode.CancellationTokenSource(); // Emits after the `vscode.TestController` has been updated. @@ -45,9 +46,13 @@ export class TestExplorer { public onDidCreateTestRunEmitter = new vscode.EventEmitter(); public onCreateTestRun: vscode.Event; + private codeLensProvider?: TestCodeLensProvider; + constructor(public folderContext: FolderContext) { this.onTestItemsDidChange = this.onTestItemsDidChangeEmitter.event; this.onCreateTestRun = this.onDidCreateTestRunEmitter.event; + this.lspTestDiscovery = this.configureLSPTestDiscovery(folderContext); + this.codeLensProvider = new TestCodeLensProvider(this); this.controller = vscode.tests.createTestController( folderContext.name, @@ -66,55 +71,109 @@ export class TestExplorer { this.onDidCreateTestRunEmitter ); + this.subscriptions = [ + this.tokenSource, + this.controller, + this.onTestItemsDidChangeEmitter, + this.onDidCreateTestRunEmitter, + ...this.testRunProfiles, + this.onTestItemsDidChange(() => this.updateSwiftTestContext()), + this.discoverUpdatedTestsAfterBuild(folderContext), + ]; + } + + /** + * Query the LSP for tests in the document. If the LSP is not available + * this method will fallback to the legacy method of parsing document symbols, + * but only for XCTests. + * @param folder The folder context. + * @param uri The document URI. If the document is not part of a test target, this method will do nothing. + * @param symbols The document symbols. + * @returns A promise that resolves when the tests have been retrieved. + */ + public async getDocumentTests( + folder: FolderContext, + uri: vscode.Uri, + symbols: vscode.DocumentSymbol[] + ): Promise { + const target = await folder.swiftPackage.getTarget(uri.fsPath); + if (!target || target.type !== "test") { + return; + } + + try { + const tests = await this.lspTestDiscovery.getDocumentTests(folder.swiftPackage, uri); + TestDiscovery.updateTestsForTarget( + this.controller, + { id: target.c99name, label: target.name }, + tests, + uri + ); + this.onTestItemsDidChangeEmitter.fire(this.controller); + } catch { + // Fallback to parsing document symbols for XCTests only + const tests = parseTestsFromDocumentSymbols(target.name, symbols, uri); + this.updateTests(this.controller, tests, uri); + } + } + + /** + * Creates an LSPTestDiscovery client for the given folder context. + */ + private configureLSPTestDiscovery(folderContext: FolderContext): LSPTestDiscovery { const workspaceContext = folderContext.workspaceContext; const languageClientManager = workspaceContext.languageClientManager.get(folderContext); - this.lspTestDiscovery = new LSPTestDiscovery(languageClientManager); + return new LSPTestDiscovery(languageClientManager); + } - // add end of task handler to be called whenever a build task has finished. If - // it is the build task for this folder then update the tests - const onDidEndTask = folderContext.workspaceContext.tasks.onDidEndTaskProcess(event => { - const task = event.execution.task; - const execution = task.execution as vscode.ProcessExecution; - if ( - task.scope === this.folderContext.workspaceFolder && - task.group === vscode.TaskGroup.Build && - execution?.options?.cwd === this.folderContext.folder.fsPath && - event.exitCode === 0 && - task.definition.dontTriggerTestDiscovery !== true && - this.testFileEdited - ) { - this.testFileEdited = false; + /** + * Configure test discovery for updated tests after a build task has completed. + */ + private discoverUpdatedTestsAfterBuild(folderContext: FolderContext): vscode.Disposable { + let testFileEdited = true; + const endProcessDisposable = folderContext.workspaceContext.tasks.onDidEndTaskProcess( + event => { + const task = event.execution.task; + const execution = task.execution as vscode.ProcessExecution; + if ( + task.scope === folderContext.workspaceFolder && + task.group === vscode.TaskGroup.Build && + execution?.options?.cwd === folderContext.folder.fsPath && + event.exitCode === 0 && + task.definition.dontTriggerTestDiscovery !== true && + testFileEdited + ) { + testFileEdited = false; - // only run discover tests if the library has tests - void this.folderContext.swiftPackage.getTargets(TargetType.test).then(targets => { - if (targets.length > 0) { - void this.discoverTestsInWorkspace(this.tokenSource.token); - } - }); + // only run discover tests if the library has tests + void folderContext.swiftPackage.getTargets(TargetType.test).then(targets => { + if (targets.length > 0) { + void this.discoverTestsInWorkspace(this.tokenSource.token); + } + }); + } } - }); + ); // add file watcher to catch changes to swift test files - const fileWatcher = this.folderContext.workspaceContext.onDidChangeSwiftFiles(({ uri }) => { - if (this.testFileEdited === false) { - void this.folderContext.getTestTarget(uri).then(target => { - if (target) { - this.testFileEdited = true; - } - }); + const didChangeSwiftFileDisposable = folderContext.workspaceContext.onDidChangeSwiftFiles( + ({ uri }) => { + if (testFileEdited === false) { + void folderContext.getTestTarget(uri).then(target => { + if (target) { + testFileEdited = true; + } + }); + } } - }); + ); - this.subscriptions = [ - this.tokenSource, - fileWatcher, - onDidEndTask, - this.controller, - this.onTestItemsDidChangeEmitter, - this.onDidCreateTestRunEmitter, - ...this.testRunProfiles, - this.onTestItemsDidChange(() => this.updateSwiftTestContext()), - ]; + return { + dispose: () => { + endProcessDisposable.dispose(); + didChangeSwiftFileDisposable.dispose(); + }, + }; } dispose() { @@ -193,32 +252,6 @@ export class TestExplorer { }); } - async getDocumentTests( - folder: FolderContext, - uri: vscode.Uri, - symbols: vscode.DocumentSymbol[] - ): Promise { - const target = await folder.swiftPackage.getTarget(uri.fsPath); - if (!target || target.type !== "test") { - return; - } - - try { - const tests = await this.lspTestDiscovery.getDocumentTests(folder.swiftPackage, uri); - TestDiscovery.updateTestsForTarget( - this.controller, - { id: target.c99name, label: target.name }, - tests, - uri - ); - this.onTestItemsDidChangeEmitter.fire(this.controller); - } catch { - // Fallback to parsing document symbols for XCTests only - const tests = parseTestsFromDocumentSymbols(target.name, symbols, uri); - this.updateTests(this.controller, tests, uri); - } - } - private updateTests( controller: vscode.TestController, tests: TestDiscovery.TestClass[], @@ -231,7 +264,7 @@ export class TestExplorer { /** * Discover tests */ - async discoverTestsInWorkspace(token: vscode.CancellationToken) { + private async discoverTestsInWorkspace(token: vscode.CancellationToken) { try { // If the LSP cannot produce a list of tests it throws and // we fall back to discovering tests with SPM. @@ -249,7 +282,7 @@ export class TestExplorer { * Discover tests * Uses `swift test --list-tests` to get the list of tests */ - async discoverTestsInWorkspaceSPM(token: vscode.CancellationToken) { + private async discoverTestsInWorkspaceSPM(token: vscode.CancellationToken) { async function runDiscover(explorer: TestExplorer, firstTry: boolean) { try { // we depend on sourcekit-lsp to detect swift-testing tests so let the user know @@ -379,7 +412,7 @@ export class TestExplorer { /** * Discover tests */ - async discoverTestsInWorkspaceLSP(token: vscode.CancellationToken) { + private async discoverTestsInWorkspaceLSP(token: vscode.CancellationToken) { const tests = await this.lspTestDiscovery.getWorkspaceTests( this.folderContext.swiftPackage ); diff --git a/src/commands.ts b/src/commands.ts index 08d58f6e8..e395a5faa 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -48,6 +48,7 @@ import { pickProcess } from "./commands/pickProcess"; import { openDocumentation } from "./commands/openDocumentation"; import restartLSPServer from "./commands/restartLSPServer"; import { generateLaunchConfigurations } from "./commands/generateLaunchConfigurations"; +import { runTest } from "./commands/runTest"; /** * References: @@ -97,6 +98,9 @@ export enum Commands { RUN_ALL_TESTS_PARALLEL = "swift.runAllTestsParallel", DEBUG_ALL_TESTS = "swift.debugAllTests", COVER_ALL_TESTS = "swift.coverAllTests", + RUN_TEST = "swift.runTest", + DEBUG_TEST = "swift.debugTest", + RUN_TEST_WITH_COVERAGE = "swift.runTestWithCoverage", OPEN_MANIFEST = "swift.openManifest", RESTART_LSP = "swift.restartLSPServer", SELECT_TOOLCHAIN = "swift.selectToolchain", @@ -230,6 +234,18 @@ export function register(ctx: WorkspaceContext): vscode.Disposable[] { Commands.COVER_ALL_TESTS, async item => await runAllTests(ctx, TestKind.coverage, ...unwrapTreeItem(item)) ), + vscode.commands.registerCommand( + Commands.RUN_TEST, + async item => await runTest(ctx, TestKind.standard, item) + ), + vscode.commands.registerCommand( + Commands.DEBUG_TEST, + async item => await runTest(ctx, TestKind.debug, item) + ), + vscode.commands.registerCommand( + Commands.RUN_TEST_WITH_COVERAGE, + async item => await runTest(ctx, TestKind.coverage, item) + ), vscode.commands.registerCommand( Commands.PREVIEW_DOCUMENTATION, async () => await ctx.documentation.launchDocumentationPreview() diff --git a/src/commands/runTest.ts b/src/commands/runTest.ts new file mode 100644 index 000000000..9b90cdd06 --- /dev/null +++ b/src/commands/runTest.ts @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the VS Code Swift open source project +// +// Copyright (c) 2021-2024 the VS Code Swift project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of VS Code Swift project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import * as vscode from "vscode"; +import { TestKind } from "../TestExplorer/TestKind"; +import { WorkspaceContext } from "../WorkspaceContext"; + +export async function runTest(ctx: WorkspaceContext, testKind: TestKind, test: vscode.TestItem) { + const testExplorer = ctx.currentFolder?.testExplorer; + if (testExplorer === undefined) { + return; + } + + const profile = testExplorer.testRunProfiles.find(profile => profile.label === testKind); + if (profile === undefined) { + return; + } + + const tokenSource = new vscode.CancellationTokenSource(); + await profile.runHandler( + new vscode.TestRunRequest([test], undefined, profile), + tokenSource.token + ); + + await vscode.commands.executeCommand("testing.showMostRecentOutput"); +} diff --git a/src/configuration.ts b/src/configuration.ts index 7a9739b6e..e6cae9def 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -39,6 +39,7 @@ export type DiagnosticCollectionOptions = | "keepSourceKit" | "keepAll"; export type DiagnosticStyle = "default" | "llvm" | "swift"; +export type ValidCodeLens = "run" | "debug" | "coverage"; /** sourcekit-lsp configuration */ export interface LSPConfiguration { @@ -290,6 +291,12 @@ const configuration = { .get("excludeFromCodeCoverage", []) .map(substituteVariablesInString); }, + /** Whether to show inline code lenses for running and debugging tests. */ + get showTestCodeLenses(): boolean | ValidCodeLens[] { + return vscode.workspace + .getConfiguration("swift") + .get("showTestCodeLenses", true); + }, /** Files and directories to exclude from the Package Dependencies view. */ get excludePathsFromPackageDependencies(): string[] { return vscode.workspace diff --git a/src/sourcekit-lsp/LanguageClientConfiguration.ts b/src/sourcekit-lsp/LanguageClientConfiguration.ts index 6947a4f4c..9af9ecd66 100644 --- a/src/sourcekit-lsp/LanguageClientConfiguration.ts +++ b/src/sourcekit-lsp/LanguageClientConfiguration.ts @@ -179,10 +179,10 @@ export function lspClientOptions( return result?.map(codelens => { switch (codelens.command?.command) { case "swift.run": - codelens.command.title = `$(play) ${codelens.command.title}`; + codelens.command.title = `$(play)\u00A0${codelens.command.title}`; break; case "swift.debug": - codelens.command.title = `$(debug) ${codelens.command.title}`; + codelens.command.title = `$(debug)\u00A0${codelens.command.title}`; break; } return codelens; diff --git a/test/unit-tests/sourcekit-lsp/LanguageClientManager.test.ts b/test/unit-tests/sourcekit-lsp/LanguageClientManager.test.ts index 0e531ca56..d434a8074 100644 --- a/test/unit-tests/sourcekit-lsp/LanguageClientManager.test.ts +++ b/test/unit-tests/sourcekit-lsp/LanguageClientManager.test.ts @@ -560,7 +560,7 @@ suite("LanguageClientManager Suite", () => { { range: new vscode.Range(0, 0, 0, 0), command: { - title: "$(play) Run", + title: "$(play)\u00A0Run", command: "swift.run", }, isResolved: true, @@ -568,7 +568,7 @@ suite("LanguageClientManager Suite", () => { { range: new vscode.Range(0, 0, 0, 0), command: { - title: "$(debug) Debug", + title: "$(debug)\u00A0Debug", command: "swift.debug", }, isResolved: true,