@@ -2,6 +2,7 @@ local u = require("null-ls.utils")
2
2
3
3
local api = vim .api
4
4
local uv = vim .loop
5
+ local wrap = vim .schedule_wrap
5
6
6
7
local close_handle = function (handle )
7
8
if handle and not handle :is_closing () then handle :close () end
@@ -24,37 +25,43 @@ local parse_args = function(args, bufnr)
24
25
return parsed
25
26
end
26
27
28
+ local TIMEOUT_EXIT_CODE = 7451
29
+
27
30
local M = {}
28
31
29
32
M .spawn = function (cmd , args , opts )
30
- local handler , input , bufnr , check_exit_code = opts .handler , opts .input ,
31
- opts .bufnr ,
32
- opts .check_exit_code
33
+ local handler , input , bufnr , check_exit_code , timeout = opts .handler ,
34
+ opts .input ,
35
+ opts .bufnr ,
36
+ opts .check_exit_code ,
37
+ opts .timeout
33
38
39
+ local timer
34
40
local output , error_output , exit_ok = " " , " " , _G ._TEST and true or nil
35
- local handle_stdout = vim .schedule_wrap (
36
- function (err , chunk )
37
- if err then error (" stdout error: " .. err ) end
38
-
39
- if chunk then output = output .. chunk end
40
- if not chunk then
41
- -- wait for handler callback to check exit code
42
- vim .wait (500 , function () return exit_ok ~= nil end , 10 )
43
-
44
- -- convert empty strings to make nil checks easier
45
- if output == " " then output = nil end
46
- if error_output == " " then error_output = nil end
47
-
48
- -- if exit code is not ok and program did not output to stderr,
49
- -- assign output to error_output, so handler can process it as an error
50
- if not exit_ok and not error_output then
51
- error_output = output
52
- output = nil
53
- end
54
-
55
- handler (error_output , output )
41
+ local handle_stdout = wrap (function (err , chunk )
42
+ if err then error (" stdout error: " .. err ) end
43
+
44
+ if chunk then output = output .. chunk end
45
+ if not chunk then
46
+ if timer then timer .stop (true ) end
47
+
48
+ -- wait for handler callback to check exit code
49
+ vim .wait (500 , function () return exit_ok ~= nil end , 10 )
50
+
51
+ -- convert empty strings to make nil checks easier
52
+ if output == " " then output = nil end
53
+ if error_output == " " then error_output = nil end
54
+
55
+ -- if exit code is not ok and program did not output to stderr,
56
+ -- assign output to error_output, so handler can process it as an error
57
+ if not exit_ok and not error_output then
58
+ error_output = output
59
+ output = nil
56
60
end
57
- end )
61
+
62
+ handler (error_output , output )
63
+ end
64
+ end )
58
65
59
66
local handle_stderr = function (err , chunk )
60
67
if err then error (" stderr error: " .. err ) end
@@ -68,9 +75,12 @@ M.spawn = function(cmd, args, opts)
68
75
local stdio = {stdin , stdout , stderr }
69
76
70
77
local handle
71
- handle = uv .spawn (cmd , {args = parse_args (args , bufnr ), stdio = stdio },
72
- vim .schedule_wrap (function (code )
73
- exit_ok = check_exit_code and check_exit_code (code ) or code == 0
78
+ local close = wrap (function (code )
79
+ if code == TIMEOUT_EXIT_CODE then
80
+ exit_ok = false
81
+ else
82
+ exit_ok = check_exit_code and check_exit_code (code ) or code == 0
83
+ end
74
84
75
85
stdout :read_stop ()
76
86
stderr :read_stop ()
@@ -79,7 +89,17 @@ M.spawn = function(cmd, args, opts)
79
89
close_handle (stdout )
80
90
close_handle (stderr )
81
91
close_handle (handle )
82
- end ))
92
+ end )
93
+
94
+ handle = uv .spawn (cmd , {args = parse_args (args , bufnr ), stdio = stdio },
95
+ close )
96
+ if timeout then
97
+ timer = M .timer (timeout , nil , true , function ()
98
+ close (TIMEOUT_EXIT_CODE )
99
+ handler ()
100
+ timer .stop (true )
101
+ end )
102
+ end
83
103
84
104
uv .read_start (stdout , handle_stdout )
85
105
uv .read_start (stderr , handle_stderr )
@@ -91,18 +111,32 @@ M.timer = function(timeout, interval, should_start, callback)
91
111
interval = interval or 0
92
112
93
113
local timer = uv .new_timer ()
94
- local wrapped = vim .schedule_wrap (callback )
114
+ local wrapped = wrap (callback )
115
+
95
116
local start = function () timer :start (timeout , interval , wrapped ) end
96
- local stop = function () timer :stop () end
117
+ local close = function () close_handle (timer ) end
118
+ local stop = function (should_close )
119
+ timer :stop ()
120
+ if should_close then close () end
121
+ end
97
122
local restart = function (new_timeout , new_interval )
98
123
timer :stop ()
99
124
timer :start (new_timeout or timeout , new_interval or interval , wrapped )
100
125
end
101
126
102
127
if should_start then timer :start (timeout , interval , wrapped ) end
103
- return {_timer = timer , start = start , stop = stop , restart = restart }
128
+ return {
129
+ _timer = timer ,
130
+ start = start ,
131
+ stop = stop ,
132
+ restart = restart ,
133
+ close = close
134
+ }
104
135
end
105
136
106
- if _G ._TEST then M ._parse_args = parse_args end
137
+ if _G ._TEST then
138
+ M ._parse_args = parse_args
139
+ M ._TIMEOUT_EXIT_CODE = TIMEOUT_EXIT_CODE
140
+ end
107
141
108
142
return M
0 commit comments