mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-11-11 04:48:44 +03:00
PathWatcher executes callback when path is changed
This commit is contained in:
parent
3f97d98ef9
commit
5aed1b012a
@ -78,6 +78,7 @@
|
||||
0487D15D14FEE6D60045E5E3 /* libcef_dll_wrapper2.cc in Sources */ = {isa = PBXBuildFile; fileRef = 0487D12514FEE6D60045E5E3 /* libcef_dll_wrapper2.cc */; };
|
||||
0487D15F14FEE7880045E5E3 /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 0487D15E14FEE7880045E5E3 /* Resources */; };
|
||||
0487D16014FEE78E0045E5E3 /* Resources in Copy Chrome Resources */ = {isa = PBXBuildFile; fileRef = 0487D15E14FEE7880045E5E3 /* Resources */; };
|
||||
048A2FC7154870DC0051715C /* PathWatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 048A2FC6154870DC0051715C /* PathWatcher.m */; };
|
||||
04E1DDDD152A0941001A9D07 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04E1DDDC152A0941001A9D07 /* Sparkle.framework */; };
|
||||
04E1DDDF152A09C3001A9D07 /* Sparkle.framework in Copy Sparkle Framework */ = {isa = PBXBuildFile; fileRef = 04E1DDDC152A0941001A9D07 /* Sparkle.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
@ -299,6 +300,8 @@
|
||||
0487D12414FEE6D60045E5E3 /* libcef_dll_wrapper.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libcef_dll_wrapper.cc; sourceTree = "<group>"; };
|
||||
0487D12514FEE6D60045E5E3 /* libcef_dll_wrapper2.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libcef_dll_wrapper2.cc; sourceTree = "<group>"; };
|
||||
0487D15E14FEE7880045E5E3 /* Resources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Resources; sourceTree = "<group>"; };
|
||||
048A2FC5154870DC0051715C /* PathWatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PathWatcher.h; sourceTree = "<group>"; };
|
||||
048A2FC6154870DC0051715C /* PathWatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PathWatcher.m; sourceTree = "<group>"; };
|
||||
04E1DDDC152A0941001A9D07 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = frameworks/Sparkle.framework; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -405,6 +408,8 @@
|
||||
0487C93E14FED6090045E5E3 /* AtomController.h */,
|
||||
0487C93F14FED6090045E5E3 /* AtomController.mm */,
|
||||
042180E614FF080D00DF25EA /* BrowserDelegate.h */,
|
||||
048A2FC5154870DC0051715C /* PathWatcher.h */,
|
||||
048A2FC6154870DC0051715C /* PathWatcher.m */,
|
||||
0487C94014FED6090045E5E3 /* client_handler.h */,
|
||||
0487C94114FED6090045E5E3 /* client_handler.mm */,
|
||||
0487C94314FED6090045E5E3 /* native_handler.h */,
|
||||
@ -758,6 +763,7 @@
|
||||
0487CD9614FEE1360045E5E3 /* client_handler.mm in Sources */,
|
||||
0487CD9714FEE1380045E5E3 /* main.mm in Sources */,
|
||||
0487CD9814FEE13B0045E5E3 /* native_handler.mm in Sources */,
|
||||
048A2FC7154870DC0051715C /* PathWatcher.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
12
Atom/src/PathWatcher.h
Normal file
12
Atom/src/PathWatcher.h
Normal file
@ -0,0 +1,12 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef void (^WatchCallback)(NSArray *);
|
||||
|
||||
@interface PathWatcher : NSObject {
|
||||
int _kq;
|
||||
NSMutableDictionary *_callbacksByFileDescriptor;
|
||||
}
|
||||
|
||||
+ (void)watchPath:(NSString *)path callback:(WatchCallback)callback;
|
||||
|
||||
@end
|
103
Atom/src/PathWatcher.m
Normal file
103
Atom/src/PathWatcher.m
Normal file
@ -0,0 +1,103 @@
|
||||
#import "PathWatcher.h"
|
||||
|
||||
#import <sys/event.h>
|
||||
#import <sys/time.h>
|
||||
#import <fcntl.h>
|
||||
|
||||
@interface PathWatcher ()
|
||||
- (void)watchPath:(NSString *)path callback:(WatchCallback)callback;
|
||||
@end
|
||||
|
||||
@implementation PathWatcher
|
||||
|
||||
+ (void)watchPath:(NSString *)path callback:(WatchCallback)callback {
|
||||
static PathWatcher *pathWatcher;
|
||||
|
||||
if (!pathWatcher) pathWatcher = [[PathWatcher alloc] init];
|
||||
[pathWatcher watchPath:path callback:callback];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
close(_kq);
|
||||
for (NSNumber *fdNumber in [_callbacksByFileDescriptor allKeys]) {
|
||||
close([fdNumber intValue]);
|
||||
}
|
||||
[_callbacksByFileDescriptor release];
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
|
||||
_callbacksByFileDescriptor = [[NSMutableDictionary alloc] init];
|
||||
_kq = kqueue();
|
||||
|
||||
if (_kq == -1) {
|
||||
[NSException raise:@"Could not create kqueue" format:nil];
|
||||
}
|
||||
|
||||
[self performSelectorInBackground:@selector(watch) withObject:NULL];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)watchPath:(NSString *)path callback:(WatchCallback)callback {
|
||||
struct timespec timeout = { 0, 0 };
|
||||
struct kevent event;
|
||||
int fd = open([path fileSystemRepresentation], O_EVTONLY, 0);
|
||||
|
||||
if (fd >= 0) {
|
||||
int filter = EVFILT_VNODE;
|
||||
int flags = EV_ADD | EV_ENABLE | EV_CLEAR;
|
||||
int filterFlags = NOTE_ATTRIB | NOTE_WRITE | NOTE_EXTEND | NOTE_RENAME | NOTE_DELETE;
|
||||
|
||||
EV_SET(&event, fd, filter, flags, filterFlags, 0, (void *)path);
|
||||
|
||||
@synchronized(self) {
|
||||
NSNumber *fdNumber = [NSNumber numberWithInt:fd];
|
||||
NSMutableArray *callbacks = [_callbacksByFileDescriptor objectForKey:fdNumber];
|
||||
if (!callbacks) {
|
||||
callbacks = [NSMutableArray array];
|
||||
[_callbacksByFileDescriptor setObject:callbacks forKey:fdNumber];
|
||||
}
|
||||
[callbacks addObject:callback];
|
||||
|
||||
kevent(_kq, &event, 1, NULL, 0, &timeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)watch {
|
||||
@autoreleasepool {
|
||||
struct kevent event;
|
||||
struct timespec timeout = { 5, 0 }; // 5 seconds timeout.
|
||||
|
||||
while (true) {
|
||||
int numberOfEvents = kevent(_kq, NULL, 0, &event, 1, &timeout);
|
||||
|
||||
if (numberOfEvents < 0) {
|
||||
[NSException raise:@"KQueue Error" format:@"error %d", numberOfEvents, nil];
|
||||
}
|
||||
if (numberOfEvents == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSMutableArray *eventFlags = [NSMutableArray array];
|
||||
|
||||
if (event.fflags & NOTE_WRITE) {
|
||||
[eventFlags addObject:@"modified"];
|
||||
}
|
||||
|
||||
@synchronized(self) {
|
||||
NSNumber *fdNumber = [NSNumber numberWithInt:event.ident];
|
||||
for (WatchCallback callback in [_callbacksByFileDescriptor objectForKey:fdNumber]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
callback(eventFlags);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[self release];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
@ -3,6 +3,7 @@
|
||||
#import "Atom.h"
|
||||
#import "AtomController.h"
|
||||
#import "client_handler.h"
|
||||
#import "PathWatcher.h"
|
||||
|
||||
NSString *stringFromCefV8Value(const CefRefPtr<CefV8Value>& value) {
|
||||
std::string cc_value = value->GetStringValue().ToString();
|
||||
@ -12,7 +13,7 @@ NSString *stringFromCefV8Value(const CefRefPtr<CefV8Value>& value) {
|
||||
NativeHandler::NativeHandler() : CefV8Handler() {
|
||||
m_object = CefV8Value::CreateObject(NULL);
|
||||
|
||||
const char *functionNames[] = {"exists", "read", "write", "absolute", "list", "isFile", "isDirectory", "remove", "asyncList", "open", "openDialog", "quit", "writeToPasteboard", "readFromPasteboard", "showDevTools", "newWindow", "saveDialog", "exit"};
|
||||
const char *functionNames[] = {"exists", "read", "write", "absolute", "list", "isFile", "isDirectory", "remove", "asyncList", "open", "openDialog", "quit", "writeToPasteboard", "readFromPasteboard", "showDevTools", "newWindow", "saveDialog", "exit", "watchPath"};
|
||||
NSUInteger arrayLength = sizeof(functionNames) / sizeof(const char *);
|
||||
for (NSUInteger i = 0; i < arrayLength; i++) {
|
||||
const char *functionName = functionNames[i];
|
||||
@ -271,6 +272,34 @@ bool NativeHandler::Execute(const CefString& name,
|
||||
exit(exitStatus);
|
||||
return true;
|
||||
}
|
||||
else if (name == "watchPath") {
|
||||
NSString *path = stringFromCefV8Value(arguments[0]);
|
||||
CefRefPtr<CefV8Value> function = arguments[1];
|
||||
|
||||
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
|
||||
|
||||
WatchCallback callback = ^(NSArray *eventList) {
|
||||
context->Enter();
|
||||
|
||||
CefV8ValueList args;
|
||||
CefRefPtr<CefV8Value> retval;
|
||||
CefRefPtr<CefV8Exception> e;
|
||||
|
||||
CefRefPtr<CefV8Value> eventObject = CefV8Value::CreateObject(NULL);
|
||||
for (NSString *event in eventList) {
|
||||
eventObject->SetValue([event UTF8String], CefV8Value::CreateBool(true), V8_PROPERTY_ATTRIBUTE_NONE);
|
||||
}
|
||||
|
||||
args.push_back(eventObject);
|
||||
function->ExecuteFunction(function, args, retval, e, true);
|
||||
|
||||
context->Exit();
|
||||
};
|
||||
|
||||
[PathWatcher watchPath:path callback:[[callback copy] autorelease]];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
@ -260,6 +260,7 @@ describe "TreeView", ->
|
||||
temporaryFilePath = fs.join(require.resolve('fixtures'), 'temporary')
|
||||
|
||||
afterEach ->
|
||||
console.log "REMOVE" if fs.exists(temporaryFilePath)
|
||||
fs.remove(temporaryFilePath) if fs.exists(temporaryFilePath)
|
||||
|
||||
describe "when a file is added or removed in an expanded directory", ->
|
||||
@ -268,8 +269,12 @@ describe "TreeView", ->
|
||||
|
||||
fs.write temporaryFilePath, 'hi'
|
||||
|
||||
expect(rootDirectoryView.entries.find('.entry').length).toBe entriesCountBefore + 1
|
||||
expect(rootDirectoryView.entries.find('.file:contains(temporary)')).toExist()
|
||||
waitsFor "file to be added", ->
|
||||
rootDirectoryView.entries.find('.entry').length == entriesCountBefore + 1
|
||||
|
||||
runs ->
|
||||
expect(rootDirectoryView.entries.find('.entry').length).toBe entriesCountBefore + 1
|
||||
expect(rootDirectoryView.entries.find('.file:contains(temporary)')).toExist()
|
||||
|
||||
describe "when a file is renamed in an expanded directory", ->
|
||||
|
||||
|
@ -34,4 +34,3 @@ class Directory
|
||||
$native.unwatchPath(@path)
|
||||
|
||||
_.extend Directory.prototype, EventEmitter
|
||||
|
||||
|
@ -119,7 +119,8 @@ class DirectoryView extends View
|
||||
@isExpanded = false
|
||||
|
||||
watchEntries: ->
|
||||
@directory.on "contents-change.#{@directory.path}", => @buildEntries()
|
||||
@directory.on "contents-change.#{@directory.path}", =>
|
||||
@buildEntries()
|
||||
|
||||
unwatchEntries: ->
|
||||
@directory.off ".#{@directory.path}"
|
||||
|
Loading…
Reference in New Issue
Block a user