1515import * as vscode from "vscode" ;
1616import * as path from "path" ;
1717import * as fs from "fs/promises" ;
18- import { createSwiftTask } from "../tasks/SwiftTaskProvider" ;
19- import { WorkspaceContext } from "../WorkspaceContext" ;
20- import { Version } from "../utilities/version" ;
2118import configuration from "../configuration" ;
19+ import { createSwiftTask } from "../tasks/SwiftTaskProvider" ;
20+ import { TemporaryFolder } from "../utilities/tempFolder" ;
21+ import { TaskManager } from "../tasks/TaskManager" ;
22+ import { SwiftToolchain } from "../toolchain/toolchain" ;
2223
2324/**
24- * Run the active document through the Swift REPL
25+ * Runs the Swift code in the supplied document.
26+ *
27+ * This function checks for a valid document and Swift version, then creates and executes
28+ * a Swift task to run the script file. The task is configured to always reveal its output
29+ * and clear previous output. The working directory is set to the script's location.
30+ *
31+ * @param document - The text document containing the Swift script to run. If undefined, the function returns early.
32+ * @param tasks - The TaskManager instance used to execute and manage the Swift task.
33+ * @param toolchain - The SwiftToolchain to use for running the script.
34+ * @returns A promise that resolves when the script has finished running, or returns early if the user is prompted
35+ * for which swift version to use and they exit the dialog without choosing one.
2536 */
26- export async function runSwiftScript ( ctx : WorkspaceContext ) {
27- const document = vscode . window . activeTextEditor ?. document ;
28- if ( ! document ) {
29- return ;
30- }
31-
32- if ( ! ctx . currentFolder ) {
37+ export async function runSwiftScript (
38+ document : vscode . TextDocument ,
39+ tasks : TaskManager ,
40+ toolchain : SwiftToolchain
41+ ) {
42+ const targetVersion = await targetSwiftVersion ( ) ;
43+ if ( ! targetVersion ) {
3344 return ;
3445 }
3546
36- // Swift scripts require new swift driver to work on Windows. Swift driver is available
37- // from v5.7 of Windows Swift
38- if (
39- process . platform === "win32" &&
40- ctx . currentFolder . swiftVersion . isLessThan ( new Version ( 5 , 7 , 0 ) )
41- ) {
42- void vscode . window . showErrorMessage (
43- "Run Swift Script is unavailable with the legacy driver on Windows."
47+ await withDocumentFile ( document , async filename => {
48+ const runTask = createSwiftTask (
49+ [ "-swift-version" , targetVersion , filename ] ,
50+ `Run ${ filename } ` ,
51+ {
52+ scope : vscode . TaskScope . Global ,
53+ cwd : vscode . Uri . file ( path . dirname ( filename ) ) ,
54+ presentationOptions : { reveal : vscode . TaskRevealKind . Always , clear : true } ,
55+ } ,
56+ toolchain
4457 ) ;
45- return ;
46- }
47-
48- let target : string ;
58+ await tasks . executeTaskAndWait ( runTask ) ;
59+ } ) ;
60+ }
4961
62+ /**
63+ * Determines the target Swift language version to use for script execution.
64+ * If the configuration is set to "Ask Every Run", prompts the user to select a version.
65+ * Otherwise, returns the default version from the user's settings.
66+ *
67+ * @returns {Promise<string | undefined> } The selected Swift version, or undefined if no selection was made.
68+ */
69+ async function targetSwiftVersion ( ) {
5070 const defaultVersion = configuration . scriptSwiftLanguageVersion ;
5171 if ( defaultVersion === "Ask Every Run" ) {
5272 const picked = await vscode . window . showQuickPick (
@@ -59,41 +79,36 @@ export async function runSwiftScript(ctx: WorkspaceContext) {
5979 placeHolder : "Select a target Swift version" ,
6080 }
6181 ) ;
62-
63- if ( ! picked ) {
64- return ;
65- }
66- target = picked . value ;
82+ return picked ?. value ;
6783 } else {
68- target = defaultVersion ;
84+ return defaultVersion ;
6985 }
86+ }
7087
71- let filename = document . fileName ;
72- let isTempFile = false ;
88+ /**
89+ * Executes a callback with the filename of the given `vscode.TextDocument`.
90+ * If the document is untitled (not yet saved to disk), it creates a temporary file,
91+ * writes the document's content to it, and passes its filename to the callback.
92+ * Otherwise, it ensures the document is saved and passes its actual filename.
93+ *
94+ * The temporary file is automatically deleted when the callback completes.
95+ *
96+ * @param document - The VSCode text document to operate on.
97+ * @param callback - An async function that receives the filename of the document or temporary file.
98+ * @returns A promise that resolves when the callback has completed.
99+ */
100+ async function withDocumentFile (
101+ document : vscode . TextDocument ,
102+ callback : ( filename : string ) => Promise < void >
103+ ) {
73104 if ( document . isUntitled ) {
74- // if document hasn't been saved, save it to a temporary file
75- isTempFile = true ;
76- filename = ctx . tempFolder . filename ( document . fileName , "swift" ) ;
77- const text = document . getText ( ) ;
78- await fs . writeFile ( filename , text ) ;
105+ const tmpFolder = await TemporaryFolder . create ( ) ;
106+ await tmpFolder . withTemporaryFile ( "swift" , async filename => {
107+ await fs . writeFile ( filename , document . getText ( ) ) ;
108+ await callback ( filename ) ;
109+ } ) ;
79110 } else {
80- // otherwise save document
81111 await document . save ( ) ;
82- }
83- const runTask = createSwiftTask (
84- [ "-swift-version" , target , filename ] ,
85- `Run ${ filename } ` ,
86- {
87- scope : vscode . TaskScope . Global ,
88- cwd : vscode . Uri . file ( path . dirname ( filename ) ) ,
89- presentationOptions : { reveal : vscode . TaskRevealKind . Always , clear : true } ,
90- } ,
91- ctx . currentFolder . toolchain
92- ) ;
93- await ctx . tasks . executeTaskAndWait ( runTask ) ;
94-
95- // delete file after running swift
96- if ( isTempFile ) {
97- await fs . rm ( filename ) ;
112+ await callback ( document . fileName ) ;
98113 }
99114}
0 commit comments