First integration of dmon for directory monitoring
This commit is contained in:
parent
18189e63b6
commit
1ba385eb5e
|
@ -99,6 +99,18 @@ local function compare_file(a, b)
|
|||
return a.filename < b.filename
|
||||
end
|
||||
|
||||
|
||||
-- compute a file's info entry completed with "filename" to be used
|
||||
-- in project scan or falsy if it shouldn't appear in the list.
|
||||
local function get_project_file_info(root, file, size_limit)
|
||||
local info = system.get_file_info(root .. file)
|
||||
if info then
|
||||
info.filename = strip_leading_path(file)
|
||||
end
|
||||
return info and info.size < size_limit and info
|
||||
end
|
||||
|
||||
|
||||
-- "root" will by an absolute path without trailing '/'
|
||||
-- "path" will be a path starting with '/' and without trailing '/'
|
||||
-- or the empty string.
|
||||
|
@ -117,10 +129,8 @@ local function get_directory_files(root, path, t, recursive, begin_hook)
|
|||
local max_entries = config.max_project_files
|
||||
for _, file in ipairs(all) do
|
||||
if not common.match_pattern(file, config.ignore_files) then
|
||||
local file = path .. PATHSEP .. file
|
||||
local info = system.get_file_info(root .. file)
|
||||
if info and info.size < size_limit then
|
||||
info.filename = strip_leading_path(file)
|
||||
local info = get_project_file_info(root, path .. PATHSEP .. file, size_limit)
|
||||
if info then
|
||||
table.insert(info.type == "dir" and dirs or files, info)
|
||||
entries_count = entries_count + 1
|
||||
if recursive and entries_count > max_entries then return nil, entries_count end
|
||||
|
@ -276,6 +286,69 @@ function core.project_files_number()
|
|||
end
|
||||
|
||||
|
||||
local function file_search(files, info)
|
||||
local filename, type = info.filename, info.type
|
||||
local inf, sup = 1, #files
|
||||
while sup - inf > 8 do
|
||||
local curr = math.floor((inf + sup) / 2)
|
||||
if system.path_compare(filename, type, files[curr].filename, files[curr].type) then
|
||||
sup = curr - 1
|
||||
else
|
||||
inf = curr
|
||||
end
|
||||
end
|
||||
repeat
|
||||
if files[inf].filename == filename then
|
||||
return inf, true
|
||||
end
|
||||
inf = inf + 1
|
||||
until inf > sup or system.path_compare(filename, type, files[inf].filename, files[inf].type)
|
||||
return inf, false
|
||||
end
|
||||
|
||||
|
||||
local function project_scan_remove_file(watch_id, filepath)
|
||||
local project_dir_entry
|
||||
for i = 1, #core.project_directories do
|
||||
if core.project_directories[i].watch_id == watch_id then
|
||||
project_dir_entry = core.project_directories[i]
|
||||
end
|
||||
end
|
||||
if not project_dir_entry then return end
|
||||
print("LOOKING for", filepath, " in", project_dir_entry and project_dir_entry.name)
|
||||
local fileinfo = { filename = filepath }
|
||||
for _, filetype in ipairs {"dir", "file"} do
|
||||
fileinfo.type = filetype
|
||||
local index, match = file_search(project_dir_entry.files, fileinfo)
|
||||
if match then
|
||||
print("FOUND", filepath, " at index", index)
|
||||
table.remove(project_dir_entry.files, index)
|
||||
project_dir_entry.is_dirty = true
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function project_scan_add_file(watch_id, filepath)
|
||||
local project_dir_entry
|
||||
for i = 1, #core.project_directories do
|
||||
if core.project_directories[i].watch_id == watch_id then
|
||||
project_dir_entry = core.project_directories[i]
|
||||
end
|
||||
end
|
||||
if not project_dir_entry then return end
|
||||
local size_limit = config.file_size_limit * 10e5
|
||||
local fileinfo = get_project_file_info(project_dir_entry.name, PATHSEP .. filepath, size_limit)
|
||||
local index, match = file_search(project_dir_entry.files, fileinfo)
|
||||
if not match then
|
||||
table.insert(project_dir_entry.files, index, fileinfo)
|
||||
project_dir_entry.is_dirty = true
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- create a directory using mkdir but may need to create the parent
|
||||
-- directories as well.
|
||||
local function create_user_directory()
|
||||
|
@ -373,10 +446,13 @@ function core.add_project_directory(path)
|
|||
-- 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 watch_id = system.watch_dir(path);
|
||||
table.insert(core.project_directories, {
|
||||
name = path,
|
||||
item = {filename = common.basename(path), type = "dir", topdir = true},
|
||||
files = {}
|
||||
files = {},
|
||||
is_dirty = true,
|
||||
watch_id = watch_id,
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -916,6 +992,15 @@ function core.try(fn, ...)
|
|||
end
|
||||
|
||||
|
||||
function core.on_dir_change(watch_id, action, filepath)
|
||||
if action == "delete" then
|
||||
project_scan_remove_file(watch_id, filepath)
|
||||
elseif action == "create" then
|
||||
project_scan_add_file(watch_id, filepath)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function core.on_event(type, ...)
|
||||
local did_keymap = false
|
||||
if type == "textinput" then
|
||||
|
@ -951,6 +1036,9 @@ function core.on_event(type, ...)
|
|||
end
|
||||
elseif type == "focuslost" then
|
||||
core.root_view:on_focus_lost(...)
|
||||
elseif type == "dirchange" then
|
||||
print("DEBUG: dirchange", select(1, ...), select(2, ...), select(3, ...))
|
||||
core.on_dir_change(...)
|
||||
elseif type == "quit" then
|
||||
core.quit()
|
||||
end
|
||||
|
|
|
@ -111,11 +111,12 @@ function TreeView:check_cache()
|
|||
if not last_files then
|
||||
self.last[dir.name] = dir.files
|
||||
else
|
||||
if dir.files ~= last_files then
|
||||
if dir.is_dirty or dir.files ~= last_files then
|
||||
self:invalidate_cache(dir.name)
|
||||
self.last[dir.name] = dir.files
|
||||
end
|
||||
end
|
||||
dir.is_dirty = false
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -22,6 +22,33 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
## septag/dmon
|
||||
|
||||
Copyright 2019 Sepehr Taghdisian. All rights reserved.
|
||||
|
||||
https://github.com/septag/dmon
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
## Fira Sans
|
||||
|
||||
Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include "api.h"
|
||||
#include "dirmonitor.h"
|
||||
#include "rencache.h"
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
|
@ -222,6 +223,14 @@ top:
|
|||
lua_pushnumber(L, e.wheel.y);
|
||||
return 2;
|
||||
|
||||
case SDL_USEREVENT:
|
||||
lua_pushstring(L, "dirchange");
|
||||
lua_pushnumber(L, e.user.code >> 16);
|
||||
lua_pushstring(L, (e.user.code & 0xffff) == DMON_ACTION_DELETE ? "delete" : "create");
|
||||
lua_pushstring(L, e.user.data1);
|
||||
free(e.user.data1);
|
||||
return 4;
|
||||
|
||||
default:
|
||||
goto top;
|
||||
}
|
||||
|
@ -637,6 +646,56 @@ static int f_set_window_opacity(lua_State *L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void watch_callback(dmon_watch_id watch_id, dmon_action action, const char* rootdir,
|
||||
const char* filepath, const char* oldfilepath, void* user)
|
||||
{
|
||||
(void)(user);
|
||||
(void)(rootdir);
|
||||
dirmonitor_push_event(watch_id, action, filepath, oldfilepath);
|
||||
}
|
||||
|
||||
static int f_watch_dir(lua_State *L) {
|
||||
const char *path = luaL_checkstring(L, 1);
|
||||
fprintf(stderr, "DEBUG: watching dir: %s\n", path); fflush(stderr);
|
||||
dmon_watch_id watch_id = dmon_watch(path, watch_callback, DMON_WATCHFLAGS_RECURSIVE, NULL);
|
||||
lua_pushnumber(L, watch_id.id);
|
||||
// FIXME: we ignore if there is an error.
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PATHSEP '\\'
|
||||
#else
|
||||
#define PATHSEP '/'
|
||||
#endif
|
||||
|
||||
|
||||
static int f_path_compare(lua_State *L) {
|
||||
const char *path1 = luaL_checkstring(L, 1);
|
||||
const char *type1_s = luaL_checkstring(L, 2);
|
||||
const char *path2 = luaL_checkstring(L, 3);
|
||||
const char *type2_s = luaL_checkstring(L, 4);
|
||||
const int len1 = strlen(path1), len2 = strlen(path2);
|
||||
int type1 = strcmp(type1_s, "dir") != 0;
|
||||
int type2 = strcmp(type2_s, "dir") != 0;
|
||||
int i;
|
||||
for (i = 0; i < len1 && i < len2; i++) {
|
||||
if (path1[i] != path2[i]) break;
|
||||
}
|
||||
if (strchr(path1 + i, PATHSEP)) {
|
||||
type1 = 0;
|
||||
}
|
||||
if (strchr(path2 + i, PATHSEP)) {
|
||||
type2 = 0;
|
||||
}
|
||||
if (type1 != type2) {
|
||||
lua_pushboolean(L, type1 < type2);
|
||||
return 1;
|
||||
}
|
||||
lua_pushboolean(L, strcmp(path1 + i, path2 + i) < 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg lib[] = {
|
||||
{ "poll_event", f_poll_event },
|
||||
|
@ -664,6 +723,8 @@ static const luaL_Reg lib[] = {
|
|||
{ "exec", f_exec },
|
||||
{ "fuzzy_match", f_fuzzy_match },
|
||||
{ "set_window_opacity", f_set_window_opacity },
|
||||
{ "watch_dir", f_watch_dir },
|
||||
{ "path_compare", f_path_compare },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#define DMON_IMPL
|
||||
#include "dmon.h"
|
||||
|
||||
#include "dirmonitor.h"
|
||||
|
||||
static void send_sdl_event(dmon_watch_id watch_id, dmon_action action, const char *filepath) {
|
||||
SDL_Event ev;
|
||||
const int size = strlen(filepath) + 1;
|
||||
char *new_filepath = malloc(size);
|
||||
if (!new_filepath) return;
|
||||
memcpy(new_filepath, filepath, size);
|
||||
#ifdef _WIN32
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (new_filepath[i] == '/') {
|
||||
new_filepath[i] = '\\';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
SDL_zero(ev);
|
||||
ev.type = SDL_USEREVENT;
|
||||
fprintf(stderr, "DEBUG: send watch_id: %d action; %d\n", watch_id.id, action); fflush(stderr);
|
||||
ev.user.code = ((watch_id.id & 0xffff) << 16) | (action & 0xffff);
|
||||
ev.user.data1 = new_filepath;
|
||||
SDL_PushEvent(&ev);
|
||||
}
|
||||
|
||||
void dirmonitor_init() {
|
||||
dmon_init();
|
||||
/* FIXME: not needed ? */
|
||||
/* sdl_dmon_event_type = SDL_RegisterEvents(1); */
|
||||
}
|
||||
|
||||
void dirmonitor_deinit() {
|
||||
dmon_deinit();
|
||||
}
|
||||
|
||||
void dirmonitor_push_event(dmon_watch_id watch_id, dmon_action action, const char *filepath,
|
||||
const char *oldfilepath)
|
||||
{
|
||||
switch (action) {
|
||||
case DMON_ACTION_MOVE:
|
||||
send_sdl_event(watch_id, DMON_ACTION_DELETE, oldfilepath);
|
||||
send_sdl_event(watch_id, DMON_ACTION_CREATE, filepath);
|
||||
break;
|
||||
case DMON_ACTION_MODIFY:
|
||||
break;
|
||||
default:
|
||||
send_sdl_event(watch_id, action, filepath);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef DIRMONITOR_H
|
||||
#define DIRMONITOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "dmon.h"
|
||||
|
||||
void dirmonitor_init();
|
||||
void dirmonitor_deinit();
|
||||
void dirmonitor_push_event(dmon_watch_id watch_id, dmon_action action, const char *filepath,
|
||||
const char *oldfilepath);
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -14,6 +14,8 @@
|
|||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
|
||||
#include "dirmonitor.h"
|
||||
|
||||
|
||||
SDL_Window *window;
|
||||
|
||||
|
@ -108,6 +110,8 @@ int main(int argc, char **argv) {
|
|||
SDL_DisplayMode dm;
|
||||
SDL_GetCurrentDisplayMode(0, &dm);
|
||||
|
||||
dirmonitor_init();
|
||||
|
||||
window = SDL_CreateWindow(
|
||||
"", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w * 0.8, dm.h * 0.8,
|
||||
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
|
||||
|
@ -191,6 +195,7 @@ init_lua:
|
|||
|
||||
lua_close(L);
|
||||
ren_free_window_resources();
|
||||
dirmonitor_deinit();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ lite_sources = [
|
|||
'api/regex.c',
|
||||
'api/system.c',
|
||||
'api/process.c',
|
||||
'dirmonitor.c',
|
||||
'renderer.c',
|
||||
'renwindow.c',
|
||||
'fontdesc.c',
|
||||
|
|
Loading…
Reference in New Issue