1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
local fw = require 'bee.filewatch'
local fs = require 'bee.filesystem'
local plat = require 'bee.platform'
local await = require 'await'
local MODIFY = 1 << 0
local RENAME = 1 << 1
local function isExists(filename)
local path = fs.path(filename)
local suc, exists = pcall(fs.exists, path)
if not suc or not exists then
return false
end
if plat.OS ~= 'Windows' then
return true
end
local suc, res = pcall(fs.fullpath, path)
if not suc then
return false
end
if res :string():gsub('^%w+:', string.lower)
~= path:string():gsub('^%w+:', string.lower) then
return false
end
return true
end
---@class filewatch
local m = {}
m._eventList = {}
m._watchings = {}
---@async
---@param path string
---@param recursive boolean
---@param filter? async fun(path: string):boolean
function m.watch(path, recursive, filter)
if path == '' or not fs.is_directory(fs.path(path)) then
return function () end
end
if m._watchings[path] then
m._watchings[path].count = m._watchings[path].count + 1
else
local watch = fw.create()
watch:add(path)
log.debug('Watch add:', path)
if recursive then
local count = 1
---@async
local function scanDirctory(dir)
count = count + 1
if count % 10 == 0 then
await.delay()
if count % 100 == 0 then
log.warn('Watching so many dirs:', count, dir:string())
end
end
for fullpath, status in fs.pairs(dir) do
local st = status:type()
if st == 'directory'
or st == 'symlink'
or st == 'junction' then
if not filter or filter(fullpath:string()) then
watch:add(fullpath:string())
log.trace('Watch add:', fullpath:string())
xpcall(scanDirctory, log.error, fullpath)
end
end
end
end
xpcall(scanDirctory, log.error, fs.path(path))
end
m._watchings[path] = {
count = 1,
watch = watch,
}
log.debug('fw.add', path)
end
local removed
return function ()
if removed then
return
end
removed = true
m._watchings[path].count = m._watchings[path].count - 1
if m._watchings[path].count == 0 then
m._watchings[path] = nil
log.debug('fw.remove', path)
end
end
end
---@param callback async fun(ev: string, path: string)
function m.event(callback)
m._eventList[#m._eventList+1] = callback
end
function m._callEvent(ev, path)
for _, callback in ipairs(m._eventList) do
await.call(function ()
callback(ev, path)
end)
end
end
function m.update()
local collect
for _, watching in pairs(m._watchings) do
local watch = watching.watch
for _ = 1, 10000 do
local ev, path = watch:select()
if not ev then
break
end
log.debug('filewatch:', ev, path)
if not collect then
collect = {}
end
if ev == 'modify' then
collect[path] = (collect[path] or 0) | MODIFY
elseif ev == 'rename' then
collect[path] = (collect[path] or 0) | RENAME
end
end
end
if not collect or not next(collect) then
return
end
for path, flag in pairs(collect) do
if flag & RENAME ~= 0 then
if isExists(path) then
m._callEvent('create', path)
else
m._callEvent('delete', path)
end
elseif flag & MODIFY ~= 0 then
m._callEvent('change', path)
end
end
end
return m
|