Skip to content

Commit 759a108

Browse files
committed
feat: add new batch method for git status async, along with options, part of #333
1 parent 5968811 commit 759a108

File tree

3 files changed

+65
-14
lines changed

3 files changed

+65
-14
lines changed

lua/neo-tree/defaults.lua

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ local config = {
1010
enable_modified_markers = true, -- Show markers for files with unsaved changes.
1111
enable_refresh_on_write = true, -- Refresh the tree when a file is written. Only used if `use_libuv_file_watcher` is false.
1212
git_status_async = true,
13+
-- These options are for people with VERY large git repos
14+
git_status_async_options = {
15+
batch_size = 1000, -- how many lines of git status results to process at a time
16+
batch_delay = 10, -- delay in ms between batches. Spreads out the workload to let other processes run.
17+
max_lines = 100000, -- How many lines of git status results to process. Anything after this will be dropped.
18+
-- Anything before this will be used. The last items to be processed are the untracked files.
19+
},
1320
log_level = "info", -- "trace", "debug", "info", "warn", "error", "fatal"
1421
log_to_file = false, -- true, false, "/path/to/file.log", use :NeoTreeLogs to show the file
1522
open_files_in_last_window = true, -- false = open files in top left window

lua/neo-tree/git/status.lua

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,40 @@ M.status = function(base, exclude_directories, path)
166166
return context.git_status, git_root
167167
end
168168

169-
M.status_async = function(path, base)
169+
local function parse_lines_batch(context, job_complete_callback)
170+
if context.lines_total == nil then
171+
context.lines_total = math.min(context.max_lines, #context.lines)
172+
context.lines_parsed = 0
173+
if context.lines_total == 0 then
174+
if type(job_complete_callback) == "function" then
175+
job_complete_callback()
176+
end
177+
return
178+
end
179+
-- don't do anything on the first batch, we just finished what might have been
180+
-- a lot of work in gathering these lines...
181+
else
182+
local batch_size = math.min(context.batch_size, context.lines_total - context.lines_parsed)
183+
local i = 0
184+
while i < batch_size do
185+
i = i + 1
186+
parse_git_status_line(context, context.lines[context.lines_parsed + 1])
187+
end
188+
end
189+
190+
if context.lines_parsed >= context.lines_total then
191+
if type(job_complete_callback) == "function" then
192+
job_complete_callback()
193+
end
194+
else
195+
-- add small delay so other work can happen
196+
vim.defer_fn(function()
197+
parse_lines_batch(context, job_complete_callback)
198+
end, context.batch_delay)
199+
end
200+
end
201+
202+
M.status_async = function(path, base, opts)
170203
local git_root = git_utils.get_repository_root(path)
171204
if utils.truthy(git_root) then
172205
log.trace("git.status.status_async called")
@@ -175,13 +208,28 @@ M.status_async = function(path, base)
175208
return false
176209
end
177210

211+
local event_id = "git_status_" .. git_root
178212
local context = {
179213
git_root = git_root,
180214
git_status = {},
181215
exclude_directories = false,
182-
lines_parsed = 0
216+
lines = {},
217+
lines_parsed = 0,
218+
batch_size = opts.batch_size or 1000,
219+
batch_delay = opts.batch_delay or 10,
220+
max_lines = opts.max_lines or 100000,
183221
}
184222

223+
local job_complete_callback = function ()
224+
utils.debounce(event_id, nil, nil, nil, utils.debounce_action.COMPLETE_ASYNC_JOB)
225+
vim.schedule(function()
226+
events.fire_event(events.GIT_STATUS_CHANGED, {
227+
git_root = context.git_root,
228+
git_status = context.git_status,
229+
})
230+
end)
231+
end
232+
185233
local should_process = function(err, line, job, err_msg)
186234
if vim.v.dying > 0 or vim.v.exiting ~= vim.NIL then
187235
job:shutdown()
@@ -194,16 +242,16 @@ M.status_async = function(path, base)
194242
return true
195243
end
196244

197-
local event_id = "git_status_" .. git_root
198245
utils.debounce(event_id, function()
199246
local staged_job = Job
200247
:new({
201248
command = "git",
202249
args = { "-C", git_root, "diff", "--staged", "--name-status", base, "--" },
203250
enable_recording = false,
251+
maximium_results = context.max_lines,
204252
on_stdout = vim.schedule_wrap(function(err, line, job)
205253
if should_process(err, line, job, "status_async staged error:") then
206-
parse_git_status_line(context, line)
254+
table.insert(context.lines, line)
207255
end
208256
end),
209257
on_stderr = function(err, line)
@@ -221,12 +269,13 @@ M.status_async = function(path, base)
221269
command = "git",
222270
args = { "-C", git_root, "diff", "--name-status" },
223271
enable_recording = false,
272+
maximium_results = context.max_lines,
224273
on_stdout = vim.schedule_wrap(function(err, line, job)
225274
if should_process(err, line, job, "status_async unstaged error:") then
226275
if line then
227276
line = " " .. line
228277
end
229-
parse_git_status_line(context, line)
278+
table.insert(context.lines, line)
230279
end
231280
end),
232281
on_stderr = function(err, line)
@@ -244,12 +293,13 @@ M.status_async = function(path, base)
244293
command = "git",
245294
args = { "-C", git_root, "ls-files", "--exclude-standard", "--others" },
246295
enable_recording = false,
296+
maximium_results = context.max_lines,
247297
on_stdout = vim.schedule_wrap(function(err, line, job)
248298
if should_process(err, line, job, "status_async untracked error:") then
249299
if line then
250300
line = "? " .. line
251301
end
252-
parse_git_status_line(context, line)
302+
table.insert(context.lines, line)
253303
end
254304
end),
255305
on_stderr = function(err, line)
@@ -258,14 +308,8 @@ M.status_async = function(path, base)
258308
end
259309
end,
260310
on_exit = function(_, return_val)
261-
utils.debounce(event_id, nil, nil, nil, utils.debounce_action.COMPLETE_ASYNC_JOB)
262311
log.trace("status_async untracked completed with return_val:", return_val, ";", context.lines_parsed, "lines parsed")
263-
vim.schedule(function()
264-
events.fire_event(events.GIT_STATUS_CHANGED, {
265-
git_root = context.git_root,
266-
git_status = context.git_status,
267-
})
268-
end)
312+
parse_lines_batch(context, job_complete_callback)
269313
end,
270314
})
271315

lua/neo-tree/sources/filesystem/init.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ M._navigate_internal = function(state, path, path_to_reveal, callback, async)
166166
end
167167
local config = require("neo-tree").config
168168
if config.enable_git_status and not is_search and config.git_status_async then
169-
git.status_async(state.path, state.git_base)
169+
git.status_async(state.path, state.git_base, config.git_status_async_options)
170170
end
171171
end
172172

0 commit comments

Comments
 (0)