Compare commits
7 Commits
amiga2.1
...
incrementa
Author | SHA1 | Date |
---|---|---|
Francesco Abbate | 5c8ba47f2c | |
Francesco Abbate | 7027f15d55 | |
Francesco Abbate | 1794765f07 | |
Francesco Abbate | bed103b7a6 | |
Francesco Abbate | 80db3cb027 | |
Francesco Abbate | a0f4ac93e1 | |
Francesco Abbate | 43fc35d7dc |
|
@ -280,24 +280,61 @@ local function split_on_slash(s, sep_pattern)
|
||||||
end
|
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 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
|
if PATHSEP == '\\' then
|
||||||
filename = filename:gsub('[/\\]', '\\')
|
filename = filename:gsub('[/\\]', '\\')
|
||||||
local drive, rem = filename:match('^([a-zA-Z])(:.*)')
|
local drive, rem = filename:match('^([a-zA-Z]:\\)(.*)')
|
||||||
filename = drive and drive:upper() .. rem or filename
|
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
|
end
|
||||||
local parts = split_on_slash(filename, PATHSEP)
|
local parts = split_on_slash(filename, PATHSEP)
|
||||||
local accu = {}
|
local accu = {}
|
||||||
for _, part in ipairs(parts) do
|
for _, part in ipairs(parts) do
|
||||||
if part == '..' and #accu > 0 and accu[#accu] ~= ".." then
|
if part == '..' then
|
||||||
|
if #accu > 0 and accu[#accu] ~= ".." then
|
||||||
table.remove(accu)
|
table.remove(accu)
|
||||||
|
elseif volume then
|
||||||
|
error("invalid path " .. volume .. filename)
|
||||||
|
else
|
||||||
|
table.insert(accu, part)
|
||||||
|
end
|
||||||
elseif part ~= '.' then
|
elseif part ~= '.' then
|
||||||
table.insert(accu, part)
|
table.insert(accu, part)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local npath = table.concat(accu, PATHSEP)
|
local npath = table.concat(accu, PATHSEP)
|
||||||
return npath == "" and PATHSEP or npath
|
return (volume or "") .. (npath == "" and PATHSEP or npath)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ end
|
||||||
|
|
||||||
|
|
||||||
local function update_recents_project(action, dir_path_abs)
|
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
|
if not dirname then return end
|
||||||
local recents = core.recent_projects
|
local recents = core.recent_projects
|
||||||
local n = #recents
|
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)
|
local chdir_ok = pcall(system.chdir, new_dir)
|
||||||
if chdir_ok then
|
if chdir_ok then
|
||||||
if change_project_fn then change_project_fn() end
|
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.project_directories = {}
|
||||||
core.add_project_directory(new_dir)
|
core.add_project_directory(new_dir)
|
||||||
return true
|
return true
|
||||||
|
@ -173,45 +173,10 @@ local function show_max_files_warning()
|
||||||
)
|
)
|
||||||
end
|
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
|
|
||||||
|
|
||||||
|
local function watch_folder(dir)
|
||||||
function core.add_project_directory(path)
|
local rec = dir.entries_count <= config.max_project_files or PLATFORM ~= "Linux"
|
||||||
-- top directories has a file-like "item" but the item.filename
|
dir.watch_id = system.watch_dir(dir.name, rec)
|
||||||
-- 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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -304,12 +269,59 @@ local function rescan_project_subdir(dir, filename_rooted)
|
||||||
local filename = strip_leading_path(filename_rooted)
|
local filename = strip_leading_path(filename_rooted)
|
||||||
index, n = project_subdir_bounds(dir, filename)
|
index, n = project_subdir_bounds(dir, filename)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not files_list_match(dir.files, index, n, new_files) then
|
if not files_list_match(dir.files, index, n, new_files) then
|
||||||
files_list_replace(dir.files, index, n, new_files)
|
files_list_replace(dir.files, index, n, new_files)
|
||||||
dir.is_dirty = true
|
dir.is_dirty = true
|
||||||
return true
|
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -572,9 +584,9 @@ function core.init()
|
||||||
Doc = require "core.doc"
|
Doc = require "core.doc"
|
||||||
|
|
||||||
if PATHSEP == '\\' then
|
if PATHSEP == '\\' then
|
||||||
USERDIR = common.normalize_path(USERDIR)
|
USERDIR = common.normalize_volume(USERDIR)
|
||||||
DATADIR = common.normalize_path(DATADIR)
|
DATADIR = common.normalize_volume(DATADIR)
|
||||||
EXEDIR = common.normalize_path(EXEDIR)
|
EXEDIR = common.normalize_volume(EXEDIR)
|
||||||
end
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
|
@ -919,10 +931,17 @@ function core.show_title_bar(show)
|
||||||
end
|
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 key = weak_ref or #core.threads + 1
|
||||||
local fn = function() return core.try(f) end
|
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
|
return key
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1066,12 +1085,6 @@ end
|
||||||
|
|
||||||
local scheduled_rescan = {}
|
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)
|
function core.dir_rescan_add_job(dir, filepath)
|
||||||
local dirpath = filepath:match("^(.+)[/\\].+$")
|
local dirpath = filepath:match("^(.+)[/\\].+$")
|
||||||
|
@ -1108,8 +1121,8 @@ function core.dir_rescan_add_job(dir, filepath)
|
||||||
local rescan = scheduled_rescan[abs_dirpath]
|
local rescan = scheduled_rescan[abs_dirpath]
|
||||||
if not rescan then return end
|
if not rescan then return end
|
||||||
if system.get_time() > rescan.time_limit then
|
if system.get_time() > rescan.time_limit then
|
||||||
local has_changes = rescan_project_subdir(rescan.dir, rescan.path)
|
local n_changes = rescan_project_subdir(rescan.dir, rescan.path)
|
||||||
if has_changes then
|
if n_changes > 0 then
|
||||||
core.redraw = true -- we run without an event, from a thread
|
core.redraw = true -- we run without an event, from a thread
|
||||||
rescan.time_limit = new_time
|
rescan.time_limit = new_time
|
||||||
else
|
else
|
||||||
|
@ -1119,7 +1132,7 @@ function core.dir_rescan_add_job(dir, filepath)
|
||||||
end
|
end
|
||||||
coroutine.yield(0.2)
|
coroutine.yield(0.2)
|
||||||
end
|
end
|
||||||
end)
|
end, nil, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -1286,7 +1299,7 @@ function core.run()
|
||||||
while true do
|
while true do
|
||||||
core.frame_start = system.get_time()
|
core.frame_start = system.get_time()
|
||||||
local did_redraw = core.step()
|
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 core.restart_request or core.quit_request then break end
|
||||||
if not did_redraw and not need_more_work then
|
if not did_redraw and not need_more_work then
|
||||||
idle_iterations = idle_iterations + 1
|
idle_iterations = idle_iterations + 1
|
||||||
|
|
|
@ -412,7 +412,7 @@ end
|
||||||
-- axis are swapped; this function lets us use the same code for both
|
-- 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 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 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[x] = self.position[x]
|
||||||
self.a.position[y] = self.position[y]
|
self.a.position[y] = self.position[y]
|
||||||
self.a.size[x] = n - ds
|
self.a.size[x] = n - ds
|
||||||
|
@ -675,6 +675,10 @@ end
|
||||||
|
|
||||||
|
|
||||||
function Node:resize(axis, value)
|
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 self.type == 'leaf' then
|
||||||
-- If it is not locked we don't accept the
|
-- If it is not locked we don't accept the
|
||||||
-- resize operation here because for proportional panes the resize is
|
-- resize operation here because for proportional panes the resize is
|
||||||
|
|
Loading…
Reference in New Issue