Compare commits

...

7 Commits

Author SHA1 Message Date
Francesco Abbate 5c8ba47f2c Add priority threads to keep editor busy 2021-10-18 23:26:33 +02:00
Francesco Abbate 7027f15d55 Add a mechanism for incremental loading 2021-10-18 23:24:23 +02:00
Francesco Abbate 1794765f07 Fix problem with treeview keeping the editor busy
Fix a problem introduced when fixing the dirty pixel problem, commit
cb08c5c. The node, when determining the layout was rounding the size
of the fixed-size view. In turns this latter was calling move_towards
to the default_size it wanted. If default_size was non-integer the
value vas never archieved because it was rounded during layout and
move_towars was keeping the editor busy by setting the
core.need_redraw flag.
2021-10-14 09:13:06 +02:00
Francesco Abbate bed103b7a6 Fix error introduced with 43fc35d7 2021-10-12 22:19:24 +02:00
Francesco Abbate 80db3cb027 Fix problem with treeview x resizing
The x size of the treeview plugin cannot really change expect if explicitly
resized.

The call to move_towards for x seems to raise a state where core.redraw is
always set to true and this prevent the application to go idle.

It is seen after the introduction of the dmon directory monitoring but it
is not clear why it wasn't seen before.
2021-10-12 18:06:00 +02:00
Francesco Abbate a0f4ac93e1 Do not use normalize_path when not needed 2021-10-12 16:08:06 +02:00
Francesco Abbate 43fc35d7dc First attempt to treat correctly network volumes
On windows paths belonging to network volumes will be gives like:

\\address\share-name\path

Now the code recognize these paths and treat them correctly.
2021-10-12 14:28:28 +02:00
3 changed files with 118 additions and 64 deletions

View File

@ -280,24 +280,61 @@ local function split_on_slash(s, sep_pattern)
end
function common.normalize_path(filename)
-- The filename argument given to the function is supposed to
-- come from system.absolute_path and as such should be an
-- absolute path without . or .. elements.
-- This function exists because on Windows the drive letter returned
-- by system.absolute_path is sometimes with a lower case and sometimes
-- with an upper case to we normalize to upper case.
function common.normalize_volume(filename)
if not filename then return end
if PATHSEP == '\\' then
local drive, rem = filename:match('^([a-zA-Z]:\\)(.*)')
if drive then
return drive:upper() .. rem
end
end
return filename
end
function common.normalize_path(filename)
if not filename then return end
local volume
if PATHSEP == '\\' then
filename = filename:gsub('[/\\]', '\\')
local drive, rem = filename:match('^([a-zA-Z])(:.*)')
filename = drive and drive:upper() .. rem or filename
local drive, rem = filename:match('^([a-zA-Z]:\\)(.*)')
if drive then
volume, filename = drive:upper(), rem
else
drive, rem = filename:match('^(\\\\[^\\]+\\[^\\]+\\)(.*)')
if drive then
volume, filename = drive, rem
end
end
else
local relpath = filename:match('^/(.+)')
if relpath then
volume, filename = "/", relpath
end
end
local parts = split_on_slash(filename, PATHSEP)
local accu = {}
for _, part in ipairs(parts) do
if part == '..' and #accu > 0 and accu[#accu] ~= ".." then
table.remove(accu)
if part == '..' then
if #accu > 0 and accu[#accu] ~= ".." then
table.remove(accu)
elseif volume then
error("invalid path " .. volume .. filename)
else
table.insert(accu, part)
end
elseif part ~= '.' then
table.insert(accu, part)
end
end
local npath = table.concat(accu, PATHSEP)
return npath == "" and PATHSEP or npath
return (volume or "") .. (npath == "" and PATHSEP or npath)
end

View File

@ -36,7 +36,7 @@ end
local function update_recents_project(action, dir_path_abs)
local dirname = common.normalize_path(dir_path_abs)
local dirname = common.normalize_volume(dir_path_abs)
if not dirname then return end
local recents = core.recent_projects
local n = #recents
@ -56,7 +56,7 @@ function core.set_project_dir(new_dir, change_project_fn)
local chdir_ok = pcall(system.chdir, new_dir)
if chdir_ok then
if change_project_fn then change_project_fn() end
core.project_dir = common.normalize_path(new_dir)
core.project_dir = common.normalize_volume(new_dir)
core.project_directories = {}
core.add_project_directory(new_dir)
return true
@ -173,45 +173,10 @@ local function show_max_files_warning()
)
end
-- Populate a project folder top directory by scanning the filesystem.
local function scan_project_folder(index)
local dir = core.project_directories[index]
local t, entries_count = get_directory_files(dir, dir.name, "", {}, nil, config.max_project_files)
if entries_count > config.max_project_files then
dir.files_limit = true
-- Watch non-recursively on Linux only.
-- The reason is recursively watching with dmon on linux
-- doesn't work on very large directories.
dir.watch_id = system.watch_dir(dir.name, PLATFORM ~= "Linux")
if core.status_view then -- May be not yet initialized.
show_max_files_warning()
end
else
dir.watch_id = system.watch_dir(dir.name, true)
end
dir.files = t
core.dir_rescan_add_job(dir, ".")
end
function core.add_project_directory(path)
-- top directories has a file-like "item" but the item.filename
-- will be simply the name of the directory, without its path.
-- The field item.topdir will identify it as a top level directory.
path = common.normalize_path(path)
local dir = {
name = path,
item = {filename = common.basename(path), type = "dir", topdir = true},
files_limit = false,
is_dirty = true,
shown_subdir = {},
}
table.insert(core.project_directories, dir)
scan_project_folder(#core.project_directories)
if path == core.project_dir then
core.project_files = dir.files
end
core.redraw = true
local function watch_folder(dir)
local rec = dir.entries_count <= config.max_project_files or PLATFORM ~= "Linux"
dir.watch_id = system.watch_dir(dir.name, rec)
end
@ -304,12 +269,59 @@ local function rescan_project_subdir(dir, filename_rooted)
local filename = strip_leading_path(filename_rooted)
index, n = project_subdir_bounds(dir, filename)
end
if not files_list_match(dir.files, index, n, new_files) then
files_list_replace(dir.files, index, n, new_files)
dir.is_dirty = true
return true
end
return #new_files - n
end
-- Populate a project folder top directory by scanning the filesystem.
local function scan_project_folder(index)
local dir = core.project_directories[index]
dir.files, dir.entries_count = get_directory_files(dir, dir.name, "", {}, nil, 0)
local dirs_list = {}
for _, info in pairs(dir.files) do
if info.type == 'dir' then
dirs_list[#dirs_list + 1] = info
end
end
core.add_thread(function()
for _, info in pairs(dirs_list) do
local n = rescan_project_subdir(dir, PATHSEP .. info.filename)
dir.entries_count = dir.entries_count + n
if dir.entries_count > config.max_project_files then
dir.files_limit = true
show_max_files_warning()
break
end
coroutine.yield()
end
watch_folder(dir)
core.dir_rescan_add_job(dir, ".")
end, nil, true)
end
function core.add_project_directory(path)
-- top directories has a file-like "item" but the item.filename
-- will be simply the name of the directory, without its path.
-- The field item.topdir will identify it as a top level directory.
path = common.normalize_volume(path)
local dir = {
name = path,
item = {filename = common.basename(path), type = "dir", topdir = true},
files_limit = false,
is_dirty = true,
shown_subdir = {},
}
table.insert(core.project_directories, dir)
scan_project_folder(#core.project_directories)
if path == core.project_dir then
core.project_files = dir.files
end
core.redraw = true
end
@ -572,9 +584,9 @@ function core.init()
Doc = require "core.doc"
if PATHSEP == '\\' then
USERDIR = common.normalize_path(USERDIR)
DATADIR = common.normalize_path(DATADIR)
EXEDIR = common.normalize_path(EXEDIR)
USERDIR = common.normalize_volume(USERDIR)
DATADIR = common.normalize_volume(DATADIR)
EXEDIR = common.normalize_volume(EXEDIR)
end
do
@ -919,10 +931,17 @@ function core.show_title_bar(show)
end
function core.add_thread(f, weak_ref)
function core.has_priority_threads()
for _, t in ipairs(core.threads) do
if t.prio then return true end
end
end
function core.add_thread(f, weak_ref, priority)
local key = weak_ref or #core.threads + 1
local fn = function() return core.try(f) end
core.threads[key] = { cr = coroutine.create(fn), wake = 0 }
core.threads[key] = { cr = coroutine.create(fn), wake = 0, prio = priority }
return key
end
@ -1066,12 +1085,6 @@ end
local scheduled_rescan = {}
function core.has_pending_rescan()
for _ in pairs(scheduled_rescan) do
return true
end
end
function core.dir_rescan_add_job(dir, filepath)
local dirpath = filepath:match("^(.+)[/\\].+$")
@ -1108,8 +1121,8 @@ function core.dir_rescan_add_job(dir, filepath)
local rescan = scheduled_rescan[abs_dirpath]
if not rescan then return end
if system.get_time() > rescan.time_limit then
local has_changes = rescan_project_subdir(rescan.dir, rescan.path)
if has_changes then
local n_changes = rescan_project_subdir(rescan.dir, rescan.path)
if n_changes > 0 then
core.redraw = true -- we run without an event, from a thread
rescan.time_limit = new_time
else
@ -1119,7 +1132,7 @@ function core.dir_rescan_add_job(dir, filepath)
end
coroutine.yield(0.2)
end
end)
end, nil, true)
end
@ -1286,7 +1299,7 @@ function core.run()
while true do
core.frame_start = system.get_time()
local did_redraw = core.step()
local need_more_work = run_threads() or core.has_pending_rescan()
local need_more_work = run_threads() or core.has_priority_threads()
if core.restart_request or core.quit_request then break end
if not did_redraw and not need_more_work then
idle_iterations = idle_iterations + 1

View File

@ -412,7 +412,7 @@ end
-- axis are swapped; this function lets us use the same code for both
local function calc_split_sizes(self, x, y, x1, x2, y1, y2)
local ds = ((x1 and x1 < 1) or (x2 and x2 < 1)) and 0 or style.divider_size
local n = math.floor(x1 and x1 + ds or (x2 and self.size[x] - x2 or self.size[x] * self.divider))
local n = x1 and x1 + ds or (x2 and self.size[x] - x2 or math.floor(self.size[x] * self.divider))
self.a.position[x] = self.position[x]
self.a.position[y] = self.position[y]
self.a.size[x] = n - ds
@ -675,6 +675,10 @@ end
function Node:resize(axis, value)
-- the application works fine with non-integer values but to have pixel-perfect
-- placements of view elements, like the scrollbar, we round the value to be
-- an integer.
value = math.floor(value)
if self.type == 'leaf' then
-- If it is not locked we don't accept the
-- resize operation here because for proportional panes the resize is