summaryrefslogtreecommitdiffstats
path: root/lua/mason-core/terminator.lua
blob: 4c7d0125d8418343a86e857105a846350246ee3c (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
local a = require "mason-core.async"

-- Hasta la vista, baby.
--                      ______
--                    <((((((\\\
--                    /      . }\
--                    ;--..--._|}
-- (\                 '--/\--'  )
--  \\                | '-'  :'|
--   \\               . -==- .-|
--    \\               \.__.'   \--._
--    [\\          __.--|       //  _/'--.
--    \ \\       .'-._ ('-----'/ __/      \
--     \ \\     /   __>|      | '--.       |
--      \ \\   |   \   |     /    /       /
--       \ '\ /     \  |     |  _/       /
--        \  \       \ |     | /        /
--  snd    \  \      \        /

local M = {}

---@async
---@param handles InstallHandle[]
---@param grace_ms integer
local function terminate_handles(handles, grace_ms)
    a.wait_all(vim.tbl_map(
        ---@param handle InstallHandle
        function(handle)
            return function()
                local timer
                if not handle:is_closed() then
                    handle:terminate()
                    timer = vim.defer_fn(function()
                        if not handle:is_closed() then
                            handle:kill(9) -- SIGKILL
                        end
                    end, grace_ms)
                end
                a.wait(function(resolve)
                    if handle:is_closed() then
                        resolve()
                    else
                        handle:once("closed", resolve)
                    end
                end)
                if timer then
                    timer:stop()
                end
            end
        end,
        handles
    ))
end

local active_handles = {}

---@param handle InstallHandle
function M.register(handle)
    if handle:is_closed() then
        return
    end
    active_handles[handle] = true
    handle:once("closed", function()
        active_handles[handle] = nil
    end)
end

---@param grace_ms integer
function M.terminate(grace_ms)
    local handles = vim.tbl_keys(active_handles)
    if #handles > 0 then
        local package_names = vim.tbl_map(function(h)
            return h.package.name
        end, handles)
        table.sort(package_names)

        -- 1. Print warning message.
        vim.api.nvim_echo({
            {
                "[mason.nvim] Neovim is exiting while packages are still installing. Terminating all installations…",
                "WarningMsg",
            },
        }, true, {})
        vim.cmd "redraw"

        -- 2. Synchronously terminate all installation handles.
        a.run_blocking(function()
            terminate_handles(handles, grace_ms)
        end)

        -- 3. Schedule error message to be displayed so that Neovim prints it to the tty.
        --    XXX: does this need to be conditional on which UIs are attached?
        vim.schedule(function()
            vim.api.nvim_err_writeln(
                ("[mason.nvim] Neovim exited while the following packages were installing. Installation was aborted.\n- %s"):format(
                    table.concat(package_names, #package_names > 5 and ", " or "\n- ")
                )
            )
        end)
    end
end

return M