Fix `dirmonitor` sorting issues (#1599)

* Use `PATHSEP` in path-related functions

* Don't stop on digits when getting the common part in `system.path_compare`

* Avoid sorting multiple times in `dirwatch.get_directory_files`

This also fixes the timeout detection in `recurse_pred`.
This commit is contained in:
Guldoman 2023-11-29 15:55:38 +01:00 committed by George Sokianos
parent 1dceaf65f5
commit 3ee903b16c
5 changed files with 37 additions and 42 deletions

View File

@ -226,7 +226,7 @@ function common.path_suggest(text, root)
if root and root:sub(-1) ~= PATHSEP then if root and root:sub(-1) ~= PATHSEP then
root = root .. PATHSEP root = root .. PATHSEP
end end
local path, name = text:match("^(.-)([^/\\]*)$") local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
local clean_dotslash = false local clean_dotslash = false
-- ignore root if path is absolute -- ignore root if path is absolute
local is_absolute = common.is_absolute_path(text) local is_absolute = common.is_absolute_path(text)
@ -279,7 +279,7 @@ end
---@param text string The input path. ---@param text string The input path.
---@return string[] ---@return string[]
function common.dir_path_suggest(text) function common.dir_path_suggest(text)
local path, name = text:match("^(.-)([^/\\]*)$") local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
local files = system.list_dir(path == "" and "." or path) or {} local files = system.list_dir(path == "" and "." or path) or {}
local res = {} local res = {}
for _, file in ipairs(files) do for _, file in ipairs(files) do
@ -298,7 +298,7 @@ end
---@param dir_list string[] A list of paths to filter. ---@param dir_list string[] A list of paths to filter.
---@return string[] ---@return string[]
function common.dir_list_suggest(text, dir_list) function common.dir_list_suggest(text, dir_list)
local path, name = text:match("^(.-)([^/\\]*)$") local path, name = text:match("^(.-)([^"..PATHSEP.."]*)$")
local res = {} local res = {}
for _, dir_path in ipairs(dir_list) do for _, dir_path in ipairs(dir_list) do
if dir_path:lower():find(text:lower(), nil, true) == 1 then if dir_path:lower():find(text:lower(), nil, true) == 1 then
@ -461,7 +461,7 @@ end
function common.basename(path) function common.basename(path)
-- a path should never end by / or \ except if it is '/' (unix root) or -- a path should never end by / or \ except if it is '/' (unix root) or
-- 'X:\' (windows drive) -- 'X:\' (windows drive)
return path:match("[^\\/]+$") or path return path:match("[^"..PATHSEP.."]+$") or path
end end
@ -470,7 +470,7 @@ end
---@param path string ---@param path string
---@return string|nil ---@return string|nil
function common.dirname(path) function common.dirname(path)
return path:match("(.+)[\\/][^\\/]+$") return path:match("(.+)["..PATHSEP.."][^"..PATHSEP.."]+$")
end end
@ -513,10 +513,10 @@ end
local function split_on_slash(s, sep_pattern) local function split_on_slash(s, sep_pattern)
local t = {} local t = {}
if s:match("^[/\\]") then if s:match("^["..PATHSEP.."]") then
t[#t + 1] = "" t[#t + 1] = ""
end end
for fragment in string.gmatch(s, "([^/\\]+)") do for fragment in string.gmatch(s, "([^"..PATHSEP.."]+)") do
t[#t + 1] = fragment t[#t + 1] = fragment
end end
return t return t
@ -649,7 +649,7 @@ function common.mkdirp(path)
while path and path ~= "" do while path and path ~= "" do
local success_mkdir = system.mkdir(path) local success_mkdir = system.mkdir(path)
if success_mkdir then break end if success_mkdir then break end
local updir, basedir = path:match("(.*)[/\\](.+)$") local updir, basedir = path:match("(.*)["..PATHSEP.."](.+)$")
table.insert(subdirs, 1, basedir or path) table.insert(subdirs, 1, basedir or path)
path = updir path = updir
end end

View File

@ -190,49 +190,47 @@ end
-- "root" will by an absolute path without trailing '/' -- "root" will by an absolute path without trailing '/'
-- "path" will be a path starting without '/' and without trailing '/' -- "path" will be a path starting without '/' and without trailing '/'
-- or the empty string. -- or the empty string.
-- It will identifies a sub-path within "root. -- It identifies a sub-path within "root".
-- The current path location will therefore always be: root .. path. -- The current path location will therefore always be: root .. path.
-- When recursing "root" will always be the same, only "path" will change. -- When recursing, "root" will always be the same, only "path" will change.
-- Returns a list of file "items". In each item the "filename" will be the -- Returns a list of file "items". In each item the "filename" will be the
-- complete file path relative to "root" *without* the trailing '/', and without the starting '/'. -- complete file path relative to "root" *without* the trailing '/', and without the starting '/'.
function dirwatch.get_directory_files(dir, root, path, t, entries_count, recurse_pred) function dirwatch.get_directory_files(dir, root, path, entries_count, recurse_pred)
local t = {}
local t0 = system.get_time() local t0 = system.get_time()
local t_elapsed = system.get_time() - t0
local dirs, files = {}, {}
local ignore_compiled = compile_ignore_files() local ignore_compiled = compile_ignore_files()
local all = system.list_dir(root .. PATHSEP .. path) local all = system.list_dir(root .. PATHSEP .. path)
if not all then return nil end if not all then return nil end
local entries = { }
for _, file in ipairs(all or {}) do for _, file in ipairs(all) do
local info = get_project_file_info(root, (path ~= "" and (path .. PATHSEP) or "") .. file, ignore_compiled) local info = get_project_file_info(root, (path ~= "" and (path .. PATHSEP) or "") .. file, ignore_compiled)
if info then if info then
table.insert(info.type == "dir" and dirs or files, info) table.insert(entries, info)
entries_count = entries_count + 1
end end
end end
table.sort(entries, compare_file)
local recurse_complete = true local recurse_complete = true
table.sort(dirs, compare_file) for _, info in ipairs(entries) do
for _, f in ipairs(dirs) do table.insert(t, info)
table.insert(t, f) entries_count = entries_count + 1
if recurse_pred(dir, f.filename, entries_count, t_elapsed) then if info.type == "dir" then
local _, complete, n = dirwatch.get_directory_files(dir, root, f.filename, t, entries_count, recurse_pred) if recurse_pred(dir, info.filename, entries_count, system.get_time() - t0) then
recurse_complete = recurse_complete and complete local t_rec, complete, n = dirwatch.get_directory_files(dir, root, info.filename, entries_count, recurse_pred)
if n ~= nil then recurse_complete = recurse_complete and complete
entries_count = n if n ~= nil then
entries_count = n
for _, info_rec in ipairs(t_rec) do
table.insert(t, info_rec)
end
end
else
recurse_complete = false
end end
else
recurse_complete = false
end end
end end
table.sort(files, compare_file)
for _, f in ipairs(files) do
table.insert(t, f)
end
return t, recurse_complete, entries_count return t, recurse_complete, entries_count
end end

View File

@ -102,7 +102,7 @@ local function strip_leading_path(filename)
end end
local function strip_trailing_slash(filename) local function strip_trailing_slash(filename)
if filename:match("[^:][/\\]$") then if filename:match("[^:]["..PATHSEP.."]$") then
return filename:sub(1, -2) return filename:sub(1, -2)
end end
return filename return filename
@ -120,9 +120,7 @@ local function show_max_files_warning(dir)
"Too many files in project directory: stopped reading at ".. "Too many files in project directory: stopped reading at "..
config.max_project_files.." files. For more information see ".. config.max_project_files.." files. For more information see "..
"usage.md at https://github.com/lite-xl/lite-xl." "usage.md at https://github.com/lite-xl/lite-xl."
if core.status_view then core.warn(message)
core.status_view:show_message("!", style.accent, message)
end
end end
@ -184,7 +182,7 @@ local function refresh_directory(topdir, target)
directory_start_idx = directory_start_idx + 1 directory_start_idx = directory_start_idx + 1
end end
local files = dirwatch.get_directory_files(topdir, topdir.name, (target or ""), {}, 0, function() return false end) local files = dirwatch.get_directory_files(topdir, topdir.name, (target or ""), 0, function() return false end)
local change = false local change = false
-- If this file doesn't exist, we should be calling this on our parent directory, assume we'll do that. -- If this file doesn't exist, we should be calling this on our parent directory, assume we'll do that.
@ -265,7 +263,7 @@ function core.add_project_directory(path)
local fstype = PLATFORM == "Linux" and system.get_fs_type(topdir.name) or "unknown" local fstype = PLATFORM == "Linux" and system.get_fs_type(topdir.name) or "unknown"
topdir.force_scans = (fstype == "nfs" or fstype == "fuse") topdir.force_scans = (fstype == "nfs" or fstype == "fuse")
local t, complete, entries_count = dirwatch.get_directory_files(topdir, topdir.name, "", {}, 0, timed_max_files_pred) local t, complete, entries_count = dirwatch.get_directory_files(topdir, topdir.name, "", 0, timed_max_files_pred)
topdir.files = t topdir.files = t
if not complete then if not complete then
topdir.slow_filesystem = not complete and (entries_count <= config.max_project_files) topdir.slow_filesystem = not complete and (entries_count <= config.max_project_files)

View File

@ -29,7 +29,7 @@ local tooltip_alpha_rate = 1
local function get_depth(filename) local function get_depth(filename)
local n = 1 local n = 1
for sep in filename:gmatch("[\\/]") do for _ in filename:gmatch(PATHSEP) do
n = n + 1 n = n + 1
end end
return n return n

View File

@ -1058,7 +1058,7 @@ static int f_load_native_plugin(lua_State *L) {
#endif #endif
/* Special purpose filepath compare function. Corresponds to the /* Special purpose filepath compare function. Corresponds to the
order used in the TreeView view of the project's files. Returns true iff order used in the TreeView view of the project's files. Returns true if
path1 < path2 in the TreeView order. */ path1 < path2 in the TreeView order. */
static int f_path_compare(lua_State *L) { static int f_path_compare(lua_State *L) {
size_t len1, len2; size_t len1, len2;
@ -1072,7 +1072,6 @@ static int f_path_compare(lua_State *L) {
size_t offset = 0, i, j; size_t offset = 0, i, j;
for (i = 0; i < len1 && i < len2; i++) { for (i = 0; i < len1 && i < len2; i++) {
if (path1[i] != path2[i]) break; if (path1[i] != path2[i]) break;
if (isdigit(path1[i])) break;
if (path1[i] == PATHSEP) { if (path1[i] == PATHSEP) {
offset = i + 1; offset = i + 1;
} }