Skip to content

Commit

Permalink
fix: guess_scheme feature + optimization + refactoring (#288)
Browse files Browse the repository at this point in the history
See: `xcodebuild.guess-scheme`

Connected with: #286
  • Loading branch information
wojciech-kulik authored Feb 3, 2025
1 parent 0f80894 commit 08f1067
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 165 deletions.
75 changes: 55 additions & 20 deletions doc/xcodebuild.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ M.setup({options}) *xcodebuild.setup*
prepare_snapshot_test_previews = true, -- prepares a list with failing snapshot tests
test_search = {
file_matching = "filename_lsp", -- one of: filename, lsp, lsp_filename, filename_lsp. Check out README for details
target_matching = true, -- automatically select scheme based on the current file
target_matching = true, -- checks if the test file target matches the one from logs. Try disabling it in case of not showing test results
lsp_client = "sourcekit", -- name of your LSP for Swift files
lsp_timeout = 200, -- LSP timeout in milliseconds
},
Expand Down Expand Up @@ -198,6 +198,7 @@ M.setup({options}) *xcodebuild.setup*
},
xcode_build_server = {
enabled = true, -- enable calling "xcode-build-server config" when project config changes
guess_scheme = false, -- run "xcode-build-server config" with the scheme matching the current file's target
},
nvim_tree = {
enabled = true, -- enable updating Xcode project files when using nvim-tree
Expand Down Expand Up @@ -1828,7 +1829,7 @@ M.is_configured() *xcodebuild.project.config.is_configured*

*xcodebuild.project.config.update_settings*
M.update_settings({opts}, {callback})
Updates the settings (`appPath`, `productName`, and `bundleId`) based on
Updates the settings (`appPath`, `productName`, `buildDir`, and `bundleId`) based on
the current project.
Calls `xcodebuild` commands to get the build settings.

Expand Down Expand Up @@ -2224,19 +2225,6 @@ M.update_current_file_targets()
Asks the user to select the targets.


*xcodebuild.project.manager.clear_cached_schemes*
M.clear_cached_schemes()
Clears cached schemes table


*xcodebuild.project.manager.update_current_file_scheme*
M.update_current_file_scheme()
Updates the project scheme based on current file targets.

Retrieves the current file's targets and, if valid targets are found,
selects an appropriate scheme for those targets if exists.


M.guess_target({groupPath}) *xcodebuild.project.manager.guess_target*
Finds the first existing group in the project for the provided {groupPath}
and tries to list targets for the first Swift file it encounters there or in subgroups.
Expand All @@ -2253,6 +2241,14 @@ M.guess_target({groupPath}) *xcodebuild.project.manager.guess_target*
(string[]|nil)


*xcodebuild.project.manager.get_current_file_targets*
M.get_current_file_targets()
Finds the targets for the current file.

Returns: ~
(string[])


*xcodebuild.project.manager.show_current_file_targets*
M.show_current_file_targets()
Shows the targets for the current file.
Expand Down Expand Up @@ -4062,6 +4058,50 @@ M.run_config({projectFile}, {scheme})
(number) job id


*xcodebuild.integrations.xcode-build-server.run_config_if_enabled*
M.run_config_if_enabled({scheme})
Calls "config" command of xcode-build-server in order to update buildServer.json file.
This function is called only if the integration is enabled and the
xcode-build-server is installed.

Parameters: ~
{scheme} (string|nil)


*xcodebuild.integrations.xcode-build-server.update_cached_schemes*
M.update_cached_schemes({schemes})
Updates cached schemes.

Parameters: ~
{schemes} (string[])


*xcodebuild.integrations.xcode-build-server.clear_cached_schemes*
M.clear_cached_schemes()
Clears cached schemes.


*xcodebuild.guess-scheme*
*xcodebuild.integrations.xcode-build-server.guess_scheme*
M.guess_scheme()
This function is responsible for running the `xcode-build-server config` command
with the scheme matching the current file's target. It's not perfect, because
schemes can have different names than targets, but it might be helpful in some
cases.

You can enable it by setting `guess_scheme` to `true` in the configuration.
It will be triggered every time you enter a `Swift` buffer, so the performance
might be affected.

Schemes are cached to avoid unnecessary calls to `xcodebuild` when switching
between files. To clear the cache, you can use the `clear_cached_schemes`
function, restart Neovim, or show schemes picker.


*xcodebuild.integrations.xcode-build-server.setup*
M.setup()


==============================================================================
Xcodebuild Workaround *xcodebuild.integrations.xcodebuild-offline*

Expand Down Expand Up @@ -4381,11 +4421,6 @@ M.update_readonly_buffer({bufnr}, {updateFoo})
{updateFoo} (function)


*xcodebuild.helpers.update_xcode_build_server_config*
M.update_xcode_build_server_config()
Updates xcode-build-server config if needed.


==============================================================================
Lua Utils *xcodebuild.util*

Expand Down
11 changes: 0 additions & 11 deletions lua/xcodebuild/core/autocmd.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ function M.setup()
local appdata = require("xcodebuild.project.appdata")
local config = require("xcodebuild.core.config").options
local projectConfig = require("xcodebuild.project.config")
local projectManager = require("xcodebuild.project.manager")
local diagnostics = require("xcodebuild.tests.diagnostics")
local logsPanel = require("xcodebuild.xcode_logs.panel")
local coverage = require("xcodebuild.code_coverage.coverage")
Expand Down Expand Up @@ -45,16 +44,6 @@ function M.setup()
command = "set filetype=swift",
})

if config.guess_scheme then
vim.api.nvim_create_autocmd({ "BufEnter" }, {
group = autogroup,
pattern = "*.swift",
callback = function()
projectManager.update_current_file_scheme()
end,
})
end

if config.marks.show_diagnostics or config.marks.show_signs then
vim.api.nvim_create_autocmd({ "BufReadPost" }, {
group = autogroup,
Expand Down
2 changes: 1 addition & 1 deletion lua/xcodebuild/core/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ local defaults = {
auto_save = true, -- save all buffers before running build or tests (command: silent wa!)
show_build_progress_bar = true, -- shows [ ... ] progress bar during build, based on the last duration
prepare_snapshot_test_previews = true, -- prepares a list with failing snapshot tests
guess_scheme = false, -- automatically select scheme based on the current file
test_search = {
file_matching = "filename_lsp", -- one of: filename, lsp, lsp_filename, filename_lsp. Check out README for details
target_matching = true, -- checks if the test file target matches the one from logs. Try disabling it in case of not showing test results
Expand Down Expand Up @@ -136,6 +135,7 @@ local defaults = {
},
xcode_build_server = {
enabled = true, -- enable calling "xcode-build-server config" when project config changes
guess_scheme = false, -- run "xcode-build-server config" with the scheme matching the current file's target
},
nvim_tree = {
enabled = true, -- enable updating Xcode project files when using nvim-tree
Expand Down
17 changes: 0 additions & 17 deletions lua/xcodebuild/helpers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -198,21 +198,4 @@ function M.update_readonly_buffer(bufnr, updateFoo)
M.buf_set_option(bufnr, "readonly", true)
end

---Updates xcode-build-server config if needed.
function M.update_xcode_build_server_config()
local projectConfig = require("xcodebuild.project.config")
local xcodeBuildServer = require("xcodebuild.integrations.xcode-build-server")

if not xcodeBuildServer.is_enabled() or not xcodeBuildServer.is_installed() then
return
end

local projectFile = projectConfig.settings.projectFile
local scheme = projectConfig.settings.scheme

if projectFile and scheme then
xcodeBuildServer.run_config(projectFile, scheme)
end
end

return M
5 changes: 4 additions & 1 deletion lua/xcodebuild/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ end
--- prepare_snapshot_test_previews = true, -- prepares a list with failing snapshot tests
--- test_search = {
--- file_matching = "filename_lsp", -- one of: filename, lsp, lsp_filename, filename_lsp. Check out README for details
--- target_matching = true, -- automatically select scheme based on the current file
--- target_matching = true, -- checks if the test file target matches the one from logs. Try disabling it in case of not showing test results
--- lsp_client = "sourcekit", -- name of your LSP for Swift files
--- lsp_timeout = 200, -- LSP timeout in milliseconds
--- },
Expand Down Expand Up @@ -219,6 +219,7 @@ end
--- },
--- xcode_build_server = {
--- enabled = true, -- enable calling "xcode-build-server config" when project config changes
--- guess_scheme = false, -- run "xcode-build-server config" with the scheme matching the current file's target
--- },
--- nvim_tree = {
--- enabled = true, -- enable updating Xcode project files when using nvim-tree
Expand Down Expand Up @@ -256,6 +257,7 @@ function M.setup(options)
local nvimTree = require("xcodebuild.integrations.nvim-tree")
local oilNvim = require("xcodebuild.integrations.oil-nvim")
local neoTree = require("xcodebuild.integrations.neo-tree")
local xcodeBuildServer = require("xcodebuild.integrations.xcode-build-server")

autocmd.setup()
projectConfig.load_settings()
Expand All @@ -267,6 +269,7 @@ function M.setup(options)
nvimTree.setup()
oilNvim.setup()
neoTree.setup()
xcodeBuildServer.setup()
setupHighlights()
warnAboutOldConfig()

Expand Down
131 changes: 130 additions & 1 deletion lua/xcodebuild/integrations/xcode-build-server.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,47 @@

local config = require("xcodebuild.core.config").options.integrations.xcode_build_server
local util = require("xcodebuild.util")
local xcode = require("xcodebuild.core.xcode")
local projectConfig = require("xcodebuild.project.config")
local projectManager = require("xcodebuild.project.manager")
local notifications = require("xcodebuild.broadcasting.notifications")

local M = {}

---Current xcode-build-server job id
---@type number|nil
local configJobId

---Current find schemes job id
---@type number|nil
local findSchemesJobId

---Cached schemes
---@type string[]
local cachedSchemes = {}

---Last selected scheme
---@type string|nil
local lastSelectedScheme

---Finds the first scheme in the project whose name matches one of the provided target names.
---@param targets string[]
---@param schemes string[]
local function select_matching_scheme(targets, schemes)
for _, target in ipairs(targets) do
for _, scheme in ipairs(schemes) do
if target == scheme then
if lastSelectedScheme ~= scheme then
notifications.send("Scheme changed to: " .. scheme)
M.run_config_if_enabled(scheme)
end

return
end
end
end
end

---Returns whether the xcode-build-server is installed.
---@return boolean
function M.is_installed()
Expand All @@ -28,6 +66,8 @@ end
---@param scheme string
---@return number # job id
function M.run_config(projectFile, scheme)
lastSelectedScheme = scheme

local command = {
"xcode-build-server",
"config",
Expand All @@ -37,11 +77,100 @@ function M.run_config(projectFile, scheme)
scheme,
}

return vim.fn.jobstart(command, {
if configJobId then
vim.fn.jobstop(configJobId)
end

configJobId = vim.fn.jobstart(command, {
on_exit = function()
require("xcodebuild.integrations.lsp").restart_sourcekit_lsp()
end,
})

return configJobId
end

---Calls "config" command of xcode-build-server in order to update buildServer.json file.
---This function is called only if the integration is enabled and the
---xcode-build-server is installed.
---@param scheme string|nil
function M.run_config_if_enabled(scheme)
if not M.is_enabled() or not M.is_installed() then
return
end

local projectFile = projectConfig.settings.projectFile
local projectScheme = scheme or projectConfig.settings.scheme

if projectFile and projectScheme then
M.run_config(projectFile, projectScheme)
end
end

---Updates cached schemes.
---@param schemes string[]
function M.update_cached_schemes(schemes)
cachedSchemes = schemes
end

---Clears cached schemes.
function M.clear_cached_schemes()
cachedSchemes = {}
end

---@tag xcodebuild.guess-scheme
---This function is responsible for running the `xcode-build-server config` command
---with the scheme matching the current file's target. It's not perfect, because
---schemes can have different names than targets, but it might be helpful in some
---cases.
---
---You can enable it by setting `guess_scheme` to `true` in the configuration.
---It will be triggered every time you enter a `Swift` buffer, so the performance
---might be affected.
---
---Schemes are cached to avoid unnecessary calls to `xcodebuild` when switching
---between files. To clear the cache, you can use the `clear_cached_schemes`
---function, restart Neovim, or show schemes picker.
function M.guess_scheme()
local xcodeproj = projectConfig.settings.xcodeproj
local workingDirectory = projectConfig.settings.workingDirectory

if not xcodeproj and not workingDirectory then
return
end

local fileTargets = projectManager.get_current_file_targets()
if #fileTargets == 0 then
notifications.send("No targets found for the current file")
return
end

if #cachedSchemes > 0 then
select_matching_scheme(fileTargets, cachedSchemes)
return
end

if findSchemesJobId then
vim.fn.jobstop(findSchemesJobId)
end

findSchemesJobId = xcode.find_schemes(xcodeproj, workingDirectory, function(schemes)
cachedSchemes = vim.tbl_map(function(scheme)
return scheme.name
end, schemes)

select_matching_scheme(fileTargets, cachedSchemes)
end)
end

function M.setup()
if config.guess_scheme then
vim.api.nvim_create_autocmd({ "BufEnter" }, {
group = vim.api.nvim_create_augroup("xcodebuild-integrations-xcode-build-server", { clear = true }),
pattern = "*.swift",
callback = M.guess_scheme,
})
end
end

return M
2 changes: 1 addition & 1 deletion lua/xcodebuild/project/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ function M.is_configured()
return M.is_app_configured() or M.is_spm_configured() or M.is_library_configured()
end

---Updates the settings (`appPath`, `productName`, and `bundleId`) based on
---Updates the settings (`appPath`, `productName`, `buildDir`, and `bundleId`) based on
---the current project.
---Calls `xcodebuild` commands to get the build settings.
---@param opts {skipIfSamePlatform:boolean} the options table
Expand Down
Loading

0 comments on commit 08f1067

Please sign in to comment.