aboutsummaryrefslogtreecommitdiffstats
path: root/lua/nvim-lsp-installer/core
diff options
context:
space:
mode:
Diffstat (limited to 'lua/nvim-lsp-installer/core')
-rw-r--r--lua/nvim-lsp-installer/core/async/init.lua224
-rw-r--r--lua/nvim-lsp-installer/core/async/uv.lua49
-rw-r--r--lua/nvim-lsp-installer/core/clients/eclipse.lua15
-rw-r--r--lua/nvim-lsp-installer/core/fetch.lua54
-rw-r--r--lua/nvim-lsp-installer/core/fs.lua147
-rw-r--r--lua/nvim-lsp-installer/core/functional/data.lua30
-rw-r--r--lua/nvim-lsp-installer/core/functional/function.lua89
-rw-r--r--lua/nvim-lsp-installer/core/functional/init.lua97
-rw-r--r--lua/nvim-lsp-installer/core/functional/list.lua123
-rw-r--r--lua/nvim-lsp-installer/core/functional/logic.lua51
-rw-r--r--lua/nvim-lsp-installer/core/functional/number.lua34
-rw-r--r--lua/nvim-lsp-installer/core/functional/relation.lua17
-rw-r--r--lua/nvim-lsp-installer/core/functional/string.lua30
-rw-r--r--lua/nvim-lsp-installer/core/functional/table.lua9
-rw-r--r--lua/nvim-lsp-installer/core/functional/type.lua14
-rw-r--r--lua/nvim-lsp-installer/core/installer/context.lua199
-rw-r--r--lua/nvim-lsp-installer/core/installer/init.lua87
-rw-r--r--lua/nvim-lsp-installer/core/managers/cargo/client.lua14
-rw-r--r--lua/nvim-lsp-installer/core/managers/cargo/init.lua126
-rw-r--r--lua/nvim-lsp-installer/core/managers/composer/init.lua120
-rw-r--r--lua/nvim-lsp-installer/core/managers/dotnet/init.lua50
-rw-r--r--lua/nvim-lsp-installer/core/managers/gem/init.lua145
-rw-r--r--lua/nvim-lsp-installer/core/managers/git/init.lua73
-rw-r--r--lua/nvim-lsp-installer/core/managers/github/client.lua120
-rw-r--r--lua/nvim-lsp-installer/core/managers/github/init.lua175
-rw-r--r--lua/nvim-lsp-installer/core/managers/go/init.lua130
-rw-r--r--lua/nvim-lsp-installer/core/managers/luarocks/init.lua130
-rw-r--r--lua/nvim-lsp-installer/core/managers/npm/init.lua132
-rw-r--r--lua/nvim-lsp-installer/core/managers/opam/init.lua58
-rw-r--r--lua/nvim-lsp-installer/core/managers/pip3/init.lua160
-rw-r--r--lua/nvim-lsp-installer/core/managers/powershell/init.lua46
-rw-r--r--lua/nvim-lsp-installer/core/managers/std/init.lua187
-rw-r--r--lua/nvim-lsp-installer/core/optional.lua100
-rw-r--r--lua/nvim-lsp-installer/core/path.lua36
-rw-r--r--lua/nvim-lsp-installer/core/platform.lua154
-rw-r--r--lua/nvim-lsp-installer/core/process.lua314
-rw-r--r--lua/nvim-lsp-installer/core/receipt.lua170
-rw-r--r--lua/nvim-lsp-installer/core/result.lua152
-rw-r--r--lua/nvim-lsp-installer/core/spawn.lua121
-rw-r--r--lua/nvim-lsp-installer/core/ui/display.lua458
-rw-r--r--lua/nvim-lsp-installer/core/ui/init.lua141
-rw-r--r--lua/nvim-lsp-installer/core/ui/state.lua24
42 files changed, 0 insertions, 4605 deletions
diff --git a/lua/nvim-lsp-installer/core/async/init.lua b/lua/nvim-lsp-installer/core/async/init.lua
deleted file mode 100644
index 5c82e64a..00000000
--- a/lua/nvim-lsp-installer/core/async/init.lua
+++ /dev/null
@@ -1,224 +0,0 @@
-local functional = require "nvim-lsp-installer.core.functional"
-local co = coroutine
-
-local exports = {}
-
-local Promise = {}
-Promise.__index = Promise
-
-function Promise.new(resolver)
- return setmetatable({ resolver = resolver, has_resolved = false }, Promise)
-end
-
----@param success boolean
----@param cb fun(success: boolean, value: table)
-function Promise:_wrap_resolver_cb(success, cb)
- return function(...)
- if self.has_resolved then
- return
- end
- self.has_resolved = true
- cb(success, { ... })
- end
-end
-
-function Promise:__call(callback)
- self.resolver(self:_wrap_resolver_cb(true, callback), self:_wrap_resolver_cb(false, callback))
-end
-
-local function await(resolver)
- local ok, value = co.yield(Promise.new(resolver))
- if not ok then
- error(value[1], 2)
- end
- return unpack(value)
-end
-
-local function table_pack(...)
- return { n = select("#", ...), ... }
-end
-
----@param async_fn fun(...)
----@param should_reject_err boolean|nil @Whether the provided async_fn takes a callback with the signature `fun(err, result)`
-local function promisify(async_fn, should_reject_err)
- return function(...)
- local args = table_pack(...)
- return await(function(resolve, reject)
- if should_reject_err then
- args[args.n + 1] = function(err, result)
- if err then
- reject(err)
- else
- resolve(result)
- end
- end
- else
- args[args.n + 1] = resolve
- end
- local ok, err = pcall(async_fn, unpack(args, 1, args.n + 1))
- if not ok then
- reject(err)
- end
- end)
- end
-end
-
-local function new_execution_context(suspend_fn, callback, ...)
- local thread = co.create(suspend_fn)
- local cancelled = false
- local step
- step = function(...)
- if cancelled then
- return
- end
- local ok, promise_or_result = co.resume(thread, ...)
- if ok then
- if co.status(thread) == "suspended" then
- if getmetatable(promise_or_result) == Promise then
- promise_or_result(step)
- else
- -- yield to parent coroutine
- step(coroutine.yield(promise_or_result))
- end
- else
- callback(true, promise_or_result)
- thread = nil
- end
- else
- callback(false, promise_or_result)
- thread = nil
- end
- end
-
- step(...)
- return function()
- cancelled = true
- thread = nil
- end
-end
-
-exports.run = function(suspend_fn, callback, ...)
- return new_execution_context(suspend_fn, callback, ...)
-end
-
-exports.scope = function(suspend_fn)
- return function(...)
- return new_execution_context(suspend_fn, function(success, err)
- if not success then
- error(err, 0)
- end
- end, ...)
- end
-end
-
-exports.run_blocking = function(suspend_fn, ...)
- local resolved, ok, result
- local cancel_coroutine = new_execution_context(suspend_fn, function(a, b)
- resolved = true
- ok = a
- result = b
- end, ...)
-
- if vim.wait(60000, function()
- return resolved == true
- end, 50) then
- if not ok then
- error(result, 2)
- end
- return result
- else
- cancel_coroutine()
- error("async function failed to resolve in time.", 2)
- end
-end
-
-exports.wait = await
-exports.promisify = promisify
-
-exports.sleep = function(ms)
- await(function(resolve)
- vim.defer_fn(resolve, ms)
- end)
-end
-
-exports.scheduler = function()
- await(vim.schedule)
-end
-
----Creates a oneshot channel that can only send once.
-local function oneshot_channel()
- local has_sent = false
- local sent_value
- local saved_callback
-
- return {
- is_closed = function()
- return has_sent
- end,
- send = function(...)
- assert(not has_sent, "Oneshot channel can only send once.")
- has_sent = true
- sent_value = { ... }
- if saved_callback then
- saved_callback(unpack(sent_value))
- end
- end,
- receive = function()
- return await(function(resolve)
- if has_sent then
- resolve(unpack(sent_value))
- else
- saved_callback = resolve
- end
- end)
- end,
- }
-end
-
----@async
----@param suspend_fns async fun()[]
-exports.wait_all = function(suspend_fns)
- local channel = oneshot_channel()
-
- do
- local results = {}
- local thread_cancellations = {}
- local count = #suspend_fns
- local completed = 0
-
- for i, suspend_fn in ipairs(suspend_fns) do
- thread_cancellations[i] = exports.run(suspend_fn, function(success, result)
- completed = completed + 1
- if not success then
- if not channel.is_closed() then
- for _, cancel_thread in ipairs(thread_cancellations) do
- cancel_thread()
- end
- channel.send(false, result)
- results = nil
- thread_cancellations = {}
- end
- else
- results[i] = result
- if completed >= count then
- channel.send(true, results)
- results = nil
- thread_cancellations = {}
- end
- end
- end)
- end
- end
-
- local ok, results = channel.receive()
- if not ok then
- error(results, 2)
- end
- return unpack(results)
-end
-
-function exports.blocking(suspend_fn)
- return functional.partial(exports.run_blocking, suspend_fn)
-end
-
-return exports
diff --git a/lua/nvim-lsp-installer/core/async/uv.lua b/lua/nvim-lsp-installer/core/async/uv.lua
deleted file mode 100644
index 69af4f26..00000000
--- a/lua/nvim-lsp-installer/core/async/uv.lua
+++ /dev/null
@@ -1,49 +0,0 @@
-local a = require "nvim-lsp-installer.core.async"
-
----@type table<UvMethod, async fun(...)>
-local M = setmetatable({}, {
- __index = function(cache, method)
- cache[method] = a.promisify(vim.loop[method], true)
- return cache[method]
- end,
-})
-
-return M
-
----@alias UvMethod
----| '"fs_close"'
----| '"fs_open"'
----| '"fs_read"'
----| '"fs_unlink"'
----| '"fs_write"'
----| '"fs_mkdir"'
----| '"fs_mkdtemp"'
----| '"fs_mkstemp"'
----| '"fs_rmdir"'
----| '"fs_scandir"'
----| '"fs_stat"'
----| '"fs_fstat"'
----| '"fs_lstat"'
----| '"fs_rename"'
----| '"fs_fsync"'
----| '"fs_fdatasync"'
----| '"fs_ftruncate"'
----| '"fs_sendfile"'
----| '"fs_access"'
----| '"fs_chmod"'
----| '"fs_fchmod"'
----| '"fs_utime"'
----| '"fs_futime"'
----| '"fs_lutime"'
----| '"fs_link"'
----| '"fs_symlink"'
----| '"fs_readlink"'
----| '"fs_realpath"'
----| '"fs_chown"'
----| '"fs_fchown"'
----| '"fs_lchown"'
----| '"fs_copyfile"'
----| '"fs_opendir"'
----| '"fs_readdir"'
----| '"fs_closedir"'
----| '"fs_statfs"'
diff --git a/lua/nvim-lsp-installer/core/clients/eclipse.lua b/lua/nvim-lsp-installer/core/clients/eclipse.lua
deleted file mode 100644
index 0d169d9d..00000000
--- a/lua/nvim-lsp-installer/core/clients/eclipse.lua
+++ /dev/null
@@ -1,15 +0,0 @@
-local fetch = require "nvim-lsp-installer.core.fetch"
-local M = {}
-
----@param version string The version string as found in the latest.txt endpoint.
----@return string The parsed version number.
-function M._parse_jdtls_version_string(version)
- return vim.trim(version):gsub("^jdt%-language%-server%-", ""):gsub("%.tar%.gz$", "")
-end
-
----@async
-function M.fetch_latest_jdtls_version()
- return fetch("https://download.eclipse.org/jdtls/snapshots/latest.txt"):map(M._parse_jdtls_version_string)
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/fetch.lua b/lua/nvim-lsp-installer/core/fetch.lua
deleted file mode 100644
index 4a5ff4df..00000000
--- a/lua/nvim-lsp-installer/core/fetch.lua
+++ /dev/null
@@ -1,54 +0,0 @@
-local log = require "nvim-lsp-installer.log"
-local platform = require "nvim-lsp-installer.core.platform"
-local Result = require "nvim-lsp-installer.core.result"
-local spawn = require "nvim-lsp-installer.core.spawn"
-local powershell = require "nvim-lsp-installer.core.managers.powershell"
-
-local USER_AGENT = "nvim-lsp-installer (+https://github.com/williamboman/nvim-lsp-installer)"
-
-local HEADERS = {
- wget = { "--header", ("User-Agent: %s"):format(USER_AGENT) },
- curl = { "-H", ("User-Agent: %s"):format(USER_AGENT) },
- iwr = ("-Headers @{'User-Agent' = '%s'}"):format(USER_AGENT),
-}
-
----@alias FetchOpts {out_file:string}
-
----@async
----@param url string @The url to fetch.
----@param opts FetchOpts
-local function fetch(url, opts)
- opts = opts or {}
- log.fmt_debug("Fetching URL %s", url)
-
- local platform_specific = Result.failure()
-
- if platform.is_win then
- if opts.out_file then
- platform_specific = powershell.command(
- ([[iwr %s -UseBasicParsing -Uri %q -OutFile %q;]]):format(HEADERS.iwr, url, opts.out_file)
- )
- else
- platform_specific = powershell.command(
- ([[Write-Output (iwr %s -UseBasicParsing -Uri %q).Content;]]):format(HEADERS.iwr, url)
- )
- end
- end
-
- return platform_specific
- :recover_catching(function()
- return spawn.wget({ HEADERS.wget, "-nv", "-O", opts.out_file or "-", url }):get_or_throw()
- end)
- :recover_catching(function()
- return spawn.curl({ HEADERS.curl, "-fsSL", opts.out_file and { "-o", opts.out_file } or vim.NIL, url }):get_or_throw()
- end)
- :map(function(result)
- if opts.out_file then
- return result
- else
- return result.stdout
- end
- end)
-end
-
-return fetch
diff --git a/lua/nvim-lsp-installer/core/fs.lua b/lua/nvim-lsp-installer/core/fs.lua
deleted file mode 100644
index 08e6f04f..00000000
--- a/lua/nvim-lsp-installer/core/fs.lua
+++ /dev/null
@@ -1,147 +0,0 @@
-local log = require "nvim-lsp-installer.log"
-local a = require "nvim-lsp-installer.core.async"
-local Path = require "nvim-lsp-installer.core.path"
-local settings = require "nvim-lsp-installer.settings"
-
-local function make_module(uv)
- local M = {}
-
- ---@param path string
- function M.fstat(path)
- log.trace("fs: fstat", path)
- local fd = uv.fs_open(path, "r", 438)
- local fstat = uv.fs_fstat(fd)
- uv.fs_close(fd)
- return fstat
- end
-
- ---@param path string
- function M.file_exists(path)
- log.trace("fs: file_exists", path)
- local ok, fstat = pcall(M.fstat, path)
- if not ok then
- return false
- end
- return fstat.type == "file"
- end
-
- ---@param path string
- function M.dir_exists(path)
- log.trace("fs: dir_exists", path)
- local ok, fstat = pcall(M.fstat, path)
- if not ok then
- return false
- end
- return fstat.type == "directory"
- end
-
- ---@param path string
- function M.rmrf(path)
- assert(
- Path.is_subdirectory(settings.current.install_root_dir, path),
- (
- "Refusing to rmrf %q which is outside of the allowed boundary %q. Please report this error at https://github.com/williamboman/nvim-lsp-installer/issues/new"
- ):format(path, settings.current.install_root_dir)
- )
- log.debug("fs: rmrf", path)
- if vim.in_fast_event() then
- a.scheduler()
- end
- if vim.fn.delete(path, "rf") ~= 0 then
- log.debug "fs: rmrf failed"
- error(("rmrf: Could not remove directory %q."):format(path))
- end
- end
-
- ---@param path string
- function M.unlink(path)
- log.debug("fs: unlink", path)
- uv.fs_unlink(path)
- end
-
- ---@param path string
- function M.mkdir(path)
- log.debug("fs: mkdir", path)
- uv.fs_mkdir(path, 493) -- 493(10) == 755(8)
- end
-
- ---@param path string
- function M.mkdirp(path)
- log.debug("fs: mkdirp", path)
- if vim.in_fast_event() then
- a.scheduler()
- end
- if vim.fn.mkdir(path, "p") ~= 1 then
- log.debug "fs: mkdirp failed"
- error(("mkdirp: Could not create directory %q."):format(path))
- end
- end
-
- ---@param path string
- ---@param new_path string
- function M.rename(path, new_path)
- log.debug("fs: rename", path, new_path)
- uv.fs_rename(path, new_path)
- end
-
- ---@param path string
- ---@param contents string
- ---@param flags string|nil @Defaults to "w".
- function M.write_file(path, contents, flags)
- log.debug("fs: write_file", path)
- local fd = uv.fs_open(path, flags or "w", 438)
- uv.fs_write(fd, contents, -1)
- uv.fs_close(fd)
- end
-
- ---@param path string
- ---@param contents string
- function M.append_file(path, contents)
- M.write_file(path, contents, "a")
- end
-
- ---@param path string
- function M.read_file(path)
- log.trace("fs: read_file", path)
- local fd = uv.fs_open(path, "r", 438)
- local fstat = uv.fs_fstat(fd)
- local contents = uv.fs_read(fd, fstat.size, 0)
- uv.fs_close(fd)
- return contents
- end
-
- ---@alias ReaddirEntry {name: string, type: string}
-
- ---@param path string @The full path to the directory to read.
- ---@return ReaddirEntry[]
- function M.readdir(path)
- log.trace("fs: fs_opendir", path)
- local dir = vim.loop.fs_opendir(path, nil, 25)
- local all_entries = {}
- local exhausted = false
-
- repeat
- local entries = uv.fs_readdir(dir)
- log.trace("fs: fs_readdir", path, entries)
- if entries and #entries > 0 then
- for i = 1, #entries do
- all_entries[#all_entries + 1] = entries[i]
- end
- else
- log.trace("fs: fs_readdir exhausted scan", path)
- exhausted = true
- end
- until exhausted
-
- uv.fs_closedir(dir)
-
- return all_entries
- end
-
- return M
-end
-
-return {
- async = make_module(require "nvim-lsp-installer.core.async.uv"),
- sync = make_module(vim.loop),
-}
diff --git a/lua/nvim-lsp-installer/core/functional/data.lua b/lua/nvim-lsp-installer/core/functional/data.lua
deleted file mode 100644
index da6f1efd..00000000
--- a/lua/nvim-lsp-installer/core/functional/data.lua
+++ /dev/null
@@ -1,30 +0,0 @@
-local _ = {}
-
-_.table_pack = function(...)
- return { n = select("#", ...), ... }
-end
-
----@generic T : string
----@param values T[]
----@return table<T, T>
-_.enum = function(values)
- local result = {}
- for i = 1, #values do
- local v = values[i]
- result[v] = v
- end
- return result
-end
-
----@generic T
----@param list T[]
----@return table<T, boolean>
-_.set_of = function(list)
- local set = {}
- for i = 1, #list do
- set[list[i]] = true
- end
- return set
-end
-
-return _
diff --git a/lua/nvim-lsp-installer/core/functional/function.lua b/lua/nvim-lsp-installer/core/functional/function.lua
deleted file mode 100644
index 0d70aa92..00000000
--- a/lua/nvim-lsp-installer/core/functional/function.lua
+++ /dev/null
@@ -1,89 +0,0 @@
-local data = require "nvim-lsp-installer.core.functional.data"
-
-local _ = {}
-
----@generic T : fun(...)
----@param fn T
----@param arity integer
----@return T
-_.curryN = function(fn, arity)
- return function(...)
- local args = data.table_pack(...)
- if args.n >= arity then
- return fn(unpack(args, 1, arity))
- else
- return _.curryN(_.partial(fn, unpack(args, 1, args.n)), arity - args.n)
- end
- end
-end
-
-_.compose = function(...)
- local functions = data.table_pack(...)
- assert(functions.n > 0, "compose requires at least one function")
- return function(...)
- local result = data.table_pack(...)
- for i = functions.n, 1, -1 do
- result = data.table_pack(functions[i](unpack(result, 1, result.n)))
- end
- return unpack(result, 1, result.n)
- end
-end
-
----@generic T
----@param fn fun(...): T
----@return fun(...): T
-_.partial = function(fn, ...)
- local bound_args = data.table_pack(...)
- return function(...)
- local args = data.table_pack(...)
- local merged_args = {}
- for i = 1, bound_args.n do
- merged_args[i] = bound_args[i]
- end
- for i = 1, args.n do
- merged_args[bound_args.n + i] = args[i]
- end
- return fn(unpack(merged_args, 1, bound_args.n + args.n))
- end
-end
-
-_.identity = function(a)
- return a
-end
-
-_.always = function(a)
- return function()
- return a
- end
-end
-
-_.T = _.always(true)
-_.F = _.always(false)
-
----@generic T : fun(...)
----@param fn T
----@param cache_key_generator (fun(...): string | nil)|nil
----@return T
-_.memoize = function(fn, cache_key_generator)
- cache_key_generator = cache_key_generator or _.identity
- local cache = {}
- return function(...)
- local key = cache_key_generator(...)
- if not cache[key] then
- cache[key] = data.table_pack(fn(...))
- end
- return unpack(cache[key], 1, cache[key].n)
- end
-end
-
----@generic T
----@param fn fun(): T
----@return fun(): T
-_.lazy = function(fn)
- local memoized = _.memoize(fn, _.always "lazyval")
- return function()
- return memoized()
- end
-end
-
-return _
diff --git a/lua/nvim-lsp-installer/core/functional/init.lua b/lua/nvim-lsp-installer/core/functional/init.lua
deleted file mode 100644
index 3e29037f..00000000
--- a/lua/nvim-lsp-installer/core/functional/init.lua
+++ /dev/null
@@ -1,97 +0,0 @@
-local _ = {}
-
--- data
-local data = require "nvim-lsp-installer.core.functional.data"
-_.table_pack = data.table_pack
-_.enum = data.enum
-_.set_of = data.set_of
-
--- function
-local fun = require "nvim-lsp-installer.core.functional.function"
-_.curryN = fun.curryN
-_.compose = fun.compose
-_.partial = fun.partial
-_.identity = fun.identity
-_.always = fun.always
-_.T = fun.T
-_.F = fun.F
-_.memoize = fun.memoize
-_.lazy = fun.lazy
-
--- list
-local list = require "nvim-lsp-installer.core.functional.list"
-_.reverse = list.reverse
-_.list_not_nil = list.list_not_nil
-_.list_copy = list.list_copy
-_.find_first = list.find_first
-_.any = list.any
-_.filter = list.filter
-_.map = list.map
-_.each = list.each
-_.concat = list.concat
-_.zip_table = list.zip_table
-_.nth = list.nth
-_.head = list.head
-_.length = list.length
-
--- relation
-local relation = require "nvim-lsp-installer.core.functional.relation"
-_.equals = relation.equals
-_.prop_eq = relation.prop_eq
-_.prop_satisfies = relation.prop_satisfies
-
--- logic
-local logic = require "nvim-lsp-installer.core.functional.logic"
-_.all_pass = logic.all_pass
-_.if_else = logic.if_else
-_.is_not = logic.is_not
-_.complement = logic.complement
-_.cond = logic.cond
-
--- number
-local number = require "nvim-lsp-installer.core.functional.number"
-_.negate = number.negate
-_.gt = number.gt
-_.gte = number.gte
-_.lt = number.lt
-_.lte = number.lte
-_.inc = number.inc
-_.dec = number.dec
-
--- string
-local string = require "nvim-lsp-installer.core.functional.string"
-_.matches = string.matches
-_.format = string.format
-_.split = string.split
-_.gsub = string.gsub
-
--- table
-local tbl = require "nvim-lsp-installer.core.functional.table"
-_.prop = tbl.prop
-
--- type
-local typ = require "nvim-lsp-installer.core.functional.type"
-_.is_nil = typ.is_nil
-_.is = typ.is
-
--- TODO do something else with these
-
-_.coalesce = function(...)
- local args = _.table_pack(...)
- for i = 1, args.n do
- local variable = args[i]
- if variable ~= nil then
- return variable
- end
- end
-end
-
-_.when = function(condition, value)
- return condition and value or nil
-end
-
-_.lazy_when = function(condition, value)
- return condition and value() or nil
-end
-
-return _
diff --git a/lua/nvim-lsp-installer/core/functional/list.lua b/lua/nvim-lsp-installer/core/functional/list.lua
deleted file mode 100644
index 89393cfb..00000000
--- a/lua/nvim-lsp-installer/core/functional/list.lua
+++ /dev/null
@@ -1,123 +0,0 @@
-local fun = require "nvim-lsp-installer.core.functional.function"
-local data = require "nvim-lsp-installer.core.functional.data"
-
-local _ = {}
-
----@generic T
----@param list T[]
----@return T[]
-_.reverse = function(list)
- local result = {}
- for i = #list, 1, -1 do
- result[#result + 1] = list[i]
- end
- return result
-end
-
-_.list_not_nil = function(...)
- local result = {}
- local args = data.table_pack(...)
- for i = 1, args.n do
- if args[i] ~= nil then
- result[#result + 1] = args[i]
- end
- end
- return result
-end
-
----@generic T
----@param predicate fun(item: T): boolean
----@param list T[]
----@return T | nil
-_.find_first = fun.curryN(function(predicate, list)
- local result
- for i = 1, #list do
- local entry = list[i]
- if predicate(entry) then
- return entry
- end
- end
- return result
-end, 2)
-
----@generic T
----@param predicate fun(item: T): boolean
----@param list T[]
----@return boolean
-_.any = fun.curryN(function(predicate, list)
- for i = 1, #list do
- if predicate(list[i]) then
- return true
- end
- end
- return false
-end, 2)
-
----@generic T
----@param filter_fn fun(item: T): boolean
----@return fun(list: T[]): T[]
-_.filter = fun.curryN(vim.tbl_filter, 2)
-
----@generic T
----@param map_fn fun(item: T): boolean
----@return fun(list: T[]): T[]
-_.map = fun.curryN(vim.tbl_map, 2)
-
----@generic T
----@param fn fun(item: T, index: integer)
----@param list T[]
-_.each = fun.curryN(function(fn, list)
- for k, v in pairs(list) do
- fn(v, k)
- end
-end, 2)
-
----@generic T
----@param list T[]
----@return T[] @A shallow copy of the list.
-_.list_copy = _.map(fun.identity)
-
-_.concat = fun.curryN(function(a, b)
- if type(a) == "table" then
- assert(type(b) == "table", "concat: expected table")
- return vim.list_extend(_.list_copy(a), b)
- elseif type(a) == "string" then
- assert(type(b) == "string", "concat: expected string")
- return a .. b
- end
-end, 2)
-
----@generic T
----@generic U
----@param keys T[]
----@param values U[]
----@return table<T, U>
-_.zip_table = fun.curryN(function(keys, values)
- local res = {}
- for i, key in ipairs(keys) do
- res[key] = values[i]
- end
- return res
-end, 2)
-
----@generic T
----@param offset number
----@param value T[]|string
----@return T|string|nil
-_.nth = fun.curryN(function(offset, value)
- local index = offset < 0 and (#value + (offset + 1)) or offset
- if type(value) == "string" then
- return string.sub(value, index, index)
- else
- return value[index]
- end
-end, 2)
-
-_.head = _.nth(1)
-
----@param value string|any[]
-_.length = function(value)
- return #value
-end
-
-return _
diff --git a/lua/nvim-lsp-installer/core/functional/logic.lua b/lua/nvim-lsp-installer/core/functional/logic.lua
deleted file mode 100644
index 70b349dd..00000000
--- a/lua/nvim-lsp-installer/core/functional/logic.lua
+++ /dev/null
@@ -1,51 +0,0 @@
-local fun = require "nvim-lsp-installer.core.functional.function"
-
-local _ = {}
-
----@generic T
----@param predicates (fun(item: T): boolean)[]
----@return fun(item: T): boolean
-_.all_pass = fun.curryN(function(predicates, item)
- for i = 1, #predicates do
- if not predicates[i](item) then
- return false
- end
- end
- return true
-end, 2)
-
----@generic T
----@param predicate fun(item: T): boolean
----@param a fun(item: T): any
----@param b fun(item: T): any
----@param value T
-_.if_else = fun.curryN(function(predicate, a, b, value)
- if predicate(value) then
- return a(value)
- else
- return b(value)
- end
-end, 4)
-
----@param value boolean
-_.is_not = function(value)
- return not value
-end
-
----@generic T
----@param predicate fun(value: T): boolean
----@param value T
-_.complement = fun.curryN(function(predicate, value)
- return not predicate(value)
-end, 2)
-
-_.cond = fun.curryN(function(predicate_transformer_pairs, value)
- for _, pair in ipairs(predicate_transformer_pairs) do
- local predicate, transformer = pair[1], pair[2]
- if predicate(value) then
- return transformer(value)
- end
- end
-end, 2)
-
-return _
diff --git a/lua/nvim-lsp-installer/core/functional/number.lua b/lua/nvim-lsp-installer/core/functional/number.lua
deleted file mode 100644
index b123eb20..00000000
--- a/lua/nvim-lsp-installer/core/functional/number.lua
+++ /dev/null
@@ -1,34 +0,0 @@
-local fun = require "nvim-lsp-installer.core.functional.function"
-
-local _ = {}
-
----@param number number
-_.negate = function(number)
- return -number
-end
-
-_.gt = fun.curryN(function(number, value)
- return value > number
-end, 2)
-
-_.gte = fun.curryN(function(number, value)
- return value >= number
-end, 2)
-
-_.lt = fun.curryN(function(number, value)
- return value < number
-end, 2)
-
-_.lte = fun.curryN(function(number, value)
- return value <= number
-end, 2)
-
-_.inc = fun.curryN(function(increment, value)
- return value + increment
-end, 2)
-
-_.dec = fun.curryN(function(decrement, value)
- return value - decrement
-end, 2)
-
-return _
diff --git a/lua/nvim-lsp-installer/core/functional/relation.lua b/lua/nvim-lsp-installer/core/functional/relation.lua
deleted file mode 100644
index d9786a6a..00000000
--- a/lua/nvim-lsp-installer/core/functional/relation.lua
+++ /dev/null
@@ -1,17 +0,0 @@
-local fun = require "nvim-lsp-installer.core.functional.function"
-
-local _ = {}
-
-_.equals = fun.curryN(function(expected, value)
- return value == expected
-end, 2)
-
-_.prop_eq = fun.curryN(function(property, value, tbl)
- return tbl[property] == value
-end, 3)
-
-_.prop_satisfies = fun.curryN(function(predicate, property, tbl)
- return predicate(tbl[property])
-end, 3)
-
-return _
diff --git a/lua/nvim-lsp-installer/core/functional/string.lua b/lua/nvim-lsp-installer/core/functional/string.lua
deleted file mode 100644
index 212fc0d9..00000000
--- a/lua/nvim-lsp-installer/core/functional/string.lua
+++ /dev/null
@@ -1,30 +0,0 @@
-local fun = require "nvim-lsp-installer.core.functional.function"
-
-local _ = {}
-
----@param pattern string
----@param str string
-_.matches = fun.curryN(function(pattern, str)
- return str:match(pattern) ~= nil
-end, 2)
-
----@param template string
----@param str string
-_.format = fun.curryN(function(template, str)
- return template:format(str)
-end, 2)
-
----@param sep string
----@param str string
-_.split = fun.curryN(function(sep, str)
- return vim.split(str, sep)
-end, 2)
-
----@param pattern string
----@param repl string|function|table
----@param str string
-_.gsub = fun.curryN(function(pattern, repl, str)
- return string.gsub(str, pattern, repl)
-end, 3)
-
-return _
diff --git a/lua/nvim-lsp-installer/core/functional/table.lua b/lua/nvim-lsp-installer/core/functional/table.lua
deleted file mode 100644
index 37aee19e..00000000
--- a/lua/nvim-lsp-installer/core/functional/table.lua
+++ /dev/null
@@ -1,9 +0,0 @@
-local fun = require "nvim-lsp-installer.core.functional.function"
-
-local _ = {}
-
-_.prop = fun.curryN(function(index, tbl)
- return tbl[index]
-end, 2)
-
-return _
diff --git a/lua/nvim-lsp-installer/core/functional/type.lua b/lua/nvim-lsp-installer/core/functional/type.lua
deleted file mode 100644
index 23d961ba..00000000
--- a/lua/nvim-lsp-installer/core/functional/type.lua
+++ /dev/null
@@ -1,14 +0,0 @@
-local fun = require "nvim-lsp-installer.core.functional.function"
-local rel = require "nvim-lsp-installer.core.functional.relation"
-
-local _ = {}
-
-_.is_nil = rel.equals(nil)
-
----@param typ type
----@param value any
-_.is = fun.curryN(function(typ, value)
- return type(value) == typ
-end, 2)
-
-return _
diff --git a/lua/nvim-lsp-installer/core/installer/context.lua b/lua/nvim-lsp-installer/core/installer/context.lua
deleted file mode 100644
index aaec4ff5..00000000
--- a/lua/nvim-lsp-installer/core/installer/context.lua
+++ /dev/null
@@ -1,199 +0,0 @@
-local spawn = require "nvim-lsp-installer.core.spawn"
-local log = require "nvim-lsp-installer.log"
-local fs = require "nvim-lsp-installer.core.fs"
-local path = require "nvim-lsp-installer.core.path"
-local platform = require "nvim-lsp-installer.core.platform"
-local receipt = require "nvim-lsp-installer.core.receipt"
-local installer = require "nvim-lsp-installer.core.installer"
-local a = require "nvim-lsp-installer.core.async"
-
----@class ContextualSpawn
----@field cwd CwdManager
----@field stdio_sink StdioSink
-local ContextualSpawn = {}
-
----@param cwd CwdManager
----@param stdio_sink StdioSink
-function ContextualSpawn.new(cwd, stdio_sink)
- return setmetatable({ cwd = cwd, stdio_sink = stdio_sink }, ContextualSpawn)
-end
-function ContextualSpawn.__index(self, cmd)
- return function(args)
- args.cwd = args.cwd or self.cwd:get()
- args.stdio_sink = args.stdio_sink or self.stdio_sink
- -- We get_or_throw() here for convenience reasons.
- -- Almost every time spawn is called via context we want the command to succeed.
- return spawn[cmd](args):get_or_throw()
- end
-end
-
----@class ContextualFs
----@field private cwd CwdManager
-local ContextualFs = {}
-ContextualFs.__index = ContextualFs
-
----@param cwd CwdManager
-function ContextualFs.new(cwd)
- return setmetatable({ cwd = cwd }, ContextualFs)
-end
-
----@async
----@param rel_path string @The relative path from the current working directory to the file to append.
----@param contents string
-function ContextualFs:append_file(rel_path, contents)
- return fs.async.append_file(path.concat { self.cwd:get(), rel_path }, contents)
-end
-
----@async
----@param rel_path string @The relative path from the current working directory to the file to write.
----@param contents string
-function ContextualFs:write_file(rel_path, contents)
- return fs.async.write_file(path.concat { self.cwd:get(), rel_path }, contents)
-end
-
----@async
----@param rel_path string @The relative path from the current working directory.
-function ContextualFs:file_exists(rel_path)
- return fs.async.file_exists(path.concat { self.cwd:get(), rel_path })
-end
-
----@async
----@param rel_path string @The relative path from the current working directory.
-function ContextualFs:dir_exists(rel_path)
- return fs.async.dir_exists(path.concat { self.cwd:get(), rel_path })
-end
-
----@async
----@param rel_path string @The relative path from the current working directory.
-function ContextualFs:rmrf(rel_path)
- return fs.async.rmrf(path.concat { self.cwd:get(), rel_path })
-end
-
----@async
----@param rel_path string @The relative path from the current working directory.
-function ContextualFs:unlink(rel_path)
- return fs.async.unlink(path.concat { self.cwd:get(), rel_path })
-end
-
----@async
----@param old_path string
----@param new_path string
-function ContextualFs:rename(old_path, new_path)
- return fs.async.rename(path.concat { self.cwd:get(), old_path }, path.concat { self.cwd:get(), new_path })
-end
-
----@async
----@param dirpath string
-function ContextualFs:mkdir(dirpath)
- return fs.async.mkdir(path.concat { self.cwd:get(), dirpath })
-end
-
----@class CwdManager
----@field private boundary_path string @Defines the upper boundary for which paths are allowed as cwd.
----@field private cwd string
-local CwdManager = {}
-CwdManager.__index = CwdManager
-
-function CwdManager.new(boundary_path, cwd)
- assert(type(boundary_path) == "string")
- return setmetatable({
- boundary_path = boundary_path,
- cwd = cwd,
- }, CwdManager)
-end
-
-function CwdManager:get()
- assert(self.cwd ~= nil, "Tried to access cwd before it was set.")
- return self.cwd
-end
-
----@param new_cwd string
-function CwdManager:set(new_cwd)
- assert(type(new_cwd) == "string")
- assert(
- path.is_subdirectory(self.boundary_path, new_cwd),
- ("%q is not a subdirectory of %q"):format(new_cwd, self.boundary_path)
- )
- self.cwd = new_cwd
-end
-
----@class InstallContext
----@field public name string
----@field public receipt InstallReceiptBuilder
----@field public requested_version Optional
----@field public fs ContextualFs
----@field public spawn JobSpawn
----@field public cwd CwdManager
----@field public destination_dir string
----@field public stdio_sink StdioSink
----@field public boundary_path string
-local InstallContext = {}
-InstallContext.__index = InstallContext
-
-function InstallContext.new(opts)
- local cwd_manager = CwdManager.new(opts.boundary_path)
- return setmetatable({
- name = opts.name,
- cwd = cwd_manager,
- spawn = ContextualSpawn.new(cwd_manager, opts.stdio_sink),
- fs = ContextualFs.new(cwd_manager),
- receipt = receipt.InstallReceiptBuilder.new(),
- boundary_path = opts.boundary_path,
- destination_dir = opts.destination_dir,
- requested_version = opts.requested_version,
- stdio_sink = opts.stdio_sink,
- }, InstallContext)
-end
-
----@async
-function InstallContext:promote_cwd()
- local cwd = self.cwd:get()
- if self.destination_dir == cwd then
- log.fmt_debug("cwd %s is already promoted (at %s)", cwd, self.destination_dir)
- return
- end
- log.fmt_debug("Promoting cwd %s to %s", cwd, self.destination_dir)
- -- 1. Remove destination dir, if it exists
- if fs.async.dir_exists(self.destination_dir) then
- fs.async.rmrf(self.destination_dir)
- end
- -- 2. Prepare for renaming cwd to destination
- if platform.is_unix then
- -- Some Unix systems will raise an error when renaming a directory to a destination that does not already exist.
- fs.async.mkdir(self.destination_dir)
- end
- -- 3. Move the cwd to the final installation directory
- fs.async.rename(cwd, self.destination_dir)
- -- 4. Update cwd
- self.cwd:set(self.destination_dir)
-end
-
----Runs the provided async functions concurrently and returns their result, once all are resolved.
----This is really just a wrapper around a.wait_all() that makes sure to patch the coroutine context before creating the
----new async execution contexts.
----@async
----@param suspend_fns async fun(ctx: InstallContext)[]
-function InstallContext:run_concurrently(suspend_fns)
- return a.wait_all(vim.tbl_map(function(suspend_fn)
- return function()
- return installer.run_installer(self, suspend_fn)
- end
- end, suspend_fns))
-end
-
----@param rel_path string @The relative path from the current working directory to change cwd to. Will only restore to the initial cwd after execution of fn (if provided).
----@param fn async fun() @(optional) The function to run in the context of the given path.
-function InstallContext:chdir(rel_path, fn)
- local old_cwd = self.cwd:get()
- self.cwd:set(path.concat { old_cwd, rel_path })
- if fn then
- local ok, result = pcall(fn)
- self.cwd:set(old_cwd)
- if not ok then
- error(result, 0)
- end
- return result
- end
-end
-
-return InstallContext
diff --git a/lua/nvim-lsp-installer/core/installer/init.lua b/lua/nvim-lsp-installer/core/installer/init.lua
deleted file mode 100644
index 3bb9590d..00000000
--- a/lua/nvim-lsp-installer/core/installer/init.lua
+++ /dev/null
@@ -1,87 +0,0 @@
-local log = require "nvim-lsp-installer.log"
-local path = require "nvim-lsp-installer.core.path"
-local fs = require "nvim-lsp-installer.core.fs"
-local Result = require "nvim-lsp-installer.core.result"
-
-local M = {}
-
----@async
----@param context InstallContext
-local function write_receipt(context)
- if context.receipt.is_marked_invalid then
- return log.fmt_debug("Skipping writing receipt for %s because it is marked as invalid.", context.name)
- end
- context.receipt:with_name(context.name):with_schema_version("1.0a"):with_completion_time(vim.loop.gettimeofday())
- local receipt_success, install_receipt = pcall(context.receipt.build, context.receipt)
- if receipt_success then
- local receipt_path = path.concat { context.cwd:get(), "nvim-lsp-installer-receipt.json" }
- pcall(fs.async.write_file, receipt_path, vim.json.encode(install_receipt))
- else
- log.fmt_error("Failed to build receipt for installation=%s, error=%s", context.name, install_receipt)
- end
-end
-
-local CONTEXT_REQUEST = {}
-
----@return InstallContext
-function M.context()
- return coroutine.yield(CONTEXT_REQUEST)
-end
-
----@async
----@param context InstallContext
----@param installer async fun(context: InstallContext)
-function M.run_installer(context, installer)
- local thread = coroutine.create(installer)
- local step
- local ret_val
- step = function(...)
- local ok, result = coroutine.resume(thread, ...)
- if not ok then
- error(result, 0)
- elseif result == CONTEXT_REQUEST then
- step(context)
- elseif coroutine.status(thread) == "suspended" then
- -- yield to parent coroutine
- step(coroutine.yield(result))
- else
- ret_val = result
- end
- end
- step(context)
- return ret_val
-end
-
----@async
----@param context InstallContext
----@param installer async fun(ctx: InstallContext)
-function M.execute(context, installer)
- log.fmt_debug("Executing installer for name=%s", context.name)
- local tmp_installation_dir = ("%s.tmp"):format(context.destination_dir)
- return Result.run_catching(function()
- -- 1. prepare installation dir
- context.receipt:with_start_time(vim.loop.gettimeofday())
- if fs.async.dir_exists(tmp_installation_dir) then
- fs.async.rmrf(tmp_installation_dir)
- end
- fs.async.mkdirp(tmp_installation_dir)
- context.cwd:set(tmp_installation_dir)
-
- -- 2. run installer
- M.run_installer(context, installer)
-
- -- 3. finalize
- log.fmt_debug("Finalizing installer for name=%s", context.name)
- write_receipt(context)
- context:promote_cwd()
- pcall(fs.async.rmrf, tmp_installation_dir)
- end):on_failure(function(failure)
- log.fmt_error("Installation failed, name=%s, error=%s", context.name, tostring(failure))
- context.stdio_sink.stderr(tostring(failure))
- context.stdio_sink.stderr "\n"
- pcall(fs.async.rmrf, tmp_installation_dir)
- pcall(fs.async.rmrf, context.cwd:get())
- end)
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/cargo/client.lua b/lua/nvim-lsp-installer/core/managers/cargo/client.lua
deleted file mode 100644
index d4f0e2a7..00000000
--- a/lua/nvim-lsp-installer/core/managers/cargo/client.lua
+++ /dev/null
@@ -1,14 +0,0 @@
-local fetch = require "nvim-lsp-installer.core.fetch"
-
-local M = {}
-
----@alias CrateResponse {crate: {id: string, max_stable_version: string, max_version: string, newest_version: string}}
-
----@async
----@param crate string
----@return Result @of Crate
-function M.fetch_crate(crate)
- return fetch(("https://crates.io/api/v1/crates/%s"):format(crate)):map_catching(vim.json.decode)
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/cargo/init.lua b/lua/nvim-lsp-installer/core/managers/cargo/init.lua
deleted file mode 100644
index 77ec5ae8..00000000
--- a/lua/nvim-lsp-installer/core/managers/cargo/init.lua
+++ /dev/null
@@ -1,126 +0,0 @@
-local process = require "nvim-lsp-installer.core.process"
-local path = require "nvim-lsp-installer.core.path"
-local spawn = require "nvim-lsp-installer.core.spawn"
-local a = require "nvim-lsp-installer.core.async"
-local Optional = require "nvim-lsp-installer.core.optional"
-local installer = require "nvim-lsp-installer.core.installer"
-local client = require "nvim-lsp-installer.core.managers.cargo.client"
-
----@param crate string
-local function with_receipt(crate)
- return function()
- local ctx = installer.context()
- ctx.receipt:with_primary_source(ctx.receipt.cargo(crate))
- end
-end
-
-local M = {}
-
----@async
----@param crate string The crate to install.
----@param opts {git:boolean, features:string|nil}
-function M.crate(crate, opts)
- return function()
- return M.install(crate, opts).with_receipt()
- end
-end
-
----@async
----@param crate string The crate to install.
----@param opts {git:boolean, features:string|nil}
-function M.install(crate, opts)
- local ctx = installer.context()
- opts = opts or {}
- ctx.requested_version:if_present(function()
- assert(not opts.git, "Providing a version when installing a git crate is not allowed.")
- end)
-
- local final_crate = crate
-
- if opts.git then
- final_crate = { "--git" }
- if type(opts.git) == "string" then
- table.insert(final_crate, opts.git)
- end
- table.insert(final_crate, crate)
- end
-
- ctx.spawn.cargo {
- "install",
- "--root",
- ".",
- "--locked",
- ctx.requested_version
- :map(function(version)
- return { "--version", version }
- end)
- :or_else(vim.NIL),
- opts.features and { "--features", opts.features } or vim.NIL,
- final_crate,
- }
-
- return {
- with_receipt = with_receipt(crate),
- }
-end
-
----@param output string @The `cargo install --list` output.
----@return table<string, string> @Key is the crate name, value is its version.
-function M.parse_installed_crates(output)
- local installed_crates = {}
- for _, line in ipairs(vim.split(output, "\n")) do
- local name, version = line:match "^(.+)%s+v([.%S]+)[%s:]"
- if name and version then
- installed_crates[name] = version
- end
- end
- return installed_crates
-end
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.check_outdated_primary_package(receipt, install_dir)
- return M.get_installed_primary_package_version(receipt, install_dir):map_catching(function(installed_version)
- ---@type CrateResponse
- local crate_response = client.fetch_crate(receipt.primary_source.package):get_or_throw()
- if installed_version ~= crate_response.crate.max_stable_version then
- return {
- name = receipt.primary_source.package,
- current_version = installed_version,
- latest_version = crate_response.crate.max_stable_version,
- }
- else
- error "Primary package is not outdated."
- end
- end)
-end
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.get_installed_primary_package_version(receipt, install_dir)
- return spawn.cargo({
- "install",
- "--list",
- "--root",
- ".",
- cwd = install_dir,
- }):map_catching(function(result)
- local installed_crates = M.parse_installed_crates(result.stdout)
- if vim.in_fast_event() then
- a.scheduler() -- needed because vim.fn.* call
- end
- local package = vim.fn.fnamemodify(receipt.primary_source.package, ":t")
- return Optional.of_nilable(installed_crates[package]):or_else_throw "Failed to find cargo package version."
- end)
-end
-
----@param install_dir string
-function M.env(install_dir)
- return {
- PATH = process.extend_path { path.concat { install_dir, "bin" } },
- }
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/composer/init.lua b/lua/nvim-lsp-installer/core/managers/composer/init.lua
deleted file mode 100644
index e2330c40..00000000
--- a/lua/nvim-lsp-installer/core/managers/composer/init.lua
+++ /dev/null
@@ -1,120 +0,0 @@
-local _ = require "nvim-lsp-installer.core.functional"
-local process = require "nvim-lsp-installer.core.process"
-local path = require "nvim-lsp-installer.core.path"
-local Result = require "nvim-lsp-installer.core.result"
-local spawn = require "nvim-lsp-installer.core.spawn"
-local Optional = require "nvim-lsp-installer.core.optional"
-local installer = require "nvim-lsp-installer.core.installer"
-
-local M = {}
-
----@param packages string[]
-local function with_receipt(packages)
- return function()
- local ctx = installer.context()
-
- ctx.receipt:with_primary_source(ctx.receipt.composer(packages[1]))
- for i = 2, #packages do
- ctx.receipt:with_secondary_source(ctx.receipt.composer(packages[i]))
- end
- end
-end
-
----@async
----@param packages string[] The composer packages to install. The first item in this list will be the recipient of the server version, should the user request a specific one.
-function M.packages(packages)
- return function()
- return M.require(packages).with_receipt()
- end
-end
-
----@async
----@param packages string[] The composer packages to install. The first item in this list will be the recipient of the server version, should the user request a specific one.
-function M.require(packages)
- local ctx = installer.context()
- local pkgs = _.list_copy(packages)
-
- if not ctx.fs:file_exists "composer.json" then
- ctx.spawn.composer { "init", "--no-interaction", "--stability=stable" }
- end
-
- ctx.requested_version:if_present(function(version)
- pkgs[1] = ("%s:%s"):format(pkgs[1], version)
- end)
-
- ctx.spawn.composer { "require", pkgs }
-
- return {
- with_receipt = with_receipt(packages),
- }
-end
-
----@async
-function M.install()
- local ctx = installer.context()
- ctx.spawn.composer {
- "install",
- "--no-interaction",
- "--no-dev",
- "--optimize-autoloader",
- "--classmap-authoritative",
- }
-end
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.check_outdated_primary_package(receipt, install_dir)
- if receipt.primary_source.type ~= "composer" then
- return Result.failure "Receipt does not have a primary source of type composer"
- end
- return spawn.composer({
- "outdated",
- "--no-interaction",
- "--format=json",
- cwd = install_dir,
- }):map_catching(function(result)
- local outdated_packages = vim.json.decode(result.stdout)
- local outdated_package = _.find_first(function(package)
- return package.name == receipt.primary_source.package
- end, outdated_packages.installed)
- return Optional.of_nilable(outdated_package)
- :map(function(package)
- if package.version ~= package.latest then
- return {
- name = package.name,
- current_version = package.version,
- latest_version = package.latest,
- }
- end
- end)
- :or_else_throw "Primary package is not outdated."
- end)
-end
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.get_installed_primary_package_version(receipt, install_dir)
- if receipt.primary_source.type ~= "composer" then
- return Result.failure "Receipt does not have a primary source of type composer"
- end
- return spawn.composer({
- "info",
- "--format=json",
- receipt.primary_source.package,
- cwd = install_dir,
- }):map_catching(function(result)
- local info = vim.json.decode(result.stdout)
- return info.versions[1]
- end)
-end
-
----@param install_dir string
-function M.env(install_dir)
- return {
- PATH = process.extend_path { path.concat { install_dir, "vendor", "bin" } },
- }
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/dotnet/init.lua b/lua/nvim-lsp-installer/core/managers/dotnet/init.lua
deleted file mode 100644
index 3c16a4ad..00000000
--- a/lua/nvim-lsp-installer/core/managers/dotnet/init.lua
+++ /dev/null
@@ -1,50 +0,0 @@
-local process = require "nvim-lsp-installer.core.process"
-local installer = require "nvim-lsp-installer.core.installer"
-
-local M = {}
-
----@param package string
-local function with_receipt(package)
- return function()
- local ctx = installer.context()
- ctx.receipt:with_primary_source(ctx.receipt.dotnet(package))
- end
-end
-
----@async
----@param package string
-function M.package(package)
- return function()
- return M.install(package).with_receipt()
- end
-end
-
----@async
----@param package string
-function M.install(package)
- local ctx = installer.context()
- ctx.spawn.dotnet {
- "tool",
- "update",
- "--tool-path",
- ".",
- ctx.requested_version
- :map(function(version)
- return { "--version", version }
- end)
- :or_else(vim.NIL),
- package,
- }
-
- return {
- with_receipt = with_receipt(package),
- }
-end
-
-function M.env(root_dir)
- return {
- PATH = process.extend_path { root_dir },
- }
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/gem/init.lua b/lua/nvim-lsp-installer/core/managers/gem/init.lua
deleted file mode 100644
index e70c89c7..00000000
--- a/lua/nvim-lsp-installer/core/managers/gem/init.lua
+++ /dev/null
@@ -1,145 +0,0 @@
-local _ = require "nvim-lsp-installer.core.functional"
-local process = require "nvim-lsp-installer.core.process"
-local path = require "nvim-lsp-installer.core.path"
-local Result = require "nvim-lsp-installer.core.result"
-local spawn = require "nvim-lsp-installer.core.spawn"
-local Optional = require "nvim-lsp-installer.core.optional"
-local installer = require "nvim-lsp-installer.core.installer"
-
-local M = {}
-
----@param packages string[]
-local function with_receipt(packages)
- return function()
- local ctx = installer.context()
- ctx.receipt:with_primary_source(ctx.receipt.gem(packages[1]))
- for i = 2, #packages do
- ctx.receipt:with_secondary_source(ctx.receipt.gem(packages[i]))
- end
- end
-end
-
----@async
----@param packages string[] @The Gem packages to install. The first item in this list will be the recipient of the server version, should the user request a specific one.
-function M.packages(packages)
- return function()
- return M.install(packages).with_receipt()
- end
-end
-
----@async
----@param packages string[] @The Gem packages to install. The first item in this list will be the recipient of the server version, should the user request a specific one.
-function M.install(packages)
- local ctx = installer.context()
- local pkgs = _.list_copy(packages or {})
-
- ctx.requested_version:if_present(function(version)
- pkgs[1] = ("%s:%s"):format(pkgs[1], version)
- end)
-
- ctx.spawn.gem {
- "install",
- "--no-user-install",
- "--install-dir=.",
- "--bindir=bin",
- "--no-document",
- pkgs,
- }
-
- return {
- with_receipt = with_receipt(packages),
- }
-end
-
----@alias GemOutdatedPackage {name:string, current_version: string, latest_version: string}
-
----Parses a string input like "package (0.1.0 < 0.2.0)" into its components
----@param outdated_gem string
----@return GemOutdatedPackage
-function M.parse_outdated_gem(outdated_gem)
- local package_name, version_expression = outdated_gem:match "^(.+) %((.+)%)"
- if not package_name or not version_expression then
- -- unparseable
- return nil
- end
- local current_version, latest_version = unpack(vim.split(version_expression, "<"))
-
- ---@type GemOutdatedPackage
- local outdated_package = {
- name = vim.trim(package_name),
- current_version = vim.trim(current_version),
- latest_version = vim.trim(latest_version),
- }
- return outdated_package
-end
-
----Parses the stdout of the `gem list` command into a table<package_name, version>
----@param output string
-function M.parse_gem_list_output(output)
- ---@type table<string, string>
- local gem_versions = {}
- for _, line in ipairs(vim.split(output, "\n")) do
- local gem_package, version = line:match "^(%S+) %((%S+)%)$"
- if gem_package and version then
- gem_versions[gem_package] = version
- end
- end
- return gem_versions
-end
-
-local function not_empty(s)
- return s ~= nil and s ~= ""
-end
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.check_outdated_primary_package(receipt, install_dir)
- if receipt.primary_source.type ~= "gem" then
- return Result.failure "Receipt does not have a primary source of type gem"
- end
- return spawn.gem({ "outdated", cwd = install_dir, env = M.env(install_dir) }):map_catching(function(result)
- ---@type string[]
- local lines = vim.split(result.stdout, "\n")
- local outdated_gems = vim.tbl_map(M.parse_outdated_gem, vim.tbl_filter(not_empty, lines))
-
- local outdated_gem = _.find_first(function(gem)
- return gem.name == receipt.primary_source.package and gem.current_version ~= gem.latest_version
- end, outdated_gems)
-
- return Optional.of_nilable(outdated_gem)
- :map(function(gem)
- return {
- name = receipt.primary_source.package,
- current_version = assert(gem.current_version),
- latest_version = assert(gem.latest_version),
- }
- end)
- :or_else_throw "Primary package is not outdated."
- end)
-end
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.get_installed_primary_package_version(receipt, install_dir)
- return spawn.gem({
- "list",
- cwd = install_dir,
- env = M.env(install_dir),
- }):map_catching(function(result)
- local gems = M.parse_gem_list_output(result.stdout)
- return Optional.of_nilable(gems[receipt.primary_source.package]):or_else_throw "Failed to find gem package version."
- end)
-end
-
----@param install_dir string
-function M.env(install_dir)
- return {
- GEM_HOME = install_dir,
- GEM_PATH = install_dir,
- PATH = process.extend_path { path.concat { install_dir, "bin" } },
- }
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/git/init.lua b/lua/nvim-lsp-installer/core/managers/git/init.lua
deleted file mode 100644
index 559703c9..00000000
--- a/lua/nvim-lsp-installer/core/managers/git/init.lua
+++ /dev/null
@@ -1,73 +0,0 @@
-local spawn = require "nvim-lsp-installer.core.spawn"
-local Result = require "nvim-lsp-installer.core.result"
-local installer = require "nvim-lsp-installer.core.installer"
-local _ = require "nvim-lsp-installer.core.functional"
-
-local M = {}
-
----@param repo string
-local function with_receipt(repo)
- return function()
- local ctx = installer.context()
- ctx.receipt:with_primary_source(ctx.receipt.git_remote(repo))
- end
-end
-
----@async
----@param opts {[1]: string, recursive: boolean, version: Optional|nil} @The first item in the table is the repository to clone.
-function M.clone(opts)
- local ctx = installer.context()
- local repo = assert(opts[1], "No git URL provided.")
- ctx.spawn.git {
- "clone",
- "--depth",
- "1",
- opts.recursive and "--recursive" or vim.NIL,
- repo,
- ".",
- }
- _.coalesce(opts.version, ctx.requested_version):if_present(function(version)
- ctx.spawn.git { "fetch", "--depth", "1", "origin", version }
- ctx.spawn.git { "checkout", "FETCH_HEAD" }
- end)
-
- return {
- with_receipt = with_receipt(repo),
- }
-end
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.check_outdated_git_clone(receipt, install_dir)
- if receipt.primary_source.type ~= "git" then
- return Result.failure "Receipt does not have a primary source of type git"
- end
- return spawn.git({ "fetch", "origin", "HEAD", cwd = install_dir }):map_catching(function()
- local result = spawn.git({ "rev-parse", "FETCH_HEAD", "HEAD", cwd = install_dir }):get_or_throw()
- local remote_head, local_head = unpack(vim.split(result.stdout, "\n"))
- if remote_head == local_head then
- error("Git clone is up to date.", 2)
- end
- return {
- name = receipt.primary_source.remote,
- current_version = assert(local_head),
- latest_version = assert(remote_head),
- }
- end)
-end
-
----@async
----@param install_dir string
-function M.get_installed_revision(install_dir)
- return spawn.git({
- "rev-parse",
- "--short",
- "HEAD",
- cwd = install_dir,
- }):map_catching(function(result)
- return assert(vim.trim(result.stdout))
- end)
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/github/client.lua b/lua/nvim-lsp-installer/core/managers/github/client.lua
deleted file mode 100644
index 530fee06..00000000
--- a/lua/nvim-lsp-installer/core/managers/github/client.lua
+++ /dev/null
@@ -1,120 +0,0 @@
-local _ = require "nvim-lsp-installer.core.functional"
-local log = require "nvim-lsp-installer.log"
-local fetch = require "nvim-lsp-installer.core.fetch"
-local spawn = require "nvim-lsp-installer.core.spawn"
-
-local M = {}
-
----@alias GitHubReleaseAsset {url: string, id: integer, name: string, browser_download_url: string, created_at: string, updated_at: string, size: integer, download_count: integer}
----@alias GitHubRelease {tag_name: string, prerelease: boolean, draft: boolean, assets:GitHubReleaseAsset[]}
----@alias GitHubTag {name: string}
-
----@param path string
----@return Result @JSON decoded response.
-local function api_call(path)
- return spawn.gh({ "api", path })
- :map(function(result)
- return result.stdout
- end)
- :recover_catching(function()
- return fetch(("https://api.github.com/%s"):format(path)):get_or_throw()
- end)
- :map_catching(vim.json.decode)
-end
-
----@async
----@param repo string @The GitHub repo ("username/repo").
----@return Result @of GitHubRelease[]
-function M.fetch_releases(repo)
- log.fmt_trace("Fetching GitHub releases for repo=%s", repo)
- local path = ("repos/%s/releases"):format(repo)
- return api_call(path):map_err(function()
- return ("Failed to fetch releases for GitHub repository %s."):format(repo)
- end)
-end
-
----@async
----@param repo string @The GitHub repo ("username/repo").
----@param tag_name string @The tag_name of the release to fetch.
-function M.fetch_release(repo, tag_name)
- log.fmt_trace("Fetching GitHub release for repo=%s, tag_name=%s", repo, tag_name)
- local path = ("repos/%s/releases/tags/%s"):format(repo, tag_name)
- return api_call(path):map_err(function()
- return ("Failed to fetch release %q for GitHub repository %s."):format(tag_name, repo)
- end)
-end
-
----@param opts {include_prerelease: boolean, tag_name_pattern: string}
-function M.release_predicate(opts)
- local is_not_draft = _.prop_eq("draft", false)
- local is_not_prerelease = _.prop_eq("prerelease", false)
- local tag_name_matches = _.prop_satisfies(_.matches(opts.tag_name_pattern), "tag_name")
-
- return _.all_pass {
- _.if_else(_.always(opts.include_prerelease), _.T, is_not_prerelease),
- _.if_else(_.always(opts.tag_name_pattern), tag_name_matches, _.T),
- is_not_draft,
- }
-end
-
----@alias FetchLatestGithubReleaseOpts {tag_name_pattern:string|nil, include_prerelease: boolean}
-
----@async
----@param repo string @The GitHub repo ("username/repo").
----@param opts FetchLatestGithubReleaseOpts|nil
----@return Result @of GitHubRelease
-function M.fetch_latest_release(repo, opts)
- opts = opts or {
- tag_name_pattern = nil,
- include_prerelease = false,
- }
- return M.fetch_releases(repo):map_catching(
- ---@param releases GitHubRelease[]
- function(releases)
- local is_stable_release = M.release_predicate(opts)
- ---@type GitHubRelease|nil
- local latest_release = _.find_first(is_stable_release, releases)
-
- if not latest_release then
- log.fmt_info("Failed to find latest release. repo=%s, opts=%s", repo, opts)
- error "Failed to find latest release."
- end
-
- log.fmt_debug("Resolved latest version repo=%s, tag_name=%s", repo, latest_release.tag_name)
- return latest_release
- end
- )
-end
-
----@async
----@param repo string @The GitHub repo ("username/repo").
----@return Result @of GitHubTag[]
-function M.fetch_tags(repo)
- local path = ("repos/%s/tags"):format(repo)
- return api_call(path):map_err(function()
- return ("Failed to fetch tags for GitHub repository %s."):format(repo)
- end)
-end
-
----@async
----@param repo string @The GitHub repo ("username/repo").
----@return Result @of GitHubTag
-function M.fetch_latest_tag(repo)
- return M.fetch_tags(repo):map_catching(function(tags)
- if vim.tbl_count(tags) == 0 then
- error "No tags found."
- end
- return tags[1]
- end)
-end
-
----@alias GitHubRateLimit {limit: integer, remaining: integer, reset: integer, used: integer}
----@alias GitHubRateLimitResponse {resources: { core: GitHubRateLimit }}
-
----@async
---@return Result @of GitHubRateLimitResponse
-function M.fetch_rate_limit()
- return api_call "rate_limit"
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/github/init.lua b/lua/nvim-lsp-installer/core/managers/github/init.lua
deleted file mode 100644
index f74fde76..00000000
--- a/lua/nvim-lsp-installer/core/managers/github/init.lua
+++ /dev/null
@@ -1,175 +0,0 @@
-local installer = require "nvim-lsp-installer.core.installer"
-local std = require "nvim-lsp-installer.core.managers.std"
-local client = require "nvim-lsp-installer.core.managers.github.client"
-local platform = require "nvim-lsp-installer.core.platform"
-local Result = require "nvim-lsp-installer.core.result"
-local _ = require "nvim-lsp-installer.core.functional"
-local settings = require "nvim-lsp-installer.settings"
-
-local M = {}
-
----@param repo string
----@param asset_file string
----@param release string
-local function with_release_file_receipt(repo, asset_file, release)
- return function()
- local ctx = installer.context()
- ctx.receipt:with_primary_source {
- type = "github_release_file",
- repo = repo,
- file = asset_file,
- release = release,
- }
- end
-end
-
----@param repo string
----@param tag string
-local function with_tag_receipt(repo, tag)
- return function()
- local ctx = installer.context()
- ctx.receipt:with_primary_source {
- type = "github_tag",
- repo = repo,
- tag = tag,
- }
- end
-end
-
----@async
----@param opts {repo: string, version: Optional|nil, asset_file: string|fun(release: string):string}
-function M.release_file(opts)
- local ctx = installer.context()
- local release = _.coalesce(opts.version, ctx.requested_version):or_else_get(function()
- return client.fetch_latest_release(opts.repo)
- :map(_.prop "tag_name")
- :get_or_throw "Failed to fetch latest release from GitHub API. Refer to :h nvim-lsp-installer-errors-github-api for more information."
- end)
- ---@type string
- local asset_file
- if type(opts.asset_file) == "function" then
- asset_file = opts.asset_file(release)
- else
- asset_file = opts.asset_file
- end
- if not asset_file then
- error(
- (
- "Could not find which release file to download.\nMost likely the current operating system, architecture (%s), or libc (%s) is not supported."
- ):format(platform.arch, platform.get_libc()),
- 0
- )
- end
- local download_url = settings.current.github.download_url_template:format(opts.repo, release, asset_file)
- return {
- release = release,
- download_url = download_url,
- asset_file = asset_file,
- with_receipt = with_release_file_receipt(opts.repo, download_url, release),
- }
-end
-
----@async
----@param opts {repo: string, version: Optional|nil}
-function M.tag(opts)
- local ctx = installer.context()
- local tag = _.coalesce(opts.version, ctx.requested_version):or_else_get(function()
- return client.fetch_latest_tag(opts.repo)
- :map(_.prop "name")
- :get_or_throw "Failed to fetch latest tag from GitHub API."
- end)
-
- return {
- tag = tag,
- with_receipt = with_tag_receipt(opts.repo, tag),
- }
-end
-
----@param filename string
----@param processor async fun()
-local function release_file_processor(filename, processor)
- ---@async
- ---@param opts {repo: string, asset_file: string|fun(release: string):string}
- return function(opts)
- local release_file_source = M.release_file(opts)
- std.download_file(release_file_source.download_url, filename)
- processor(release_file_source)
- return release_file_source
- end
-end
-
-M.unzip_release_file = release_file_processor("archive.zip", function()
- std.unzip("archive.zip", ".")
-end)
-
-M.untarxz_release_file = release_file_processor("archive.tar.xz", function()
- std.untarxz "archive.tar.xz"
-end)
-
-M.untargz_release_file = release_file_processor("archive.tar.gz", function()
- std.untar "archive.tar.gz"
-end)
-
----@async
----@param opts {repo: string, out_file:string, asset_file: string|fun(release: string):string}
-function M.download_release_file(opts)
- local release_file_source = M.release_file(opts)
- std.download_file(release_file_source.download_url, assert(opts.out_file, "out_file is required"))
- return release_file_source
-end
-
----@async
----@param opts {repo: string, out_file:string, asset_file: string|fun(release: string):string}
-function M.gunzip_release_file(opts)
- local release_file_source = M.release_file(opts)
- local gzipped_file = ("%s.gz"):format(assert(opts.out_file, "out_file must be specified"))
- std.download_file(release_file_source.download_url, gzipped_file)
- std.gunzip(gzipped_file)
- return release_file_source
-end
-
----@async
----@param receipt InstallReceipt
-function M.check_outdated_primary_package_release(receipt)
- local source = receipt.primary_source
- if source.type ~= "github_release" and source.type ~= "github_release_file" then
- return Result.failure "Receipt does not have a primary source of type (github_release|github_release_file)."
- end
- return client.fetch_latest_release(source.repo, { tag_name_pattern = source.tag_name_pattern }):map_catching(
- ---@param latest_release GitHubRelease
- function(latest_release)
- if source.release ~= latest_release.tag_name then
- return {
- name = source.repo,
- current_version = source.release,
- latest_version = latest_release.tag_name,
- }
- end
- error "Primary package is not outdated."
- end
- )
-end
-
----@async
----@param receipt InstallReceipt
-function M.check_outdated_primary_package_tag(receipt)
- local source = receipt.primary_source
- if source.type ~= "github_tag" then
- return Result.failure "Receipt does not have a primary source of type github_tag."
- end
- return client.fetch_latest_tag(source.repo):map_catching(
- ---@param latest_tag GitHubTag
- function(latest_tag)
- if source.tag ~= latest_tag.name then
- return {
- name = source.repo,
- current_version = source.tag,
- latest_version = latest_tag.name,
- }
- end
- error "Primary package is not outdated."
- end
- )
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/go/init.lua b/lua/nvim-lsp-installer/core/managers/go/init.lua
deleted file mode 100644
index 067cff58..00000000
--- a/lua/nvim-lsp-installer/core/managers/go/init.lua
+++ /dev/null
@@ -1,130 +0,0 @@
-local installer = require "nvim-lsp-installer.core.installer"
-local process = require "nvim-lsp-installer.core.process"
-local platform = require "nvim-lsp-installer.core.platform"
-local spawn = require "nvim-lsp-installer.core.spawn"
-local a = require "nvim-lsp-installer.core.async"
-local Optional = require "nvim-lsp-installer.core.optional"
-
-local M = {}
-
----@param packages string[]
-local function with_receipt(packages)
- return function()
- local ctx = installer.context()
- ctx.receipt:with_primary_source(ctx.receipt.go(packages[1]))
- -- Install secondary packages
- for i = 2, #packages do
- local package = packages[i]
- ctx.receipt:with_secondary_source(ctx.receipt.go(package))
- end
- end
-end
-
----@async
----@param packages string[] The Go packages to install. The first item in this list will be the recipient of the server version, should the user request a specific one.
-function M.packages(packages)
- return function()
- M.install(packages).with_receipt()
- end
-end
-
----@async
----@param packages string[] The Go packages to install. The first item in this list will be the recipient of the server version, should the user request a specific one.
-function M.install(packages)
- local ctx = installer.context()
- local env = {
- GOBIN = ctx.cwd:get(),
- }
- -- Install the head package
- do
- local head_package = packages[1]
- local version = ctx.requested_version:or_else "latest"
- ctx.spawn.go {
- "install",
- "-v",
- ("%s@%s"):format(head_package, version),
- env = env,
- }
- end
-
- -- Install secondary packages
- for i = 2, #packages do
- ctx.spawn.go { "install", "-v", ("%s@latest"):format(packages[i]), env = env }
- end
-
- return {
- with_receipt = with_receipt(packages),
- }
-end
-
----@param output string @The output from `go version -m` command.
-function M.parse_mod_version_output(output)
- ---@type {path: string[], mod: string[], dep: string[], build: string[]}
- local result = {}
- local lines = vim.split(output, "\n")
- for _, line in ipairs { unpack(lines, 2) } do
- local type, id, value = unpack(vim.split(line, "%s+", { trimempty = true }))
- if type and id then
- result[type] = result[type] or {}
- result[type][id] = value or ""
- end
- end
- return result
-end
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.get_installed_primary_package_version(receipt, install_dir)
- if vim.in_fast_event() then
- a.scheduler()
- end
- -- trims e.g. golang.org/x/tools/gopls to gopls
- local executable = vim.fn.fnamemodify(receipt.primary_source.package, ":t")
- return spawn.go({
- "version",
- "-m",
- platform.is_win and ("%s.exe"):format(executable) or executable,
- cwd = install_dir,
- }):map_catching(function(result)
- local parsed_output = M.parse_mod_version_output(result.stdout)
- return Optional.of_nilable(parsed_output.mod[receipt.primary_source.package]):or_else_throw "Failed to parse mod version"
- end)
-end
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.check_outdated_primary_package(receipt, install_dir)
- return spawn.go({
- "list",
- "-json",
- "-m",
- ("%s@latest"):format(receipt.primary_source.package),
- cwd = install_dir,
- }):map_catching(function(result)
- ---@type {Path: string, Version: string}
- local output = vim.json.decode(result.stdout)
- return Optional.of_nilable(output.Version)
- :map(function(latest_version)
- local installed_version = M.get_installed_primary_package_version(receipt, install_dir):get_or_throw()
- if installed_version ~= latest_version then
- return {
- name = receipt.primary_source.package,
- current_version = assert(installed_version),
- latest_version = assert(latest_version),
- }
- end
- end)
- :or_else_throw "Primary package is not outdated."
- end)
-end
-
----@param install_dir string
-function M.env(install_dir)
- return {
- PATH = process.extend_path { install_dir },
- }
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/luarocks/init.lua b/lua/nvim-lsp-installer/core/managers/luarocks/init.lua
deleted file mode 100644
index e25c92a2..00000000
--- a/lua/nvim-lsp-installer/core/managers/luarocks/init.lua
+++ /dev/null
@@ -1,130 +0,0 @@
-local installer = require "nvim-lsp-installer.core.installer"
-local _ = require "nvim-lsp-installer.core.functional"
-local process = require "nvim-lsp-installer.core.process"
-local path = require "nvim-lsp-installer.core.path"
-local Result = require "nvim-lsp-installer.core.result"
-local spawn = require "nvim-lsp-installer.core.spawn"
-local Optional = require "nvim-lsp-installer.core.optional"
-
-local M = {}
-
----@param package string
-local function with_receipt(package)
- return function()
- local ctx = installer.context()
- ctx.receipt:with_primary_source(ctx.receipt.luarocks(package))
- end
-end
-
----@param package string @The luarock package to install.
----@param opts {dev: boolean}|nil
-function M.package(package, opts)
- return function()
- return M.install(package, opts).with_receipt()
- end
-end
-
----@async
----@param package string @The luarock package to install.
----@param opts {dev: boolean}|nil
-function M.install(package, opts)
- opts = opts or {}
- local ctx = installer.context()
- ctx:promote_cwd()
- ctx.spawn.luarocks {
- "install",
- "--tree",
- ctx.cwd:get(),
- opts.dev and "--dev" or vim.NIL,
- package,
- ctx.requested_version:or_else(vim.NIL),
- }
- return {
- with_receipt = with_receipt(package),
- }
-end
-
----@alias InstalledLuarock {package: string, version: string, arch: string, nrepo: string, namespace: string}
-
----@type fun(output: string): InstalledLuarock[]
-M.parse_installed_rocks = _.compose(
- _.map(_.compose(
- -- https://github.com/luarocks/luarocks/blob/fbd3566a312e647cde57b5d774533731e1aa844d/src/luarocks/search.lua#L317
- _.zip_table { "package", "version", "arch", "nrepo", "namespace" },
- _.split "\t"
- )),
- _.split "\n"
-)
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.get_installed_primary_package_version(receipt, install_dir)
- if receipt.primary_source.type ~= "luarocks" then
- return Result.failure "Receipt does not have a primary source of type luarocks"
- end
- local primary_package = receipt.primary_source.package
- return spawn.luarocks({
- "list",
- "--tree",
- install_dir,
- "--porcelain",
- }):map_catching(function(result)
- local luarocks = M.parse_installed_rocks(result.stdout)
- return Optional.of_nilable(_.find_first(_.prop_eq("package", primary_package), luarocks))
- :map(_.prop "version")
- :or_else_throw()
- end)
-end
-
----@alias OutdatedLuarock {name: string, installed: string, available: string, repo: string}
-
----@type fun(output: string): OutdatedLuarock[]
-M.parse_outdated_rocks = _.compose(
- _.map(_.compose(
- -- https://github.com/luarocks/luarocks/blob/fbd3566a312e647cde57b5d774533731e1aa844d/src/luarocks/cmd/list.lua#L59
- _.zip_table { "name", "installed", "available", "repo" },
- _.split "\t"
- )),
- _.split "\n"
-)
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.check_outdated_primary_package(receipt, install_dir)
- if receipt.primary_source.type ~= "luarocks" then
- return Result.failure "Receipt does not have a primary source of type luarocks"
- end
- local primary_package = receipt.primary_source.package
- return spawn.luarocks({
- "list",
- "--outdated",
- "--tree",
- install_dir,
- "--porcelain",
- }):map_catching(function(result)
- local outdated_rocks = M.parse_outdated_rocks(result.stdout)
- return Optional.of_nilable(_.find_first(_.prop_eq("name", primary_package), outdated_rocks))
- :map(
- ---@param outdated_rock OutdatedLuarock
- function(outdated_rock)
- return {
- name = outdated_rock.name,
- current_version = assert(outdated_rock.installed),
- latest_version = assert(outdated_rock.available),
- }
- end
- )
- :or_else_throw()
- end)
-end
-
----@param install_dir string
-function M.env(install_dir)
- return {
- PATH = process.extend_path { path.concat { install_dir, "bin" } },
- }
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/npm/init.lua b/lua/nvim-lsp-installer/core/managers/npm/init.lua
deleted file mode 100644
index ba3d670e..00000000
--- a/lua/nvim-lsp-installer/core/managers/npm/init.lua
+++ /dev/null
@@ -1,132 +0,0 @@
-local functional = require "nvim-lsp-installer.core.functional"
-local spawn = require "nvim-lsp-installer.core.spawn"
-local Optional = require "nvim-lsp-installer.core.optional"
-local installer = require "nvim-lsp-installer.core.installer"
-local Result = require "nvim-lsp-installer.core.result"
-local process = require "nvim-lsp-installer.core.process"
-local path = require "nvim-lsp-installer.core.path"
-
-local list_copy = functional.list_copy
-
-local M = {}
-
----@async
----@param ctx InstallContext
-local function ensure_npm_root(ctx)
- if not (ctx.fs:dir_exists "node_modules" or ctx.fs:file_exists "package.json") then
- -- Create a package.json to set a boundary for where npm installs packages.
- ctx.spawn.npm { "init", "--yes", "--scope=lsp-installer" }
- end
-end
-
----@param packages string[]
-local function with_receipt(packages)
- return function()
- local ctx = installer.context()
- ctx.receipt:with_primary_source(ctx.receipt.npm(packages[1]))
- for i = 2, #packages do
- ctx.receipt:with_secondary_source(ctx.receipt.npm(packages[i]))
- end
- end
-end
-
----@async
----@param packages string[] @The npm packages to install. The first item in this list will be the recipient of the requested version, if set.
-function M.packages(packages)
- return function()
- return M.install(packages).with_receipt()
- end
-end
-
----@async
----@param packages string[] @The npm packages to install. The first item in this list will be the recipient of the requested version, if set.
-function M.install(packages)
- local ctx = installer.context()
- local pkgs = list_copy(packages)
- ctx.requested_version:if_present(function(version)
- pkgs[1] = ("%s@%s"):format(pkgs[1], version)
- end)
-
- -- Use global-style. The reasons for this are:
- -- a) To avoid polluting the executables (aka bin-links) that npm creates.
- -- b) The installation is, after all, more similar to a "global" installation. We don't really gain
- -- any of the benefits of not using global style (e.g., deduping the dependency tree).
- --
- -- We write to .npmrc manually instead of going through npm because managing a local .npmrc file
- -- is a bit unreliable across npm versions (especially <7), so we take extra measures to avoid
- -- inadvertently polluting global npm config.
- ctx.fs:append_file(".npmrc", "global-style=true")
-
- ensure_npm_root(ctx)
- ctx.spawn.npm { "install", pkgs }
-
- return {
- with_receipt = with_receipt(packages),
- }
-end
-
----@async
----@param exec_args string[] @The arguments to pass to npm exec.
-function M.exec(exec_args)
- local ctx = installer.context()
- ctx.spawn.npm { "exec", "--yes", "--", exec_args }
-end
-
----@async
----@param script string @The npm script to run.
-function M.run(script)
- local ctx = installer.context()
- ctx.spawn.npm { "run", script }
-end
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.get_installed_primary_package_version(receipt, install_dir)
- if receipt.primary_source.type ~= "npm" then
- return Result.failure "Receipt does not have a primary source of type npm"
- end
- return spawn.npm({ "ls", "--json", cwd = install_dir }):map_catching(function(result)
- local npm_packages = vim.json.decode(result.stdout)
- return npm_packages.dependencies[receipt.primary_source.package].version
- end)
-end
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.check_outdated_primary_package(receipt, install_dir)
- if receipt.primary_source.type ~= "npm" then
- return Result.failure "Receipt does not have a primary source of type npm"
- end
- local primary_package = receipt.primary_source.package
- local npm_outdated = spawn.npm { "outdated", "--json", primary_package, cwd = install_dir }
- if npm_outdated:is_success() then
- return Result.failure "Primary package is not outdated."
- end
- return npm_outdated:recover_catching(function(result)
- assert(result.exit_code == 1, "Expected npm outdated to return exit code 1.")
- local data = vim.json.decode(result.stdout)
-
- return Optional.of_nilable(data[primary_package])
- :map(function(outdated_package)
- if outdated_package.current ~= outdated_package.latest then
- return {
- name = primary_package,
- current_version = assert(outdated_package.current),
- latest_version = assert(outdated_package.latest),
- }
- end
- end)
- :or_else_throw()
- end)
-end
-
----@param install_dir string
-function M.env(install_dir)
- return {
- PATH = process.extend_path { path.concat { install_dir, "node_modules", ".bin" } },
- }
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/opam/init.lua b/lua/nvim-lsp-installer/core/managers/opam/init.lua
deleted file mode 100644
index a4e1163e..00000000
--- a/lua/nvim-lsp-installer/core/managers/opam/init.lua
+++ /dev/null
@@ -1,58 +0,0 @@
-local functional = require "nvim-lsp-installer.core.functional"
-local path = require "nvim-lsp-installer.core.path"
-local process = require "nvim-lsp-installer.core.process"
-local installer = require "nvim-lsp-installer.core.installer"
-
-local M = {}
-
-local list_copy = functional.list_copy
-
----@param packages string[]
-local function with_receipt(packages)
- return function()
- local ctx = installer.context()
- ctx.receipt:with_primary_source(ctx.receipt.opam(packages[1]))
- for i = 2, #packages do
- ctx.receipt:with_secondary_source(ctx.receipt.opam(packages[i]))
- end
- end
-end
-
----@async
----@param packages string[] @The opam packages to install. The first item in this list will be the recipient of the requested version, if set.
-function M.packages(packages)
- return function()
- return M.install(packages).with_receipt()
- end
-end
-
----@async
----@param packages string[] @The opam packages to install. The first item in this list will be the recipient of the requested version, if set.
-function M.install(packages)
- local ctx = installer.context()
- local pkgs = list_copy(packages)
-
- ctx.requested_version:if_present(function(version)
- pkgs[1] = ("%s.%s"):format(pkgs[1], version)
- end)
-
- ctx.spawn.opam {
- "install",
- "--destdir=.",
- "--yes",
- "--verbose",
- pkgs,
- }
-
- return {
- with_receipt = with_receipt(packages),
- }
-end
-
-function M.env(root_dir)
- return {
- PATH = process.extend_path { path.concat { root_dir, "bin" } },
- }
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/pip3/init.lua b/lua/nvim-lsp-installer/core/managers/pip3/init.lua
deleted file mode 100644
index 3b617745..00000000
--- a/lua/nvim-lsp-installer/core/managers/pip3/init.lua
+++ /dev/null
@@ -1,160 +0,0 @@
-local _ = require "nvim-lsp-installer.core.functional"
-local settings = require "nvim-lsp-installer.settings"
-local process = require "nvim-lsp-installer.core.process"
-local path = require "nvim-lsp-installer.core.path"
-local platform = require "nvim-lsp-installer.core.platform"
-local Optional = require "nvim-lsp-installer.core.optional"
-local installer = require "nvim-lsp-installer.core.installer"
-local Result = require "nvim-lsp-installer.core.result"
-local spawn = require "nvim-lsp-installer.core.spawn"
-
-local VENV_DIR = "venv"
-
-local M = {}
-
----@param packages string[]
-local function with_receipt(packages)
- return function()
- local ctx = installer.context()
- ctx.receipt:with_primary_source(ctx.receipt.pip3(packages[1]))
- for i = 2, #packages do
- ctx.receipt:with_secondary_source(ctx.receipt.pip3(packages[i]))
- end
- end
-end
-
----@async
----@param packages string[] @The pip packages to install. The first item in this list will be the recipient of the requested version, if set.
-function M.packages(packages)
- return function()
- return M.install(packages).with_receipt()
- end
-end
-
----@async
----@param packages string[] @The pip packages to install. The first item in this list will be the recipient of the requested version, if set.
-function M.install(packages)
- local ctx = installer.context()
- local pkgs = _.list_copy(packages)
-
- ctx.requested_version:if_present(function(version)
- pkgs[1] = ("%s==%s"):format(pkgs[1], version)
- end)
-
- local executables = platform.is_win and _.list_not_nil(vim.g.python3_host_prog, "python", "python3")
- or _.list_not_nil(vim.g.python3_host_prog, "python3", "python")
-
- -- pip3 will hardcode the full path to venv executables, so we need to promote cwd to make sure pip uses the final destination path.
- ctx:promote_cwd()
-
- -- Find first executable that manages to create venv
- local executable = _.find_first(function(executable)
- return pcall(ctx.spawn[executable], { "-m", "venv", VENV_DIR })
- end, executables)
-
- Optional.of_nilable(executable)
- :if_present(function()
- ctx.spawn.python {
- "-m",
- "pip",
- "install",
- "-U",
- settings.current.pip.install_args,
- pkgs,
- with_paths = { M.venv_path(ctx.cwd:get()) },
- }
- end)
- :or_else_throw "Unable to create python3 venv environment."
-
- return {
- with_receipt = with_receipt(packages),
- }
-end
-
----@param package string
----@return string
-function M.normalize_package(package)
- -- https://stackoverflow.com/a/60307740
- local s = package:gsub("%[.*%]", "")
- return s
-end
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.check_outdated_primary_package(receipt, install_dir)
- if receipt.primary_source.type ~= "pip3" then
- return Result.failure "Receipt does not have a primary source of type pip3"
- end
- local normalized_package = M.normalize_package(receipt.primary_source.package)
- return spawn.python({
- "-m",
- "pip",
- "list",
- "--outdated",
- "--format=json",
- cwd = install_dir,
- with_paths = { M.venv_path(install_dir) },
- }):map_catching(function(result)
- ---@alias PipOutdatedPackage {name: string, version: string, latest_version: string}
- ---@type PipOutdatedPackage[]
- local packages = vim.json.decode(result.stdout)
-
- local outdated_primary_package = _.find_first(function(outdated_package)
- return outdated_package.name == normalized_package
- and outdated_package.version ~= outdated_package.latest_version
- end, packages)
-
- return Optional.of_nilable(outdated_primary_package)
- :map(function(package)
- return {
- name = normalized_package,
- current_version = assert(package.version),
- latest_version = assert(package.latest_version),
- }
- end)
- :or_else_throw "Primary package is not outdated."
- end)
-end
-
----@async
----@param receipt InstallReceipt
----@param install_dir string
-function M.get_installed_primary_package_version(receipt, install_dir)
- if receipt.primary_source.type ~= "pip3" then
- return Result.failure "Receipt does not have a primary source of type pip3"
- end
- return spawn.python({
- "-m",
- "pip",
- "list",
- "--format=json",
- cwd = install_dir,
- with_paths = { M.venv_path(install_dir) },
- }):map_catching(function(result)
- local pip_packages = vim.json.decode(result.stdout)
- local normalized_pip_package = M.normalize_package(receipt.primary_source.package)
- local pip_package = _.find_first(function(package)
- return package.name == normalized_pip_package
- end, pip_packages)
- return Optional.of_nilable(pip_package)
- :map(function(package)
- return package.version
- end)
- :or_else_throw "Unable to find pip package."
- end)
-end
-
----@param install_dir string
-function M.env(install_dir)
- return {
- PATH = process.extend_path { M.venv_path(install_dir) },
- }
-end
-
----@param install_dir string
-function M.venv_path(install_dir)
- return path.concat { install_dir, VENV_DIR, platform.is_win and "Scripts" or "bin" }
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/powershell/init.lua b/lua/nvim-lsp-installer/core/managers/powershell/init.lua
deleted file mode 100644
index 8e94820a..00000000
--- a/lua/nvim-lsp-installer/core/managers/powershell/init.lua
+++ /dev/null
@@ -1,46 +0,0 @@
-local spawn = require "nvim-lsp-installer.core.spawn"
-local process = require "nvim-lsp-installer.core.process"
-
-local M = {}
-
-local PWSHOPT = {
- progress_preference = [[ $ProgressPreference = 'SilentlyContinue'; ]], -- https://stackoverflow.com/a/63301751
- security_protocol = [[ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; ]],
-}
-
----@param script string
----@param opts JobSpawnOpts
----@param custom_spawn JobSpawn
-function M.script(script, opts, custom_spawn)
- opts = opts or {}
- ---@type JobSpawn
- local spawner = custom_spawn or spawn
- return spawner.powershell(vim.tbl_extend("keep", {
- "-NoProfile",
- on_spawn = function(_, stdio)
- local stdin = stdio[1]
- stdin:write(PWSHOPT.progress_preference)
- stdin:write(PWSHOPT.security_protocol)
- stdin:write(script)
- stdin:close()
- end,
- env_raw = process.graft_env(opts.env or {}, { "PSMODULEPATH" }),
- }, opts))
-end
-
----@param command string
----@param opts JobSpawnOpts
----@param custom_spawn JobSpawn
-function M.command(command, opts, custom_spawn)
- opts = opts or {}
- ---@type JobSpawn
- local spawner = custom_spawn or spawn
- return spawner.powershell(vim.tbl_extend("keep", {
- "-NoProfile",
- "-Command",
- PWSHOPT.progress_preference .. PWSHOPT.security_protocol .. command,
- env_raw = process.graft_env(opts.env or {}, { "PSMODULEPATH" }),
- }, opts))
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/managers/std/init.lua b/lua/nvim-lsp-installer/core/managers/std/init.lua
deleted file mode 100644
index e46a1045..00000000
--- a/lua/nvim-lsp-installer/core/managers/std/init.lua
+++ /dev/null
@@ -1,187 +0,0 @@
-local a = require "nvim-lsp-installer.core.async"
-local installer = require "nvim-lsp-installer.core.installer"
-local fetch = require "nvim-lsp-installer.core.fetch"
-local platform = require "nvim-lsp-installer.core.platform"
-local powershell = require "nvim-lsp-installer.core.managers.powershell"
-local path = require "nvim-lsp-installer.core.path"
-local Result = require "nvim-lsp-installer.core.result"
-
-local M = {}
-
-local function with_system_executable_receipt(executable)
- return function()
- local ctx = installer.context()
- ctx.receipt:with_primary_source(ctx.receipt.system(executable))
- end
-end
-
----@async
----@param executable string
----@param opts {help_url:string|nil}
-function M.system_executable(executable, opts)
- return function()
- M.ensure_executable(executable, opts).with_receipt()
- end
-end
-
----@async
----@param executable string
----@param opts {help_url:string|nil}
-function M.ensure_executable(executable, opts)
- local ctx = installer.context()
- opts = opts or {}
- if vim.in_fast_event() then
- a.scheduler()
- end
- if vim.fn.executable(executable) ~= 1 then
- ctx.stdio_sink.stderr(("%s was not found in path.\n"):format(executable))
- if opts.help_url then
- ctx.stdio_sink.stderr(("See %s for installation instructions.\n"):format(opts.help_url))
- end
- error("Installation failed: system executable was not found.", 0)
- end
-
- return {
- with_receipt = with_system_executable_receipt(executable),
- }
-end
-
----@async
----@param url string
----@param out_file string
-function M.download_file(url, out_file)
- local ctx = installer.context()
- ctx.stdio_sink.stdout(("Downloading file %q...\n"):format(url))
- fetch(url, {
- out_file = path.concat { ctx.cwd:get(), out_file },
- })
- :map_err(function(err)
- return ("Failed to download file %q.\n%s"):format(url, err)
- end)
- :get_or_throw()
-end
-
----@async
----@param file string
----@param dest string
-function M.unzip(file, dest)
- local ctx = installer.context()
- platform.when {
- unix = function()
- ctx.spawn.unzip { "-d", dest, file }
- end,
- win = function()
- powershell.command(
- ("Microsoft.PowerShell.Archive\\Expand-Archive -Path %q -DestinationPath %q"):format(file, dest),
- {},
- ctx.spawn
- )
- end,
- }
- pcall(function()
- -- make sure the .zip archive doesn't linger
- ctx.fs:unlink(file)
- end)
-end
-
----@param file string
-local function win_extract(file)
- local ctx = installer.context()
- Result.run_catching(function()
- ctx.spawn["7z"] { "x", "-y", "-r", file }
- end)
- :recover_catching(function()
- ctx.spawn.peazip { "-ext2here", path.concat { ctx.cwd:get(), file } } -- peazip requires absolute paths
- end)
- :recover_catching(function()
- ctx.spawn.wzunzip { file }
- end)
- :recover_catching(function()
- ctx.spawn.winrar { "e", file }
- end)
- :get_or_throw(("Unable to unpack %s."):format(file))
-end
-
----@async
----@param file string
----@param opts {strip_components:integer}
-function M.untar(file, opts)
- opts = opts or {}
- local ctx = installer.context()
- ctx.spawn.tar {
- opts.strip_components and { "--strip-components", opts.strip_components } or vim.NIL,
- "-xvf",
- file,
- }
- pcall(function()
- ctx.fs:unlink(file)
- end)
-end
-
----@async
----@param file string
----@param opts {strip_components:integer}
-function M.untarxz(file, opts)
- opts = opts or {}
- local ctx = installer.context()
- platform.when {
- unix = function()
- M.untar(file, opts)
- end,
- win = function()
- Result.run_catching(function()
- win_extract(file) -- unpack .tar.xz to .tar
- local uncompressed_tar = file:gsub(".xz$", "")
- M.untar(uncompressed_tar, opts)
- end):recover(function()
- ctx.spawn.arc {
- "unarchive",
- opts.strip_components and { "--strip-components", opts.strip_components } or vim.NIL,
- file,
- }
- pcall(function()
- ctx.fs:unlink(file)
- end)
- end)
- end,
- }
-end
-
----@async
----@param file string
-function M.gunzip(file)
- platform.when {
- unix = function()
- local ctx = installer.context()
- ctx.spawn.gzip { "-d", file }
- end,
- win = function()
- win_extract(file)
- end,
- }
-end
-
----@async
----@param flags string @The chmod flag to apply.
----@param files string[] @A list of relative paths to apply the chmod on.
-function M.chmod(flags, files)
- if platform.is_unix then
- local ctx = installer.context()
- ctx.spawn.chmod { flags, files }
- end
-end
-
----@async
----Wrapper around vim.ui.select.
----@param items table
----@params opts
-function M.select(items, opts)
- assert(not platform.is_headless, "Tried to prompt for user input while in headless mode.")
- if vim.in_fast_event() then
- a.scheduler()
- end
- local async_select = a.promisify(vim.ui.select)
- return async_select(items, opts)
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/optional.lua b/lua/nvim-lsp-installer/core/optional.lua
deleted file mode 100644
index 10af8ccb..00000000
--- a/lua/nvim-lsp-installer/core/optional.lua
+++ /dev/null
@@ -1,100 +0,0 @@
----@class Optional<T>
----@field private _value unknown
-local Optional = {}
-Optional.__index = Optional
-
----@param value any
-function Optional.new(value)
- return setmetatable({ _value = value }, Optional)
-end
-
-local EMPTY = Optional.new(nil)
-
----@param value any
-function Optional.of_nilable(value)
- if value == nil then
- return EMPTY
- else
- return Optional.new(value)
- end
-end
-
-function Optional.empty()
- return EMPTY
-end
-
----@param value any
-function Optional.of(value)
- return Optional.new(value)
-end
-
----@param mapper_fn fun(value: any): any
-function Optional:map(mapper_fn)
- if self:is_present() then
- return Optional.of_nilable(mapper_fn(self._value))
- else
- return EMPTY
- end
-end
-
-function Optional:get()
- if not self:is_present() then
- error("No value present.", 2)
- end
- return self._value
-end
-
----@param value any
-function Optional:or_else(value)
- if self:is_present() then
- return self._value
- else
- return value
- end
-end
-
----@param supplier fun(): any
-function Optional:or_else_get(supplier)
- if self:is_present() then
- return self._value
- else
- return supplier()
- end
-end
-
----@param supplier fun(): Optional
----@return Optional
-function Optional:or_(supplier)
- if self:is_present() then
- return self
- else
- return supplier()
- end
-end
-
----@param exception any @(optional) The exception to throw if the result is a failure.
-function Optional:or_else_throw(exception)
- if self:is_present() then
- return self._value
- else
- if exception then
- error(exception, 2)
- else
- error("No value present.", 2)
- end
- end
-end
-
----@param fn fun(value: any)
-function Optional:if_present(fn)
- if self:is_present() then
- fn(self._value)
- end
- return self
-end
-
-function Optional:is_present()
- return self._value ~= nil
-end
-
-return Optional
diff --git a/lua/nvim-lsp-installer/core/path.lua b/lua/nvim-lsp-installer/core/path.lua
deleted file mode 100644
index 2f0fd84b..00000000
--- a/lua/nvim-lsp-installer/core/path.lua
+++ /dev/null
@@ -1,36 +0,0 @@
-local uv = vim.loop
-
-local sep = (function()
- ---@diagnostic disable-next-line: undefined-global
- if jit then
- ---@diagnostic disable-next-line: undefined-global
- local os = string.lower(jit.os)
- if os == "linux" or os == "osx" or os == "bsd" then
- return "/"
- else
- return "\\"
- end
- else
- return package.config:sub(1, 1)
- end
-end)()
-
-local M = {}
-
-function M.cwd()
- return uv.fs_realpath "."
-end
-
----@param path_components string[]
----@return string
-function M.concat(path_components)
- return table.concat(path_components, sep)
-end
-
----@path root_path string
----@path path string
-function M.is_subdirectory(root_path, path)
- return root_path == path or path:sub(1, #root_path + 1) == root_path .. sep
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/platform.lua b/lua/nvim-lsp-installer/core/platform.lua
deleted file mode 100644
index 10427a63..00000000
--- a/lua/nvim-lsp-installer/core/platform.lua
+++ /dev/null
@@ -1,154 +0,0 @@
-local _ = require "nvim-lsp-installer.core.functional"
-local Result = require "nvim-lsp-installer.core.result"
-local M = {}
-
-local uname = vim.loop.os_uname()
-
----@alias Platform
----| '"win"'
----| '"unix"'
----| '"linux"'
----| '"mac"'
-
-local arch_aliases = {
- ["x86_64"] = "x64",
- ["i386"] = "x86",
- ["i686"] = "x86", -- x86 compat
- ["aarch64"] = "arm64",
- ["aarch64_be"] = "arm64",
- ["armv8b"] = "arm64", -- arm64 compat
- ["armv8l"] = "arm64", -- arm64 compat
-}
-
-M.arch = arch_aliases[uname.machine] or uname.machine
-
-M.is_win = vim.fn.has "win32" == 1
-M.is_unix = vim.fn.has "unix" == 1
-M.is_mac = vim.fn.has "mac" == 1
-M.is_linux = not M.is_mac and M.is_unix
-
--- PATH separator
-M.path_sep = M.is_win and ";" or ":"
-
-M.is_headless = #vim.api.nvim_list_uis() == 0
-
----@generic T
----@param platform_table table<Platform, T>
----@return T
-local function get_by_platform(platform_table)
- if M.is_mac then
- return platform_table.mac or platform_table.unix
- elseif M.is_linux then
- return platform_table.linux or platform_table.unix
- elseif M.is_unix then
- return platform_table.unix
- elseif M.is_win then
- return platform_table.win
- else
- return nil
- end
-end
-
-function M.when(cases)
- local case = get_by_platform(cases)
- if case then
- return case()
- else
- error "Current platform is not supported."
- end
-end
-
----@type async fun(): table
-M.os_distribution = _.lazy(function()
- ---Parses the provided contents of an /etc/\*-release file and identifies the Linux distribution.
- ---@param contents string @The contents of a /etc/\*-release file.
- ---@return table<string, any>
- local function parse_linux_dist(contents)
- local lines = vim.split(contents, "\n")
-
- local entries = {}
-
- for i = 1, #lines do
- local line = lines[i]
- local index = line:find "="
- if index then
- local key = line:sub(1, index - 1)
- local value = line:sub(index + 1)
- entries[key] = value
- end
- end
-
- if entries.ID == "ubuntu" then
- -- Parses the Ubuntu OS VERSION_ID into their version components, e.g. "18.04" -> {major=18, minor=04}
- local version_id = entries.VERSION_ID:gsub([["]], "")
- local version_parts = vim.split(version_id, "%.")
- local major = tonumber(version_parts[1])
- local minor = tonumber(version_parts[2])
-
- return {
- id = "ubuntu",
- version_id = version_id,
- version = { major = major, minor = minor },
- }
- else
- return {
- id = "linux-generic",
- }
- end
- end
-
- return M.when {
- linux = function()
- local spawn = require "nvim-lsp-installer.core.spawn"
- return spawn.bash({ "-c", "cat /etc/*-release" })
- :map_catching(function(result)
- return parse_linux_dist(result.stdout)
- end)
- :recover(function()
- return { id = "linux-generic" }
- end)
- :get_or_throw()
- end,
- mac = function()
- return Result.success { id = "macOS" }
- end,
- win = function()
- return Result.success { id = "windows" }
- end,
- }
-end)
-
----@type async fun() Result @of String
-M.get_homebrew_prefix = _.lazy(function()
- assert(M.is_mac, "Can only locate Homebrew installation on Mac systems.")
- local spawn = require "nvim-lsp-installer.core.spawn"
- return spawn.brew({ "--prefix" })
- :map_catching(function(result)
- return vim.trim(result.stdout)
- end)
- :map_err(function()
- return "Failed to locate Homebrew installation."
- end)
-end)
-
--- @return string @The libc found on the system, musl or glibc (glibc if ldd is not found)
-M.get_libc = _.lazy(function()
- local _, _, libc_exit_code = os.execute "ldd --version 2>&1 | grep -q musl"
- if libc_exit_code == 0 then
- return "musl"
- else
- return "glibc"
- end
-end)
-
-M.is = setmetatable({}, {
- __index = function(_, key)
- local platform, arch = unpack(vim.split(key, "_", { plain = true }))
- if arch and M.arch ~= arch then
- return false
- end
- return M["is_" .. platform] == true
- end,
-})
-
-return M
diff --git a/lua/nvim-lsp-installer/core/process.lua b/lua/nvim-lsp-installer/core/process.lua
deleted file mode 100644
index 9841c11c..00000000
--- a/lua/nvim-lsp-installer/core/process.lua
+++ /dev/null
@@ -1,314 +0,0 @@
-local log = require "nvim-lsp-installer.log"
-local _ = require "nvim-lsp-installer.core.functional"
-local platform = require "nvim-lsp-installer.core.platform"
-local uv = vim.loop
-
----@alias luv_pipe any
----@alias luv_handle any
-
----@class StdioSink
----@field stdout fun(chunk: string)
----@field stderr fun(chunk: string)
-
-local M = {}
-
----@param pipe luv_pipe
----@param sink fun(chunk: string)
-local function connect_sink(pipe, sink)
- ---@param err string | nil
- ---@param data string | nil
- return function(err, data)
- if err then
- log.error("Unexpected error when reading pipe.", err)
- end
- if data ~= nil then
- sink(data)
- else
- pipe:read_stop()
- pipe:close()
- end
- end
-end
-
--- We gather the root env immediately, primarily because of E5560.
--- Also, there's no particular reason we need to refresh the environment (yet).
-local initial_environ = vim.fn.environ()
-
----@param new_paths string[] @A list of paths to prepend the existing PATH with.
-function M.extend_path(new_paths)
- local new_path_str = table.concat(new_paths, platform.path_sep)
- return ("%s%s%s"):format(new_path_str, platform.path_sep, initial_environ.PATH or "")
-end
-
----Merges the provided env param with the user's full environent. Provided env has precedence.
----@param env table<string, string>
----@param excluded_var_names string[]|nil
-function M.graft_env(env, excluded_var_names)
- local excluded_var_names_set = excluded_var_names and _.set_of(excluded_var_names) or {}
- local merged_env = {}
- for key, val in pairs(initial_environ) do
- if not excluded_var_names_set[key] and env[key] == nil then
- merged_env[#merged_env + 1] = key .. "=" .. val
- end
- end
- for key, val in pairs(env) do
- if not excluded_var_names_set[key] then
- merged_env[#merged_env + 1] = key .. "=" .. val
- end
- end
- return merged_env
-end
-
----@param env_list string[]
-local function sanitize_env_list(env_list)
- local sanitized_list = {}
- for __, env in ipairs(env_list) do
- local safe_envs = {
- "GO111MODULE",
- "GOBIN",
- "GOPATH",
- "PATH",
- "GEM_HOME",
- "GEM_PATH",
- }
- local is_safe_env = _.any(function(safe_env)
- return env:find(safe_env .. "=") == 1
- end, safe_envs)
- if is_safe_env then
- sanitized_list[#sanitized_list + 1] = env
- else
- local idx = env:find "="
- sanitized_list[#sanitized_list + 1] = env:sub(1, idx) .. "<redacted>"
- end
- end
- return sanitized_list
-end
-
----@alias JobSpawnCallback fun(success: boolean, exit_code: integer)
-
----@class JobSpawnOpts
----@field env string[] @List of "key=value" string.
----@field args string[]
----@field cwd string
----@field stdio_sink StdioSink
-
----@param cmd string @The command/executable.
----@param opts JobSpawnOpts
----@param callback JobSpawnCallback
----@return luv_handle,luv_pipe[]|nil @Returns the job handle and the stdio array on success, otherwise returns nil.
-function M.spawn(cmd, opts, callback)
- local stdin = uv.new_pipe(false)
- local stdout = uv.new_pipe(false)
- local stderr = uv.new_pipe(false)
-
- local stdio = { stdin, stdout, stderr }
-
- local spawn_opts = {
- env = opts.env,
- stdio = stdio,
- args = opts.args,
- cwd = opts.cwd,
- detached = false,
- hide = true,
- }
-
- log.lazy_debug(function()
- local sanitized_env = opts.env and sanitize_env_list(opts.env) or nil
- return "Spawning cmd=%s, spawn_opts=%s",
- cmd,
- {
- args = opts.args,
- cwd = opts.cwd,
- env = sanitized_env,
- }
- end)
-
- local handle, pid_or_err
- handle, pid_or_err = uv.spawn(cmd, spawn_opts, function(exit_code, signal)
- local successful = exit_code == 0 and signal == 0
- handle:close()
- if not stdin:is_closing() then
- stdin:close()
- end
-
- -- ensure all pipes are closed, for I am a qualified plumber
- local check = uv.new_check()
- check:start(function()
- for i = 1, #stdio do
- local pipe = stdio[i]
- if not pipe:is_closing() then
- return
- end
- end
- check:stop()
- callback(successful, exit_code)
- end)
-
- log.fmt_debug("Job pid=%s exited with exit_code=%s, signal=%s", pid_or_err, exit_code, signal)
- end)
-
- if handle == nil then
- log.fmt_error("Failed to spawn process. cmd=%s, err=%s", cmd, pid_or_err)
- if type(pid_or_err) == "string" and pid_or_err:find "ENOENT" == 1 then
- opts.stdio_sink.stderr(("Could not find executable %q in path.\n"):format(cmd))
- else
- opts.stdio_sink.stderr(("Failed to spawn process cmd=%s err=%s\n"):format(cmd, pid_or_err))
- end
- callback(false)
- return nil, nil
- end
-
- log.debug("Spawned with pid", pid_or_err)
-
- stdout:read_start(connect_sink(stdout, opts.stdio_sink.stdout))
- stderr:read_start(connect_sink(stderr, opts.stdio_sink.stderr))
-
- return handle, stdio
-end
-
----@param opts JobSpawnOpts @The job spawn opts to apply in every job in this "chain".
-function M.chain(opts)
- local jobs = {}
- return {
- ---@param cmd string
- ---@param args string[]
- run = function(cmd, args)
- jobs[#jobs + 1] = M.lazy_spawn(
- cmd,
- vim.tbl_deep_extend("force", opts, {
- args = args,
- })
- )
- end,
- ---@param callback JobSpawnCallback
- spawn = function(callback)
- local function execute(idx)
- local ok, err = pcall(jobs[idx], function(successful)
- if successful and jobs[idx + 1] then
- -- iterate
- execute(idx + 1)
- else
- -- we done
- callback(successful)
- end
- end)
- if not ok then
- log.fmt_error("Chained job failed to execute. Error=%s", tostring(err))
- callback(false)
- end
- end
-
- execute(1)
- end,
- }
-end
-
-function M.empty_sink()
- local function noop() end
- return {
- stdout = noop,
- stderr = noop,
- }
-end
-
-function M.simple_sink()
- return {
- stdout = vim.schedule_wrap(vim.api.nvim_out_write),
- stderr = vim.schedule_wrap(vim.api.nvim_err_write),
- }
-end
-
-function M.in_memory_sink()
- local buffers = { stdout = {}, stderr = {} }
- return {
- buffers = buffers,
- sink = {
- stdout = function(chunk)
- buffers.stdout[#buffers.stdout + 1] = chunk
- end,
- stderr = function(chunk)
- buffers.stderr[#buffers.stderr + 1] = chunk
- end,
- },
- }
-end
-
---- This probably belongs elsewhere ¯\_(ツ)_/¯
----@generic T
----@param debounced_fn fun(arg1: T)
----@return fun(arg1: T)
-function M.debounced(debounced_fn)
- local queued = false
- local last_arg = nil
- return function(a)
- last_arg = a
- if queued then
- return
- end
- queued = true
- vim.schedule(function()
- debounced_fn(last_arg)
- queued = false
- last_arg = nil
- end)
- end
-end
-
----@alias LazyJob fun(callback: JobSpawnCallback)
-
----@param cmd string
----@param opts JobSpawnOpts
-function M.lazy_spawn(cmd, opts)
- ---@param callback JobSpawnCallback
- return function(callback)
- return M.spawn(cmd, opts, callback)
- end
-end
-
----@class JobAttemptOpts
----@field jobs LazyJob[]
----@field on_finish JobSpawnCallback
----@field on_iterate fun()
-
----@param opts JobAttemptOpts
-function M.attempt(opts)
- local jobs, on_finish, on_iterate = opts.jobs, opts.on_finish, opts.on_iterate
- if #jobs == 0 then
- error "process.attempt(...) needs at least one job."
- end
-
- local spawn, on_job_exit
-
- on_job_exit = function(cur_idx, success)
- if success then
- -- this job succeeded. exit early
- on_finish(true)
- elseif jobs[cur_idx + 1] then
- -- iterate
- if on_iterate then
- on_iterate()
- end
- log.debug "Previous job failed, attempting next."
- spawn(cur_idx + 1)
- else
- -- we exhausted all jobs without success
- log.debug "All jobs failed."
- on_finish(false)
- end
- end
-
- spawn = function(idx)
- local ok, err = pcall(jobs[idx], function(success)
- on_job_exit(idx, success)
- end)
- if not ok then
- log.fmt_error("Job failed to execute. Error=%s", tostring(err))
- on_job_exit(idx, false)
- on_finish(false)
- end
- end
-
- spawn(1)
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/receipt.lua b/lua/nvim-lsp-installer/core/receipt.lua
deleted file mode 100644
index 608673a6..00000000
--- a/lua/nvim-lsp-installer/core/receipt.lua
+++ /dev/null
@@ -1,170 +0,0 @@
-local M = {}
-
----@alias InstallReceiptSchemaVersion
----| '"1.0"'
----| '"1.0a"'
-
----@alias InstallReceiptSourceType
----| '"npm"'
----| '"pip3"'
----| '"gem"'
----| '"go"'
----| '"cargo"'
----| '"opam"'
----| '"dotnet"'
----| '"r_package"'
----| '"unmanaged"'
----| '"system"'
----| '"jdtls"'
----| '"git"'
----| '"github_tag"'
----| '"github_release"'
----| '"github_release_file"'
-
----@alias InstallReceiptSource {type: InstallReceiptSourceType}
-
----@class InstallReceiptBuilder
----@field public is_marked_invalid boolean Whether this instance of the builder has been marked as invalid. This is an exception that only apply to a few select servers whose installation is not yet compatible with the receipt schema due to having a too complicated installation structure.
----@field private secondary_sources InstallReceiptSource[]
----@field private epoch_time number
-local InstallReceiptBuilder = {}
-InstallReceiptBuilder.__index = InstallReceiptBuilder
-
-function InstallReceiptBuilder.new()
- return setmetatable({
- is_marked_invalid = false,
- secondary_sources = {},
- }, InstallReceiptBuilder)
-end
-
-function InstallReceiptBuilder:mark_invalid()
- self.is_marked_invalid = true
- return self
-end
-
----@param name string
-function InstallReceiptBuilder:with_name(name)
- self.name = name
- return self
-end
-
----@param version InstallReceiptSchemaVersion
-function InstallReceiptBuilder:with_schema_version(version)
- self.schema_version = version
- return self
-end
-
----@param source InstallReceiptSource
-function InstallReceiptBuilder:with_primary_source(source)
- self.primary_source = source
- return self
-end
-
----@param source InstallReceiptSource
-function InstallReceiptBuilder:with_secondary_source(source)
- table.insert(self.secondary_sources, source)
- return self
-end
-
----@param seconds integer
----@param microseconds integer
-local function to_ms(seconds, microseconds)
- return (seconds * 1000) + math.floor(microseconds / 1000)
-end
-
----vim.loop.gettimeofday()
----@param seconds integer
----@param microseconds integer
-function InstallReceiptBuilder:with_completion_time(seconds, microseconds)
- self.completion_time = to_ms(seconds, microseconds)
- return self
-end
-
----vim.loop.gettimeofday()
----@param seconds integer
----@param microseconds integer
-function InstallReceiptBuilder:with_start_time(seconds, microseconds)
- self.start_time = to_ms(seconds, microseconds)
- return self
-end
-
-function InstallReceiptBuilder:build()
- assert(self.name, "name is required")
- assert(self.schema_version, "schema_version is required")
- assert(self.start_time, "start_time is required")
- assert(self.completion_time, "completion_time is required")
- assert(self.primary_source, "primary_source is required")
- return {
- name = self.name,
- schema_version = self.schema_version,
- metrics = {
- start_time = self.start_time,
- completion_time = self.completion_time,
- },
- primary_source = self.primary_source,
- secondary_sources = self.secondary_sources,
- }
-end
-
----@param type InstallReceiptSourceType
-local function package_source(type)
- ---@param package string
- return function(package)
- return { type = type, package = package }
- end
-end
-
-InstallReceiptBuilder.npm = package_source "npm"
-InstallReceiptBuilder.pip3 = package_source "pip3"
-InstallReceiptBuilder.gem = package_source "gem"
-InstallReceiptBuilder.go = package_source "go"
-InstallReceiptBuilder.dotnet = package_source "dotnet"
-InstallReceiptBuilder.cargo = package_source "cargo"
-InstallReceiptBuilder.composer = package_source "composer"
-InstallReceiptBuilder.r_package = package_source "r_package"
-InstallReceiptBuilder.opam = package_source "opam"
-InstallReceiptBuilder.luarocks = package_source "luarocks"
-
-InstallReceiptBuilder.unmanaged = { type = "unmanaged" }
-
----@param repo string
----@param release string
-function InstallReceiptBuilder.github_release(repo, release)
- return {
- type = "github_release",
- repo = repo,
- release = release,
- }
-end
-
----@param dependency string
-function InstallReceiptBuilder.system(dependency)
- return { type = "system", dependency = dependency }
-end
-
----@param remote_url string
-function InstallReceiptBuilder.git_remote(remote_url)
- return { type = "git", remote = remote_url }
-end
-
----@class InstallReceipt
----@field public name string
----@field public schema_version InstallReceiptSchemaVersion
----@field public metrics {start_time:integer, completion_time:integer}
----@field public primary_source InstallReceiptSource
----@field public secondary_sources InstallReceiptSource[]
-local InstallReceipt = {}
-InstallReceipt.__index = InstallReceipt
-
-function InstallReceipt.new(props)
- return setmetatable(props, InstallReceipt)
-end
-
-function InstallReceipt.from_json(json)
- return InstallReceipt.new(json)
-end
-
-M.InstallReceiptBuilder = InstallReceiptBuilder
-M.InstallReceipt = InstallReceipt
-
-return M
diff --git a/lua/nvim-lsp-installer/core/result.lua b/lua/nvim-lsp-installer/core/result.lua
deleted file mode 100644
index 132e2758..00000000
--- a/lua/nvim-lsp-installer/core/result.lua
+++ /dev/null
@@ -1,152 +0,0 @@
----@class Failure
----@field error any
-local Failure = {}
-Failure.__index = Failure
-
-function Failure.new(error)
- return setmetatable({ error = error }, Failure)
-end
-
----@class Result
----@field value any
-local Result = {}
-Result.__index = Result
-
-function Result.new(value)
- return setmetatable({
- value = value,
- }, Result)
-end
-
-function Result.success(value)
- return Result.new(value)
-end
-
-function Result.failure(error)
- return Result.new(Failure.new(error))
-end
-
-function Result:get_or_nil()
- if self:is_success() then
- return self.value
- end
-end
-
-function Result:get_or_else(value)
- if self:is_success() then
- return self.value
- else
- return value
- end
-end
-
----@param exception any @(optional) The exception to throw if the result is a failure.
-function Result:get_or_throw(exception)
- if self:is_success() then
- return self.value
- else
- if exception ~= nil then
- error(exception, 2)
- else
- error(self.value.error, 2)
- end
- end
-end
-
-function Result:err_or_nil()
- if self:is_failure() then
- return self.value.error
- end
-end
-
-function Result:is_failure()
- return getmetatable(self.value) == Failure
-end
-
-function Result:is_success()
- return getmetatable(self.value) ~= Failure
-end
-
----@param mapper_fn fun(value: any): any
-function Result:map(mapper_fn)
- if self:is_success() then
- return Result.success(mapper_fn(self.value))
- else
- return self
- end
-end
-
----@param mapper_fn fun(value: any): any
-function Result:map_err(mapper_fn)
- if self:is_failure() then
- return Result.failure(mapper_fn(self.value.error))
- else
- return self
- end
-end
-
----@param mapper_fn fun(value: any): any
-function Result:map_catching(mapper_fn)
- if self:is_success() then
- local ok, result = pcall(mapper_fn, self.value)
- if ok then
- return Result.success(result)
- else
- return Result.failure(result)
- end
- else
- return self
- end
-end
-
----@param recover_fn fun(value: any): any
-function Result:recover(recover_fn)
- if self:is_failure() then
- return Result.success(recover_fn(self:err_or_nil()))
- else
- return self
- end
-end
-
----@param recover_fn fun(value: any): any
-function Result:recover_catching(recover_fn)
- if self:is_failure() then
- local ok, value = pcall(recover_fn, self:err_or_nil())
- if ok then
- return Result.success(value)
- else
- return Result.failure(value)
- end
- else
- return self
- end
-end
-
----@param fn fun(value: any): any
-function Result:on_failure(fn)
- if self:is_failure() then
- fn(self.value.error)
- end
- return self
-end
-
----@param fn fun(value: any): any
-function Result:on_success(fn)
- if self:is_success() then
- fn(self.value)
- end
- return self
-end
-
----@param fn fun(): any
----@return Result
-function Result.run_catching(fn)
- local ok, result = pcall(fn)
- if ok then
- return Result.success(result)
- else
- return Result.failure(result)
- end
-end
-
-return Result
diff --git a/lua/nvim-lsp-installer/core/spawn.lua b/lua/nvim-lsp-installer/core/spawn.lua
deleted file mode 100644
index 13e05a5b..00000000
--- a/lua/nvim-lsp-installer/core/spawn.lua
+++ /dev/null
@@ -1,121 +0,0 @@
-local a = require "nvim-lsp-installer.core.async"
-local Result = require "nvim-lsp-installer.core.result"
-local process = require "nvim-lsp-installer.core.process"
-local platform = require "nvim-lsp-installer.core.platform"
-local functional = require "nvim-lsp-installer.core.functional"
-local log = require "nvim-lsp-installer.log"
-
----@alias JobSpawn table<string, async fun(opts: JobSpawnOpts): Result>
----@type JobSpawn
-local spawn = {
- _aliases = {
- npm = platform.is_win and "npm.cmd" or "npm",
- gem = platform.is_win and "gem.cmd" or "gem",
- composer = platform.is_win and "composer.bat" or "composer",
- gradlew = platform.is_win and "gradlew.bat" or "gradlew",
- -- for hererocks installations
- luarocks = (platform.is_win and vim.fn.executable "luarocks.bat" == 1) and "luarocks.bat" or "luarocks",
- },
-}
-
-local function Failure(err, cmd)
- return Result.failure(setmetatable(err, {
- __tostring = function()
- if err.exit_code ~= nil then
- return ("spawn: %s failed with exit code %d. %s"):format(cmd, err.exit_code, err.stderr or "")
- else
- return ("spawn: %s failed with no exit code. %s"):format(cmd, err.stderr or "")
- end
- end,
- }))
-end
-
-local function parse_args(args, dest)
- for _, arg in ipairs(args) do
- if type(arg) == "table" then
- parse_args(arg, dest)
- elseif arg ~= vim.NIL then
- dest[#dest + 1] = arg
- end
- end
- return dest
-end
-
-local is_executable = functional.memoize(function(cmd)
- if vim.in_fast_event() then
- a.scheduler()
- end
- return vim.fn.executable(cmd) == 1
-end, functional.identity)
-
----@class SpawnArgs
----@field with_paths string[] @Optional. Paths to add to the PATH environment variable.
----@field env table<string, string> @Optional. Example { SOME_ENV = "value", SOME_OTHER_ENV = "some_value" }
----@field env_raw string[] @Optional. Example: { "SOME_ENV=value", "SOME_OTHER_ENV=some_value" }
----@field stdio_sink StdioSink @Optional. If provided, will be used to write to stdout and stderr.
----@field cwd string @Optional
----@field on_spawn fun(handle: luv_handle, stdio: luv_pipe[]) @Optional. Will be called when the process successfully spawns.
----@field check_executable boolean @Optional. Whether to check if the provided command is executable (defaults to true).
-
-setmetatable(spawn, {
- ---@param normalized_cmd string
- __index = function(self, normalized_cmd)
- ---@param args SpawnArgs
- return function(args)
- local cmd_args = {}
- parse_args(args, cmd_args)
-
- local env = args.env
-
- if args.with_paths then
- env = env or {}
- env.PATH = process.extend_path(args.with_paths)
- end
-
- ---@type JobSpawnOpts
- local spawn_args = {
- stdio_sink = args.stdio_sink,
- cwd = args.cwd,
- env = env and process.graft_env(env) or args.env_raw,
- args = cmd_args,
- }
-
- local stdio
- if not spawn_args.stdio_sink then
- stdio = process.in_memory_sink()
- spawn_args.stdio_sink = stdio.sink
- end
-
- local cmd = self._aliases[normalized_cmd] or normalized_cmd
-
- if (env and env.PATH) == nil and args.check_executable ~= false and not is_executable(cmd) then
- log.fmt_debug("%s is not executable", cmd)
- return Failure({
- stderr = ("%s is not executable"):format(cmd),
- }, cmd)
- end
-
- local _, exit_code = a.wait(function(resolve)
- local handle, stdio = process.spawn(cmd, spawn_args, resolve)
- if args.on_spawn and handle and stdio then
- args.on_spawn(handle, stdio)
- end
- end)
-
- if exit_code == 0 then
- return Result.success {
- stdout = stdio and table.concat(stdio.buffers.stdout, "") or nil,
- stderr = stdio and table.concat(stdio.buffers.stderr, "") or nil,
- }
- else
- return Failure({
- exit_code = exit_code,
- stdout = stdio and table.concat(stdio.buffers.stdout, "") or nil,
- stderr = stdio and table.concat(stdio.buffers.stderr, "") or nil,
- }, cmd)
- end
- end
- end,
-})
-
-return spawn
diff --git a/lua/nvim-lsp-installer/core/ui/display.lua b/lua/nvim-lsp-installer/core/ui/display.lua
deleted file mode 100644
index 5e4618f5..00000000
--- a/lua/nvim-lsp-installer/core/ui/display.lua
+++ /dev/null
@@ -1,458 +0,0 @@
-local log = require "nvim-lsp-installer.log"
-local process = require "nvim-lsp-installer.core.process"
-local state = require "nvim-lsp-installer.core.ui.state"
-
-local M = {}
-
----@param line string
----@param render_context RenderContext
-local function get_styles(line, render_context)
- local indentation = 0
-
- for i = 1, #render_context.applied_block_styles do
- local styles = render_context.applied_block_styles[i]
- for j = 1, #styles do
- local style = styles[j]
- if style == "INDENT" then
- indentation = indentation + 2
- elseif style == "CENTERED" then
- local padding = math.floor((render_context.viewport_context.win_width - #line) / 2)
- indentation = math.max(0, padding) -- CENTERED overrides any already applied indentation
- end
- end
- end
-
- return {
- indentation = indentation,
- }
-end
-
----@param viewport_context ViewportContext
----@param node INode
----@param _render_context RenderContext|nil
----@param _output RenderOutput|nil
-local function render_node(viewport_context, node, _render_context, _output)
- ---@class RenderContext
- ---@field viewport_context ViewportContext
- ---@field applied_block_styles CascadingStyle[]
- local render_context = _render_context
- or {
- viewport_context = viewport_context,
- applied_block_styles = {},
- }
- ---@class RenderHighlight
- ---@field hl_group string
- ---@field line number
- ---@field col_start number
- ---@field col_end number
-
- ---@class RenderKeybind
- ---@field line number
- ---@field key string
- ---@field effect string
- ---@field payload any
-
- ---@class RenderDiagnostic
- ---@field line number
- ---@field diagnostic {message: string, severity: integer, source: string|nil}
-
- ---@class RenderOutput
- ---@field lines string[] @The buffer lines.
- ---@field virt_texts string[][] @List of (text, highlight) tuples.
- ---@field highlights RenderHighlight[]
- ---@field keybinds RenderKeybind[]
- ---@field diagnostics RenderDiagnostic[]
- local output = _output
- or {
- lines = {},
- virt_texts = {},
- highlights = {},
- keybinds = {},
- diagnostics = {},
- }
-
- if node.type == "VIRTUAL_TEXT" then
- output.virt_texts[#output.virt_texts + 1] = {
- line = #output.lines - 1,
- content = node.virt_text,
- }
- elseif node.type == "HL_TEXT" then
- for i = 1, #node.lines do
- local line = node.lines[i]
- local line_highlights = {}
- local full_line = ""
- for j = 1, #line do
- local span = line[j]
- local content, hl_group = span[1], span[2]
- local col_start = #full_line
- full_line = full_line .. content
- if hl_group ~= "" then
- line_highlights[#line_highlights + 1] = {
- hl_group = hl_group,
- line = #output.lines,
- col_start = col_start,
- col_end = col_start + #content,
- }
- end
- end
-
- local active_styles = get_styles(full_line, render_context)
-
- -- apply indentation
- full_line = (" "):rep(active_styles.indentation) .. full_line
- for j = 1, #line_highlights do
- local highlight = line_highlights[j]
- highlight.col_start = highlight.col_start + active_styles.indentation
- highlight.col_end = highlight.col_end + active_styles.indentation
- output.highlights[#output.highlights + 1] = highlight
- end
-
- output.lines[#output.lines + 1] = full_line
- end
- elseif node.type == "NODE" or node.type == "CASCADING_STYLE" then
- if node.type == "CASCADING_STYLE" then
- render_context.applied_block_styles[#render_context.applied_block_styles + 1] = node.styles
- end
- for i = 1, #node.children do
- render_node(viewport_context, node.children[i], render_context, output)
- end
- if node.type == "CASCADING_STYLE" then
- render_context.applied_block_styles[#render_context.applied_block_styles] = nil
- end
- elseif node.type == "KEYBIND_HANDLER" then
- output.keybinds[#output.keybinds + 1] = {
- line = node.is_global and -1 or #output.lines,
- key = node.key,
- effect = node.effect,
- payload = node.payload,
- }
- elseif node.type == "DIAGNOSTICS" then
- output.diagnostics[#output.diagnostics + 1] = {
- line = #output.lines,
- message = node.diagnostic.message,
- severity = node.diagnostic.severity,
- source = node.diagnostic.source,
- }
- end
-
- return output
-end
-
--- exported for tests
-M._render_node = render_node
-
----@param opts DisplayOpenOpts
----@param sizes_only boolean @Whether to only return properties that control the window size.
-local function create_popup_window_opts(opts, sizes_only)
- local win_height = vim.o.lines - vim.o.cmdheight - 2 -- Add margin for status and buffer line
- local win_width = vim.o.columns
- local height = math.floor(win_height * 0.9)
- local width = math.floor(win_width * 0.8)
- local popup_layout = {
- height = height,
- width = width,
- row = math.floor((win_height - height) / 2),
- col = math.floor((win_width - width) / 2),
- relative = "editor",
- style = "minimal",
- zindex = 1,
- }
-
- if not sizes_only then
- popup_layout.border = opts.border
- end
-
- return popup_layout
-end
-
-function M.new_view_only_win(name)
- local namespace = vim.api.nvim_create_namespace(("lsp_installer_%s"):format(name))
- local bufnr, renderer, mutate_state, get_state, unsubscribe, win_id, window_mgmt_augroup, autoclose_augroup, registered_keymaps, registered_keybinds, registered_effect_handlers
- local has_initiated = false
-
- local function delete_win_buf()
- -- We queue the win_buf to be deleted in a schedule call, otherwise when used with folke/which-key (and
- -- set timeoutlen=0) we run into a weird segfault.
- -- It should probably be unnecessary once https://github.com/neovim/neovim/issues/15548 is resolved
- vim.schedule(function()
- if win_id and vim.api.nvim_win_is_valid(win_id) then
- log.trace "Deleting window"
- vim.api.nvim_win_close(win_id, true)
- end
- if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
- log.trace "Deleting buffer"
- vim.api.nvim_buf_delete(bufnr, { force = true })
- end
- end)
- end
-
- ---@param line number
- ---@param key string
- local function call_effect_handler(line, key)
- local line_keybinds = registered_keybinds[line]
- if line_keybinds then
- local keybind = line_keybinds[key]
- if keybind then
- local effect_handler = registered_effect_handlers[keybind.effect]
- if effect_handler then
- log.fmt_trace("Calling handler for effect %s on line %d for key %s", keybind.effect, line, key)
- effect_handler { payload = keybind.payload }
- return true
- end
- end
- end
- return false
- end
-
- local function dispatch_effect(key)
- local line = vim.api.nvim_win_get_cursor(0)[1]
- log.fmt_trace("Dispatching effect on line %d, key %s, bufnr %s", line, key, bufnr)
- call_effect_handler(line, key) -- line keybinds
- call_effect_handler(-1, key) -- global keybinds
- end
-
- local draw = function(view)
- local win_valid = win_id ~= nil and vim.api.nvim_win_is_valid(win_id)
- local buf_valid = bufnr ~= nil and vim.api.nvim_buf_is_valid(bufnr)
- log.fmt_trace("got bufnr=%s", bufnr)
- log.fmt_trace("got win_id=%s", win_id)
-
- if not win_valid or not buf_valid then
- -- the window has been closed or the buffer is somehow no longer valid
- unsubscribe(true)
- log.trace("Buffer or window is no longer valid", win_id, bufnr)
- return
- end
-
- local win_width = vim.api.nvim_win_get_width(win_id)
- ---@class ViewportContext
- local viewport_context = {
- win_width = win_width,
- }
- local output = render_node(viewport_context, view)
- local lines, virt_texts, highlights, keybinds, diagnostics =
- output.lines, output.virt_texts, output.highlights, output.keybinds, output.diagnostics
-
- -- set line contents
- vim.api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1)
- vim.api.nvim_buf_set_option(bufnr, "modifiable", true)
- vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
- vim.api.nvim_buf_set_option(bufnr, "modifiable", false)
-
- -- set virtual texts
- for i = 1, #virt_texts do
- local virt_text = virt_texts[i]
- vim.api.nvim_buf_set_extmark(bufnr, namespace, virt_text.line, 0, {
- virt_text = virt_text.content,
- })
- end
-
- -- set diagnostics
- vim.diagnostic.set(
- namespace,
- bufnr,
- vim.tbl_map(function(diagnostic)
- return {
- lnum = diagnostic.line - 1,
- col = 0,
- message = diagnostic.message,
- severity = diagnostic.severity,
- source = diagnostic.source,
- }
- end, diagnostics),
- {
- signs = false,
- }
- )
-
- -- set highlights
- for i = 1, #highlights do
- local highlight = highlights[i]
- vim.api.nvim_buf_add_highlight(
- bufnr,
- namespace,
- highlight.hl_group,
- highlight.line,
- highlight.col_start,
- highlight.col_end
- )
- end
-
- -- set keybinds
- registered_keybinds = {}
- for i = 1, #keybinds do
- local keybind = keybinds[i]
- if not registered_keybinds[keybind.line] then
- registered_keybinds[keybind.line] = {}
- end
- registered_keybinds[keybind.line][keybind.key] = keybind
- if not registered_keymaps[keybind.key] then
- registered_keymaps[keybind.key] = true
- vim.keymap.set("n", keybind.key, function()
- dispatch_effect(keybind.key)
- end, {
- buffer = bufnr,
- nowait = true,
- silent = true,
- })
- end
- end
- end
-
- ---@param opts DisplayOpenOpts
- local function open(opts)
- opts = opts or {}
- local highlight_groups = opts.highlight_groups
- bufnr = vim.api.nvim_create_buf(false, true)
- win_id = vim.api.nvim_open_win(bufnr, true, create_popup_window_opts(opts, false))
-
- registered_effect_handlers = opts.effects
- registered_keybinds = {}
- registered_keymaps = {}
-
- local buf_opts = {
- modifiable = false,
- swapfile = false,
- textwidth = 0,
- buftype = "nofile",
- bufhidden = "wipe",
- buflisted = false,
- filetype = "lsp-installer",
- undolevels = -1,
- }
-
- local win_opts = {
- number = false,
- relativenumber = false,
- wrap = false,
- spell = false,
- foldenable = false,
- signcolumn = "no",
- colorcolumn = "",
- cursorline = true,
- }
-
- -- window options
- for key, value in pairs(win_opts) do
- vim.api.nvim_win_set_option(win_id, key, value)
- end
-
- -- buffer options
- for key, value in pairs(buf_opts) do
- vim.api.nvim_buf_set_option(bufnr, key, value)
- end
-
- vim.cmd [[ syntax clear ]]
-
- window_mgmt_augroup = vim.api.nvim_create_augroup("LspInstallerWindowMgmt", {})
- autoclose_augroup = vim.api.nvim_create_augroup("LspInstallerWindow", {})
-
- vim.api.nvim_create_autocmd({ "VimResized" }, {
- group = window_mgmt_augroup,
- buffer = bufnr,
- callback = function()
- if vim.api.nvim_win_is_valid(win_id) then
- draw(renderer(get_state()))
- vim.api.nvim_win_set_config(win_id, create_popup_window_opts(opts, true))
- end
- end,
- })
-
- vim.api.nvim_create_autocmd({ "BufHidden", "BufUnload" }, {
- group = autoclose_augroup,
- buffer = bufnr,
- callback = function()
- -- Schedule is done because otherwise the window wont actually close in some cases (for example if
- -- you're loading another buffer into it)
- vim.schedule(function()
- if vim.api.nvim_win_is_valid(win_id) then
- vim.api.nvim_win_close(win_id, true)
- end
- end)
- end,
- })
-
- local win_enter_aucmd
- win_enter_aucmd = vim.api.nvim_create_autocmd({ "WinEnter" }, {
- group = autoclose_augroup,
- pattern = "*",
- callback = function()
- local buftype = vim.api.nvim_buf_get_option(0, "buftype")
- -- This allows us to keep the floating window open for things like diagnostic popups, UI inputs á la dressing.nvim, etc.
- if buftype ~= "prompt" and buftype ~= "nofile" then
- delete_win_buf()
- vim.api.nvim_del_autocmd(win_enter_aucmd)
- end
- end,
- })
-
- if highlight_groups then
- for i = 1, #highlight_groups do
- -- TODO use vim.api.nvim_set_hl()
- vim.cmd(highlight_groups[i])
- end
- end
-
- return win_id
- end
-
- return {
- ---@param _renderer fun(state: table): table
- view = function(_renderer)
- renderer = _renderer
- end,
- ---@generic T : table
- ---@param initial_state T
- ---@return fun(mutate_fn: fun(current_state: T)), fun(): T
- init = function(initial_state)
- assert(renderer ~= nil, "No view function has been registered. Call .view() before .init().")
- has_initiated = true
-
- mutate_state, get_state, unsubscribe = state.create_state_container(
- initial_state,
- process.debounced(function(new_state)
- draw(renderer(new_state))
- end)
- )
-
- -- we don't need to subscribe to state changes until the window is actually opened
- unsubscribe(true)
-
- return mutate_state, get_state
- end,
- ---@alias DisplayOpenOpts {effects: table<string, fun()>, highlight_groups: string[]|nil, border: string|table}
- ---@type fun(opts: DisplayOpenOpts)
- open = vim.schedule_wrap(function(opts)
- log.trace "Opening window"
- assert(has_initiated, "Display has not been initiated, cannot open.")
- if win_id and vim.api.nvim_win_is_valid(win_id) then
- -- window is already open
- return
- end
- unsubscribe(false)
- open(opts)
- draw(renderer(get_state()))
- end),
- ---@type fun()
- close = vim.schedule_wrap(function()
- assert(has_initiated, "Display has not been initiated, cannot close.")
- unsubscribe(true)
- log.fmt_trace("Closing window win_id=%s, bufnr=%s", win_id, bufnr)
- delete_win_buf()
- vim.api.nvim_del_augroup_by_id(window_mgmt_augroup)
- vim.api.nvim_del_augroup_by_id(autoclose_augroup)
- end),
- ---@param pos number[] @(row, col) tuple
- set_cursor = function(pos)
- assert(win_id ~= nil, "Window has not been opened, cannot set cursor.")
- return vim.api.nvim_win_set_cursor(win_id, pos)
- end,
- ---@return number[] @(row, col) tuple
- get_cursor = function()
- assert(win_id ~= nil, "Window has not been opened, cannot get cursor.")
- return vim.api.nvim_win_get_cursor(win_id)
- end,
- }
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/ui/init.lua b/lua/nvim-lsp-installer/core/ui/init.lua
deleted file mode 100644
index 5b855949..00000000
--- a/lua/nvim-lsp-installer/core/ui/init.lua
+++ /dev/null
@@ -1,141 +0,0 @@
-local _ = require "nvim-lsp-installer.core.functional"
-local M = {}
-
----@alias NodeType
----| '"NODE"'
----| '"CASCADING_STYLE"'
----| '"VIRTUAL_TEXT"'
----| '"DIAGNOSTICS"'
----| '"HL_TEXT"'
----| '"KEYBIND_HANDLER"'
-
----@alias INode Node | HlTextNode | CascadingStyleNode | VirtualTextNode | KeybindHandlerNode | DiagnosticsNode
-
----@param children INode[]
-function M.Node(children)
- ---@class Node
- local node = {
- type = "NODE",
- children = children,
- }
- return node
-end
-
----@param lines_with_span_tuples string[][]|string[]
-function M.HlTextNode(lines_with_span_tuples)
- if type(lines_with_span_tuples[1]) == "string" then
- -- this enables a convenience API for just rendering a single line (with just a single span)
- lines_with_span_tuples = { { lines_with_span_tuples } }
- end
- ---@class HlTextNode
- local node = {
- type = "HL_TEXT",
- lines = lines_with_span_tuples,
- }
- return node
-end
-
-local create_unhighlighted_lines = _.map(function(line)
- return { { line, "" } }
-end)
-
----@param lines string[]
-function M.Text(lines)
- return M.HlTextNode(create_unhighlighted_lines(lines))
-end
-
----@alias CascadingStyle
----| '"INDENT"'
----| '"CENTERED"'
-
----@param styles CascadingStyle[]
----@param children INode[]
-function M.CascadingStyleNode(styles, children)
- ---@class CascadingStyleNode
- local node = {
- type = "CASCADING_STYLE",
- styles = styles,
- children = children,
- }
- return node
-end
-
----@param virt_text string[][] @List of (text, highlight) tuples.
-function M.VirtualTextNode(virt_text)
- ---@class VirtualTextNode
- local node = {
- type = "VIRTUAL_TEXT",
- virt_text = virt_text,
- }
- return node
-end
-
----@param diagnostic {message: string, severity: integer, source: string|nil}
-function M.DiagnosticsNode(diagnostic)
- ---@class DiagnosticsNode
- local node = {
- type = "DIAGNOSTICS",
- diagnostic = diagnostic,
- }
- return node
-end
-
----@param condition boolean
----@param node INode | fun(): INode
----@param default_val any
-function M.When(condition, node, default_val)
- if condition then
- if type(node) == "function" then
- return node()
- else
- return node
- end
- end
- return default_val or M.Node {}
-end
-
----@param key string @The keymap to register to. Example: "<CR>".
----@param effect string @The effect to call when keymap is triggered by the user.
----@param payload any @The payload to pass to the effect handler when triggered.
----@param is_global boolean @Whether to register the keybind to apply on all lines in the buffer.
-function M.Keybind(key, effect, payload, is_global)
- ---@class KeybindHandlerNode
- local node = {
- type = "KEYBIND_HANDLER",
- key = key,
- effect = effect,
- payload = payload,
- is_global = is_global or false,
- }
- return node
-end
-
-function M.EmptyLine()
- return M.Text { "" }
-end
-
----@param rows string[][][] @A list of rows to include in the table. Each row consists of an array of (text, highlight) tuples (aka spans).
-function M.Table(rows)
- local col_maxwidth = {}
- for i = 1, #rows do
- local row = rows[i]
- for j = 1, #row do
- local col = row[j]
- local content = col[1]
- col_maxwidth[j] = math.max(vim.api.nvim_strwidth(content), col_maxwidth[j] or 0)
- end
- end
-
- for i = 1, #rows do
- local row = rows[i]
- for j = 1, #row do
- local col = row[j]
- local content = col[1]
- col[1] = content .. string.rep(" ", col_maxwidth[j] - vim.api.nvim_strwidth(content) + 1) -- +1 for default minimum padding
- end
- end
-
- return M.HlTextNode(rows)
-end
-
-return M
diff --git a/lua/nvim-lsp-installer/core/ui/state.lua b/lua/nvim-lsp-installer/core/ui/state.lua
deleted file mode 100644
index 9d7bcdda..00000000
--- a/lua/nvim-lsp-installer/core/ui/state.lua
+++ /dev/null
@@ -1,24 +0,0 @@
-local M = {}
-
----@generic T : table
----@param initial_state T
----@param subscriber fun(state: T)
-function M.create_state_container(initial_state, subscriber)
- -- we do deepcopy to make sure instances of state containers doesn't mutate the initial state
- local state = vim.deepcopy(initial_state)
- local has_unsubscribed = false
-
- ---@param mutate_fn fun(current_state: table)
- return function(mutate_fn)
- mutate_fn(state)
- if not has_unsubscribed then
- subscriber(state)
- end
- end, function()
- return state
- end, function(val)
- has_unsubscribed = val
- end
-end
-
-return M