From 77b1d06d1893d63a6dc04c61fe6325f8164f5188 Mon Sep 17 00:00:00 2001 From: musjj <72612857+musjj@users.noreply.github.com> Date: Thu, 24 Nov 2022 23:04:00 +0700 Subject: [PATCH 1/3] fix(git): make `mark_ignored` async --- lua/neo-tree/git/ignored.lua | 100 +++++++++++++----- .../sources/filesystem/lib/fs_scan.lua | 29 +++-- 2 files changed, 91 insertions(+), 38 deletions(-) diff --git a/lua/neo-tree/git/ignored.lua b/lua/neo-tree/git/ignored.lua index 9d4e9c4c..2902df97 100644 --- a/lua/neo-tree/git/ignored.lua +++ b/lua/neo-tree/git/ignored.lua @@ -1,3 +1,5 @@ +local Job = require("plenary.job") + local utils = require("neo-tree.utils") local log = require("neo-tree.log") local git_utils = require("neo-tree.git.utils") @@ -38,10 +40,11 @@ local get_root_for_item = function(item) return root end -M.mark_ignored = function(state, items) +M.mark_ignored = function(state, items, callback) local folders = {} log.trace("================================================================================") log.trace("IGNORED: mark_ignore BEGIN...") + for _, item in ipairs(items) do local folder = utils.split_path(item.path) if folder then @@ -52,19 +55,7 @@ M.mark_ignored = function(state, items) end end - local all_results = {} - for folder, folder_items in pairs(folders) do - local cmd = { "git", "-C", folder, "check-ignore" } - for _, item in ipairs(folder_items) do - table.insert(cmd, item) - end - log.trace("IGNORED: Running cmd: ", cmd) - local result = vim.fn.systemlist(cmd) - if vim.v.shell_error == 128 then - log.debug("Failed to load ignored files for", state.path, ":", result) - result = {} - end - + local function process_result(result) if utils.is_windows then --on Windows, git seems to return quotes and double backslash "path\\directory" result = vim.tbl_map(function(item) @@ -83,26 +74,77 @@ M.mark_ignored = function(state, items) end end end + return result + end - vim.list_extend(all_results, result) + local function finalize(all_results) + local show_anyway = state.filtered_items and state.filtered_items.hide_gitignored == false + log.trace("IGNORED: Comparing results to mark items as ignored, show_anyway:", show_anyway) + local ignored, not_ignored = 0, 0 + for _, item in ipairs(items) do + if M.is_ignored(all_results, item.path, item.type) then + item.filtered_by = item.filtered_by or {} + item.filtered_by.gitignored = true + item.filtered_by.show_anyway = show_anyway + ignored = ignored + 1 + else + not_ignored = not_ignored + 1 + end + end + log.trace("IGNORED: mark_ignored is complete, ignored:", ignored, ", not ignored:", not_ignored) + log.trace("================================================================================") end - local show_anyway = state.filtered_items and state.filtered_items.hide_gitignored == false - log.trace("IGNORED: Comparing results to mark items as ignored, show_anyway:", show_anyway) - local ignored, not_ignored = 0, 0 - for _, item in ipairs(items) do - if M.is_ignored(all_results, item.path, item.type) then - item.filtered_by = item.filtered_by or {} - item.filtered_by.gitignored = true - item.filtered_by.show_anyway = show_anyway - ignored = ignored + 1 - else - not_ignored = not_ignored + 1 + local all_results = {} + if type(callback) == "function" then + local jobs = {} + local progress = 0 + for folder, folder_items in pairs(folders) do + local args = { "-C", folder, "check-ignore", "--stdin" } + local job = Job:new({ + command = "git", + args = args, + enabled_recording = true, + writer = folder_items, + on_start = function() + log.trace("IGNORED: Running async git with args: ", args) + end, + on_exit = function(self, code, _) + local result + if code ~= 0 then + log.debug("Failed to load ignored files for", state.path, ":", self:stderr_result()) + result = {} + else + result = self:result() + end + vim.list_extend(all_results, process_result(result)) + progress = progress + 1 + if progress == #jobs then + finalize(all_results) + callback(all_results) + end + end, + }) + table.insert(jobs, job) + end + + for _, job in ipairs(jobs) do + job:start() end + else + for folder, folder_items in pairs(folders) do + local cmd = { "git", "-C", folder, "check-ignore", unpack(folder_items) } + log.trace("IGNORED: Running cmd: ", cmd) + local result = vim.fn.systemlist(cmd) + if vim.v.shell_error == 128 then + log.debug("Failed to load ignored files for", state.path, ":", result) + result = {} + end + vim.list_extend(all_results, process_result(result)) + end + finalize(all_results) + return all_results end - log.trace("IGNORED: mark_ignored is complete, ignored:", ignored, ", not ignored:", not_ignored) - log.trace("================================================================================") - return all_results end return M diff --git a/lua/neo-tree/sources/filesystem/lib/fs_scan.lua b/lua/neo-tree/sources/filesystem/lib/fs_scan.lua index c521f1d0..ca08bea0 100644 --- a/lua/neo-tree/sources/filesystem/lib/fs_scan.lua +++ b/lua/neo-tree/sources/filesystem/lib/fs_scan.lua @@ -71,18 +71,10 @@ local dir_complete = function(context, dir_path) return next_path end -local job_complete = function(context) +local render_context = function(context) local state = context.state local root = context.root local parent_id = context.parent_id - if state.filtered_items.hide_gitignored or state.enable_git_status then - local git_ignored = git.mark_ignored(state, context.all_items) - if parent_id then - vim.list_extend(state.git_ignored, git_ignored) - else - state.git_ignored = git_ignored - end - end if not parent_id and state.use_libuv_file_watcher and state.enable_git_status then log.trace("Starting .git folder watcher") @@ -111,6 +103,25 @@ local job_complete = function(context) context = nil end +local job_complete = function(context) + local state = context.state + local parent_id = context.parent_id + if state.filtered_items.hide_gitignored or state.enable_git_status then + git.mark_ignored(state, context.all_items, function(all_items) + if parent_id then + vim.list_extend(state.git_ignored, all_items) + else + state.git_ignored = all_items + end + vim.schedule(function() + render_context(context) + end) + end) + else + render_context(context) + end +end + -- async_scan scans all the directories in context.paths_to_load -- and adds them as items to render in the UI. local function async_scan(context, path) From f0bdc84d6935db6152f19dc5107eea588deb16a1 Mon Sep 17 00:00:00 2001 From: musjj <72612857+musjj@users.noreply.github.com> Date: Sat, 26 Nov 2022 00:26:58 +0700 Subject: [PATCH 2/3] fix(filesystem): respect `git_status_async` option --- .../sources/filesystem/lib/fs_scan.lua | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/lua/neo-tree/sources/filesystem/lib/fs_scan.lua b/lua/neo-tree/sources/filesystem/lib/fs_scan.lua index ca08bea0..90a8aeb9 100644 --- a/lua/neo-tree/sources/filesystem/lib/fs_scan.lua +++ b/lua/neo-tree/sources/filesystem/lib/fs_scan.lua @@ -107,19 +107,28 @@ local job_complete = function(context) local state = context.state local parent_id = context.parent_id if state.filtered_items.hide_gitignored or state.enable_git_status then - git.mark_ignored(state, context.all_items, function(all_items) + if require("neo-tree").config.git_status_async then + git.mark_ignored(state, context.all_items, function(all_items) + if parent_id then + vim.list_extend(state.git_ignored, all_items) + else + state.git_ignored = all_items + end + vim.schedule(function() + render_context(context) + end) + end) + return + else + local all_items = git.mark_ignored(state, context.all_items) if parent_id then vim.list_extend(state.git_ignored, all_items) else state.git_ignored = all_items end - vim.schedule(function() - render_context(context) - end) - end) - else - render_context(context) + end end + render_context(context) end -- async_scan scans all the directories in context.paths_to_load From 16382c8312fef99a3f8406ad94c421c552632b90 Mon Sep 17 00:00:00 2001 From: musjj <72612857+musjj@users.noreply.github.com> Date: Thu, 1 Dec 2022 18:28:07 +0700 Subject: [PATCH 3/3] fix(tests): add delay to prevent race condition This adds an arbitrary delay after sending the keys in order to wait for the tree to be fully expanded --- tests/neo-tree/ui/icons_spec.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/neo-tree/ui/icons_spec.lua b/tests/neo-tree/ui/icons_spec.lua index 16583f6c..feef7111 100644 --- a/tests/neo-tree/ui/icons_spec.lua +++ b/tests/neo-tree/ui/icons_spec.lua @@ -68,6 +68,8 @@ describe("ui/icons", function() vim.api.nvim_win_set_cursor(winid, { 3, 0 }) u.feedkeys("") + + vim.wait(100) u.assert_buf_lines(bufnr, { string.format("  %s", fs_tree.abspath):sub(1, 42), @@ -105,6 +107,8 @@ describe("ui/icons", function() vim.api.nvim_win_set_cursor(winid, { 3, 0 }) u.feedkeys("") + + vim.wait(100) u.assert_buf_lines(bufnr, { vim.fn.strcharpart(string.format("  %s", fs_tree.abspath), 0, 40), @@ -166,6 +170,8 @@ describe("ui/icons", function() vim.api.nvim_win_set_cursor(winid, { 3, 0 }) u.feedkeys("") + + vim.wait(100) u.assert_buf_lines(bufnr, { string.format(" o %s", fs_tree.abspath):sub(1, 40), @@ -203,6 +209,8 @@ describe("ui/icons", function() vim.api.nvim_win_set_cursor(winid, { 3, 0 }) u.feedkeys("") + + vim.wait(100) u.assert_buf_lines(bufnr, { vim.fn.strcharpart(string.format(" o %s", fs_tree.abspath), 0, 40),