diff --git a/README.md b/README.md index 5fc7f8929..bc73067ad 100644 --- a/README.md +++ b/README.md @@ -137,8 +137,11 @@ lsp_installer.settings({ | Ansible | `ansiblels` | | Bash | `bashls` | | Bicep | `bicep` | +| C | `ccls` | +| C | `clangd` | | C# | `csharpls` | | C# | `omnisharp` | +| C++ | `ccls` | | C++ | `clangd` | | CMake | `cmake` | | CSS | `cssls` | @@ -172,6 +175,7 @@ lsp_installer.settings({ | LaTeX | `texlab` | | Lua | `sumneko_lua` | | OCaml | `ocamlls` | +| Objective C | `ccls` | | PHP | `intelephense` | | PHP | `phpactor` | | Powershell | `powershell_es` | diff --git a/lua/nvim-lsp-installer/_generated/filetype_map.lua b/lua/nvim-lsp-installer/_generated/filetype_map.lua index 75da25fb3..3ca0be4a2 100644 --- a/lua/nvim-lsp-installer/_generated/filetype_map.lua +++ b/lua/nvim-lsp-installer/_generated/filetype_map.lua @@ -7,10 +7,10 @@ return { bib = { "ltex", "texlab" }, bicep = { "bicep" }, blade = { "tailwindcss" }, - c = { "clangd" }, + c = { "ccls", "clangd" }, clojure = { "clojure_lsp" }, cmake = { "cmake" }, - cpp = { "clangd" }, + cpp = { "ccls", "clangd" }, cs = { "csharp_ls", "omnisharp" }, css = { "cssls", "emmet_ls", "stylelint_lsp", "tailwindcss" }, d = { "serve_d" }, @@ -61,8 +61,8 @@ return { mysql = { "sqlls", "sqls" }, njk = { "tailwindcss" }, nunjucks = { "tailwindcss" }, - objc = { "clangd" }, - objcpp = { "clangd" }, + objc = { "ccls", "clangd" }, + objcpp = { "ccls", "clangd" }, ocaml = { "ocamlls" }, org = { "ltex" }, php = { "intelephense", "phpactor", "tailwindcss" }, diff --git a/lua/nvim-lsp-installer/_generated/language_autocomplete_map.lua b/lua/nvim-lsp-installer/_generated/language_autocomplete_map.lua index ab5cdfbb5..4bf055a32 100644 --- a/lua/nvim-lsp-installer/_generated/language_autocomplete_map.lua +++ b/lua/nvim-lsp-installer/_generated/language_autocomplete_map.lua @@ -1,8 +1,9 @@ -- THIS FILE IS GENERATED. DO NOT EDIT MANUALLY. -- stylua: ignore start return { + c = { "ccls", "clangd" }, ["c#"] = { "csharp_ls", "omnisharp" }, - ["c++"] = { "clangd" }, + ["c++"] = { "ccls", "clangd" }, d = { "serve_d" }, ["f#"] = { "fsautocomplete" }, fortran = { "fortls" }, @@ -11,6 +12,7 @@ return { javascript = { "rome", "tsserver" }, latex = { "ltex", "texlab" }, lua = { "sumneko_lua" }, + ["objective-c"] = { "ccls" }, php = { "intelephense", "phpactor" }, python = { "jedi_language_server", "pylsp", "pyright" }, ruby = { "solargraph", "sorbet" }, diff --git a/lua/nvim-lsp-installer/_generated/metadata.lua b/lua/nvim-lsp-installer/_generated/metadata.lua index d409fa5ba..f239ad52e 100644 --- a/lua/nvim-lsp-installer/_generated/metadata.lua +++ b/lua/nvim-lsp-installer/_generated/metadata.lua @@ -13,6 +13,9 @@ return { bicep = { filetypes = { "bicep" } }, + ccls = { + filetypes = { "c", "cpp", "objc", "objcpp" } + }, clangd = { filetypes = { "c", "cpp", "objc", "objcpp" } }, diff --git a/lua/nvim-lsp-installer/installers/context.lua b/lua/nvim-lsp-installer/installers/context.lua index bdb993a5a..cf47c1799 100644 --- a/lua/nvim-lsp-installer/installers/context.lua +++ b/lua/nvim-lsp-installer/installers/context.lua @@ -3,6 +3,7 @@ local log = require "nvim-lsp-installer.log" local process = require "nvim-lsp-installer.process" local installers = require "nvim-lsp-installer.installers" local platform = require "nvim-lsp-installer.platform" +local fs = require "nvim-lsp-installer.fs" local M = {} @@ -96,7 +97,12 @@ function M.use_github_release_file(repo, file) M.use_github_release(repo), function(server, callback, context) local function get_download_url(version) - local target_file = type(file) == "function" and file(version) or file + local target_file + if type(file) == "function" then + target_file = file(version) + else + target_file = file + end if not target_file then log.fmt_error( "Unable to find which release file to download. server_name=%s, repo=%s", diff --git a/lua/nvim-lsp-installer/installers/init.lua b/lua/nvim-lsp-installer/installers/init.lua index fdf3b0f4a..06428245a 100644 --- a/lua/nvim-lsp-installer/installers/init.lua +++ b/lua/nvim-lsp-installer/installers/init.lua @@ -1,6 +1,8 @@ local platform = require "nvim-lsp-installer.platform" local log = require "nvim-lsp-installer.log" local Data = require "nvim-lsp-installer.data" +local fs = require "nvim-lsp-installer.fs" +local path = require "nvim-lsp-installer.path" local M = {} @@ -52,6 +54,20 @@ function M.compose(installers) return M.pipe(Data.list_reverse(installers)) end +---@param installer ServerInstallerFunction +function M.unset_requested_version(installer) + ---@type ServerInstallerFunction + return function(server, callback, context) + local requested_server_version = context.requested_server_version + context.requested_server_version = nil + installer(server, function(...) + -- Restore version in context for any subsequent installers + context.requested_server_version = Data.coalesce(context.requested_server_version, requested_server_version) + callback(...) + end, context) + end +end + ---@param installers ServerInstallerFunction[] ---@return ServerInstallerFunction @An installer function that will serially execute the provided installers, until the first one succeeds. function M.first_successful(installers) @@ -153,4 +169,33 @@ function M.when(platform_table) end end +---@param rel_path string @The relative path from the current install_dir. +---@param installer ServerInstallerFunction @The installer function to execute in the new install dir context. +function M.run_in_dir(rel_path, installer) + return M.pipe { + vim.schedule_wrap(function(_, callback, ctx) + local new_install_dir = path.concat { ctx.install_dir, rel_path } + log.debug("Changing install_dir from %q to %q.", ctx.install_dir, new_install_dir) + ctx.parent_install_dir = ctx.install_dir + ctx.install_dir = new_install_dir + if not fs.dir_exists(new_install_dir) then + local mkdir_ok = pcall(fs.mkdirp, new_install_dir) + if not mkdir_ok then + ctx.stdio_sink.stderr(("Failed to create directory %q.\n"):format(new_install_dir)) + callback(false) + return + end + end + callback(true) + end), + type(installer) == "table" and M.pipe(installer) or installer, + function(_, callback, ctx) + log.debug("Restoring install_dir from %q to %q.", ctx.install_dir, ctx.parent_install_dir) + ctx.install_dir = ctx.parent_install_dir + ctx.parent_install_dir = nil + callback(true) + end, + } +end + return M diff --git a/lua/nvim-lsp-installer/installers/std.lua b/lua/nvim-lsp-installer/installers/std.lua index 4a95e9c8c..1889b50ee 100644 --- a/lua/nvim-lsp-installer/installers/std.lua +++ b/lua/nvim-lsp-installer/installers/std.lua @@ -248,6 +248,17 @@ function M.git_clone(repo_url) end end +function M.git_update_submodules() + ---@type ServerInstallerFunction + return function(_, callback, context) + process.spawn("git", { + args = { "submodule", "update", "--init", "--recursive" }, + cwd = context.install_dir, + stdio_sink = context.stdio_sink, + }, callback) + end +end + ---@param opts {args: string[]} function M.gradlew(opts) ---@type ServerInstallerFunction diff --git a/lua/nvim-lsp-installer/servers/ccls/init.lua b/lua/nvim-lsp-installer/servers/ccls/init.lua new file mode 100644 index 000000000..42b417fbb --- /dev/null +++ b/lua/nvim-lsp-installer/servers/ccls/init.lua @@ -0,0 +1,91 @@ +local server = require "nvim-lsp-installer.server" +local path = require "nvim-lsp-installer.path" +local installers = require "nvim-lsp-installer.installers" +local Data = require "nvim-lsp-installer.data" +local std = require "nvim-lsp-installer.installers.std" +local platform = require "nvim-lsp-installer.platform" +local context = require "nvim-lsp-installer.installers.context" +local process = require "nvim-lsp-installer.process" + +local coalesce, when = Data.coalesce, Data.when + +return function(name, root_dir) + local llvm_installer + + do + ---@param version string + ---@return string|nil + local function get_archive_name(version) + local name_template = coalesce( + when(platform.is_mac, "clang+llvm-%s-x86_64-apple-darwin"), + when( + platform.is_linux, + coalesce( + when(platform.arch == "x64", "clang+llvm-%s-amd64-unknown-freebsd13"), + when(platform.arch == "arm64", "clang+llvm-%s-aarch64-linux-gnu") + ) + ) + ) + return name_template and name_template:format(version) + end + + ---@param version string + local function normalize_version(version) + return version:gsub("^llvmorg%-", "") + end + + -- We unset the requested version for llvm because it's not the primary target - version should only apply to ccls. + llvm_installer = installers.unset_requested_version(installers.pipe { + context.use_github_release_file("llvm/llvm-project", function(version) + -- Strip the "llvmorg-" prefix from tags (llvm releases tags like llvmorg-13.0.0) + local archive_name = get_archive_name(normalize_version(version)) + return archive_name and ("%s.tar.xz"):format(archive_name) + end), + context.capture(function(ctx) + return installers.pipe { + std.untarxz_remote(ctx.github_release_file), + std.rename(get_archive_name(normalize_version(ctx.requested_server_version)), "llvm"), + } + end), + }) + end + + local ccls_installer = installers.run_in_dir("ccls", { + std.git_clone "https://github.com/MaskRay/ccls", + std.git_update_submodules(), + function(_, callback, ctx) + process.spawn("cmake", { + args = { + "-H.", + "-BRelease", + "-DCMAKE_BUILD_TYPE=Release", + ("-DCMAKE_PREFIX_PATH=%s"):format(path.concat { ctx.parent_install_dir, "llvm", "lib", "cmake" }), + }, + cwd = ctx.install_dir, + stdio_sink = ctx.stdio_sink, + }, callback) + end, + function(_, callback, ctx) + process.spawn("cmake", { + args = { "--build", "Release" }, + cwd = ctx.install_dir, + stdio_sink = ctx.stdio_sink, + }, callback) + end, + }) + + return server.Server:new { + name = name, + root_dir = root_dir, + homepage = "https://github.com/MaskRay/ccls", + languages = { "c", "c++", "objective-c" }, + installer = { + context.promote_install_dir(), -- ccls hardcodes the path to llvm at compile time, so we need to compile everything in the final directory + llvm_installer, + ccls_installer, + }, + default_options = { + cmd = { path.concat { root_dir, "ccls", "Release", "ccls" } }, + }, + } +end diff --git a/lua/nvim-lsp-installer/servers/init.lua b/lua/nvim-lsp-installer/servers/init.lua index cb217573d..0f66d64c6 100644 --- a/lua/nvim-lsp-installer/servers/init.lua +++ b/lua/nvim-lsp-installer/servers/init.lua @@ -35,6 +35,7 @@ local CORE_SERVERS = Data.set_of { "ansiblels", "bashls", "bicep", + "ccls", "clangd", "clojure_lsp", "cmake",