@@ -2,17 +2,31 @@ local log = require("nvim-tree.log")
2
2
local utils = require (" nvim-tree.utils" )
3
3
local notify = require (" nvim-tree.notify" )
4
4
5
- --- @class Runner
6
- local Runner = {}
7
- Runner .__index = Runner
5
+ local Class = require (" nvim-tree.class" )
6
+
7
+ --- @alias GitStatusesXYByPath table<string , string>
8
+
9
+ --- @class (exact ) RunnerOpts
10
+ --- @field toplevel string absolute path
11
+ --- @field path string ? absolute path
12
+ --- @field list_untracked boolean
13
+ --- @field list_ignored boolean
14
+ --- @field timeout integer
15
+ --- @field callback fun ( statuses : GitStatusesXYByPath )?
16
+
17
+ --- @class (exact ) Runner : Class
18
+ --- @field opts RunnerOpts
19
+ --- @field statuses GitStatusesXYByPath
20
+ --- @field rc integer ? -- -1 indicates timeout
21
+ local Runner = Class :new ()
8
22
9
23
local timeouts = 0
10
24
local MAX_TIMEOUTS = 5
11
25
12
26
--- @private
13
27
--- @param status string
14
28
--- @param path string | nil
15
- function Runner :_parse_status_output (status , path )
29
+ function Runner :parse_status_output (status , path )
16
30
if not path then
17
31
return
18
32
end
@@ -22,15 +36,15 @@ function Runner:_parse_status_output(status, path)
22
36
path = path :gsub (" /" , " \\ " )
23
37
end
24
38
if # status > 0 and # path > 0 then
25
- self .output [utils .path_remove_trailing (utils .path_join ({ self .toplevel , path }))] = status
39
+ self .statuses [utils .path_remove_trailing (utils .path_join ({ self . opts .toplevel , path }))] = status
26
40
end
27
41
end
28
42
29
43
--- @private
30
44
--- @param prev_output string
31
45
--- @param incoming string
32
46
--- @return string
33
- function Runner :_handle_incoming_data (prev_output , incoming )
47
+ function Runner :handle_incoming_data (prev_output , incoming )
34
48
if incoming and utils .str_find (incoming , " \n " ) then
35
49
local prev = prev_output .. incoming
36
50
local i = 1
@@ -45,7 +59,7 @@ function Runner:_handle_incoming_data(prev_output, incoming)
45
59
-- skip next line if it is a rename entry
46
60
skip_next_line = true
47
61
end
48
- self :_parse_status_output (status , path )
62
+ self :parse_status_output (status , path )
49
63
end
50
64
i = i + # line
51
65
end
@@ -58,35 +72,38 @@ function Runner:_handle_incoming_data(prev_output, incoming)
58
72
end
59
73
60
74
for line in prev_output :gmatch (" [^\n ]*\n " ) do
61
- self :_parse_status_output (line )
75
+ self :parse_status_output (line )
62
76
end
63
77
64
78
return " "
65
79
end
66
80
81
+ --- @private
67
82
--- @param stdout_handle uv.uv_pipe_t
68
83
--- @param stderr_handle uv.uv_pipe_t
69
- --- @return table
70
- function Runner :_getopts (stdout_handle , stderr_handle )
71
- local untracked = self .list_untracked and " -u" or nil
72
- local ignored = (self .list_untracked and self .list_ignored ) and " --ignored=matching" or " --ignored=no"
84
+ --- @return uv.spawn.options
85
+ function Runner :get_spawn_options (stdout_handle , stderr_handle )
86
+ local untracked = self .opts . list_untracked and " -u" or nil
87
+ local ignored = (self .opts . list_untracked and self . opts .list_ignored ) and " --ignored=matching" or " --ignored=no"
73
88
return {
74
- args = { " --no-optional-locks" , " status" , " --porcelain=v1" , " -z" , ignored , untracked , self .path },
75
- cwd = self .toplevel ,
89
+ args = { " --no-optional-locks" , " status" , " --porcelain=v1" , " -z" , ignored , untracked , self .opts . path },
90
+ cwd = self .opts . toplevel ,
76
91
stdio = { nil , stdout_handle , stderr_handle },
77
92
}
78
93
end
79
94
95
+ --- @private
80
96
--- @param output string
81
- function Runner :_log_raw_output (output )
97
+ function Runner :log_raw_output (output )
82
98
if log .enabled (" git" ) and output and type (output ) == " string" then
83
99
log .raw (" git" , " %s" , output )
84
100
log .line (" git" , " done" )
85
101
end
86
102
end
87
103
104
+ --- @private
88
105
--- @param callback function | nil
89
- function Runner :_run_git_job (callback )
106
+ function Runner :run_git_job (callback )
90
107
local handle , pid
91
108
local stdout = vim .loop .new_pipe (false )
92
109
local stderr = vim .loop .new_pipe (false )
@@ -123,20 +140,20 @@ function Runner:_run_git_job(callback)
123
140
end
124
141
end
125
142
126
- local opts = self :_getopts (stdout , stderr )
127
- log .line (" git" , " running job with timeout %dms" , self .timeout )
128
- log .line (" git" , " git %s" , table.concat (utils .array_remove_nils (opts .args ), " " ))
143
+ local spawn_options = self :get_spawn_options (stdout , stderr )
144
+ log .line (" git" , " running job with timeout %dms" , self .opts . timeout )
145
+ log .line (" git" , " git %s" , table.concat (utils .array_remove_nils (spawn_options .args ), " " ))
129
146
130
147
handle , pid = vim .loop .spawn (
131
148
" git" ,
132
- opts ,
149
+ spawn_options ,
133
150
vim .schedule_wrap (function (rc )
134
151
on_finish (rc )
135
152
end )
136
153
)
137
154
138
155
timer :start (
139
- self .timeout ,
156
+ self .opts . timeout ,
140
157
0 ,
141
158
vim .schedule_wrap (function ()
142
159
on_finish (- 1 )
@@ -151,19 +168,20 @@ function Runner:_run_git_job(callback)
151
168
if data then
152
169
data = data :gsub (" %z" , " \n " )
153
170
end
154
- self :_log_raw_output (data )
155
- output_leftover = self :_handle_incoming_data (output_leftover , data )
171
+ self :log_raw_output (data )
172
+ output_leftover = self :handle_incoming_data (output_leftover , data )
156
173
end
157
174
158
175
local function manage_stderr (_ , data )
159
- self :_log_raw_output (data )
176
+ self :log_raw_output (data )
160
177
end
161
178
162
179
vim .loop .read_start (stdout , vim .schedule_wrap (manage_stdout ))
163
180
vim .loop .read_start (stderr , vim .schedule_wrap (manage_stderr ))
164
181
end
165
182
166
- function Runner :_wait ()
183
+ --- @private
184
+ function Runner :wait ()
167
185
local function is_done ()
168
186
return self .rc ~= nil
169
187
end
@@ -172,64 +190,64 @@ function Runner:_wait()
172
190
end
173
191
end
174
192
175
- --- @param opts table
176
- function Runner :_finalise ( opts )
193
+ --- @private
194
+ function Runner :finalise ( )
177
195
if self .rc == - 1 then
178
- log .line (" git" , " job timed out %s %s" , opts .toplevel , opts .path )
196
+ log .line (" git" , " job timed out %s %s" , self . opts .toplevel , self . opts .path )
179
197
timeouts = timeouts + 1
180
198
if timeouts == MAX_TIMEOUTS then
181
- notify .warn (string.format (" %d git jobs have timed out after git.timeout %dms, disabling git integration." , timeouts , opts .timeout ))
199
+ notify .warn (string.format (" %d git jobs have timed out after git.timeout %dms, disabling git integration." , timeouts ,
200
+ self .opts .timeout ))
182
201
require (" nvim-tree.git" ).disable_git_integration ()
183
202
end
184
203
elseif self .rc ~= 0 then
185
- log .line (" git" , " job fail rc %d %s %s" , self .rc , opts .toplevel , opts .path )
204
+ log .line (" git" , " job fail rc %d %s %s" , self .rc , self . opts .toplevel , self . opts .path )
186
205
else
187
- log .line (" git" , " job success %s %s" , opts .toplevel , opts .path )
206
+ log .line (" git" , " job success %s %s" , self . opts .toplevel , self . opts .path )
188
207
end
189
208
end
190
209
191
- --- Runs a git process, which will be killed if it takes more than timeout which defaults to 400ms
192
- --- @param opts table
193
- --- @param callback function | nil executed passing return when complete
194
- --- @return table | nil status by absolute path , nil if callback present
195
- function Runner .run (opts , callback )
196
- local self = setmetatable ({
197
- toplevel = opts .toplevel ,
198
- path = opts .path ,
199
- list_untracked = opts .list_untracked ,
200
- list_ignored = opts .list_ignored ,
201
- timeout = opts .timeout or 400 ,
202
- output = {},
203
- rc = nil , -- -1 indicates timeout
204
- }, Runner )
205
-
206
- local async = callback ~= nil
207
- local profile = log .profile_start (" git %s job %s %s" , async and " async" or " sync" , opts .toplevel , opts .path )
208
-
209
- if async and callback then
210
+ --- @return GitStatusesXYByPath ? statuses nil if callback present
211
+ function Runner :run ()
212
+ local async = self .opts .callback ~= nil
213
+ local profile = log .profile_start (" git %s job %s %s" , async and " async" or " sync" , self .opts .toplevel , self .opts .path )
214
+
215
+ if async and self .opts .callback then
210
216
-- async, always call back
211
- self :_run_git_job (function ()
217
+ self :run_git_job (function ()
212
218
log .profile_end (profile )
213
219
214
- self :_finalise ( opts )
220
+ self :finalise ( )
215
221
216
- callback (self .output )
222
+ self . opts . callback (self .statuses )
217
223
end )
218
224
else
219
225
-- sync, maybe call back
220
- self :_run_git_job ()
221
- self :_wait ()
226
+ self :run_git_job ()
227
+ self :wait ()
222
228
223
229
log .profile_end (profile )
224
230
225
- self :_finalise ( opts )
231
+ self :finalise ( )
226
232
227
- if callback then
228
- callback (self .output )
233
+ if self . opts . callback then
234
+ self . opts . callback (self .statuses )
229
235
else
230
- return self .output
236
+ return self .statuses
231
237
end
232
238
end
233
239
end
234
240
235
- return Runner
241
+ --- Runs a git process, which will be killed if it takes more than timeout which defaults to 400ms
242
+ --- @param opts RunnerOpts
243
+ --- @return GitStatusesXYByPath ? statuses nil if callback present
244
+ return function (opts )
245
+ --- @type Runner
246
+ local runner = {
247
+ opts = opts ,
248
+ statuses = {},
249
+ }
250
+ runner = Runner :new (runner ) --[[ @as Runner]]
251
+
252
+ return runner :run ()
253
+ end
0 commit comments