-
Notifications
You must be signed in to change notification settings - Fork 50
/
overseer.lua
171 lines (163 loc) · 4.76 KB
/
overseer.lua
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
169
170
171
local lib = require("neotest.lib")
local log = require("overseer.log")
local nio = require("nio")
local overseer = require("overseer")
local util = require("overseer.util")
local M = {}
local current_group_id = 0
local tasks_by_group = {}
local pool = {}
---@param group_id integer
M.set_group_id = function(group_id)
current_group_id = group_id
end
---@param group_id integer
M.recycle_group = function(group_id)
if not pool[group_id] then
pool[group_id] = {}
end
log:debug("Recycling neotest task group %s", group_id)
vim.list_extend(pool[group_id], tasks_by_group[group_id])
tasks_by_group[group_id] = {}
end
---@param spec neotest.RunSpec
---@param context neotest.StrategyContext
---@param output_path string
---@return overseer.Task
local function get_or_create_task(spec, context, output_path)
local recycled = pool[current_group_id]
local task
-- Get the first non-disposed task in the recycled pool
while not task and recycled and not vim.tbl_isempty(recycled) do
task = table.remove(recycled)
if task:is_disposed() then
task = nil
end
end
if task then
-- Reset the task
log:debug("Using pooled neotest task %s from group %s", task.id, current_group_id)
task:reset(false)
task:remove_components({ "on_output_write_file", "neotest.link_with_neotest" })
task:add_components({
{ "on_output_write_file", filename = output_path },
"neotest.link_with_neotest",
})
task.cmd = spec.command
task.env = spec.env
task.cwd = spec.cwd
else
-- Create a new task
local name = "Neotest"
if context.position and context.position.name then
name = string.format("%s %s", name, context.position.name)
end
local strategy = spec.strategy
if type(strategy) == "function" then
strategy = strategy(spec, context)
end
local opts = vim.tbl_extend("keep", strategy or {}, {
name = name,
components = { "default_neotest" },
})
if type(opts.components) == "function" then
opts.components = opts.components(spec)
end
opts.components = vim.list_extend(
{ { "on_output_write_file", filename = output_path }, "neotest.link_with_neotest" },
opts.components
)
opts.cmd = spec.command
opts.env = spec.env
opts.cwd = spec.cwd
opts.metadata = {
neotest_group_id = current_group_id,
}
task = overseer.new_task(opts)
log:debug("Created new neotest task %s group %s", task.id, current_group_id)
task:set_include_in_bundle(false)
task:subscribe("on_dispose", function(disposed_task)
local tasks = tasks_by_group[disposed_task.metadata.group_id]
if tasks then
util.tbl_remove(tasks, disposed_task)
end
end)
end
if not tasks_by_group[current_group_id] then
tasks_by_group[current_group_id] = {}
end
table.insert(tasks_by_group[current_group_id], task)
return task
end
---@param spec neotest.RunSpec
---@param context neotest.StrategyContext
---@return neotest.Process
local function get_strategy(spec, context)
if not overseer.component.get_alias("default_neotest") then
overseer.component.alias("default_neotest", { "default" })
end
local finish_future = nio.control.future()
local attach_win
local output_path = nio.fn.tempname()
local task = get_or_create_task(spec, context, output_path)
task:subscribe("on_complete", function()
finish_future.set()
return false
end)
task:start()
return {
is_complete = function()
return task:is_complete()
end,
output = function()
return output_path
end,
stop = vim.schedule_wrap(function()
task:stop()
end),
output_stream = function()
local queue = nio.control.queue()
task:subscribe("on_output", function(_, data)
queue.put_nowait(table.concat(data, "\n"))
end)
return function()
return nio.first({ finish_future.wait, queue.get })
end
end,
attach = function()
local bufnr = task:get_bufnr()
if not bufnr or not nio.api.nvim_buf_is_valid(bufnr) then
return
end
attach_win = lib.ui.float.open({
height = spec.strategy.height or 40,
width = spec.strategy.width or 120,
buffer = bufnr,
})
nio.api.nvim_buf_set_keymap(bufnr, "n", "q", "", {
noremap = true,
silent = true,
callback = function()
pcall(vim.api.nvim_win_close, attach_win.win_id, true)
end,
})
attach_win:jump_to()
end,
result = function()
if not task:is_complete() then
finish_future:wait()
end
if attach_win then
vim.schedule(function()
attach_win:close(true)
end)
end
return task.exit_code
end,
}
end
return setmetatable(M, {
__call = function(_, ...)
return get_strategy(...)
end,
})