-
-
Notifications
You must be signed in to change notification settings - Fork 19
Neovim Configuration
Below you will find basic information on how to prepare your Neovim for iOS development.
If you'd like to get more details, you can read my article: The complete guide to iOS & macOS development in Neovim.
Here you can find my sample config for iOS development. You can try it out without touching your own config.
Apple provides together with Xcode an LSP server called sourcekit-lsp
. You can integrate it by using nvim-lspconfig plugin. To properly display code completion you will also need nvim-cmp. On top of that, you also need Build Server Protocol (BSP), which will let the LSP understand the project structure (xcodeproj
/ xcworkspace
). For that purpose, you need to install xcode-build-server.
👉 nvim-lspconfig configuration
return {
"neovim/nvim-lspconfig",
event = { "BufReadPre", "BufNewFile" },
dependencies = {
"hrsh7th/cmp-nvim-lsp",
{ "antosha417/nvim-lsp-file-operations", config = true },
},
config = function()
local lspconfig = require("lspconfig")
local cmp_nvim_lsp = require("cmp_nvim_lsp")
local keymap = vim.keymap -- for conciseness
local opts = { noremap = true, silent = true }
local on_attach = function(_, bufnr)
opts.buffer = bufnr
-- set keybindings
opts.desc = "Show LSP definitions"
keymap.set("n", "gd", "<cmd>Telescope lsp_definitions trim_text=true<cr>", opts)
opts.desc = "Show LSP definitions in split"
keymap.set("n", "gD", "<cmd>vsplit | Telescope lsp_definitions trim_text=true<cr>", opts)
opts.desc = "Show LSP references"
vim.keymap.set(
"n",
"gr",
"<cmd>Telescope lsp_references trim_text=true include_declaration=false<cr>",
opts
)
opts.desc = "Show LSP implementation"
vim.keymap.set("n", "gi", "<cmd>Telescope lsp_implementations<cr>", opts)
opts.desc = "Show LSP code actions"
keymap.set({ "n", "v" }, "<leader>ca", vim.lsp.buf.code_action, opts)
opts.desc = "Smart rename"
keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts)
opts.desc = "Show buffer diagnostics"
keymap.set("n", "<leader><leader>d", "<cmd>Telescope diagnostics bufnr=0<CR>", opts)
opts.desc = "Go to previous diagnostic"
keymap.set("n", "[d", function()
vim.diagnostic.jump({ count = -1 })
vim.cmd("normal! zz")
end, opts)
opts.desc = "Go to next diagnostic"
keymap.set("n", "]d", function()
vim.diagnostic.jump({ count = 1 })
vim.cmd("normal! zz")
end, opts)
opts.desc = "Show documentation for what is under cursor"
keymap.set("n", "K", vim.lsp.buf.hover, opts)
opts.desc = "Restart LSP"
keymap.set("n", "<leader>rl", ":LspRestart | LspStart<CR>", opts)
end
local capabilities = cmp_nvim_lsp.default_capabilities()
local defaultLSPs = {
"sourcekit",
}
for _, lsp in ipairs(defaultLSPs) do
lspconfig[lsp].setup({
capabilities = capabilities,
on_attach = on_attach,
cmd = lsp == "sourcekit" and { vim.trim(vim.fn.system("xcrun -f sourcekit-lsp")) } or nil,
})
end
opts.desc = "Show line diagnostics"
keymap.set("n", "<leader>d", vim.diagnostic.open_float, opts)
end,
}
👉 nvim-cmp configuration
return {
"hrsh7th/nvim-cmp",
event = "InsertEnter",
dependencies = {
"hrsh7th/cmp-buffer", -- source for text in buffer
"hrsh7th/cmp-path", -- source for file system paths
"L3MON4D3/LuaSnip", -- snippet engine
"saadparwaiz1/cmp_luasnip", -- for autocompletion
"rafamadriz/friendly-snippets", -- useful snippets
"onsails/lspkind.nvim", -- vs-code like pictograms
},
config = function()
local cmp = require("cmp")
local luasnip = require("luasnip")
local lspkind = require("lspkind")
-- loads vscode style snippets from installed plugins (e.g. friendly-snippets)
require("luasnip.loaders.from_vscode").lazy_load()
cmp.setup({
completion = {
completeopt = "menu,menuone,preview",
},
snippet = { -- configure how nvim-cmp interacts with snippet engine
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
mapping = cmp.mapping.preset.insert({
["<C-k>"] = cmp.mapping.select_prev_item(), -- previous suggestion
["<C-j>"] = cmp.mapping.select_next_item(), -- next suggestion
["<C-Space>"] = cmp.mapping.complete(), -- show completion suggestions
["<C-e>"] = cmp.mapping.abort(), -- close completion window
["<CR>"] = cmp.mapping.confirm({ select = false, behavior = cmp.ConfirmBehavior.Replace }),
["<C-b>"] = cmp.mapping(function(fallback)
if luasnip.jumpable(-1) then
luasnip.jump(-1)
else
fallback()
end
end, { "i", "s" }),
["<C-f>"] = cmp.mapping(function(fallback)
if luasnip.jumpable(1) then
luasnip.jump(1)
else
fallback()
end
end, { "i", "s" }),
}),
-- sources for autocompletion
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "luasnip" }, -- snippets
{ name = "buffer" }, -- text within current buffer
{ name = "path" }, -- file system paths
}),
-- configure lspkind for vs-code like pictograms in completion menu
formatting = {
format = lspkind.cmp_format({
maxwidth = 50,
ellipsis_char = "...",
}),
},
})
end,
}
👉 xcode-build-server configuration
First, you need to install it:
brew install xcode-build-server
Then, you can configure it for your project:
xcode-build-server config -workspace <xcworkspace> -scheme <scheme>
# or
xcode-build-server config -project <xcodeproj> -scheme <scheme>
Make sure to call it from your project root directory. It should create buildServer.json
. Open it and make sure that all the information there is correct.
Once all steps are finished, you should be able to open your project in Neovim and see working code completion. If something doesn't work, make sure to run a clean build from Xcode first. Also, you can run :LspInfo
to see if the LSP is properly attached and the root directory is detected.
SwiftFormat is a very popular tool to keep formatting consistent across the project. You can easily integrate it with Neovim by using conform.nvim plugin.
Here is a sample config:
return {
"stevearc/conform.nvim",
event = { "BufReadPre", "BufNewFile" },
config = function()
local conform = require("conform")
conform.setup({
formatters_by_ft = {
swift = { "swiftformat" },
},
format_on_save = function(bufnr)
local ignore_filetypes = { "oil" }
if vim.tbl_contains(ignore_filetypes, vim.bo[bufnr].filetype) then
return
end
return { timeout_ms = 500, lsp_fallback = true }
end,
log_level = vim.log.levels.ERROR,
})
end,
}
The code will be automatically formatted on save event 🔥.
Usually, you also need some linter to detect common issues. You can easily integrate SwiftLint using nvim-lint plugin.
Here is a sample config:
return {
"mfussenegger/nvim-lint",
event = { "BufReadPre", "BufNewFile" },
config = function()
local lint = require("lint")
lint.linters_by_ft = {
swift = { "swiftlint" },
}
local lint_augroup = vim.api.nvim_create_augroup("lint", { clear = true })
vim.api.nvim_create_autocmd({ "BufWritePost", "BufReadPost", "InsertLeave", "TextChanged" }, {
group = lint_augroup,
callback = function()
if not vim.endswith(vim.fn.bufname(), "swiftinterface") then
require("lint").try_lint()
end
end,
})
vim.keymap.set("n", "<leader>ml", function()
require("lint").try_lint()
end, { desc = "Lint file" })
end,
}