Skip to content

Commit

Permalink
feat(files): custom find function
Browse files Browse the repository at this point in the history
File searching is a tiny part of parsing time so there's no real benefit
to using external commands. Also found to be about the same or slightly
faster, guessing that's because most of the work is async and so the
same string manipulation is what is taking most of the time.

See #59
  • Loading branch information
rcarriga committed Sep 11, 2022
1 parent 8f1e599 commit 5b9a840
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 73 deletions.
2 changes: 1 addition & 1 deletion lua/neotest/client/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ function NeotestClient:_update_positions(path, args)
return
end
end
local files = lib.func_util.filter_list(adapter.is_test_file, lib.files.find({ path }))
local files = lib.func_util.filter_list(adapter.is_test_file, lib.files.find(path))
return lib.files.parse_dir_from_files(path, files)
else
return adapter.discover_positions(path)
Expand Down
97 changes: 25 additions & 72 deletions lua/neotest/lib/file/find.lua
Original file line number Diff line number Diff line change
@@ -1,82 +1,35 @@
local async = require("neotest.async")
local logger = require("neotest.logging")
local uv = vim.loop
local M = {}

-- Taken from telescope find_files
local function get_find_command(search_dirs)
local find_command
if not find_command then
if 1 == async.fn.executable("fd") then
find_command = { "fd", "--type", "f" }
table.insert(find_command, ".")
for _, v in pairs(search_dirs) do
table.insert(find_command, v)
end
elseif 1 == async.fn.executable("fdfind") then
find_command = { "fdfind", "--type", "f" }
table.insert(find_command, ".")
for _, v in pairs(search_dirs) do
table.insert(find_command, v)
end
elseif 1 == async.fn.executable("rg") then
find_command = { "rg", "--files" }
for _, v in pairs(search_dirs) do
table.insert(find_command, v)
end
elseif 1 == async.fn.executable("find") and async.fn.has("win32") == 0 then
find_command = { "find", "-type", "f" }
for _, v in pairs(search_dirs) do
table.insert(find_command, 2, v)
end
end
end
return find_command
end

--- Find all files under the given directory.
--- Does not search hidden directories.
---@async
---@param search_dirs? string[] directories to search, defaults to current directory
---@param dir_path string
---@return string[] @Absolute paths of all files within directories to search
function M.find(search_dirs)
local find_command = get_find_command(search_dirs or { async.api.nvim_eval("getcwd()") })
local finish_cond = async.control.Condvar.new()
local stdin = vim.loop.new_pipe()
local stdout = vim.loop.new_pipe()
local stderr = vim.loop.new_pipe()
local result_code
logger.debug("Searching for files using command ", find_command)
vim.loop.spawn(find_command[1], {
stdio = { stdin, stdout, stderr },
detached = true,
args = #find_command > 1 and vim.list_slice(find_command, 2, #find_command) or nil,
hide = true,
}, function(code, _)
result_code = code
stdin:close()
stdout:close()
stderr:close()
finish_cond:notify_all()
end)
local files_data = {}
stdout:read_start(function(err, data)
if err then
logger.error(err)
return
function M.find(dir_path)
local sep = require("neotest.lib").files.sep
local dirs_to_scan = { dir_path }

local paths = {}
local dir, dir_handle
while dir_handle or #dirs_to_scan > 0 do
if not dir_handle then
dir = table.remove(dirs_to_scan, 1)
dir_handle = uv.fs_scandir(dir)
end
table.insert(files_data, data)
end)
stderr:read_start(function(err, data)
if err or data then
logger.error(err or data)

local next_path, path_type = uv.fs_scandir_next(dir_handle)

if not next_path then
dir_handle = nil
elseif path_type == "directory" and next_path:sub(1, 1) ~= "." then
local i = #dirs_to_scan + 1
dirs_to_scan[i] = dir .. sep .. next_path
elseif path_type == "file" then
paths[#paths + 1] = dir .. sep .. next_path
end
end)
finish_cond:wait()
logger.debug("Searching for files finished")
if result_code > 0 then
logger.error("Error while finding files")
return {}
end
local files = vim.split(table.concat(files_data, ""), "\n", { plain = true, trimempty = true })
return files
return paths
end

return M

0 comments on commit 5b9a840

Please sign in to comment.