aboutsummaryrefslogtreecommitdiffstats
path: root/lua/nvim-lsp-installer/server.lua
blob: a2bc4b5dbbfaa0b444e880b79671aa79c8f2178c (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
local dispatcher = require "nvim-lsp-installer.dispatcher"
local fs = require "nvim-lsp-installer.fs"
local path = require "nvim-lsp-installer.path"
local status_win = require "nvim-lsp-installer.ui.status-win"

local M = {}

function M.get_server_root_path(server)
    return path.concat { path.SERVERS_ROOT_DIR, server }
end

M.Server = {}
M.Server.__index = M.Server

---@class Server
--@param opts table
-- @field name (string)                  The name of the LSP server. This MUST correspond with lspconfig's naming.
--
-- @field root_dir (string)              The root directory of the installation. Most servers will make use of server.get_server_root_path() to produce its root_dir path.
--
-- @field installer (function)           The function that installs the LSP (see the .installers module). The function signature should be `function (server, callback)`, where
--                                       `server` is the Server instance being installed, and `callback` is a function that must be called upon completion. The `callback` function
--                                       has the signature `function (success, result)`, where `success` is a boolean and `result` is of any type (similar to `pcall`).
--
-- @field default_options (table)        The default options to be passed to lspconfig's .setup() function. Each server should provide at least the `cmd` field.
--
-- @field pre_install_check (function)   An optional function to be executed before the installer. This allows ensuring that any prerequisites are fulfilled.
--                                       This could for example be verifying that required build tools are installed.
--
-- @field post_setup (function)          An optional function to be executed after the setup function has been successfully called.
--                                       Use this to defer setting up server specific things until they're actually
--                                       needed, like custom commands.
--
-- @field pre_setup (function)           An optional function to be executed prior to calling lspconfig's setup().
--                                       Use this to defer setting up server specific things until they're actually needed.
--
function M.Server:new(opts)
    return setmetatable({
        name = opts.name,
        root_dir = opts.root_dir,
        _root_dir = opts.root_dir, -- @deprecated Use the `root_dir` property instead.
        _installer = opts.installer,
        _default_options = opts.default_options,
        _pre_install_check = opts.pre_install_check,
        _post_setup = opts.post_setup,
        _pre_setup = opts.pre_setup,
    }, M.Server)
end

function M.Server:setup(opts)
    if self._pre_setup then
        self._pre_setup()
    end
    -- We require the lspconfig server here in order to do it as late as possible.
    -- The reason for this is because once a lspconfig server has been imported, it's
    -- automatically registered with lspconfig and causes it to show up in :LspInfo and whatnot.
    require("lspconfig")[self.name].setup(vim.tbl_deep_extend("force", self._default_options, opts or {}))
    if self._post_setup then
        self._post_setup()
    end
end

function M.Server:get_default_options()
    return vim.deepcopy(self._default_options)
end

function M.Server:is_installed()
    return fs.dir_exists(self._root_dir)
end

function M.Server:create_root_dir()
    fs.mkdirp(self._root_dir)
end

function M.Server:install()
    status_win().install_server(self)
end

function M.Server:install_attached(opts, callback)
    local ok, err = pcall(self.pre_install, self)
    if not ok then
        opts.stdio_sink.stderr(tostring(err))
        callback(false)
        return
    end
    self._installer(self, function(success)
        if not success then
            pcall(self.uninstall, self)
        else
            dispatcher.dispatch_server_ready(self)
        end
        callback(success)
    end, opts)
end

function M.Server:pre_install()
    if self._pre_install_check then
        self._pre_install_check()
    end

    -- We run uninstall after pre_install_check because we don't want to
    -- unnecessarily uninstall a server should it no longer pass the
    -- pre_install_check.
    self:uninstall()

    self:create_root_dir()
end

function M.Server:uninstall()
    if fs.dir_exists(self._root_dir) then
        fs.rmrf(self._root_dir)
    end
end

return M