aboutsummaryrefslogtreecommitdiffstats
path: root/lua/nvim-lsp-installer/installers/init.lua
blob: c4ac27473d288c8b2675df30b236c63db0df9182 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
local platform = require "nvim-lsp-installer.platform"
local log = require "nvim-lsp-installer.log"
local Data = require "nvim-lsp-installer.data"

local M = {}

---@param installer ServerInstallerFunction[]|ServerInstallerFunction
---@return ServerInstallerFunction
local function normalize_installer(installer)
    if type(installer) == "table" then
        return M.pipe(installer)
    else
        return installer
    end
end

---@alias ServerInstallCallback fun(success: boolean)

---@class ServerInstallContext
---@field requested_server_version string|nil @The version requested by the user.
---@field stdio_sink StdioSink
---@field github_release_file string|nil @Only available if context.use_github_release_file has been called.
---@field os_distribution table<string, any> @Only available if context.use_os_distribution has been called.
---@field install_dir string

---@alias ServerInstallerFunction fun(server: Server, callback: ServerInstallCallback, context: ServerInstallContext)

--- Composes multiple installer functions into one.
---@param installers ServerInstallerFunction[]
---@return ServerInstallerFunction
function M.pipe(installers)
    if #installers == 0 then
        error "No installers to pipe."
    end

    return function(server, callback, context)
        local function execute(idx)
            local ok, err = pcall(installers[idx], server, function(success)
                if not success then
                    -- oh no, error. exit early
                    callback(success)
                elseif installers[idx + 1] then
                    -- iterate
                    execute(idx + 1)
                else
                    -- we done
                    callback(success)
                end
            end, context)
            if not ok then
                context.stdio_sink.stderr(tostring(err) .. "\n")
                callback(false)
            end
        end

        execute(1)
    end
end

--- Composes multiple installer function into one - in reversed order.
---@param installers ServerInstallerFunction[]
function M.compose(installers)
    return M.pipe(Data.list_reverse(installers))
end

---@param installers ServerInstallerFunction[]
---@return ServerInstallerFunction @An installer function that will serially execute the provided installers, until the first one succeeds.
function M.first_successful(installers)
    if #installers == 0 then
        error "No installers to pipe."
    end

    return function(server, callback, context)
        local function execute(idx)
            log.fmt_trace("Executing installer idx=%d", idx)
            local ok, err = pcall(installers[idx], server, function(success)
                log.fmt_trace("Installer idx=%d on exit with success=%s", idx, success)
                if not success and installers[idx + 1] then
                    -- iterate
                    execute(idx + 1)
                else
                    callback(success)
                end
            end, context)
            if not ok then
                context.stdio_sink.stderr(tostring(err) .. "\n")
                if installers[idx + 1] then
                    execute(idx + 1)
                else
                    callback(false)
                end
            end
        end

        execute(1)
    end
end

--- Wraps the provided server installer to always succeeds.
---@param installer ServerInstallerFunction
---@return ServerInstallerFunction
function M.always_succeed(installer)
    return function(server, callback, context)
        installer(server, function()
            callback(true)
        end, context)
    end
end

---@param platform_table table<Platform, ServerInstallerFunction>
---@return ServerInstallerFunction | ServerInstallerFunction[] | nil
local function get_by_platform(platform_table)
    if platform.is_mac then
        return platform_table.mac or platform_table.unix
    elseif platform.is_linux then
        return platform_table.linux or platform_table.unix
    elseif platform.is_unix then
        return platform_table.unix
    elseif platform.is_win then
        return platform_table.win
    else
        return nil
    end
end

--- Creates a server installer that executes the given installer for the current platform.
--- If there is no server installer provided for the current platform, the installer will instantly exit successfully.
---@param platform_table table<Platform, ServerInstallerFunction>
---@return ServerInstallerFunction
function M.on(platform_table)
    return function(server, callback, context)
        local installer = get_by_platform(platform_table)
        if installer then
            normalize_installer(installer)(server, callback, context)
        else
            callback(true)
        end
    end
end

--- Creates a server installer that executes the given installer for the current platform.
--- If there is no server installer provided for the current platform, the installer will instantly exit with a failure.
---@param platform_table table<Platform, ServerInstallerFunction|ServerInstallerFunction[]>
---@return ServerInstallerFunction
function M.when(platform_table)
    return function(server, callback, context)
        local installer = get_by_platform(platform_table)
        if installer then
            normalize_installer(installer)(server, callback, context)
        else
            context.stdio_sink.stderr(
                ("Current operating system is not yet supported for server %q.\n"):format(server.name)
            )
            callback(false)
        end
    end
end

---@param installer ServerInstallerFunction|ServerInstallerFunction[] @The installer to execute in a new installer context.
function M.branch_context(installer)
    ---@type ServerInstallerFunction
    return function(server, callback, context)
        local new_context = vim.deepcopy(context)
        normalize_installer(installer)(server, callback, new_context)
    end
end

return M