From f5a4755bf4e0b0ada05f41b8fe6bf0366e6f9a11 Mon Sep 17 00:00:00 2001 From: Asiel Cabrera Date: Sun, 3 May 2026 15:58:08 -0400 Subject: [PATCH] refactor: migrate vim.fn.system to vim.system for safe async execution This prevents Neovim UI from hanging when running external shell commands like xcodebuild, swift-format, and swiftlint. --- lua/swift/features/formatter.lua | 24 +++++++++++++++--------- lua/swift/features/linter.lua | 9 +++++---- lua/swift/features/lsp.lua | 3 ++- lua/swift/features/target_manager.lua | 14 ++++++++------ lua/swift/features/xcode.lua | 18 +++++++++++------- lua/swift/health.lua | 2 +- lua/swift/version_validator.lua | 6 +++--- 7 files changed, 45 insertions(+), 31 deletions(-) diff --git a/lua/swift/features/formatter.lua b/lua/swift/features/formatter.lua index bb30763..ce2c5e6 100644 --- a/lua/swift/features/formatter.lua +++ b/lua/swift/features/formatter.lua @@ -132,11 +132,13 @@ function M.format_with_swift_format(bufnr) table.insert(cmd, "-") - local result = vim.fn.system(cmd, content) - local exit_code = vim.v.shell_error + local obj = vim.system(cmd, { text = true, stdin = content, timeout = 5000 }):wait() + local result = obj.stdout or "" + local exit_code = obj.code if exit_code ~= 0 then - vim.notify("swift-format failed: " .. result, vim.log.levels.ERROR, { title = "swift.nvim" }) + local err_msg = obj.stderr or result + vim.notify("swift-format failed: " .. err_msg, vim.log.levels.ERROR, { title = "swift.nvim" }) return false end @@ -179,11 +181,13 @@ function M.format_with_swiftformat(bufnr) table.insert(cmd, config_file) end - local result = vim.fn.system(cmd) - local exit_code = vim.v.shell_error + local obj = vim.system(cmd, { text = true, timeout = 5000 }):wait() + local result = obj.stdout or "" + local exit_code = obj.code if exit_code ~= 0 then - vim.notify("swiftformat failed: " .. result, vim.log.levels.ERROR, { title = "swift.nvim" }) + local err_msg = obj.stderr or result + vim.notify("swiftformat failed: " .. err_msg, vim.log.levels.ERROR, { title = "swift.nvim" }) return false end @@ -274,11 +278,13 @@ function M.format_selection() table.insert(cmd, start_offset .. ":" .. end_offset) table.insert(cmd, "-") - local result = vim.fn.system(cmd, content) - local exit_code = vim.v.shell_error + local obj = vim.system(cmd, { text = true, stdin = content, timeout = 5000 }):wait() + local result = obj.stdout or "" + local exit_code = obj.code if exit_code ~= 0 then - vim.notify("swift-format failed: " .. result, vim.log.levels.ERROR, { title = "swift.nvim" }) + local err_msg = obj.stderr or result + vim.notify("swift-format failed: " .. err_msg, vim.log.levels.ERROR, { title = "swift.nvim" }) return end diff --git a/lua/swift/features/linter.lua b/lua/swift/features/linter.lua index 06e8b2f..e4735d0 100644 --- a/lua/swift/features/linter.lua +++ b/lua/swift/features/linter.lua @@ -116,8 +116,8 @@ function M.lint_file(filename) table.insert(cmd, config_file) end - local result = vim.fn.system(cmd) - return M.parse_swiftlint_output(result) + local obj = vim.system(cmd, { text = true, timeout = 5000 }):wait() + return M.parse_swiftlint_output(obj.stdout or "") end -- Lint current buffer @@ -201,8 +201,9 @@ function M.fix(filename) table.insert(cmd, config_file) end - local result = vim.fn.system(cmd) - local exit_code = vim.v.shell_error + local obj = vim.system(cmd, { text = true, timeout = 5000 }):wait() + local result = obj.stdout or "" + local exit_code = obj.code -- Reload buffer to see changes vim.cmd("silent! edit!") diff --git a/lua/swift/features/lsp.lua b/lua/swift/features/lsp.lua index f7d38a3..aa6a2ea 100644 --- a/lua/swift/features/lsp.lua +++ b/lua/swift/features/lsp.lua @@ -38,7 +38,8 @@ function M.find_sourcekit_lsp() -- Try Xcode toolchain if vim.fn.has("mac") == 1 then - local xcrun_path = vim.fn.system("xcrun --find sourcekit-lsp 2>/dev/null"):gsub("\n", "") + local xcrun_path = vim.system({ "xcrun", "--find", "sourcekit-lsp" }, { text = true }):wait().stdout or "" + xcrun_path = xcrun_path:gsub("\n", "") if xcrun_path ~= "" and vim.fn.executable(xcrun_path) == 1 then table.insert(possible_paths, 1, xcrun_path) end diff --git a/lua/swift/features/target_manager.lua b/lua/swift/features/target_manager.lua index ac181e2..da26f71 100644 --- a/lua/swift/features/target_manager.lua +++ b/lua/swift/features/target_manager.lua @@ -20,10 +20,11 @@ function M.parse_spm_targets() end -- First, try using swift package dump-package (most reliable) - local cmd = string.format('cd "%s" && swift package dump-package 2>/dev/null', project_info.root) - local output = vim.fn.system(cmd) + local sh_cmd = { "sh", "-c", string.format('cd "%s" && swift package dump-package 2>/dev/null', project_info.root) } + local obj = vim.system(sh_cmd, { text = true, timeout = 5000 }):wait() + local output = obj.stdout or "" - if vim.v.shell_error == 0 and output ~= "" then + if obj.code == 0 and output ~= "" then -- Parse JSON output local ok, package_data = pcall(vim.json.decode, output) if ok and package_data and package_data.targets then @@ -139,10 +140,11 @@ function M.parse_xcode_targets() end -- Use xcodebuild to list schemes (which correspond to targets) - local cmd = string.format('cd "%s" && xcodebuild -list -json 2>/dev/null', vim.fn.fnamemodify(project_file, ":h")) - local output = vim.fn.system(cmd) + local sh_cmd = { "sh", "-c", string.format('cd "%s" && xcodebuild -list -json 2>/dev/null', vim.fn.fnamemodify(project_file, ":h")) } + local obj = vim.system(sh_cmd, { text = true, timeout = 5000 }):wait() + local output = obj.stdout or "" - if vim.v.shell_error ~= 0 then + if obj.code ~= 0 then return nil end diff --git a/lua/swift/features/xcode.lua b/lua/swift/features/xcode.lua index 665b5a4..ec9d2c1 100644 --- a/lua/swift/features/xcode.lua +++ b/lua/swift/features/xcode.lua @@ -148,13 +148,17 @@ function M.list_schemes() project_file = info.project end - local cmd = string.format( - "xcodebuild -list -workspace %s 2>/dev/null || xcodebuild -list -project %s 2>/dev/null", - vim.fn.shellescape(project_file), - vim.fn.shellescape(project_file) - ) + local sh_cmd = { + "sh", "-c", + string.format( + "xcodebuild -list -workspace %s 2>/dev/null || xcodebuild -list -project %s 2>/dev/null", + vim.fn.shellescape(project_file), + vim.fn.shellescape(project_file) + ) + } - local output = vim.fn.system(cmd) + local obj = vim.system(sh_cmd, { text = true, timeout = 5000 }):wait() + local output = obj.stdout or "" local schemes = {} local in_schemes = false @@ -272,7 +276,7 @@ function M.open_in_xcode() file_to_open = info.project end - vim.fn.system("open " .. vim.fn.shellescape(file_to_open)) + vim.system({ "open", file_to_open }) vim.notify("Opening in Xcode.app", vim.log.levels.INFO, { title = "swift.nvim" }) end diff --git a/lua/swift/health.lua b/lua/swift/health.lua index 0c7e268..8087580 100644 --- a/lua/swift/health.lua +++ b/lua/swift/health.lua @@ -82,7 +82,7 @@ function M.check() end end else - local version = vim.fn.system("swift --version 2>&1 | head -n 1") + local version = vim.system({ "sh", "-c", "swift --version 2>&1 | head -n 1" }, { text = true, timeout = 5000 }):wait().stdout or "" health.info("Version: " .. vim.trim(version)) end else diff --git a/lua/swift/version_validator.lua b/lua/swift/version_validator.lua index f3a043e..639b02c 100644 --- a/lua/swift/version_validator.lua +++ b/lua/swift/version_validator.lua @@ -94,7 +94,7 @@ end -- Get installed Swift version function M.get_installed_swift_version() - local output = vim.fn.system("swift --version 2>&1") + local output = vim.system({ "sh", "-c", "swift --version 2>&1" }, { text = true, timeout = 5000 }):wait().stdout or "" if vim.v.shell_error ~= 0 then return nil @@ -142,7 +142,7 @@ function M.list_swiftly_versions() return nil end - local output = vim.fn.system("swiftly list 2>/dev/null") + local output = vim.system({ "sh", "-c", "swiftly list 2>/dev/null" }, { text = true, timeout = 5000 }):wait().stdout or "" if vim.v.shell_error ~= 0 then return nil @@ -198,7 +198,7 @@ function M.get_swift_format_version() return nil end - local output = vim.fn.system(swift_format .. " --version 2>&1") + local output = vim.system({ "sh", "-c", swift_format .. " --version 2>&1" }, { text = true, timeout = 5000 }):wait().stdout or "" if vim.v.shell_error ~= 0 then return nil