diff --git a/Atom.xcodeproj/project.pbxproj b/Atom.xcodeproj/project.pbxproj index 5295f0171..fb7d28c0b 100644 --- a/Atom.xcodeproj/project.pbxproj +++ b/Atom.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 047F26021457978C006DC904 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 047F26011457978C006DC904 /* JavaScriptCore.framework */; }; 047F260414579792006DC904 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 047F260314579792006DC904 /* WebKit.framework */; }; 047F260E145883B9006DC904 /* Icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 047F260D145883B9006DC904 /* Icon.icns */; }; + EC846C4114B529120021AF1F /* FileSystemHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = EC846C4014B529120021AF1F /* FileSystemHelper.m */; }; ECBB172814A4F92400ACAAC1 /* AtomMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = ECBB172714A4F92400ACAAC1 /* AtomMenuItem.m */; }; /* End PBXBuildFile section */ @@ -70,6 +71,8 @@ 047F26011457978C006DC904 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; 047F260314579792006DC904 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; 047F260D145883B9006DC904 /* Icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Icon.icns; sourceTree = ""; }; + EC846C3F14B529120021AF1F /* FileSystemHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileSystemHelper.h; path = Classes/FileSystemHelper.h; sourceTree = ""; }; + EC846C4014B529120021AF1F /* FileSystemHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FileSystemHelper.m; path = Classes/FileSystemHelper.m; sourceTree = ""; }; ECBB172614A4F92400ACAAC1 /* AtomMenuItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AtomMenuItem.h; path = Classes/AtomMenuItem.h; sourceTree = ""; }; ECBB172714A4F92400ACAAC1 /* AtomMenuItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AtomMenuItem.m; path = Classes/AtomMenuItem.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -135,6 +138,8 @@ 043D7E93145795B70078D710 /* AtomController.m */, ECBB172614A4F92400ACAAC1 /* AtomMenuItem.h */, ECBB172714A4F92400ACAAC1 /* AtomMenuItem.m */, + EC846C3F14B529120021AF1F /* FileSystemHelper.h */, + EC846C4014B529120021AF1F /* FileSystemHelper.m */, 043D7E6B145795B20078D710 /* JSCocoa */, 043D7E79145795B20078D710 /* UKKQueue */, 043D7E52145794990078D710 /* Supporting Files */, @@ -287,6 +292,7 @@ 043D7E94145795B70078D710 /* AtomApp.m in Sources */, 043D7E95145795B70078D710 /* AtomController.m in Sources */, ECBB172814A4F92400ACAAC1 /* AtomMenuItem.m in Sources */, + EC846C4114B529120021AF1F /* FileSystemHelper.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Atom/Classes/AtomController.h b/Atom/Classes/AtomController.h index b13118b6e..af61cad1a 100644 --- a/Atom/Classes/AtomController.h +++ b/Atom/Classes/AtomController.h @@ -1,8 +1,7 @@ #import #import "JSCocoa.h" -@class JSCocoa; -@class WebView; +@class JSCocoa, WebView, FileSystemHelper; struct JSGlobalContextRef; @@ -11,6 +10,7 @@ struct JSGlobalContextRef; @property (assign) WebView *webView; @property (nonatomic, retain, readonly) NSString *url; @property (nonatomic, retain, readonly) NSString *bootstrapScript; +@property (nonatomic, retain, readonly) FileSystemHelper *fs; - (id)initForSpecs; - (id)initWithURL:(NSString *)url; @@ -20,7 +20,4 @@ struct JSGlobalContextRef; - (JSValueRefAndContextRef)jsWindow; - (void)performActionForMenuItemPath:(NSString *)menuItemPath; -- (void)contentsOfDirectoryAtPath:(NSString *)path recursive:(BOOL)recursive onComplete:(JSValueRefAndContextRef)jsFunction; - - @end diff --git a/Atom/Classes/AtomController.m b/Atom/Classes/AtomController.m index 2161a674e..45a7a59a0 100644 --- a/Atom/Classes/AtomController.m +++ b/Atom/Classes/AtomController.m @@ -1,17 +1,21 @@ #import "AtomController.h" -#import "AtomApp.h" #import "JSCocoa.h" #import #import +#import "AtomApp.h" +#import "FileSystemHelper.h" + @interface AtomController () @property (nonatomic, retain) JSCocoa *jscocoa; @property (nonatomic, retain, readwrite) NSString *url; @property (nonatomic, retain, readwrite) NSString *bootstrapScript; +@property (nonatomic, retain, readwrite) FileSystemHelper *fs; - (void)createWebView; - (void)blockUntilWebViewLoads; + @end @interface WebView (Atom) @@ -26,12 +30,14 @@ @synthesize jscocoa = _jscocoa; @synthesize url = _url; @synthesize bootstrapScript = _bootstrapScript; +@synthesize fs = _fs; - (void)dealloc { self.webView = nil; self.bootstrapScript = nil; self.url = nil; self.jscocoa = nil; + self.fs = nil; [super dealloc]; } @@ -108,8 +114,7 @@ return PROJECT_DIR; } -- (void)performActionForMenuItemPath:(NSString *)menuItemPath { - +- (void)performActionForMenuItemPath:(NSString *)menuItemPath { NSString *jsCode = [NSString stringWithFormat:@"window.performActionForMenuItemPath('%@')", menuItemPath]; [self.jscocoa evalJSString:jsCode]; } @@ -120,67 +125,6 @@ return windowWithContext; } -- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path recursive:(BOOL)recursive { - NSFileManager *fm = [NSFileManager defaultManager]; - NSMutableArray *paths = [NSMutableArray array]; - - if (recursive) { - NSDirectoryEnumerator *enumerator = [fm enumeratorAtPath:path]; - - NSString *subpath; - while (subpath = [enumerator nextObject]) { - [paths addObject:[path stringByAppendingPathComponent:subpath]]; - } - } else { - NSError *error = nil; - NSArray *subpaths = [fm contentsOfDirectoryAtPath:path error:&error]; - if (error) { - NSLog(@"ERROR %@", error.localizedDescription); - return nil; - } - for (NSString *subpath in subpaths) { - [paths addObject:[path stringByAppendingPathComponent:subpath]]; - } - } - - return paths; -} - -- (JSContextRef)ctx { - return self.jscocoa.ctx; -} - -- (JSValueRef)convertToJSArrayOfStrings:(NSArray *)nsArray { - JSValueRef *cArray = malloc(sizeof(JSValueRef) * nsArray.count); - for (int i = 0; i < nsArray.count; i++) { - JSStringRef jsString = JSStringCreateWithCFString((CFStringRef)[nsArray objectAtIndex:i]); - cArray[i] = JSValueMakeString(self.ctx, jsString); - JSStringRelease(jsString); - } - JSValueRef jsArray = (JSValueRef)JSObjectMakeArray(self.ctx, nsArray.count, cArray, NULL); - free(cArray); - return jsArray; -} - -- (void)contentsOfDirectoryAtPath:(NSString *)path recursive:(BOOL)recursive onComplete:(JSValueRefAndContextRef)onComplete { - dispatch_queue_t backgroundQueue = dispatch_get_global_queue(0, 0); - dispatch_queue_t mainQueue = dispatch_get_main_queue(); - - JSValueRef onCompleteFn = onComplete.value; - JSValueProtect(self.ctx, onCompleteFn); - - dispatch_async(backgroundQueue, ^{ - NSArray *paths = [self contentsOfDirectoryAtPath:path recursive:recursive]; - JSValueRef jsPaths = [self convertToJSArrayOfStrings:paths]; - - dispatch_sync(mainQueue, ^{ - JSValueRef args[] = { jsPaths }; - JSObjectCallAsFunction(self.ctx, JSValueToObject(self.ctx, onCompleteFn, NULL), NULL, 1, args, NULL); - JSValueUnprotect(self.ctx, onCompleteFn); - }); - }); -} - - (BOOL)isFile:(NSString *)path { BOOL isDir; BOOL exists; @@ -226,6 +170,7 @@ self.jscocoa = [[JSCocoa alloc] initWithGlobalContext:[frame globalContext]]; [self.jscocoa setObject:self withName:@"$atomController"]; [self.jscocoa setObject:self.bootstrapScript withName:@"$bootstrapScript"]; + self.fs = [[[FileSystemHelper alloc] initWithJSContextRef:(JSContextRef)self.jscocoa.ctx] autorelease]; } @end diff --git a/Atom/Classes/FileSystemHelper.h b/Atom/Classes/FileSystemHelper.h new file mode 100644 index 000000000..822e144e3 --- /dev/null +++ b/Atom/Classes/FileSystemHelper.h @@ -0,0 +1,12 @@ +#import +#import +#import "JSCocoa.h" + +@interface FileSystemHelper : NSObject { + JSContextRef _ctx; +} + +- (id)initWithJSContextRef:(JSContextRef)ctx; +- (void)contentsOfDirectoryAtPath:(NSString *)path recursive:(BOOL)recursive onComplete:(JSValueRefAndContextRef)jsFunction; + +@end diff --git a/Atom/Classes/FileSystemHelper.m b/Atom/Classes/FileSystemHelper.m new file mode 100644 index 000000000..a55768b12 --- /dev/null +++ b/Atom/Classes/FileSystemHelper.m @@ -0,0 +1,74 @@ +#import "FileSystemHelper.h" + +@interface FileSystemHelper () +- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path recursive:(BOOL)recursive; +- (JSValueRef)convertToJSArrayOfStrings:(NSArray *)nsArray; +@end + +@implementation FileSystemHelper + +- (id)initWithJSContextRef:(JSContextRef)ctx { + self = [super init]; + _ctx = ctx; + return self; +} + +- (void)contentsOfDirectoryAtPath:(NSString *)path recursive:(BOOL)recursive onComplete:(JSValueRefAndContextRef)onComplete { + dispatch_queue_t backgroundQueue = dispatch_get_global_queue(0, 0); + dispatch_queue_t mainQueue = dispatch_get_main_queue(); + + JSValueRef onCompleteFn = onComplete.value; + + JSValueProtect(_ctx, onCompleteFn); + + dispatch_async(backgroundQueue, ^{ + NSArray *paths = [self contentsOfDirectoryAtPath:path recursive:recursive]; + JSValueRef jsPaths = [self convertToJSArrayOfStrings:paths]; + + dispatch_sync(mainQueue, ^{ + JSValueRef args[] = { jsPaths }; + JSObjectCallAsFunction(_ctx, JSValueToObject(_ctx, onCompleteFn, NULL), NULL, 1, args, NULL); + JSValueUnprotect(_ctx, onCompleteFn); + }); + }); +} + +- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path recursive:(BOOL)recursive { + NSFileManager *fm = [NSFileManager defaultManager]; + NSMutableArray *paths = [NSMutableArray array]; + + if (recursive) { + NSDirectoryEnumerator *enumerator = [fm enumeratorAtPath:path]; + + NSString *subpath; + while (subpath = [enumerator nextObject]) { + [paths addObject:[path stringByAppendingPathComponent:subpath]]; + } + } else { + NSError *error = nil; + NSArray *subpaths = [fm contentsOfDirectoryAtPath:path error:&error]; + if (error) { + NSLog(@"ERROR %@", error.localizedDescription); + return nil; + } + for (NSString *subpath in subpaths) { + [paths addObject:[path stringByAppendingPathComponent:subpath]]; + } + } + + return paths; +} + +- (JSValueRef)convertToJSArrayOfStrings:(NSArray *)nsArray { + JSValueRef *cArray = malloc(sizeof(JSValueRef) * nsArray.count); + for (int i = 0; i < nsArray.count; i++) { + JSStringRef jsString = JSStringCreateWithCFString((CFStringRef)[nsArray objectAtIndex:i]); + cArray[i] = JSValueMakeString(_ctx, jsString); + JSStringRelease(jsString); + } + JSValueRef jsArray = (JSValueRef)JSObjectMakeArray(_ctx, nsArray.count, cArray, NULL); + free(cArray); + return jsArray; +} + +@end diff --git a/spec/atom/app-spec.coffee b/spec/atom/app-spec.coffee index bc685ea3c..09a4adda3 100644 --- a/spec/atom/app-spec.coffee +++ b/spec/atom/app-spec.coffee @@ -38,3 +38,4 @@ describe "App", -> expect(newWindow.rootView.editor.buffer.url).toBeUndefined expect(newWindow.rootView.editor.buffer.getText()).toBe "" + diff --git a/spec/atom/app-spec.js b/spec/atom/app-spec.js new file mode 100644 index 000000000..2e67a2fc2 --- /dev/null +++ b/spec/atom/app-spec.js @@ -0,0 +1,49 @@ +(function() { + var App, fs; + App = require('app'); + fs = require('fs'); + describe("App", function() { + var app; + app = null; + beforeEach(function() { + return app = new App(); + }); + afterEach(function() { + var window, _i, _len, _ref; + _ref = app.windows(); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + window = _ref[_i]; + window.close(); + } + return waitsFor(function() { + return app.windows().length === 0; + }); + }); + return describe("open", function() { + describe("when opening a filePath", function() { + return it("loads a buffer with filePath contents and displays it in a new window", function() { + var filePath, newWindow; + filePath = require.resolve('fixtures/sample.txt'); + expect(app.windows().length).toBe(0); + app.open(filePath); + expect(app.windows().length).toBe(1); + newWindow = app.windows()[0]; + expect(newWindow.rootView.editor.buffer.url).toEqual(filePath); + return expect(newWindow.rootView.editor.buffer.getText()).toEqual(fs.read(filePath)); + }); + }); + return describe("when opening a dirPath", function() { + return it("loads an empty buffer", function() { + var dirPath, newWindow; + dirPath = require.resolve('fixtures'); + expect(app.windows().length).toBe(0); + app.open(dirPath); + expect(app.windows().length).toBe(1); + newWindow = app.windows()[0]; + expect(newWindow.rootView.editor.buffer.url).toBeUndefined; + return expect(newWindow.rootView.editor.buffer.getText()).toBe(""); + }); + }); + }); + }); +}).call(this); diff --git a/src/stdlib/fs.coffee b/src/stdlib/fs.coffee index 1b18306cc..0249d44bc 100644 --- a/src/stdlib/fs.coffee +++ b/src/stdlib/fs.coffee @@ -89,7 +89,7 @@ module.exports = async: list: (path, recursive) -> deferred = $.Deferred() - $atomController.contentsOfDirectoryAtPath_recursive_onComplete path, recursive, (subpaths) -> + $atomController.fs.contentsOfDirectoryAtPath_recursive_onComplete path, recursive, (subpaths) -> deferred.resolve subpaths deferred